CSP/Trusted Types — Content Security Policy Enforcement + Trusted Types API #103

Closed
opened 2026-06-19 12:09:49 +00:00 by Artur · 0 comments
Owner

Titel: CSP/Trusted Types — Content Security Policy Enforcement + Trusted Types API

Problembeschreibung

Content Security Policy (CSP) wird aktuell überhaupt nicht unterstützt:

  • Kein CSP-Parsing aus <meta http-equiv="Content-Security-Policy"> oder HTTP-Response-Headern
  • Kein Script/Style-Blocking nach CSP-Regeln
  • Trusted Types (trustedTypes.createPolicy()) fehlt komplett
  • SecurityPolicyViolationEvent fehlt

Warum das blockiert:

flowchart TD
    A[Site sendet CSP-Header] --> B{CSP wird geparsed?}
    B -->|Nein| C[Inline-Scripts werden NICHT blockiert]
    C --> D[Seite lädt scheinbar korrekt]
    D --> E{Aber: CSP-reporting fehlt}
    E -->|Site erwartet ViolationReports| F[Seite wartet auf Report → Timeout]
    
    G[Google Maps/YouTube] -->|Sendet CSP + TrustedTypes| H{trustedTypes existiert?}
    H -->|Nein| I[Maps-JS erzeugt HTML via innerHTML]
    I -->|Ohne TrustedTypes| J[CSP blockiert innerHTML]
    J --> K[Maps lädt nicht]
    
    L[Amazon.de] -->|CSP Header| M{Script-SRC parsed?}
    M -->|Nein| N[Alle Scripts erlaubt → Sicherheitslücke]
    N --> O[Amazon erkennt fehlendes CSP → blockiert Checkout?]

Betroffene Sites:

  1. Amazon.de — sendet CSP-Header → könnte Checkout blockieren ohne korrektes Parsing
  2. Google Maps/YouTube — erzwingen Trusted Types → ohne TT lädt Maps nicht
  3. GitHub — sendet default-src 'none' → blockiert alle Resourcen ohne korrektes Enforcing
  4. Cloudflare — CSP-Header mit nonce → benötigt nonce-Handling für inline-Scripts

Architektur

src/security/
├── csp-parser.ts           # CSP-Header/ Meta parsing
├── csp-enforcer.ts         # Script/Style-Blocking nach CSP
├── trusted-types.ts        # Trusted Types API (CH)
├── violation-event.ts      # SecurityPolicyViolationEvent
├── csp-directives.ts       # CSP-Direktiven + Source-Expression-Parsing
└── csp-report.ts           # CSP-Reporting (report-uri, report-to)

CSP-Parser (csp-parser.ts)

Parst sowohl Content-Security-Policy HTTP-Header als auch <meta http-equiv>:

interface CSPDirective {
  name: string;           // "default-src", "script-src", etc.
  sources: SourceExpr[];  // ['self', 'https://*.example.com', ...]
}

interface CSPPolicy {
  directives: CSPDirective[];
  // Metadaten
  source: "header" | "meta";     // Woher stammt die Policy?
  reportUri?: string;             // report-uri
  reportTo?: string;              // report-to (CSP Level 3)
}

type SourceExpr = {
  type: "self" | "none" | "strict-dynamic" | "unsafe-inline" | "unsafe-eval" |
        "wasm-unsafe-eval" | "nonce" | "hash" | "scheme" | "host" | "report-sample";
  value?: string;         // nonce-Wert, Hash-Wert, Hostname, etc.
  algorithm?: string;     // Hash-Algorithmus: sha256, sha384, sha512
};

CSP-Enforcer (csp-enforcer.ts)

Wird in ScriptLoader + StyleEngine eingehängt:

class CSPEnforcer {
  private policies: CSPPolicy[];
  private nonces: Set<string>;       // Aktive Nonces für diese Page
  
  /** Prüft ob ein Script geladen werden darf */
  allowScript(url: string, inlineCode?: string, nonce?: string, hash?: string): boolean;
  
  /** Prüft ob ein Style geladen werden darf */
  allowStyle(url: string, inlineCode?: string): boolean;
  
  /** Prüft ob eval() erlaubt ist */
  allowEval(): boolean;
  
  /** Prüft ob eine Resource (img, font, connect) geladen werden darf */
  allowResource(url: string, directive: string): boolean;
  
  /** Erzeugt SecurityPolicyViolationEvent */
  violate(directive: string, blockedURI: string, ...): void;
}

