Vulnerability scanning during hosted-site deploys

Every hosted-site deploy runs a vulnerability scan over every file before any byte lands in your bucket. Critical findings (committed credentials) abort the deploy outright; warnings show in the deploy log; informational notes are silent unless you ask for them.

Why we run it: it's surprisingly easy to commit a .env file or a hard-coded API key to a repo. Catching that BEFORE it ships to a public URL beats catching it after.

Scan classes:

CRITICAL (blocks deploy):
• AWS access key IDs (AKIA…)
• AWS secret access keys near "aws"/"secret" identifiers (40-char base64)
• Stripe live keys (sk_live_…) and restricted keys (rk_live_…)
• GitHub tokens (ghp_, gho_, ghs_, github_pat_…)
• OpenAI API keys (sk-…, sk-proj-…)
• Anthropic API keys (sk-ant-…)
• Google API keys (AIza…)
• Slack tokens (xox[abp]-…)
• Private keys (-----BEGIN … PRIVATE KEY-----)

WARNING (allowed but surfaced):
• JWT tokens (eyJ…eyJ…)
• Password assignments with high-entropy values
• eval(…) calls
• new Function(…) constructors

INFO (silent):
• .innerHTML = … assignments (XSS audit)
• document.write(…) (deprecated XSS risk)

How findings surface:
• A blocked deploy throws a clear error in the deploy log: "Deploy blocked by vulnerability scan: <path>:<line> (<ruleId>); ..."
• Warnings + info populate hostedSites/{siteId}.lastDeploy.scanFindings (up to 25 per deploy) with path, line hint, severity, message, and a redacted snippet
• High-entropy secret values are masked in the snippet (first 3 + last 3 chars + length) so the deploy log doesn't leak the secret

What's scanned: text files only. Binary file types (images, fonts, video, archives) are skipped via extension allowlist. Files >5 MB are skipped (heuristic: "probably binary"). Per-file finding cap of 30; total cap of 200.

Skip lists: example/sample/test paths are skipped for noisy rules (jwt, password) so demo code doesn't trigger false alarms. Specifically: example*, sample*, demo*, readme.md, docs/*, __tests__/*, *.test.*

If the scan blocks your deploy:
1. Read the error — it lists path:line and ruleId
2. Rotate the leaked credential at the source (AWS console, Stripe dashboard, etc.) immediately
3. Remove from the repo and from history (git filter-repo or BFG)
4. Push the cleaned commit — the next deploy attempt will run the scan again

False positives: if the rule fired on something that's safely public (e.g. a published Google Maps API key restricted to your domains), you have two choices:
1. Move the value into a build-time env var so it's not in the repo
2. Move the file under one of the skip paths (docs/, example/, etc.) if it's documentation rather than runtime code

Phase 2 will add a per-site allowlist UI for explicit "this is fine" exceptions. Today the rule set is intentionally narrow (regex-based, pure JS, no Docker, no third-party scanner) so you can roll forward fast.