Skip to content

#125 refuse the empty hostname in the pattern matcher - #132

Merged
matthewdevenny merged 2 commits into
mainfrom
matt/125-empty-hostname-wildcard
Sep 17, 2026
Merged

matthewdevenny merged 2 commits into
mainfrom
matt/125-empty-hostname-wildcard

Conversation

@matthewdevenny

@matthewdevenny matthewdevenny commented Sep 16, 2026 •

Copy link
Copy Markdown
Contributor

Closes #125.

Symptom

A pre-existing connection whose IP has no reverse-DNS name is meant to be kept alive on the ports it was observed using. With a * or ** hostname rule anywhere in the policy it took that rule's ports instead, so a flow on 443 was allowed on 123 and dropped mid-stream a minute later. The same branch pinned L7 scope to an empty hostname.

Cause

gateExistingConnections passes "" (the no-reverse-name result) to MatchHostnameRule. strings.Split("", ".") yields one empty label, and both wildcard forms happily consume one label, so verdict.HasAllow() was true and the observed-ports carve-out never ran.

Fix

hostnamePattern.Matches now returns false for any hostname with an empty label, at the boundary, the same way compileHostnamePattern rejects empty segments. "" is never a DNS label, so no segment may consume it; matchSegments stays a plain glob over well-formed labels and only picks up a loop-bound cleanup. This is done in the matcher rather than at the call site because MatchHostnameRule("") is reachable from other paths (a root DNS query trims to "" too) and "" is never a real name.

Tests

  • TestHostnamePatternMatches: *, **, *.x, **.x, and a literal all reject ""; ** rejects interior and trailing empty labels.
  • TestMatchHostnameRule_Table: a query: "" row against a bare ** allow returns the empty verdict.
  • TestGateExistingConnections_UnresolvableIgnoresWildcardRule: the issue's exact scenario. A ** allow on 123 plus an unresolvable IP observed on 443 yields an allow on 443 only and no L7 scope.

The table row, the wildcard pattern cases, and the gate test all fail against main's matcher. Full go test ./... is green in the Lima VM on the CI kernel; gofumpt, vet, and staticcheck are clean.

strings.Split("", ".") yields one empty label, which both `*` and `**`
consumed, so an IP with no reverse name matched any wildcard rule.
gateExistingConnections then opened it on the rule's ports instead of
its observed ports (dropping the in-flight flow mid-stream a minute
later) and pinned L7 scope to an empty name.

matchSegments now leaves an empty label unmatched: "" is never a DNS
label, so no segment may consume it. That makes "" match nothing at
every entry point (MatchHostnameRule, FindTrackedHostname,
GetAutoAllowedTypeForHostname) and also stops `**` from bridging a
stray empty label ("a..b", "github.com.").

Signed-off-by: Matthew DeVenny <matt@codecargo.com>
Copilot AI lite review requested due to automatic review settings September 16, 2026 17:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Prevents wildcard hostname rules from matching empty or unresolvable hostnames, preserving observed-port handling for existing connections.

Changes:

  • Rejects empty labels during hostname matching.
  • Adds matcher and configuration regression tests.
  • Adds an existing-connection gating test.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
pkg/config/pattern.go Rejects empty hostname labels.
pkg/config/pattern_test.go Tests wildcard and empty-label behavior.
pkg/config/config_test.go Tests configuration matching and attribution.
cmd/start_test.go Tests observed-port preservation for unresolvable IPs.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pkg/config/config_test.go Outdated
Review: the empty-label refusal belongs at the edge, the same way
compileHostnamePattern rejects empty segments, so matchSegments stays a
plain glob over well-formed labels. Matches now returns false for any
hostname with an empty label; the DP loses its special case and keeps
only the loop-bound cleanup.

The appended MatchHostnameRule test asserted two APIs that cannot
observe the bug (FindTrackedHostname attributes patterns to the query
name, so "" in is "" out either way; GetAutoAllowedTypeForHostname
skips every rule LoadConfigFromRules produces). Replaced by one
query-"" row in TestMatchHostnameRule_Table.

Signed-off-by: Matthew DeVenny <matt@codecargo.com>
@matthewdevenny
matthewdevenny merged commit 8162e25 into main Sep 17, 2026
25 checks passed
@matthewdevenny
matthewdevenny deleted the matt/125-empty-hostname-wildcard branch September 17, 2026 15:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

* and ** match the empty hostname: unresolvable pre-existing connections take a wildcard rule's ports instead of their observed ports

2 participants