From d23fe805546e992c8033d64d7177fe1454ad7716 Mon Sep 17 00:00:00 2001 From: grm Date: Fri, 18 Sep 2026 13:49:19 +0300 Subject: Security: Send security headers; refuse to frame the dashboard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pages carried no X-Content-Type-Options, no Referrer-Policy, and the dashboard could be framed by any site (clickjacking a superadmin's delete buttons). Every response now says nosniff and strict-origin-when-cross-origin, and the management paths on the root host add X-Frame-Options: DENY plus a CSP of frame-ancestors, base-uri, form-action and object-src — the directives that do not touch the dashboard's inline scripts, which are a product constraint. Blog pages get no framing rule: they are the owner's content and may be embedded on purpose. HSTS moves into the same helper. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Sd8UPWrvyYCLj97JexNw3A --- AGENTS.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'AGENTS.md') diff --git a/AGENTS.md b/AGENTS.md index 9764661..986d21d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -147,7 +147,14 @@ internal/web/ server.go (host router, middleware, render helpers) logins are logged with the username and address, and the login body is capped at `maxLoginBody` (64 KB) since it is the one POST outside `guardPOST`. `s.clientIP` is the peer address, or the last - `X-Forwarded-For` hop with `TRUST_PROXY` (earlier entries are forgeable). Flood + `X-Forwarded-For` hop with `TRUST_PROXY` (earlier entries are forgeable). + `secureHeaders` (`ServeHTTP`) puts `nosniff`, a `Referrer-Policy` and, + with `HTTPS`, HSTS on every response; the management paths on the root + host (`managementPaths` — add a new top-level management prefix there + as well as to `reservedPageSlugs`) also get `X-Frame-Options: DENY` and + a CSP of `frame-ancestors`/`base-uri`/`form-action`/`object-src` only — + no `script-src`, the dashboard's inline scripts are a product + constraint. Public blog pages carry no framing rule (owner content). Flood control for everything else stays at the reverse proxy (`limit_req`). - **Templates**: each page file is parsed together with its layout (`layouts/dashboard.html` or `layouts/blog.html` for `blog/*`) and all -- cgit v1.2.3