OWASP Top 10 Compliance
Plate is assessed against the OWASP Top 10 (2021). Plate is a read-only presentation frontend — it stores no user data, has no authentication, and performs no write operations. This significantly limits the attack surface compared to a full application.
This document records the current compliance state and known gaps.
Evaluation
| # | Risk | Status | Notes |
|---|---|---|---|
| A01 | Broken Access Control | ✅ | No access control needed — Plate serves public read-only content. The only protected resource is the Prometheus metrics endpoint, which binds to localhost:9091 by default and is never exposed on the public interface |
| A02 | Cryptographic Failures | ✅ | No sensitive data stored or transmitted by Plate — no passwords, tokens, or PII. TLS termination is explicitly delegated to the reverse proxy; HSTS is disabled in the app to avoid poisoning local dev environments (documented in src/server.ts) |
| A03 | Injection | ✅ | Post and page content is rendered through rehype-sanitize with a strict allowlist schema — <script> tags, event handlers, and javascript: hrefs are stripped before HTML reaches the browser. Handlebars templates use HTML-escaped {{value}} by default; unescaped {{{contentHtml}}} is used only for pre-sanitized rendered HTML. Route parameters (slug, page) are forwarded to Snackbox as typed URL segments via Axios — no SQL, no shell execution, no eval |
| A04 | Insecure Design | ✅ | Read-only by design — no write surface, no user accounts, no file uploads. Rate limiting applied globally to all routes. 500 error responses show a generic "Something went wrong" message; internal error detail is logged server-side only |
| A05 | Security Misconfiguration | ✅ | Helmet enforces a strict CSP: defaultSrc 'none' with explicit allowlists for styles, images, scripts, and fonts. X-Powered-By is removed. unsafe-inline for styles is intentional and documented — themes inject CSS custom properties; CSS cannot execute code and rehype-sanitize strips style attributes from content. HSTS, COEP, and upgrade-insecure-requests are each disabled for documented reasons |
| A06 | Vulnerable and Outdated Components | ✅ | npm audit --audit-level=high runs on every MR and branch pipeline and blocks deployment on high or critical findings. A separate scheduled job runs a full audit for visibility. GitLab Container Scanning is enabled with CS_SEVERITY_THRESHOLD: HIGH |
| A07 | Identification and Authentication Failures | N/A | No authentication in Plate — it is a fully public read-only frontend |
| A08 | Software and Data Integrity Failures | ✅ | npm ci installs exact versions from package-lock.json. Docker image is built from a pinned node:24-alpine tag (not latest). CSP defaultSrc 'none' prevents loading any external scripts or styles, making SRI unnecessary. External themes loaded from THEMES_DIR are operator-provided and trusted by design |
| A09 | Security Logging and Monitoring Failures | ✅ | Structured JSON logging via Pino to stdout — compatible with log aggregation (journald, Loki, ELK, etc.). HTTP access logging via pino-http. Prometheus metrics expose request counts and durations per route and status code. Log aggregation and alerting are operator responsibilities |
| A10 | Server-Side Request Forgery | ✅ | Plate makes outbound HTTP requests only to SNACKBOX_API_URL, a fully operator-controlled value. No user-supplied URLs are ever fetched. No redirects are followed based on user input |
Summary
Fully compliant (9 / 10): Broken Access Control, Cryptographic Failures, Injection, Insecure Design, Security Misconfiguration, Vulnerable and Outdated Components, Software and Data Integrity Failures, Security Logging and Monitoring Failures, Server-Side Request Forgery.
Not applicable (1 / 10): Identification and Authentication Failures.