Files
Kordant/tasks/security-fixes/02-fix-puppeteer-ssrf-report-gen.md
2026-05-29 09:03:47 -04:00

2.7 KiB

02. Fix SSRF via Puppeteer --no-sandbox in report generation

meta: id: security-fixes-02 feature: security-fixes priority: P1 depends_on: [] tags: [implementation, tests-required, medium-severity]

objective:

  • Prevent SSRF and local file read in the PDF report generator by sandboxing Puppeteer network access

deliverables:

  • Add request interception in Puppeteer to block dangerous URL schemes (file://, metadata endpoints, internal IPs)
  • Remove or mitigate the --no-sandbox flag where possible
  • Add tests verifying that blocked URLs are not accessible

steps:

  1. Examine generatePDF() at web/src/server/services/reports/generator.ts:141-150 and compileData() at lines 53-137
  2. Add page.setRequestInterception(true) before page.setContent() to intercept all network requests
  3. Implement a request filter that blocks:
    • file:// scheme (local file read)
    • Cloud metadata endpoints (169.254.169.254, metadata.google.internal, etc.)
    • Internal IP ranges (10.x.x.x, 172.16-31.x.x, 192.168.x.x, 127.x.x.x)
    • data: URIs that could load arbitrary content
  4. If --no-sandbox is required by the deployment environment (e.g., Docker), document the risk and add a compensating control (Chrome flags, network namespace isolation)
  5. Add unit tests for the request interception filter

tests:

  • Unit: Request interception blocks file://, data:, internal IPs, and cloud metadata endpoints
  • Unit: Request interception allows legitimate external URLs (CDN assets, fonts, etc.)
  • Integration: Attempting to load a report with embedded file:///etc/passwd does not succeed
  • Integration: Report generation still produces valid PDFs for legitimate content

acceptance_criteria:

  • Puppeteer page cannot make network requests to blocked URL schemes or internal IPs
  • file:// URLs are blocked, preventing local file read
  • Cloud metadata endpoints are blocked
  • Report PDFs still render correctly for legitimate content
  • The --no-sandbox flag is either removed or has documented compensating controls

validation:

  • cd web && bun test — all tests pass
  • Attempt to inject a file:///etc/passwd URL through report data and verify it is blocked
  • Verify report generation still produces valid PDFs

notes:

  • Finding p8-002: The --no-sandbox flag is likely required in containerized environments; if so, network-level sandboxing via request interception is the compensating control
  • Consider using page.setContent(html, {waitUntil: 'networkidle0'}) with interception enabled
  • The compileData() function builds HTML from database data — any user-controlled data in reports could include <img src="file://..."> or <link href="http://169.254.169.254/...">