42
TODO.md
42
TODO.md
@@ -18,7 +18,9 @@ dotfiles; `LICENSE` is the only mandated file still missing. Every external
|
||||
reference in the repo is now pinned by cryptographic hash (or, for the wrangler
|
||||
CLI install, an exact version), and the Hugo that builds the published site is a
|
||||
deliberate pinned version rather than whatever the base image's package repo
|
||||
serves.
|
||||
serves. The site now ships a Cloudflare Pages `_headers` file, so its response
|
||||
security headers are declared in the repo instead of being whatever the edge
|
||||
defaults to — unverified in production until the next deploy.
|
||||
|
||||
# Next Step
|
||||
|
||||
@@ -28,6 +30,31 @@ remaining policy scaffold is otherwise complete.
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-08-09: added `static/_headers` so Cloudflare Pages serves baseline
|
||||
response security headers (closes #14). Hugo copies `static/` verbatim into
|
||||
`public/`, which is the deploy root Pages reads the file from; this is the
|
||||
first root-level `static/` in the repo, and the built tree confirms it unions
|
||||
with the theme's rather than shadowing it — `public/css/style.css` and
|
||||
`public/index.html` are byte-identical to the previous build and the static
|
||||
file count goes 1 to 2. The live "before" was measured, not assumed:
|
||||
Cloudflare already sends `X-Content-Type-Options` and `Referrer-Policy` by
|
||||
default, so the substance here is `Strict-Transport-Security`,
|
||||
`Content-Security-Policy`, `X-Frame-Options` and `Permissions-Policy`. The CSP
|
||||
is `default-src 'none'` with `style-src 'unsafe-inline'`, which the built page
|
||||
supports exactly: it has no script, img, link, iframe, form or media element
|
||||
and no `style=`/`on*=` attribute, only the one inline `<style>` block
|
||||
`baseof.html` fills by `readFile`. Verified in a headless Chrome against a
|
||||
local server that parses the committed `_headers` and applies it as real
|
||||
response headers: zero CSP violations, the inlined stylesheet parses to 17
|
||||
rules and the computed body padding, tagline colour and link colour all come
|
||||
from the theme CSS, framing the page from another origin is refused by
|
||||
`frame-ancestors 'none'` (consistent with `X-Frame-Options: DENY`), and all
|
||||
five named outbound links still navigate with status 200. HSTS carries neither
|
||||
`preload` nor `includeSubDomains`: `www.lora.vegas` is the only other name in
|
||||
DNS and it is served by this same Pages project, so this file sets HSTS on its
|
||||
responses directly, and `includeSubDomains` would instead bind every future
|
||||
subdomain for a year with no way to walk it back inside the max-age window
|
||||
without also dropping the apex protection.
|
||||
- 2026-08-09: restructured `README.md` into the canonical section set (closes
|
||||
#11): a Description first line, then Getting Started, Entrypoints, Rationale,
|
||||
Design, TODO, License, Author. The non-standard About / Contributing /
|
||||
@@ -178,6 +205,19 @@ remaining policy scaffold is otherwise complete.
|
||||
here (#20)
|
||||
- Move the deploy container to a pinned node 22 so the wrangler pin can advance
|
||||
past 4.86.0 (#21)
|
||||
- After the next deploy, confirm the `_headers` file actually took effect, on
|
||||
both `https://lora.vegas/` and `https://www.lora.vegas/`: `curl -sSI` against
|
||||
each must show `strict-transport-security` or `content-security-policy`.
|
||||
Cloudflare Pages silently ignores a malformed `_headers`, and checking
|
||||
`x-content-type-options` would pass either way because the edge sends it
|
||||
regardless. `www` has to be checked too and not just the apex: dropping
|
||||
`includeSubDomains` rests on `www.lora.vegas` being served by this same Pages
|
||||
project, which was established behaviourally from identical response bodies
|
||||
rather than from the Cloudflare dashboard. If `www` turns out not to be
|
||||
covered, the `includeSubDomains` decision has to be revisited (#14)
|
||||
- Decide the HSTS `includeSubDomains` and `preload` posture for `lora.vegas`.
|
||||
Both are owner calls: neither can be walked back inside the max-age window,
|
||||
and `includeSubDomains` binds hostnames this repo does not control (#14)
|
||||
- Sync the reformat of `REPO_POLICIES.md` back upstream to `prompts` so the
|
||||
canonical copy is clean under the shared prettier settings and future syncs
|
||||
are a straight byte copy
|
||||
|
||||
42
static/_headers
Normal file
42
static/_headers
Normal file
@@ -0,0 +1,42 @@
|
||||
# Cloudflare Pages response headers.
|
||||
#
|
||||
# Hugo copies static/ verbatim into public/, so this file lands at the
|
||||
# deploy output root, which is where Pages reads it from. Pages consumes
|
||||
# the file rather than publishing it. Values here override what
|
||||
# Cloudflare would otherwise send.
|
||||
#
|
||||
# Every value below was checked against the built public/index.html, not
|
||||
# copied from a template. That page loads nothing: no script, img, link,
|
||||
# iframe, form, video, audio, object or embed element, no style= or on*=
|
||||
# attribute. It has exactly one inline <style> block, which
|
||||
# themes/loravega/layouts/_default/baseof.html fills with the whole of
|
||||
# themes/loravega/static/css/style.css via readFile. That inlining is a
|
||||
# deliberate theme design choice, and it is the sole reason style-src
|
||||
# needs 'unsafe-inline'.
|
||||
#
|
||||
# img-src 'self' is kept even though the page has no images. Browsers
|
||||
# request /favicon.ico unprompted and that fetch is governed by img-src;
|
||||
# measured in Chrome, with this allowance the request is made and 404s,
|
||||
# and without it the request is suppressed outright. Neither hurts
|
||||
# today, but same-origin images are the one resource class this site
|
||||
# would plausibly grow, and 'self' loosens nothing cross-origin.
|
||||
#
|
||||
# X-Content-Type-Options and Referrer-Policy are already sent by
|
||||
# Cloudflare by default and are restated here on purpose. They are a
|
||||
# default, not a guarantee, and this file is where the site's header
|
||||
# posture is declared.
|
||||
#
|
||||
# Strict-Transport-Security deliberately carries neither preload nor
|
||||
# includeSubDomains. preload is effectively irreversible and is the
|
||||
# owner's call. includeSubDomains would bind every hostname under
|
||||
# lora.vegas for a year, and it buys nothing today: www.lora.vegas is
|
||||
# the only other name in DNS, it is served by this same Pages project,
|
||||
# so this block sets HSTS on its responses directly.
|
||||
|
||||
/*
|
||||
Strict-Transport-Security: max-age=31536000
|
||||
X-Content-Type-Options: nosniff
|
||||
Referrer-Policy: strict-origin-when-cross-origin
|
||||
X-Frame-Options: DENY
|
||||
Permissions-Policy: geolocation=(), microphone=(), camera=()
|
||||
Content-Security-Policy: default-src 'none'; style-src 'unsafe-inline'; img-src 'self'; form-action 'none'; frame-ancestors 'none'; base-uri 'none'
|
||||
Reference in New Issue
Block a user