Trusted Types (trusted-types.ts)

class TrustedTypePolicy {
  readonly name: string;
  createHTML(input: string): TrustedHTML;
  createScript(input: string): TrustedScript;
  createScriptURL(input: string): TrustedScriptURL;
}

class TrustedTypePolicyFactory {
  createPolicy(name: string, policy: TrustedTypePolicyOptions): TrustedTypePolicy;
  isHTML(value: any): value is TrustedHTML;
  isScript(value: any): value is TrustedScript;
  isScriptURL(value: any): value is TrustedScriptURL;
  getPropertyType(tagName: string, property: string): string | null;
  readonly defaultPolicy: TrustedTypePolicy | null;
  readonly emptyHTML: TrustedHTML;
  readonly emptyScript: TrustedScript;
}

// Trusted Types sind opaque wrapper — nur via policy.create*() erzeugbar
class TrustedHTML { private _value: string; }
class TrustedScript { private _value: string; }
class TrustedScriptURL { private _value: string; }

Optionen

Option A: CSP Level 2 + Basis-TrustedTypes (Empfohlen)

CSP Level 2 Direktiven + Trusted Types nach Chrome 134 Spec:

  • default-src, script-src, style-src, img-src, connect-src, font-src, frame-src, media-src, object-src
  • report-uri + report-to (vereinfacht)
  • nonce + hash für inline-Scripts
  • Trusted Types: createPolicy, isHTML/isScript, getPropertyType
  • KEIN CSP Level 3 (script-src-elem, style-src-attr, etc.) — später

Vorteile: Deckt 90% der Sites ab, implementierbar in 4 Tagen
Nachteile: Level 3 fehlt (selten benötigt)

Option B: CSP Level 3 + Full TrustedTypes

Alles aus Option A plus:

  • CSP Level 3: script-src-elem, script-src-attr, style-src-elem, style-src-attr
  • strict-dynamic Propagation
  • wasm-unsafe-eval
  • report-to via ReportingObserver
  • trusted-types directive (CSP sagt welche Policies erlaubt sind)
  • require-trusted-types-for 'script'

Vorteile: 100% Spec-konform
Nachteile: Doppelter Aufwand, selten benötigt

→ Option A gewinnt (Level 2 + Basis-TT)

Akzeptanzkriterien (25 Punkte)

Phase 1: CSP-Parser — 8 Punkte

  • CSP aus <meta http-equiv="Content-Security-Policy" content="...">
  • CSP aus HTTP-Response-Header Content-Security-Policy
  • CSP aus HTTP-Response-Header Content-Security-Policy-Report-Only (nur report, kein block)
  • Parsing: default-src 'self' → directive=single source
  • Parsing: script-src 'self' 'unsafe-inline' https://*.example.com → multiple sources
  • Parsing: img-src 'self' data: blob: → scheme-sources
  • Parsing: script-src 'nonce-abc123' → nonce extrahiert
  • Parsing: script-src 'sha256-xyz...' → hash extrahiert (sha256/sha384/sha512)

Phase 2: CSP-Enforcer — 8 Punkte

  • script-src 'self' → blockiert Script von fremdem Origin
  • script-src 'unsafe-inline' → erlaubt inline Scripts (script-Tag)
  • script-src 'unsafe-eval' → erlaubt eval()
  • script-src 'nonce-abc' → erlaubt Script mit nonce="abc"
  • script-src 'sha256-xyz' → erlaubt Script mit Hash xyz
  • default-src 'none' → blockiert ALLE Resourcen (außer erlaubte)
  • style-src 'self' → blockiert inline Styles
  • img-src 'self' → blockiert externe Bilder

Phase 3: Trusted Types — 6 Punkte

  • trustedTypes.createPolicy("default", { createHTML: (s) => s }) → Policy
  • trustedTypes.isHTML(value) → boolean
  • trustedTypes.isScript(value) → boolean
  • trustedTypes.isScriptURL(value) → boolean
  • trustedTypes.getPropertyType("div", "innerHTML") → "TrustedHTML"
  • TrustedHTML ist ein opaque Wrapper (kein Zugriff auf _value von außen)

Phase 4: Violation Events — 3 Punkte

  • SecurityPolicyViolationEvent bei Script-Blockade
  • Event hat korrekte Properties: blockedURI, violatedDirective, effectiveDirective, originalPolicy, disposition, sourceFile, lineNumber, columnNumber
  • document.securityPolicyViolationEvent wird dispatched (auf document)

