-
Notifications
You must be signed in to change notification settings - Fork 92
fix(#6844): support wildcard hostnames in SSRF hook egress allowlist #6845
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -136,11 +136,36 @@ def _parse_egress_allowlist() -> set[tuple[str, int]]: | |
| if not entry: | ||
| continue | ||
| if "*" in entry: | ||
| print( | ||
| f"WARNING: wildcard entry '{entry}' in FULLSEND_EGRESS_ALLOWLIST " | ||
| "is not supported and will be ignored — use exact hostnames", | ||
| file=sys.stderr, | ||
| ) | ||
| # Accept leading wildcard entries like *.domain.com:port. | ||
| # Reject bare '*' and mid-string globs (e.g. 'atl*an.net'). | ||
| if ":" in entry: | ||
| wc_host, _, wc_port_str = entry.rpartition(":") | ||
| try: | ||
| wc_port = int(wc_port_str) | ||
| except ValueError: | ||
| print( | ||
| f"WARNING: malformed port in '{entry}' in FULLSEND_EGRESS_ALLOWLIST " | ||
| "— entry ignored", | ||
| file=sys.stderr, | ||
| ) | ||
| continue | ||
| else: | ||
| wc_host = entry | ||
| wc_port = 0 | ||
| wc_host_clean = wc_host.lower().rstrip(".") | ||
| if ( | ||
| wc_host_clean.startswith("*.") | ||
| and len(wc_host_clean) > 2 | ||
| and "*" not in wc_host_clean[2:] | ||
| ): | ||
| entries.add((wc_host_clean, wc_port)) | ||
| else: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [low] edge-case An entry like ..:443 passes validation (startswith('.') is true, len > 2 is true, no extra '' in wc_host[2:]) but after rstrip('.') the stored tuple becomes ('', 443). This entry is inert and not a security concern, but it silently accepts a malformed entry without warning, inconsistent with the explicit rejection of other malformed wildcards. Suggested fix: Move the rstrip('.') call before the validation check, or validate after stripping: wc_host_clean = wc_host.lower().rstrip('.') then check wc_host_clean.startswith('*.') and len(wc_host_clean) > 2. |
||
| print( | ||
| f"WARNING: wildcard entry '{entry}' in FULLSEND_EGRESS_ALLOWLIST " | ||
| "is not supported and will be ignored — use exact hostnames " | ||
| "or leading wildcard patterns like *.domain.com", | ||
| file=sys.stderr, | ||
| ) | ||
| continue | ||
| if ":" in entry: | ||
| host, _, port_str = entry.rpartition(":") | ||
|
|
@@ -173,7 +198,20 @@ def _is_host_allowlisted(hostname: str, port: int | None) -> bool: | |
| if port is not None and (hostname, port) in allowlist: | ||
| return True | ||
| # Check host-only match (port 0 sentinel means any port). | ||
| return (hostname, 0) in allowlist | ||
| if (hostname, 0) in allowlist: | ||
| return True | ||
| # Check wildcard entries: *.domain matches any subdomain of domain | ||
| # but not the bare domain itself (require at least one subdomain label). | ||
| for entry_host, entry_port in allowlist: | ||
| if not entry_host.startswith("*."): | ||
| continue | ||
| # *.atlassian.net → suffix ".atlassian.net" | ||
| suffix = entry_host[1:] | ||
| if hostname.endswith(suffix) and ( | ||
| entry_port == 0 or (port is not None and entry_port == port) | ||
| ): | ||
| return True | ||
| return False | ||
|
|
||
|
|
||
| def log_finding(scanner: str, name: str, severity: str, detail: str, action: str): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[low] authorization/allowlist-scope
The wildcard parser accepts broad TLD-level patterns like *.com:443 or *.net:443, which would allowlist egress to every subdomain under that TLD during DNS failures. While FULLSEND_EGRESS_ALLOWLIST is operator-controlled infrastructure (not user-facing), there is no minimum label-depth validation to prevent accidentally over-broad entries. A misconfigured *.com entry would effectively disable SSRF fail-closed behavior for the entire .com namespace.
Suggested fix: Consider requiring at least two labels after the wildcard (e.g., reject *.com but accept *.example.com) by checking wc_host_clean.count('.') >= 2 before accepting the entry, or by emitting a loud warning for single-dot patterns.