Skip to content

fix: require every Origin to be allowed on a CORS request (#1262) (1.4.x) - #1337

Merged
pjfanning merged 1 commit into
apache:1.4.xfrom
pjfanning:backport-1262-1.4.x
Oct 10, 2026
Merged

pjfanning merged 1 commit into
apache:1.4.xfrom
pjfanning:backport-1262-1.4.x

Conversation

@pjfanning

Copy link
Copy Markdown
Member

cherry pick 28def88 #1262

Motivation:
CorsDirectives.validateOrigins accepted a CORS request when any of the origins in its Origin header matched allowedOrigins. The response echoes back every origin given, so a request listing a permitted origin alongside a disallowed one was accepted, and the disallowed origin was echoed in Access-Control-Allow-Origin.

Modification:
Require every origin to match (origins.nonEmpty && origins.forall(allowedOrigins.matches)); the HttpOriginMatcher.* shortcut is unchanged. The only conflict was in the comment above validateOrigins (1.4.x still had the old one-line scaladoc), so the code change is identical to main.

Result:
A CORS request is accepted only when all of its origins are allowed; otherwise it is rejected with CorsRejection.InvalidOrigin.

Tests:

  • sbt http-cors/test - pass (41 tests). With the CorsDirectives change reverted, the new "reject an actual request listing a disallowed origin next to an allowed one" test fails.
  • sbt http-cors/scalafmtCheck http-cors/Test/scalafmtCheck - pass.
  • sbt http-cors/mimaReportBinaryIssues - pass (baseline pekko-http-cors 1.0.0; no signature change).

References:
Backport of #1262

Motivation:
`validateOrigins` accepted a request as soon as *one* of the origins in
the `Origin` header matched the allowed matcher, but the response
echoes back *every* origin it was given
(`CorsSettingsImpl.accessControlAllowOrigin` renders the whole list). A
request carrying an allowed origin next to a disallowed one therefore
passed validation and echoed the disallowed origin back in
`Access-Control-Allow-Origin`. The pre-flight path is not affected
because it already only accepts a single origin, so this was limited to
simple/actual requests.

Modification:
Require all origins to match rather than any, keeping the existing
`allowed-origins = *` short-circuit. The extra `nonEmpty` guard
preserves today's handling of a `null` origin (an empty origin list),
which must still be rejected when the allowed origins are restricted -
`forall` alone would accept it.

Result:
An actual request is accepted only when every origin it lists is
allowed, so no origin that failed validation is echoed back. A request
whose origins are all disallowed is still rejected with
`InvalidOrigin` listing them, as before.

Tests:
- sbt http-cors/test - pass (47 tests); a new test asserts that an actual request listing a disallowed origin next to an allowed one is rejected with `InvalidOrigin`. Verified it fails with the fix stashed (the request is accepted and both origins are echoed). The existing "there are two origins" rejection test and the restricted-`null`-origin test both still pass.
- sbt http-cors/mimaReportBinaryIssues - pass

References:
None - requires all origins of a CORS request to be allowed

(cherry picked from commit 28def88)
@pjfanning pjfanning added this to the 1.4.2 milestone Oct 9, 2026

@samueleresca samueleresca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Looks good.

@pjfanning
pjfanning merged commit acd87e0 into apache:1.4.x Oct 10, 2026
10 checks passed
@pjfanning
pjfanning deleted the backport-1262-1.4.x branch October 10, 2026 14:11
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.

2 participants