Abhängigkeiten

graph TD
    subgraph Existing[Bestehend]
        SL[src/js/script-loader.ts]
        SE[src/css/style-engine.ts]
        ER[src/js/execution-realm.ts]
        RI[src/runtime-isolation.ts]
        TM[src/transport/transport-manager.ts]
    end

    subgraph New[Neu]
        CP[src/security/csp-parser.ts]
        CE[src/security/csp-enforcer.ts]
        TT[src/security/trusted-types.ts]
        VE[src/security/violation-event.ts]
    end

    CP -->|liefert Policy| CE
    CE -->|blockiert Script| SL
    CE -->|blockiert Style| SE
    CE -->|blockiert eval| ER
    TT -->|TrustedHTML| SL
    TT -->|TrustedScriptURL| SL
    CE -->|feuert| VE
    RI -->|stellt trustedTypes global| Page[Page Context]
    TM -->|liest CSP aus Responseheaders| CP

Risiken

Risiko Impact Wahrscheinlichkeit Mitigation
CSP blockiert unsere eigenen Inline-Scripts Hoch Mittel Nonce-Mechanismus für eigene Scripts
Trusted Types brechen innerHTML-Nutzung Hoch Mittel require-trusted-types-for deaktiviert lassen
CSP-Report-Only vs Enforce verwechselt Mittel Niedrig Report-Only logged nur, blockiert nicht
Nonce muss pro Request frisch sein Mittel Mittel Zufallsgenerator pro Page
Strict-Dynamic-Kaskade komplex Niedrig Niedrig In Phase 2 (optional)

Performance-Impact

Operation Impact
CSP-Parsing (1 Policy, 10 Directives) ~0.01ms
Script-Check (inline, 1 nonce) ~0.001ms
Script-Check (extern, URL-Match) ~0.01ms (Regex)
Trusted Types createHTML ~0.001ms
Violation-Event-Kreation ~0.01ms

Gesamt-Overhead: < 0.1ms pro Resource-Check — vernachlässigbar.

Betroffene Dateien

Datei Änderung
src/security/csp-parser.ts NEU — CSP-Header/Meta Parsing
src/security/csp-enforcer.ts NEU — Script/Style/Resource-Blocking
src/security/trusted-types.ts NEU — Trusted Types API
src/security/violation-event.ts NEU — SecurityPolicyViolationEvent
src/security/csp-directives.ts NEU — CSP-Direktiven + Source-Expression Typen
src/security/index.ts NEU — Public API
src/js/script-loader.ts ANDERN — CSP-Check vor Script-Execute
src/js/execution-realm.ts ANDERN — CSP-Check vor eval()
src/css/style-engine.ts ANDERN — CSP-Check vor Style-Apply
src/runtime-isolation.ts ANDERN — trustedTypes + SecurityPolicyViolationEvent installieren
src/pages/page.ts ANDERN — CSP aus Response-Header an Enforcer übergeben
tests/unit/csp-parser.test.ts NEU — 15 Tests
tests/unit/csp-enforcer.test.ts NEU — 20 Tests
tests/unit/trusted-types.test.ts NEU — 15 Tests
tests/unit/violation-event.test.ts NEU — 10 Tests

Testplan (60 Tests)

Unit-Tests

csp-parser: 15 Tests

  1. Meta-Tag CSP parsen
  2. Header CSP parsen
  3. default-src 'self' → korrekte Directive
  4. script-src 'self' 'unsafe-inline' 'unsafe-eval' → 3 Sources
  5. img-src 'self' data: blob: → scheme-sources
  6. script-src 'nonce-abc123' → nonce extrahiert
  7. script-src 'sha256-abc...' → hash extrahiert
  8. Multiple Policies (2 Header) → merged
  9. Report-Only → disposition = "report"
  10. Enforce → disposition = "enforce"
  11. report-uri /csp-report → reportUri extrahiert
  12. connect-src https://api.example.com → host-source
  13. font-src 'self' https://fonts.gstatic.com → multi-source
  14. Case-Insensitivity: Default-Src → default-src
  15. Malformed Header → graceful fallback (leere Policy)

