2.8 KiB
2.8 KiB
Security posture
This site is meant to demonstrate the discipline it advertises. Security is treated as acceptance criteria, not polish.
Attack surface
- Static output - no server runtime, no database, no user input, no forms.
- Served by
nginx-unprivileged(uid 101, read-only root filesystem, all Linux capabilities dropped, no service-account token) on Kubernetes. - Exposed outbound-only via a Cloudflare Tunnel - no open inbound ports, a single public hostname, no catch-all.
Headers
Split by where they belong:
Origin (nginx, ships in the image - nginx/security-headers.conf)
| Header | Value |
|---|---|
| Content-Security-Policy | default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; worker-src 'self'; manifest-src 'self'; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'; upgrade-insecure-requests |
| X-Content-Type-Options | nosniff |
| X-Frame-Options | DENY |
| Referrer-Policy | strict-origin-when-cross-origin |
| Permissions-Policy | camera/mic/geo/payment/usb... all denied |
| Cross-Origin-Opener-Policy | same-origin |
| Cross-Origin-Resource-Policy | same-origin |
Edge (Cloudflare dashboard) - HSTS, HTTP->HTTPS redirect, SSL Full, Bot Fight,
rate-limiting. HSTS ships without includeSubDomains/preload initially because
*.bztmon.com resolves to the WAN and the subdomain form would brick non-public hosts.
CSP notes
script-src 'self'with zero inline scripts. All JS (pre-paint theme, toggle, scroll reveal) lives in one external/site.js. Nounsafe-inline, nounsafe-eval, no hashes to maintain.style-srcallows'unsafe-inline'- the one conscious exception. Shiki's dual-theme syntax highlighting emits per-token CSS custom-properties as inlinestyleattributes; hashing them is impractical (they vary per page). The security-critical directive (script-src) stays strict. Everything else is self-hosted: fonts are system stacks (zero font requests), no third-party scripts, so SRI is moot.- CSP is emitted as a real HTTP header (not
<meta>) soframe-ancestorsis honoured and header scanners can see it.
Build-time gate
npm run scan (scripts/check-build.sh) fails the build on:
- secret-like strings in
dist/ - any inline
<script>(would break the strict CSP) - third-party
<script>/<link>origins
Runs in CI before the image is built.
Targets (verified post-deploy)
- securityheaders.com -> A+
- Mozilla Observatory -> A/A+ (small deduction expected for
style-src 'unsafe-inline') - Lighthouse >= 95 across Performance / Accessibility / Best Practices / SEO
- CSP: zero console violations in a real browser (incl. code blocks + diagrams)
Reporting
Found something? Email the address on the site.