Skip to content

docs: anchor regex origin examples to avoid lookalike matches (#408)#412

Open
SAY-5 wants to merge 1 commit intoexpressjs:masterfrom
SAY-5:docs-regex-origin-anchor
Open

docs: anchor regex origin examples to avoid lookalike matches (#408)#412
SAY-5 wants to merge 1 commit intoexpressjs:masterfrom
SAY-5:docs-regex-origin-anchor

Conversation

@SAY-5
Copy link
Copy Markdown

@SAY-5 SAY-5 commented Apr 15, 2026

Closes #408.

The README's documented regex example for the `origin` option was `/example.com$/` with the explanation:

the pattern `/example.com$/` will reflect any request that is coming from an origin ending with "example.com"

The problem is that the `Origin` header sent by browsers includes the scheme (e.g. `https://example.com\`), so that pattern matches any origin whose string ends with `example.com`. That includes attacker-controlled lookalikes:

Origin Matched by `/example\.com$/`
`https://example.com\` yes (intended)
`https://sub.example.com\` yes (intended)
`https://evil-example.com\` yes (unintended)
`https://notexample.com\` yes (unintended)

An attacker only has to register `evil-example.com` to get CORS access to any app that copied the example verbatim — this is a full CORS bypass, and on any app using `credentials: true` it lets the attacker's origin read authenticated responses.

The repo's own test suite already uses the right pattern (`test/test.js:184`):
```js
{ origin: /:\/\/(.+\.)?example.com$/ }
```

This PR updates the README to teach a scheme-anchored form:

```js
/^https?:\/\/(.+\.)?example.com$/
```

which matches `https://example.com\` and its subdomains but rejects `evil-example.com` / `notexample.com`. Also anchored the array-form example for consistency and added a short note explaining why a trailing-only anchor is unsafe so future readers don't regress to the original pattern.

Docs only — no code changes.

…sjs#408)

Closes expressjs#408.

The README's regex example for the `origin` option was /example\\.com$/,
which matches any origin whose string ends with example.com. Because the
browser's Origin header includes the scheme (https://example.com), that
pattern also matches attacker-controlled lookalikes like
https://evil-example.com and https://notexample.com — the attacker
registers evil-example.com and immediately gets CORS access to any app
that copied the example verbatim, which is a CORS bypass especially when
credentials: true is set.

The fix is to teach an anchored form that matches only origins that
actually start with the scheme and end at example.com:

  /^https?:\\/\\/(.+\\.)?example\\.com$/

That is the pattern the repo's own test suite already uses
(test/test.js:184) to verify regex origins, so callers copying the
README example now match the behavior the library itself is tested
against. Also anchored the array-form example for consistency and
added a short note explaining why a trailing-only anchor is unsafe.
Docs only — no code changes.
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.

Security: README regex example allows unintended origin matching

1 participant