Skip to content

fix(v2): prevent origin bypass via suffix match in wildcardPatternToRegex#5074

Open
leaanthony wants to merge 1 commit intomasterfrom
fix/origin-validator-bypass
Open

fix(v2): prevent origin bypass via suffix match in wildcardPatternToRegex#5074
leaanthony wants to merge 1 commit intomasterfrom
fix/origin-validator-bypass

Conversation

@leaanthony
Copy link
Copy Markdown
Member

@leaanthony leaanthony commented Mar 26, 2026

Summary

  • Fixes GHSA-47hv-j4px-h3c9wildcardPatternToRegex allowed origin bypass via suffix match in BindingsAllowedOrigins
  • Wildcards (*) are now only expanded when preceded by a separator (://, ., or :), ensuring they match full domain components only
  • A trailing wildcard on a partial label (e.g. com*) is stripped, preventing https://myapp.community from matching https://myapp.com*
  • Adds comprehensive test suite for the origin validator (previously untested)

Risk Assessment

  • No breaking change for correctly-configured patterns — https://*.myapp.com works identically
  • No dependency changes
  • Only "breaks" patterns that were the vulnerability vector (e.g. myapp.com* matching myapp.community)

Test plan

  • All 12 new test cases pass covering: subdomain wildcards, trailing wildcard rejection, exact match, port/path separator boundaries, empty origin rejection
  • CI green on all platforms

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved origin validation security by restricting wildcard pattern expansion to domain boundaries, preventing wildcards from matching across domain separators.
  • Tests

    • Added comprehensive test coverage for wildcard boundary validation behavior.

…egex

Wildcards in BindingsAllowedOrigins patterns are now only expanded when
they represent a full domain component (after "://", ".", or ":").
A wildcard appended to a partial label (e.g. "com*") is stripped,
preventing attacker-controlled domains like "myapp.community" from
bypassing origin checks when "https://myapp.com*" is configured.

Fixes GHSA-47hv-j4px-h3c9

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings March 26, 2026 09:34
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

The changes refine wildcard pattern matching in origin validation by restricting * expansion to only occur after separators (://, ., :), using character-class constraints to prevent cross-boundary matching. Any * that does not follow a separator is stripped entirely, preventing patterns like com* from matching incorrectly across domain boundaries.

Changes

Cohort / File(s) Summary
Wildcard Pattern Logic
v2/internal/frontend/originvalidator/originValidator.go
Updated wildcardPatternToRegex function to restrict * expansion to occur only when preceded by separators (://, ., :), using ordered replacements with character-class constraints ([^.:/]{0,}). Remaining unmatched wildcards are stripped entirely.
Wildcard Boundary Test Suite
v2/internal/frontend/originvalidator/originValidator_test.go
Added new test TestWildcardPatternDoesNotCrossDomainBoundaries with parametrized test cases covering subdomain wildcards, rejection of trailing-wildcard bypass attempts, exact-match correctness, port/path separator enforcement, and empty-origin handling. Includes helper function mustParseURL for test setup.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Wildcards once roamed freely, crossing all bounds,
But now they respect the domain's sacred grounds!
Separators are guardians—dots, colons, slashes too,
Keeping wild patterns from mischief they'd do. ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description is comprehensive and well-structured but missing several required template sections like type of change checkboxes, test platform checkboxes, and changelog update confirmation. Complete the missing checklist sections (type of change, platforms tested, changelog update) to fully align with the repository template requirements.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main security fix: preventing origin bypass via suffix matching in wildcardPatternToRegex.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/origin-validator-bypass

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 OpenGrep (1.16.5)
v2/internal/frontend/originvalidator/originValidator.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m

v2/internal/frontend/originvalidator/originValidator_test.go

┌──────────────┐
│ Opengrep CLI │
└──────────────┘

�[32m✔�[39m �[1mOpengrep OSS�[0m
�[32m✔�[39m Basic security coverage for first-party code vulnerabilities.

�[1m Loading rules from local config...�[0m


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
v2/internal/frontend/originvalidator/originValidator.go (1)

95-105: Solid implementation of separator-anchored wildcard expansion.

The ordered replacements correctly handle overlapping patterns (:// before :) and the {0,} quantifier avoids collision with the cleanup step. The character class [^.:/] effectively prevents cross-boundary matching.

Consider documenting the single-level subdomain constraint.

The [^.:/] constraint means https://*.myapp.com matches https://api.myapp.com but not https://a.b.myapp.com. Users requiring multi-level subdomain matching would need https://*.*.myapp.com. This is a reasonable security-first tradeoff, but adding a brief note in the docstring (lines 80-85) would help users configure patterns correctly.

📝 Optional: Add documentation note about multi-level subdomains
 // wildcardPatternToRegex converts wildcard pattern to regex.
 //
 // Wildcards are only expanded when they represent a full domain component,
 // i.e. immediately after "://", ".", or ":". A wildcard appended to a
 // partial label (e.g. "com*") is stripped so that it cannot match across
 // domain boundaries (GHSA-47hv-j4px-h3c9).
+//
+// Note: A single wildcard matches a single domain level only. For example,
+// "https://*.myapp.com" matches "https://api.myapp.com" but not
+// "https://a.b.myapp.com". Use "https://*.*.myapp.com" for multi-level matching.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v2/internal/frontend/originvalidator/originValidator.go` around lines 95 -
105, Add a brief docstring note in the originvalidator package (near the comment
that introduces the wildcard expansion) explaining that the regex uses [^.:/] to
anchor wildcards to a single path/host component so patterns like
"https://*.myapp.com" match "https://api.myapp.com" but not
"https://a.b.myapp.com"; mention that multi-level subdomains must be expressed
explicitly (e.g., "https://*.*.myapp.com"). Reference the code performing the
replacements on the variable escaped (the "//*", "\\.*", ":*" and trailing "*"
cleanup) so readers can see why the single-level constraint exists.
v2/internal/frontend/originvalidator/originValidator_test.go (1)

16-94: Good coverage of the vulnerability vectors.

The test cases effectively validate the security fix, covering trailing wildcard bypass attempts, exact matching, and boundary separators.

Consider adding these edge cases for completeness:

  1. Multi-level subdomain non-match — Verifies the single-level constraint is enforced:
{
    name:           "subdomain wildcard does not match nested subdomain",
    allowedOrigins: "https://*.myapp.com",
    origin:         "https://a.b.myapp.com",
    expected:       false,
},
  1. Port wildcard — Verifies port-level wildcard works correctly:
{
    name:           "port wildcard matches different port",
    allowedOrigins: "https://localhost:*",
    origin:         "https://localhost:8080",
    expected:       true,
},
  1. Base domain with subdomain wildcard — Verifies *.domain requires an actual subdomain:
{
    name:           "subdomain wildcard does not match base domain",
    allowedOrigins: "https://*.myapp.com",
    origin:         "https://myapp.com",
    expected:       false,
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@v2/internal/frontend/originvalidator/originValidator_test.go` around lines 16
- 94, Add three edge-case table entries to
TestWildcardPatternDoesNotCrossDomainBoundaries to cover multi-level subdomain
rejection, port wildcard matching, and that a subdomain wildcard does not match
the base domain: add a test case named "subdomain wildcard does not match nested
subdomain" with allowedOrigins "https://*.myapp.com", origin
"https://a.b.myapp.com", expected false; add "port wildcard matches different
port" with allowedOrigins "https://localhost:*", origin
"https://localhost:8080", expected true; and add "subdomain wildcard does not
match base domain" with allowedOrigins "https://*.myapp.com", origin
"https://myapp.com", expected false so the
TestWildcardPatternDoesNotCrossDomainBoundaries table covers these edge cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@v2/internal/frontend/originvalidator/originValidator_test.go`:
- Around line 16-94: Add three edge-case table entries to
TestWildcardPatternDoesNotCrossDomainBoundaries to cover multi-level subdomain
rejection, port wildcard matching, and that a subdomain wildcard does not match
the base domain: add a test case named "subdomain wildcard does not match nested
subdomain" with allowedOrigins "https://*.myapp.com", origin
"https://a.b.myapp.com", expected false; add "port wildcard matches different
port" with allowedOrigins "https://localhost:*", origin
"https://localhost:8080", expected true; and add "subdomain wildcard does not
match base domain" with allowedOrigins "https://*.myapp.com", origin
"https://myapp.com", expected false so the
TestWildcardPatternDoesNotCrossDomainBoundaries table covers these edge cases.

In `@v2/internal/frontend/originvalidator/originValidator.go`:
- Around line 95-105: Add a brief docstring note in the originvalidator package
(near the comment that introduces the wildcard expansion) explaining that the
regex uses [^.:/] to anchor wildcards to a single path/host component so
patterns like "https://*.myapp.com" match "https://api.myapp.com" but not
"https://a.b.myapp.com"; mention that multi-level subdomains must be expressed
explicitly (e.g., "https://*.*.myapp.com"). Reference the code performing the
replacements on the variable escaped (the "//*", "\\.*", ":*" and trailing "*"
cleanup) so readers can see why the single-level constraint exists.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 17e09bc8-a9ea-42fd-82fa-d9c39e711757

📥 Commits

Reviewing files that changed from the base of the PR and between 25d02ee and 35437f8.

📒 Files selected for processing (2)
  • v2/internal/frontend/originvalidator/originValidator.go
  • v2/internal/frontend/originvalidator/originValidator_test.go

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Addresses a security issue in the v2 frontend origin validation used by BindingsAllowedOrigins, preventing wildcard patterns from being abused as suffix matches to bypass origin restrictions (GHSA-47hv-j4px-h3c9).

Changes:

  • Tightens wildcardPatternToRegex so * only expands when used as a full component (after ://, ., or :) and strips other * usage to prevent suffix-based origin bypass.
  • Adds a new table-driven unit test suite covering wildcard boundary behavior, trailing-wildcard rejection, exact matches, and empty-origin rejection.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
v2/internal/frontend/originvalidator/originValidator.go Updates wildcard-to-regex conversion to prevent suffix-based origin bypass.
v2/internal/frontend/originvalidator/originValidator_test.go Adds unit tests validating the new wildcard/origin matching behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

// component. Order matters: handle :// before : to avoid partial overlap.
// Use {0,} instead of * as the regex quantifier to avoid collision with
// the literal * cleanup in the next step.
escaped = strings.ReplaceAll(escaped, "//*", "//[^.:/]{0,}")
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The comment says wildcards are expanded only immediately after "://", but the implementation replaces the substring "//". That also expands wildcards after any "//" (e.g., in a path) and makes the following note about handling "://" before ":" a bit misleading. Consider changing the replacement to target exactly "://" (or "http(s)://*") to match the documented rule, or adjust the comment to reflect the broader matching behavior.

Suggested change
escaped = strings.ReplaceAll(escaped, "//*", "//[^.:/]{0,}")
escaped = strings.ReplaceAll(escaped, "://*", "://[^.:/]{0,}")

Copilot uses AI. Check for mistakes.
Comment on lines +16 to +23
func TestWildcardPatternDoesNotCrossDomainBoundaries(t *testing.T) {
tests := []struct {
name string
allowedOrigins string
origin string
expected bool
}{
// Legitimate subdomain wildcard — should still work
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

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

The PR description mentions 12 new test cases, but this table-driven test currently defines 11 entries. Either add the missing case or update the description so it matches what was actually added.

Copilot uses AI. Check for mistakes.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying wails with  Cloudflare Pages  Cloudflare Pages

Latest commit: 35437f8
Status: ✅  Deploy successful!
Preview URL: https://d46166fd.wails.pages.dev
Branch Preview URL: https://fix-origin-validator-bypass.wails.pages.dev

View logs

Copy link
Copy Markdown

@Saku0512 Saku0512 left a comment

Choose a reason for hiding this comment

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

Thanks for the fix! The implementation looks correct — restricting wildcard
expansion to separator boundaries and stripping trailing wildcards properly
addresses the bypass.

One minor note: the inline comment says wildcards are only expanded
"immediately after ://" but the implementation also handles "." and ":"
separators. The comment should be updated to reflect that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants