Skip to content

fix(sri): only penalize off-origin proto-relative URLs on HTTP - #561

Draft
caugner wants to merge 25 commits into
558-sri-same-originfrom
464-protocol-relative-urls-with-https
Draft

fix(sri): only penalize off-origin proto-relative URLs on HTTP#561
caugner wants to merge 25 commits into
558-sri-same-originfrom
464-protocol-relative-urls-with-https

Conversation

@caugner

@caugner caugner commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Description

Update the subresource-integrity analyzer to score protocol-relative script URLs (src="//cdn.example.com/…") according to the actual risk, instead of applying a blanket −50:

  • On-origin protocol-relative URLs (resolving to the page's exact origin — same scheme, host and port) are treated as a secure origin (0 points), like any same-origin script. A script on a different host (e.g. //www.mozilla.org on mozilla.org) is a distinct origin and is handled by the off-origin case below, not treated as same-origin.
  • Off-origin protocol-relative URLs are treated like the URL a visitor would actually resolve: as https:// (−5 without SRI) when a normal visitor ends up on HTTPS (no HTTP server, or the HTTP request ultimately redirects to HTTPS), and as http:// (−50 / −20 with SRI) otherwise.

This also reworks the −20 / −50 grade descriptions, which previously stated protocol-relative URLs are penalized unconditionally:

-external scripts are loaded over HTTP or they use protocol-relative URLs via `src="//..."`.
+external scripts are loaded over HTTP, including protocol-relative URLs via `src="//..."` that can resolve to HTTP.

Motivation

Only penalize protocol-relative URLs in the one case that carries additional risk, per the security team's analysis in #464.

Given a document that loads a protocol-relative sub-resource, there are four cases:

Case Document Sub-resource Additional risk
1 HTTP on-origin none — any attack on the sub-resource also applies to the document
2 HTTP off-origin yes — an attacker could MITM the sub-resource origin and serve it over HTTP
3 HTTPS on-origin none
4 HTTPS off-origin none

Only case 2 warrants a penalty. The observatory distinguishes the document scheme from the redirect chain, so it penalizes a protocol-relative URL only when it is off-origin and a normal visitor is served the document over HTTP.

The document counts as served over HTTPS whenever a visitor ends up there — including through a downgradeable intermediate hop (http → http → https), since the visitor still lands on HTTPS and the script loads over HTTPS. That insecure hop is a redirect flaw already penalized by the redirection test (RedirectionNotToHttpsOnInitialRedirection); scoring it here as well would dock one flaw twice.

Additional details

Example that previously scored −50 despite being served only over HTTPS: https://developer.mozilla.org/en-US/observatory/analyze?host=runephilosof-abtion.github.io%2Fobservatory-test%2F

Since every src is now resolved against the served base URL, a src that cannot be resolved into a URL (e.g. src="//") is skipped rather than throwing and aborting the whole scan.

Related issues and pull requests

Fixes #464.

caugner and others added 25 commits April 10, 2026 18:34
Treat protocol-relative script URLs (`src="//cdn.example.com/…"`) by the
risk they actually carry instead of a blanket -50. On-origin URLs (same
second-level domain) now score as a secure origin, since any attack on
them also applies to the document. Off-origin URLs are penalized only
when the site can be served over HTTP (no HTTP server, or HTTP redirects
to HTTPS distinguishes the document scheme); when HTTPS is enforced they
score like an `https://` external script.
The `subresource-integrity` analyzer no longer penalizes protocol-relative
URLs unconditionally, only when they can resolve to HTTP. Update the grade
descriptions to match, so users are not told a `src="//..."` URL is penalized
when it scored 0 or -5.
The `!requests.responses.http` branch of `httpEnforcesHttps` was untested;
add a case asserting an off-origin protocol-relative script scores as
loaded securely (-5) when there is no HTTP server to serve it over HTTP.
`parse(requests.site.hostname).domain` is loop-invariant and was recomputed
per script in two branches. Compute it once as `siteDomain`.
`httpEnforcesHttps` only checked the final URL of the HTTP redirect chain, so
a chain with an intermediate HTTP hop (`http` -> `http` -> `https`) that could
be downgraded was treated as enforcing HTTPS. Require every hop after the
initial HTTP request to be `https:` instead, matching how `redirection.js`
rejects `RedirectionNotToHttpsOnInitialRedirection`.
The enforcement-independence of on-origin protocol-relative scripts was
asserted inside `checks for same origin`, where the behavior was not
discoverable. Move it to a dedicated, named test and trim the comments to
what the test ID does not already convey.
…th-https

Rebuild the protocol-relative URL handling (issue #464) on top of the
exact-origin SRI model from #559.

- Drop all `tldts` usage; the origin dimension now compares the resolved
  script `origin` against the page's `baseOrigin` (scheme + host + port).
- Collapse the per-branch origin computation to a single
  `new URL(scriptSrc, baseUrl).origin === baseOrigin`, guarded on
  `baseUrl` so a missing session resolves to a foreign origin. Same-host
  protocol-relative URLs are therefore same-origin.
- Keep the scheme dimension (`httpEnforcesHttps` and the
  `relativeProtocol && httpEnforcesHttps` clause) unchanged.
- Update tests for exact-origin semantics: `//www.mozilla.org` against
  apex `mozilla.org` is now off-origin (scored by scheme), and the
  on-origin path uses a new same-host `//mozilla.org` fixture.
…e URL

Compute the scheme dimension from `new URL(scriptSrc, baseUrl).protocol`
for full and path-relative URLs, replacing the separate
`new URL(scriptSrc).protocol` extraction and the `relativeOrigin` branch.
This folds the `relativeOrigin` classification away, leaving only the
`relativeProtocol` flag, whose scheme still depends on HTTP reachability
(`httpEnforcesHttps`) rather than the resolved scheme — a protocol-relative
URL resolves to the page's own (HTTPS) scheme here, which would mask its
downgrade risk on an HTTP visit.

Path-relative URLs now take their scheme from `baseUrl` (the served origin)
instead of `session.url`; these are identical in practice, and `baseUrl` is
the scheme the resource actually loads over.
The guard is `!secureOrigin` (off-origin) under the exact-origin model, not
"not a relative URI" as the pre-existing comment claimed.
The variable tests whether the script resolves to the page's own origin, a
sameness check, not a security one. `sameOrigin` also reads clearly against
the security-focused `secureScheme` it pairs with.
Reword the "even if" phrasing to state plainly that SRI on same-origin
scripts is rewarded despite not being required there.
… test

Restore the protocol-relative sub-case to "checks for same origin" using a
genuinely same-host fixture (//mozilla.org), rather than moving it out. The
standalone on-origin test now only asserts the non-redundant property — that
the verdict is unchanged without HTTP→HTTPS enforcement.
…tive

Point test_content_sri_sameorigin3.html at //mozilla.org so it exercises a
genuine same-origin protocol-relative script, keeping the original
"checks for same origin" sub-case in place instead of adding a parallel
fixture. Drop the separate off-origin protocol-relative test — that behavior
is already covered by the fb.me no-protocol matrix and the different-subdomain
test.
An on-origin script's verdict never consults `secureScheme` (the same-origin
branch ignores it without integrity), so the no-enforcement case is identical
to the sameorigin3 case in "checks for same origin".
Judge a protocol-relative sub-resource by whether a normal visitor is
served the document over HTTPS, using the final scheme of the HTTP
redirect chain rather than requiring every hop after the initial request
to be HTTPS.

An intermediate `http` hop (`http` → `http` → `https`) still lands the
visitor on HTTPS, so the script loads over HTTPS and carries no extra SRI
risk. That downgradeable hop is a redirect flaw already penalized by the
redirection test (`RedirectionNotToHttpsOnInitialRedirection`), so
docking it here too would penalize one flaw twice.
Explain the exact-origin rule once at `baseOrigin`, and the
protocol-relative scheme handling once, referencing `httpEnforcesHttps`
from the later comments instead of restating it.
Collapse the two `new URL(scriptSrc, baseUrl)` constructions into a single `scriptUrl`, and derive `sameOrigin` and `secureScheme` from it. This also drops the resolved scheme computation for protocol-relative URLs, whose security is judged by `httpEnforcesHttps` rather than the resolved scheme.
Resolving every `src` against the base URL means a malformed value such as `src="//"` throws from `new URL`, which aborts the entire scan since `analyzeScan` runs tests without a per-test `try`/`catch`. Guard the resolution and skip such a `src`, since it is not a loadable sub-resource.
Match the spelling used in the grade descriptions and PR copy.
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