infra/stacks/f1-stream/files/.planning/phases/05-sandbox-proxy-hardening/05-VERIFICATION.md
Viktor Barzin c7c7047f1c [ci skip] Flatten module wrappers into stack roots
Remove the module "xxx" { source = "./module" } indirection layer
from all 66 service stacks. Resources are now defined directly in
each stack's main.tf instead of through a wrapper module.

- Merge module/main.tf contents into stack main.tf
- Apply variable replacements (var.tier -> local.tiers.X, renamed vars)
- Fix shared module paths (one fewer ../ at each level)
- Move extra files/dirs (factory/, chart_values, subdirs) to stack root
- Update state files to strip module.<name>. prefix
- Update CLAUDE.md to reflect flat structure

Verified: terragrunt plan shows 0 add, 0 destroy across all stacks.
2026-02-22 15:13:55 +00:00

9.4 KiB

phase verified status score re_verification
05-sandbox-proxy-hardening 2026-02-17T23:15:00Z passed 8/8 must-haves verified false

Phase 5: Sandbox and Proxy Hardening Verification Report

Phase Goal: When direct video extraction fails, the proxied page is rendered safely in a sandbox that blocks popups, ads, and access to the parent page

Verified: 2026-02-17T23:15:00Z

Status: PASSED

Re-verification: No — initial verification

Goal Achievement

Observable Truths

# Truth Status Evidence
1 When direct video extraction fails, the full proxied page renders inside a shadow DOM sandbox on the app's page ✓ VERIFIED streams.js line 188: tryExtractVideo calls renderSandboxFallback; line 241: attachShadow({ mode: 'closed' })
2 The sandbox blocks window.open, top-frame navigation, popup creation, and alert/confirm/prompt dialogs ✓ VERIFIED streams.js lines 252-257: overrides for window.open, alert, confirm, prompt, top, parent all present
3 The sandbox prevents proxied content from accessing parent page cookies and localStorage ✓ VERIFIED streams.js lines 258-260: document.cookie, localStorage, sessionStorage all blocked via Object.defineProperty
4 Known ad/tracker scripts and domains are stripped from proxied content before serving ✓ VERIFIED sanitize.go lines 26-51: removes script/link/img/iframe/object/embed from blocked domains; blocklist.go: 49+ blocked domains
5 Relative URLs in proxied content are rewritten to route through the proxy ✓ VERIFIED sanitize.go lines 114,119: relative URLs rewritten with proxyPrefix + QueryEscape
6 All proxied content is served with strict CSP headers scoped to the sandbox context ✓ VERIFIED proxy.go lines 195,207,216: sandboxCSP header set on all responses from ServeSandbox

Score: 6/6 truths verified (100%)

Required Artifacts

Plan 05-01 Artifacts

Artifact Expected Status Details
internal/proxy/sanitize.go HTML sanitizer that strips ads, rewrites URLs, and adds CSP ✓ VERIFIED Exists, 149 lines; exports Sanitize function; contains rewriteAttrs, hostBlocked, resolveURL helpers
internal/proxy/blocklist.go Ad/tracker domain blocklist for script filtering ✓ VERIFIED Exists, 98 lines; 49 blocked domains in map; IsBlockedDomain with parent-domain walk-up
internal/proxy/proxy.go New /proxy/sandbox endpoint serving sanitized content ✓ VERIFIED Exists; ServeSandbox method at line 140; calls Sanitize at line 213; sets CSP headers
internal/server/server.go Route registration for sandbox proxy endpoint ✓ VERIFIED Line 61: route registered "GET /proxy/sandbox" -> s.proxy.ServeSandbox

Plan 05-02 Artifacts

Artifact Expected Status Details
static/js/streams.js Shadow DOM sandbox fallback replacing iframe fallback ✓ VERIFIED Exists; renderSandboxFallback at line 230; attachShadow({ mode: 'closed' }) at line 241; sandbox script injection lines 249-263
From To Via Status Details
internal/server/server.go internal/proxy/proxy.go route registration for /proxy/sandbox ✓ WIRED server.go:61 registers "GET /proxy/sandbox" to s.proxy.ServeSandbox
internal/proxy/proxy.go internal/proxy/sanitize.go ServeSandbox calls Sanitize ✓ WIRED proxy.go:213 calls Sanitize(doc, parsed, "/proxy/sandbox")
static/js/streams.js /proxy/sandbox fetch call to get sanitized HTML for shadow DOM injection ✓ WIRED streams.js:235 fetches '/proxy/sandbox?url=' + encodeURIComponent(streamURL)
static/js/streams.js static/js/streams.js tryExtractVideo falls back to renderSandboxFallback ✓ WIRED streams.js:188 calls renderSandboxFallback when extraction fails

Requirements Coverage

