{"slug":"web-viewer-3877","total":12,"limit":100,"offset":0,"since":null,"commits":[{"hash":"d7af8ce","date":"2026-05-20 00:27:45 -0700","author":"Steve","subject":"add: pre-stage Meta Pixel snippet (placeholder; flip via _dw-batch set-fb-pixel.sh)","body":"Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"7a4c7ef","date":"2026-05-19 22:15:04 -0700","author":"Steve","subject":"add LEDGER NOTE flagging permissive CORS for follow-up","body":"SSRF was the prioritized hardening; CORS origin:\"*\" is intentionally\nleft in place because the only known client (public/index.html) is\nsame-origin and tightening to a guessed allowlist could break an\nundocumented embed. Note records the trade-off + recommends a\nCORS_ALLOWED_ORIGINS env-var as the next step.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"0a1b751","date":"2026-05-19 22:14:38 -0700","author":"Steve","subject":"wire 'npm test' to the SSRF test suite","body":"Makes the guard tests discoverable via the standard npm-script path so\nfuture audits / CI can run them without spelunking for the file.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"5c4df7c","date":"2026-05-19 22:14:24 -0700","author":"Steve","subject":"add test/ssrf.test.js — 25 SSRF guard cases, zero deps","body":"Covers the 4 required cases (file:// → 400, http://localhost/ → 403,\nhttp://169.254.169.254/ → 403, https://example.com/ resolves), plus\nIPv6 loopback, IPv4-mapped-loopback, gopher/javascript/ftp/data\nscheme rejection, every IPv4 private CIDR (RFC1918 + CGNAT + link-local\n+ multicast + boundary), redirect-target re-validation, and malformed\nURL handling. No mocha/jest/chai — pure node + console PASS/FAIL,\nprocess exits non-zero on any failure. Run via: node test/ssrf.test.js\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"21bf7c6","date":"2026-05-19 22:14:14 -0700","author":"Steve","subject":"route /api/fetch + Socket.IO load-url through safeFetch()","body":"Replaces the direct axios.get(userUrl) call in fetchWebContent() with\nlib/safe-fetch.js. Both POST /api/fetch and io.on('load-url') now share\nthe same hardened path — no bypass. The API layer translates\nerr.statusCode (400 / 403 / 502) onto the HTTP response so clients see\nthe right code instead of a blanket 500.\n\nCloses SSRF surface area at server.js:32-101 + :114-129 (file://,\nhttp://127.0.0.1:9xxx pm2 dashboards, 169.254.169.254 cloud metadata,\nlocalhost:5432 Postgres, etc.).\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"f15b9a4","date":"2026-05-19 22:14:05 -0700","author":"Steve","subject":"add lib/safe-fetch.js — SSRF-hardened wrapper around axios","body":"Enforces scheme allowlist (http/https only), DNS-resolves the hostname\nand rejects any private/internal/link-local/loopback/CGNAT/multicast/\nIPv6-ULA target, pins the resolved IP into the axios request to defeat\nDNS rebinding, follows redirects manually (max 5 hops, re-validates each\nLocation), and caps response size at 5 MB with a 10 s timeout. Errors\ncarry .statusCode and .isSsrfBlock for the API layer to translate.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"83bda2d","date":"2026-05-19 21:36:07 -0700","author":"steve","subject":"fix .gitignore: expand broken literal \"\\n\" line + dedupe + add snapshot patterns","body":"Line 1 was a single 39-char string containing literal backslash-n sequences\n(\"node_modules/\\n.next/\\n…\") that never got expanded — ignored nothing real.\nReplaced with proper newline-separated entries, deduped node_modules / .env*\n/ *.log / dist / .next blocks, added snapshot-file patterns to match the new\n404-guard middleware.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"3be3c67","date":"2026-05-19 21:35:27 -0700","author":"steve","subject":"add 404-guard middleware for snapshot/editor file patterns before express.static","body":"Defense-in-depth per fleet-refactor-sweep standing rule — blocks *.bak, *.orig,\n*.rej, *.swp, *.pre-*, *~ even if such a file accidentally lands in public/.\n\nCo-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>"},{"hash":"62c7166","date":"2026-05-13 08:58:43 -0700","author":"Steve","subject":"snapshot: 1 file(s) changed, ~1 modified","body":""},{"hash":"f9fefeb","date":"2026-05-07 12:29:55 -0700","author":"Steve","subject":"untrack node_modules per standing rule (was tracking 2555 files; files preserved on disk)","body":""},{"hash":"11b2d6c","date":"2026-05-07 12:29:34 -0700","author":"Steve","subject":"tighten .gitignore: add missing standing-rule patterns (node_modules/ .env* *.log dist/ .next/)","body":""},{"hash":"b85fd67","date":"2026-05-06 10:25:54 -0700","author":"Steve Abrams","subject":"initial scaffold (gitify-all 2026-05-06)","body":""}]}