@noidme/csp
v0.1.0-beta.0
Published
A fail-closed Content-Security-Policy builder for a consent-protected page. Strict default-src 'self' baseline (object-src 'none', pinned base-uri/form-action/frame-ancestors); widen only the exact origins your first-party app needs. buildCsp() → the head
Downloads
133
Maintainers
Readme
@noidme/csp
A fail-closed Content-Security-Policy builder for a consent-protected page. Zero-dependency. A noidme.js building block, usable standalone.
The threat model treats the in-page environment as hostile: a tracker injected after your SDK loads can
add <script>/<img>/beacon sinks that main-thread patchers (and even a service worker) can't fully cover.
A strict CSP is the browser-enforced backstop. This builds a default-src 'self' baseline and lets you
widen only the exact origins your first-party app needs.
import { buildCsp, cspHeaderName, applyCsp } from '@noidme/csp';
// RECOMMENDED — set the policy as an HTTP response header (server-side):
const policy = buildCsp({ connect: ['https://consent.example.com'], img: ['https://cdn.example.com'] });
res.setHeader(cspHeaderName(), policy);
// Fallback — inject a <meta http-equiv> from the client when you can't set the header:
applyCsp({ connect: ['https://consent.example.com'] }); // no-ops off-DOM + if a CSP meta already exists
// Nonce the inline scripts/styles you control (the clean path off 'unsafe-inline'):
const nonce = generateNonce(); // once per response
buildCsp({ nonce }); // adds 'nonce-…' to script-src + style-src
// → render <script nonce="{nonce}"> … </script>
// Observe before enforcing:
buildCsp({ reportOnly: true, reportUri: '/csp-report' }); // → Content-Security-Policy-Report-OnlyWhat it gives you
- Fail-closed baseline:
default-src 'self',object-src 'none',worker-src 'self', and pinnedbase-uri/form-action/frame-ancestors 'self'.img-srcis'self'only —data:is opt-in (imgData: true), because it's an exfiltration channel under XSS. Widen withconnect/img/script/style(append), ordirectives(wholesale replace — an empty list fails closed to'none'). - Injection-safe: every caller-supplied source is validated — a value containing
;,,, or whitespace throws, so a hostile or typo'd origin can't break out of its directive and silently widen the policy. - Nonce support:
generateNonce()+nonceprepend'nonce-…'toscript-src/style-src. - Header vs meta:
buildCsp()for the HTTP header (the strong path).applyCsp()is the client-side<meta>fallback — it strips the directives a meta CSP silently ignores (frame-ancestors/report-uri/sandbox) and refuses report-only, so it never implies protection it can't give. Safe to call unconditionally (first policy wins). Prefer the header. - Also:
upgradeInsecureRequests(opt-in). Deterministic directive order (stable diffs/tests).
Install
npm i @noidme/cspLicense
MIT.