Requirement Source Plan Description Status Evidence
EMBED-03 05-02 When direct extraction fails, fall back to rendering the full proxied page in a shadow DOM sandbox ✓ SATISFIED tryExtractVideo calls renderSandboxFallback; shadow DOM created with mode: 'closed'
EMBED-04 05-02 Shadow DOM sandbox blocks window.open, window.top navigation, popup creation, and alert/confirm/prompt ✓ SATISFIED Sandbox script overrides all 7 dangerous APIs (window.open, alert, confirm, prompt, top, parent, location)
EMBED-05 05-02 Shadow DOM sandbox prevents access to parent page cookies and localStorage ✓ SATISFIED document.cookie returns '', localStorage/sessionStorage return null via Object.defineProperty
EMBED-06 05-01 Proxy strips known ad/tracker scripts and domains from proxied content before serving ✓ SATISFIED Sanitizer removes script/link/img/iframe/object/embed from 49+ blocked domains
EMBED-07 05-01 Proxy rewrites relative URLs in proxied content to route through the proxy ✓ SATISFIED rewriteAttrs rewrites src/href/action/poster/data attributes through /proxy/sandbox
EMBED-08 05-01 All proxied content served with strict CSP headers scoped to the sandbox context ✓ SATISFIED sandboxCSP constant applied to all ServeSandbox responses (HTML and non-HTML)

Coverage: 6/6 requirements satisfied (100%)

Orphaned requirements: None — all requirements from REQUIREMENTS.md phase 5 mapping are claimed by plans

Anti-Patterns Found

File Line Pattern Severity Impact
- - None detected - -

Scanned files:

  • internal/proxy/sanitize.go — clean, no placeholders or stubs
  • internal/proxy/blocklist.go — clean, 49 real domains
  • internal/proxy/proxy.go — clean, full ServeSandbox implementation
  • static/js/streams.js — clean, complete shadow DOM sandbox with all overrides

Checks performed:

  • No TODO/FIXME/PLACEHOLDER comments found
  • No empty return statements (return null, return {}, return [])
  • No console.log-only implementations
  • All functions have substantive implementations

Human Verification Required

1. Shadow DOM Isolation Effectiveness

Test: Open a stream that fails video extraction. Inspect browser DevTools Console while the sandbox content loads.

Expected:

  • No popup windows appear
  • No alert/confirm/prompt dialogs appear
  • Parent page cookies remain accessible to parent JavaScript (verify document.cookie in parent console)
  • Shadow DOM content cannot access parent cookies (blocked by override)

Why human: Requires real browser testing to verify runtime isolation behavior. Cannot be verified by static analysis.

2. Ad/Tracker Blocking Effectiveness

Test: Load a stream page through /proxy/sandbox that is known to have ads (use a popular streaming site). Inspect Network tab.

Expected:

  • Requests to doubleclick.net, googlesyndication.com, etc. are NOT made
  • Ad scripts stripped from DOM before rendering
  • Page still functional (video player scripts kept)

Why human: Requires visual inspection and network monitoring to verify ads are actually blocked without breaking video playback.

3. Relative URL Rewriting

Test: Load a stream page through /proxy/sandbox that uses relative URLs for images, CSS, or sub-resources.

Expected:

  • All relative URLs (src="img/logo.png") are rewritten to route through /proxy/sandbox?url=...
  • Sub-resources load correctly (no broken images or missing CSS)
  • Protocol-relative URLs (//cdn.example.com/style.css) are rewritten

Why human: Requires testing with real stream pages that use various URL patterns. Static analysis confirms the rewrite logic exists but cannot verify it handles all edge cases.

4. CSP Header Enforcement

Test: Open browser DevTools Console while a sandboxed stream loads. Check Network tab response headers.

Expected:

  • All /proxy/sandbox responses have Content-Security-Policy header
  • Header value matches sandboxCSP constant (default-src 'self'; script-src 'unsafe-inline'; ...)
  • CSP violations (if any) appear in console but don't break video playback

Why human: CSP enforcement is browser-side. Need to verify headers are present and effective without breaking player functionality.

5. Graduated Fallback Chain

Test: Test with three types of streams:

  1. Stream with extractable HLS/MP4 source
  2. Stream where extraction fails but proxy works
  3. Stream URL that returns 404/500

Expected:

  1. Native HTML5 video player renders
  2. Shadow DOM sandbox renders the proxied page
  3. "Open stream directly" link appears

Why human: Requires testing multiple failure scenarios to verify the fallback chain works correctly at each level.

Gaps Summary

No gaps found. All must-haves verified, all requirements satisfied, no anti-patterns detected.

Implementation quality:

  • ✓ Backend: Complete sanitizer with 49 blocked domains, URL rewriting, and CSP enforcement
  • ✓ Frontend: Closed shadow DOM with 7 dangerous API overrides (window.open, alert, confirm, prompt, top, parent, cookie, localStorage, sessionStorage)
  • ✓ Wiring: All key links verified (route registration, Sanitize call, fetch to /proxy/sandbox)
  • ✓ Fallback: Graduated fallback chain (native player > sandbox > direct link)

Ready to proceed: Yes. Phase 5 goal achieved. All 6 EMBED requirements (EMBED-03 through EMBED-08) satisfied.


Verified: 2026-02-17T23:15:00Z

Verifier: Claude (gsd-verifier)