You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vibed/pages/.beans/pages-saom--core-policy-mod...

33 lines
2.3 KiB
Markdown

---
# pages-saom
title: 'Core policy modules: slug, mime, upload validation, headers'
status: completed
type: feature
priority: normal
created_at: 2026-07-13T02:09:08Z
updated_at: 2026-07-13T02:42:56Z
parent: pages-77lk
blocked_by:
- pages-lxjz
---
Implement src/core/ per design doc §2.2, §2.3, §2.6, §4.5 — pure Rust, no worker deps, compiles + unit-tested natively.
slug.rs: validate_slug() enforcing ^[a-z0-9][a-z0-9-]{0,62}$ after lowercasing, reserved-name list from design §2.3, and default_slug_from_filename() (my-project.zip -> my-project, lowercased, invalid chars -> hyphen, collapse repeats/trim).
mime.rs: extension->MIME allowlist (html, htm, css, js, mjs, json, txt, md, png, jpg, jpeg, gif, svg, webp, avif, ico, woff, woff2, ttf, otf, wasm, map, xml, webmanifest, mp3, mp4, webm, pdf); unknown -> application/octet-stream.
upload.rs: UploadLimits (10 MiB compressed, 25 MiB uncompressed total, 5 MiB per file, 300 entries, path len 180, depth 10) and build_manifest(kind, bytes) -> Result<Vec<(String, Vec<u8>)>, UploadError>. Html kind -> single index.html entry. Zip kind: use zip crate, reject path traversal (.. segments, absolute, backslash), reject duplicates, skip directories, enforce limits while decompressing (count streamed bytes, abort on breach), require root index.html. Typed UploadError with user-facing Display messages.
headers.rs: constants for CSP sandbox value (design §2.1), nosniff, referrer-policy, cache-control.
- [x] slug.rs + tests (valid/invalid/reserved/default-name derivation)
- [x] mime.rs + tests
- [x] upload.rs + tests (traversal, bomb via high-ratio zip, dup entries, missing index.html, happy paths html+zip) using zip writer in tests to build fixtures
- [x] headers.rs + tests
- [x] full validation suite passes (both targets)
## Summary of Changes
src/core/ implemented by a Sonnet agent, adversarial Opus review (verdict: APPROVE, no bypasses found).
- slug.rs (grammar + 17 reserved names + default_slug_from_filename), mime.rs (allocation-free allowlist), upload.rs (streaming zip validation: traversal, bombs, limits; html manifest), headers.rs (CSP sandbox etc.).
- 72 native unit tests + 4 doctests incl. crafted-zip attack fixtures and accept-at-limit boundary cases (added post-review).
- Design doc §2.2 wording reconciled (path length is bytes).