csp-enforcer: 20 Tests

  1. default-src 'none' → alles blockiert
  2. script-src 'self' → same-origin Script erlaubt, cross-origin blockiert
  3. script-src 'unsafe-inline' → inline Script erlaubt
  4. Ohne unsafe-inline → inline Script blockiert
  5. script-src 'unsafe-eval' → eval() erlaubt
  6. Ohne unsafe-eval' → eval() blockiert
  7. script-src 'nonce-abc' → Script mit nonce="abc" erlaubt
  8. Script ohne nonce blockiert wenn nonce benötigt
  9. script-src 'sha256-xyz' → Script mit Hash xyz erlaubt
  10. style-src 'self' → inline Style blockiert
  11. style-src 'unsafe-inline' → inline Style erlaubt
  12. img-src 'self' → externes Bild blockiert
  13. connect-src 'self' → fetch zu externem Host blockiert
  14. default-src 'self' + img-src 'self' https://images.com → override
  15. font-src 'self' → Google Fonts blockiert
  16. frame-src 'none' → iframe blockiert
  17. media-src 'self' → externes Video blockiert
  18. object-src 'none' → object/embed blockiert
  19. Nonce generiert pro Page (unterschiedliche Werte)
  20. Resource-Type-Mapping (img→img-src, font→font-src, script→script-src)

trusted-types: 15 Tests

  1. createPolicy("default", { createHTML: (s) => s }) → Policy
  2. Policy.createHTML(input) → TrustedHTML
  3. Policy.createScript(input) → TrustedScript
  4. Policy.createScriptURL(input) → TrustedScriptURL
  5. isHTML(TrustedHTML) → true
  6. isHTML(string) → false
  7. isScript(TrustedScript) → true
  8. isScriptURL(TrustedScriptURL) → true
  9. getPropertyType("div", "innerHTML") → "TrustedHTML"
  10. getPropertyType("script", "src") → "TrustedScriptURL"
  11. getPropertyType("div", "title") → null (kein Trusted Type)
  12. emptyHTML → TrustedHTML mit "" (leerer String)
  13. emptyScript → TrustedScript mit ""
  14. TrustedHTML ist nicht String (instanceof check)
  15. TrustedHTML._value ist nicht von außen lesbar (opaque wrapper)

violation-event: 10 Tests

  1. Script-Blockade → SecurityPolicyViolationEvent gefeuert
  2. blockedURI = blockierte Script-URL
  3. violatedDirective = "script-src"
  4. effectiveDirective = "script-src"
  5. originalPolicy = kompletter Policy-String
  6. disposition = "enforce" | "report"
  7. sourceFile, lineNumber, columnNumber (bei inline-Scripts)
  8. Event wird auf document dispatchen
  9. Event bubbled nicht (bubbles: false)
  10. Event cancelable: false

Integration-Tests (10)

  1. Seite mit CSP-Meta-Tag lädt → inline-Scripts blockiert
  2. Seite mit CSP-Header lädt → externe Scripts blockiert
  3. CSP-Nonce: Script mit korrektem Nonce lädt
  4. CSP-Report-Only: Script blockiert nicht, aber Violation geloggt
  5. Trusted Types: innerHTML mit String blockiert
  6. Trusted Types: innerHTML mit TrustedHTML erlaubt
  7. CSP + Worker: Worker-Script via worker-src
  8. CSP + fetch: connect-src blockiert cross-origin fetch
  9. CSP Change: Nach Meta-Änderung neue Policy aktiv
  10. Multiple Policies: Strengere gewinnt (intersection)

Zeitplan

Phase Dauer
Phase 1: CSP-Parser Tag 1
Phase 2: CSP-Enforcer + ScriptLoader-Integration Tag 2-3
Phase 3: Trusted Types Tag 3-4
Phase 4: Violation Events + Integration-Tests Tag 4-5
CI + Stabilisierung Tag 5-6

Gesamtschätzung: 5-6 Tage

Deployment-Hinweise

  • CSP wird pro Page verwaltet — beim Navigieren wird neue Policy gesetzt
  • Nonce wird pro Page-Load zufällig generiert
  • Trusted Types sind opt-in über require-trusted-types-for — standardmäßig deaktiviert
  • CSP-Report-Only logged Violations via APILogger (sichtbar im diagnostics-Modus)
  • Bei CSP-Verstoß in strict Mode: wirft MissingBrowserAPI-like Error (dev)
  • Bei CSP-Verstoß in production Mode: nur loggen, nicht blockieren (scrape)
