From db156aaa128f9eebdf5023eba824080d009e4d6b Mon Sep 17 00:00:00 2001 From: SAY-5 Date: Wed, 15 Apr 2026 02:26:41 -0700 Subject: [PATCH] docs: anchor regex origin examples to avoid lookalike matches (#408) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #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. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index bd6ebbc..36888ae 100644 --- a/README.md +++ b/README.md @@ -223,8 +223,8 @@ app.listen(80, function () { - `String` - set `origin` to a specific origin. For example, if you set it to - `"http://example.com"` only requests from "http://example.com" will be allowed. - `"*"` for all domains to be allowed. - - `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. For example the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com". - - `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com". + - `RegExp` - set `origin` to a regular expression pattern which will be used to test the request origin. If it's a match, the request origin will be reflected. The origin header includes the scheme, so the pattern must anchor at the start of the origin to avoid unintended matches — for example `/^https?:\/\/(.+\.)?example\.com$/` will reflect `https://example.com` and any of its subdomains, but will not match attacker-controlled origins like `https://evil-example.com`. A trailing-only anchor such as `/example\.com$/` is **not safe** because it also matches domains that merely *end* with `example.com`. + - `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /^https?:\/\/(.+\.)?example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com". As with the single-`RegExp` form, anchor at the start of the origin so patterns don't accept lookalike domains. - `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback (called as `callback(err, origin)`, where `origin` is a non-function value of the `origin` option) as the second. * `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`). * `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (ex: 'Content-Type,Authorization') or an array (ex: `['Content-Type', 'Authorization']`). If not specified, defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header.