Skip to content

fix(security): name manifest-src in the meta CSP so a manifest cannot load off-origin - #1289

Merged
mrbobbytables merged 1 commit into
mainfrom
sec/csp-manifest-src
Oct 11, 2026
Merged

mrbobbytables merged 1 commit into
mainfrom
sec/csp-manifest-src

Conversation

@hivecommons-hive

@hivecommons-hive hivecommons-hive Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

Security Fix

The meta Content-Security-Policy in docusaurus.config.js deliberately ships
no default-src — tests/site-config.test.mjs asserts
directives.has('default-src') === false. That means every fetch directive the
policy does not name is simply absent, and an absent directive restricts
nothing.

manifest-src was the last fetch directive left in that state. Per CSP Level 3
§6.8.2 ("Get fetch directive fallback list") its chain is literally:

"manifest-src" → Return << "manifest-src", "default-src" >>

and nothing else. worker-src is also unnamed but is not a gap, because its
chain is << "worker-src", "child-src", "script-src", "default-src" >> and
script-src 'self' is already set.

So a <link rel="manifest" href="https://attacker.example/m.json"> that reached
a page would be fetched, handing that host the visitor's IP, User-Agent and
Referer
. A manifest also carries name, icons, start_url and scope, so
an off-origin one misrepresents the site's installed-app identity to anyone who
installs it. This is defence-in-depth of the same class and severity as the
font-src gap closed in #1284 — the parser gates remain the primary control
(CONTENT-SECURITY.md:52).

What changed

  • docusaurus.config.js — adds "manifest-src 'self'" to the directive list,
    with a comment recording the CSP3 fallback chain and why worker-src is not
    a sibling gap.
  • tests/site-config.test.mjs — three additions:
    • pins manifest-src in the existing
      "names the fetch directives that have no default-src fallback" test;
    • pins it in "the CSP holds under a preview deployment origin";
    • adds "every linked web app manifest is served from this origin", which
      asserts the premise the directive rests on rather than assuming it,
      using remoteTarget() — the same classifier the SVG, MDX and stylesheet
      gates use — exactly as the font-src change asserted its premise with a
      CSS scan.

'self' constrains nothing the site actually does: the only manifest linked is
the root-relative ${baseUrl}manifest.json emitted at docusaurus.config.js:153-156
and served from static/manifest.json.

Verification

  • node --test tests/site-config.test.mjs → 29/29 pass.
  • Removing "manifest-src 'self'" from the config makes 2 tests fail
    (the no-fallback test and the preview-origin test), so the pins are not vacuous.
  • npx prettier --check docusaurus.config.js tests/site-config.test.mjs → clean.
  • The wider npm run test:unit cannot run in this sandbox: @swc/core fails
    with Cannot find module './swc.linux-x64-gnu.node' (optional native binding
    not installed here). Confirmed pre-existing — it reproduces identically
    with this branch's changes stashed, and neither file in this diff can affect
    native binding resolution. CI installs the binding normally.

Closes #1288
Closes #1285 (same finding, filed by the scanner the same day; its optional worker-src suggestion is covered by the fallback-chain note above)


Filed by sec-check agent (ACMM L4/L5 — hold-gated mode). Hold-gated: human review required.

— hive: agent=sec-check backend=copilot model=claude-opus-5 copilot=1.0.88

… load off-origin

The meta Content-Security-Policy deliberately ships no default-src, so every
fetch directive it does not name is absent rather than inherited, and the
browser allows any host. manifest-src was the last such directive: CSP3 6.8.2
gives its fallback chain as << manifest-src, default-src >> and nothing else.
(worker-src is also unnamed but is not a gap -- its chain passes through
script-src, which is already set to 'self'.)

A <link rel="manifest"> naming a third-party host would therefore be fetched,
handing that host the visitor's IP, User-Agent and Referer; a manifest also
carries name, icons, start_url and scope, so an off-origin one misrepresents
the site's installed-app identity.

The only manifest this site links is its own root-relative
${baseUrl}manifest.json, served from static/, so 'self' constrains nothing the
site actually does -- the same premise that made font-src 'self' safe to set.
A new test asserts that premise rather than assuming it, and the directive is
pinned in both the no-fallback directive test and the preview-origin test.

Signed-off-by: sec-check <sec-check@hive.kubestellar.io>
@hivecommons-hive

Copy link
Copy Markdown
Contributor Author

Important

Held for human review by the hive's ACMM level gate.

This PR was opened by the "sec-check" agent while Hive policy required a human checkpoint for that agent. Non-outreach agents are held at ACMM L3–L5; the outreach agent is always held because it publishes project-facing communication.

Hive will keep the hold label until a human removes it. Operators can make a deliberate one-off release during an ACMM level change with release_level_holds=true, but level changes never release this hold automatically.

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

Labels

Projects

None yet

1 participant