## Titel: CSP/Trusted Types — Content Security Policy Enforcement + Trusted Types API ## Problembeschreibung Content Security Policy (CSP) wird **aktuell überhaupt nicht unterstützt**: - **Kein CSP-Parsing** aus `<meta http-equiv="Content-Security-Policy">` oder HTTP-Response-Headern - **Kein Script/Style-Blocking** nach CSP-Regeln - **Trusted Types** (`trustedTypes.createPolicy()`) fehlt komplett - **SecurityPolicyViolationEvent** fehlt **Warum das blockiert:** ```mermaid flowchart TD A[Site sendet CSP-Header] --> B{CSP wird geparsed?} B -->|Nein| C[Inline-Scripts werden NICHT blockiert] C --> D[Seite lädt scheinbar korrekt] D --> E{Aber: CSP-reporting fehlt} E -->|Site erwartet ViolationReports| F[Seite wartet auf Report → Timeout] G[Google Maps/YouTube] -->|Sendet CSP + TrustedTypes| H{trustedTypes existiert?} H -->|Nein| I[Maps-JS erzeugt HTML via innerHTML] I -->|Ohne TrustedTypes| J[CSP blockiert innerHTML] J --> K[Maps lädt nicht] L[Amazon.de] -->|CSP Header| M{Script-SRC parsed?} M -->|Nein| N[Alle Scripts erlaubt → Sicherheitslücke] N --> O[Amazon erkennt fehlendes CSP → blockiert Checkout?] ``` **Betroffene Sites:** 1. **Amazon.de** — sendet CSP-Header → könnte Checkout blockieren ohne korrektes Parsing 2. **Google Maps/YouTube** — erzwingen Trusted Types → ohne TT lädt Maps nicht 3. **GitHub** — sendet `default-src 'none'` → blockiert alle Resourcen ohne korrektes Enforcing 4. **Cloudflare** — CSP-Header mit nonce → benötigt nonce-Handling für inline-Scripts ## Architektur ``` src/security/ ├── csp-parser.ts # CSP-Header/ Meta parsing ├── csp-enforcer.ts # Script/Style-Blocking nach CSP ├── trusted-types.ts # Trusted Types API (CH) ├── violation-event.ts # SecurityPolicyViolationEvent ├── csp-directives.ts # CSP-Direktiven + Source-Expression-Parsing └── csp-report.ts # CSP-Reporting (report-uri, report-to) ``` ### CSP-Parser (csp-parser.ts) Parst sowohl `Content-Security-Policy` HTTP-Header als auch `<meta http-equiv>`: ```typescript interface CSPDirective { name: string; // "default-src", "script-src", etc. sources: SourceExpr[]; // ['self', 'https://*.example.com', ...] } interface CSPPolicy { directives: CSPDirective[]; // Metadaten source: "header" | "meta"; // Woher stammt die Policy? reportUri?: string; // report-uri reportTo?: string; // report-to (CSP Level 3) } type SourceExpr = { type: "self" | "none" | "strict-dynamic" | "unsafe-inline" | "unsafe-eval" | "wasm-unsafe-eval" | "nonce" | "hash" | "scheme" | "host" | "report-sample"; value?: string; // nonce-Wert, Hash-Wert, Hostname, etc. algorithm?: string; // Hash-Algorithmus: sha256, sha384, sha512 }; ``` ### CSP-Enforcer (csp-enforcer.ts) Wird in ScriptLoader + StyleEngine eingehängt: ```typescript class CSPEnforcer { private policies: CSPPolicy[]; private nonces: Set<string>; // Aktive Nonces für diese Page /** Prüft ob ein Script geladen werden darf */ allowScript(url: string, inlineCode?: string, nonce?: string, hash?: string): boolean; /** Prüft ob ein Style geladen werden darf */ allowStyle(url: string, inlineCode?: string): boolean; /** Prüft ob eval() erlaubt ist */ allowEval(): boolean; /** Prüft ob eine Resource (img, font, connect) geladen werden darf */ allowResource(url: string, directive: string): boolean; /** Erzeugt SecurityPolicyViolationEvent */ violate(directive: string, blockedURI: string, ...): void; } ``` ### Trusted Types (trusted-types.ts) ```typescript class TrustedTypePolicy { readonly name: string; createHTML(input: string): TrustedHTML; createScript(input: string): TrustedScript; createScriptURL(input: string): TrustedScriptURL; } class TrustedTypePolicyFactory { createPolicy(name: string, policy: TrustedTypePolicyOptions): TrustedTypePolicy; isHTML(value: any): value is TrustedHTML; isScript(value: any): value is TrustedScript; isScriptURL(value: any): value is TrustedScriptURL; getPropertyType(tagName: string, property: string): string | null; readonly defaultPolicy: TrustedTypePolicy | null; readonly emptyHTML: TrustedHTML; readonly emptyScript: TrustedScript; } // Trusted Types sind opaque wrapper — nur via policy.create*() erzeugbar class TrustedHTML { private _value: string; } class TrustedScript { private _value: string; } class TrustedScriptURL { private _value: string; } ``` ## Optionen ### Option A: CSP Level 2 + Basis-TrustedTypes (Empfohlen) CSP Level 2 Direktiven + Trusted Types nach Chrome 134 Spec: - `default-src`, `script-src`, `style-src`, `img-src`, `connect-src`, `font-src`, `frame-src`, `media-src`, `object-src` - `report-uri` + `report-to` (vereinfacht) - `nonce` + `hash` für inline-Scripts - Trusted Types: createPolicy, isHTML/isScript, getPropertyType - **KEIN** CSP Level 3 (`script-src-elem`, `style-src-attr`, etc.) — später **Vorteile:** Deckt 90% der Sites ab, implementierbar in 4 Tagen **Nachteile:** Level 3 fehlt (selten benötigt) ### Option B: CSP Level 3 + Full TrustedTypes Alles aus Option A plus: - CSP Level 3: `script-src-elem`, `script-src-attr`, `style-src-elem`, `style-src-attr` - `strict-dynamic` Propagation - `wasm-unsafe-eval` - `report-to` via ReportingObserver - `trusted-types` directive (CSP sagt welche Policies erlaubt sind) - `require-trusted-types-for 'script'` **Vorteile:** 100% Spec-konform **Nachteile:** Doppelter Aufwand, selten benötigt **→ Option A gewinnt** (Level 2 + Basis-TT) ## Akzeptanzkriterien (25 Punkte) ### Phase 1: CSP-Parser — 8 Punkte - [ ] CSP aus `<meta http-equiv="Content-Security-Policy" content="...">` - [ ] CSP aus HTTP-Response-Header `Content-Security-Policy` - [ ] CSP aus HTTP-Response-Header `Content-Security-Policy-Report-Only` (nur report, kein block) - [ ] Parsing: `default-src 'self'` → directive=single source - [ ] Parsing: `script-src 'self' 'unsafe-inline' https://*.example.com` → multiple sources - [ ] Parsing: `img-src 'self' data: blob:` → scheme-sources - [ ] Parsing: `script-src 'nonce-abc123'` → nonce extrahiert - [ ] Parsing: `script-src 'sha256-xyz...'` → hash extrahiert (sha256/sha384/sha512) ### Phase 2: CSP-Enforcer — 8 Punkte - [ ] `script-src 'self'` → blockiert Script von fremdem Origin - [ ] `script-src 'unsafe-inline'` → erlaubt inline Scripts (script-Tag) - [ ] `script-src 'unsafe-eval'` → erlaubt eval() - [ ] `script-src 'nonce-abc'` → erlaubt Script mit nonce="abc" - [ ] `script-src 'sha256-xyz'` → erlaubt Script mit Hash xyz - [ ] `default-src 'none'` → blockiert ALLE Resourcen (außer erlaubte) - [ ] `style-src 'self'` → blockiert inline Styles - [ ] `img-src 'self'` → blockiert externe Bilder ### Phase 3: Trusted Types — 6 Punkte - [ ] `trustedTypes.createPolicy("default", { createHTML: (s) => s })` → Policy - [ ] `trustedTypes.isHTML(value)` → boolean - [ ] `trustedTypes.isScript(value)` → boolean - [ ] `trustedTypes.isScriptURL(value)` → boolean - [ ] `trustedTypes.getPropertyType("div", "innerHTML")` → "TrustedHTML" - [ ] `TrustedHTML` ist ein opaque Wrapper (kein Zugriff auf _value von außen) ### Phase 4: Violation Events — 3 Punkte - [ ] `SecurityPolicyViolationEvent` bei Script-Blockade - [ ] Event hat korrekte Properties: blockedURI, violatedDirective, effectiveDirective, originalPolicy, disposition, sourceFile, lineNumber, columnNumber - [ ] `document.securityPolicyViolationEvent` wird dispatched (auf document) ## Abhängigkeiten ```mermaid graph TD subgraph Existing[Bestehend] SL[src/js/script-loader.ts] SE[src/css/style-engine.ts] ER[src/js/execution-realm.ts] RI[src/runtime-isolation.ts] TM[src/transport/transport-manager.ts] end subgraph New[Neu] CP[src/security/csp-parser.ts] CE[src/security/csp-enforcer.ts] TT[src/security/trusted-types.ts] VE[src/security/violation-event.ts] end CP -->|liefert Policy| CE CE -->|blockiert Script| SL CE -->|blockiert Style| SE CE -->|blockiert eval| ER TT -->|TrustedHTML| SL TT -->|TrustedScriptURL| SL CE -->|feuert| VE RI -->|stellt trustedTypes global| Page[Page Context] TM -->|liest CSP aus Responseheaders| CP ``` ## Risiken | Risiko | Impact | Wahrscheinlichkeit | Mitigation | |--------|--------|--------------------|------------| | CSP blockiert unsere eigenen Inline-Scripts | Hoch | Mittel | Nonce-Mechanismus für eigene Scripts | | Trusted Types brechen innerHTML-Nutzung | Hoch | Mittel | `require-trusted-types-for` deaktiviert lassen | | CSP-Report-Only vs Enforce verwechselt | Mittel | Niedrig | Report-Only logged nur, blockiert nicht | | Nonce muss pro Request frisch sein | Mittel | Mittel | Zufallsgenerator pro Page | | Strict-Dynamic-Kaskade komplex | Niedrig | Niedrig | In Phase 2 (optional) | ## Performance-Impact | Operation | Impact | |-----------|--------| | CSP-Parsing (1 Policy, 10 Directives) | ~0.01ms | | Script-Check (inline, 1 nonce) | ~0.001ms | | Script-Check (extern, URL-Match) | ~0.01ms (Regex) | | Trusted Types createHTML | ~0.001ms | | Violation-Event-Kreation | ~0.01ms | **Gesamt-Overhead:** < 0.1ms pro Resource-Check — vernachlässigbar. ## Betroffene Dateien | Datei | Änderung | |-------|----------| | `src/security/csp-parser.ts` | **NEU** — CSP-Header/Meta Parsing | | `src/security/csp-enforcer.ts` | **NEU** — Script/Style/Resource-Blocking | | `src/security/trusted-types.ts` | **NEU** — Trusted Types API | | `src/security/violation-event.ts` | **NEU** — SecurityPolicyViolationEvent | | `src/security/csp-directives.ts` | **NEU** — CSP-Direktiven + Source-Expression Typen | | `src/security/index.ts` | **NEU** — Public API | | `src/js/script-loader.ts` | **ANDERN** — CSP-Check vor Script-Execute | | `src/js/execution-realm.ts` | **ANDERN** — CSP-Check vor eval() | | `src/css/style-engine.ts` | **ANDERN** — CSP-Check vor Style-Apply | | `src/runtime-isolation.ts` | **ANDERN** — trustedTypes + SecurityPolicyViolationEvent installieren | | `src/pages/page.ts` | **ANDERN** — CSP aus Response-Header an Enforcer übergeben | | `tests/unit/csp-parser.test.ts` | **NEU** — 15 Tests | | `tests/unit/csp-enforcer.test.ts` | **NEU** — 20 Tests | | `tests/unit/trusted-types.test.ts` | **NEU** — 15 Tests | | `tests/unit/violation-event.test.ts` | **NEU** — 10 Tests | ## Testplan (60 Tests) ### Unit-Tests **csp-parser: 15 Tests** 1. Meta-Tag CSP parsen 2. Header CSP parsen 3. `default-src 'self'` → korrekte Directive 4. `script-src 'self' 'unsafe-inline' 'unsafe-eval'` → 3 Sources 5. `img-src 'self' data: blob:` → scheme-sources 6. `script-src 'nonce-abc123'` → nonce extrahiert 7. `script-src 'sha256-abc...'` → hash extrahiert 8. Multiple Policies (2 Header) → merged 9. Report-Only → disposition = "report" 10. Enforce → disposition = "enforce" 11. `report-uri /csp-report` → reportUri extrahiert 12. `connect-src https://api.example.com` → host-source 13. `font-src 'self' https://fonts.gstatic.com` → multi-source 14. Case-Insensitivity: `Default-Src` → default-src 15. Malformed Header → graceful fallback (leere Policy) **csp-enforcer: 20 Tests** 1. `default-src 'none'` → alles blockiert 2. `script-src 'self'` → same-origin Script erlaubt, cross-origin blockiert 3. `script-src 'unsafe-inline'` → inline Script erlaubt 4. Ohne `unsafe-inline` → inline Script blockiert 5. `script-src 'unsafe-eval'` → eval() erlaubt 6. Ohne `unsafe-eval'` → eval() blockiert 7. `script-src 'nonce-abc'` → Script mit nonce="abc" erlaubt 8. Script ohne nonce blockiert wenn nonce benötigt 9. `script-src 'sha256-xyz'` → Script mit Hash xyz erlaubt 10. `style-src 'self'` → inline Style blockiert 11. `style-src 'unsafe-inline'` → inline Style erlaubt 12. `img-src 'self'` → externes Bild blockiert 13. `connect-src 'self'` → fetch zu externem Host blockiert 14. `default-src 'self'` + `img-src 'self' https://images.com` → override 15. `font-src 'self'` → Google Fonts blockiert 16. `frame-src 'none'` → iframe blockiert 17. `media-src 'self'` → externes Video blockiert 18. `object-src 'none'` → object/embed blockiert 19. Nonce generiert pro Page (unterschiedliche Werte) 20. Resource-Type-Mapping (img→img-src, font→font-src, script→script-src) **trusted-types: 15 Tests** 1. `createPolicy("default", { createHTML: (s) => s })` → Policy 2. Policy.createHTML(input) → TrustedHTML 3. Policy.createScript(input) → TrustedScript 4. Policy.createScriptURL(input) → TrustedScriptURL 5. `isHTML(TrustedHTML)` → true 6. `isHTML(string)` → false 7. `isScript(TrustedScript)` → true 8. `isScriptURL(TrustedScriptURL)` → true 9. `getPropertyType("div", "innerHTML")` → "TrustedHTML" 10. `getPropertyType("script", "src")` → "TrustedScriptURL" 11. `getPropertyType("div", "title")` → null (kein Trusted Type) 12. `emptyHTML` → TrustedHTML mit "" (leerer String) 13. `emptyScript` → TrustedScript mit "" 14. TrustedHTML ist nicht String (instanceof check) 15. TrustedHTML._value ist nicht von außen lesbar (opaque wrapper) **violation-event: 10 Tests** 1. Script-Blockade → SecurityPolicyViolationEvent gefeuert 2. blockedURI = blockierte Script-URL 3. violatedDirective = "script-src" 4. effectiveDirective = "script-src" 5. originalPolicy = kompletter Policy-String 6. disposition = "enforce" | "report" 7. sourceFile, lineNumber, columnNumber (bei inline-Scripts) 8. Event wird auf document dispatchen 9. Event bubbled nicht (bubbles: false) 10. Event cancelable: false ### Integration-Tests (10) 1. Seite mit CSP-Meta-Tag lädt → inline-Scripts blockiert 2. Seite mit CSP-Header lädt → externe Scripts blockiert 3. CSP-Nonce: Script mit korrektem Nonce lädt 4. CSP-Report-Only: Script blockiert nicht, aber Violation geloggt 5. Trusted Types: innerHTML mit String blockiert 6. Trusted Types: innerHTML mit TrustedHTML erlaubt 7. CSP + Worker: Worker-Script via worker-src 8. CSP + fetch: connect-src blockiert cross-origin fetch 9. CSP Change: Nach Meta-Änderung neue Policy aktiv 10. Multiple Policies: Strengere gewinnt (intersection) ## Zeitplan | Phase | Dauer | |-------|-------| | Phase 1: CSP-Parser | Tag 1 | | Phase 2: CSP-Enforcer + ScriptLoader-Integration | Tag 2-3 | | Phase 3: Trusted Types | Tag 3-4 | | Phase 4: Violation Events + Integration-Tests | Tag 4-5 | | CI + Stabilisierung | Tag 5-6 | **Gesamtschätzung:** 5-6 Tage ## Deployment-Hinweise - CSP wird **pro Page** verwaltet — beim Navigieren wird neue Policy gesetzt - Nonce wird **pro Page-Load** zufällig generiert - Trusted Types sind **opt-in** über `require-trusted-types-for` — standardmäßig deaktiviert - CSP-Report-Only logged Violations via APILogger (sichtbar im diagnostics-Modus) - Bei CSP-Verstoß in `strict` Mode: wirft MissingBrowserAPI-like Error (dev) - Bei CSP-Verstoß in `production` Mode: nur loggen, nicht blockieren (scrape)
Artur closed this issue 2026-06-19 20:22:12 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
glow-all/true-headless-browser#103
No description provided.