Files
EpicNext-Cms/docs/operations/security-review-2026-09-13.md
T
Simo fe34d4ac93
CI / check (push) Failing after 1m46s
CI / deploy (push) Skipped
CI / publish-container (push) Skipped
fix(news): enforce shared comment publication and moderation rules
2026-09-13 20:14:32 +02:00

4.6 KiB

Security report verification — 2026-09-13

The supplied review describes commit baeb54ae plus a separate port for another hotel. Its “Fixed” labels were not evidence that the changes existed in EpicNext-Cms. This verification inspected canonical main at 52f6d149 and the corrective changes prepared here. No exploit or authenticated mutation was performed against production.

Supplied finding Verified state in baseline Correction / remaining boundary
1. Logo authorization/upload Confirmed missing action permission and per-file validation Require settings edit before input or storage access; bounded decoded raster uploads
2. Favicon authorization/delete Confirmed missing action permission; SVG accepted Same permission boundary for create/delete, bounded raster/ICO validation
3. Active uploaded SVG Confirmed SVG served inline without route CSP Route CSP sandbox and nosniff on success/errors; existing SVG served as attachment
4. Client IP spoofing Confirmed direct trust in caller-controlled x-real-client-ip Shared validated resolver ignores that header. Forwarded headers still require trusted ingress that overwrites them and prevents direct public origin access
5. Email token action exports Confirmed token helpers in a use server module Move token creation/validation and delivery to a server-only module. Registration and verification call it internally
6. Locale cookie Confirmed missing allowlist Supported locales only, validate before reading/writing cookies
7. Email header injection Confirmed unsanitized values in sendmail headers Reject control characters before any mail transport or file fallback; includes configured sender
8. Gateway CORS Supplied gateway path is outside this repository Read-only GET to our /api/health with an unrelated Origin returned a fixed https://epicnabbo.nl allow-origin and no allow-credentials. This does not reproduce the report on that route, nor certify every host/route
9. Token abilities Confirmed abilities and owner type not checked by bearer authentication Enforce User owner type and explicit endpoint abilities; existing wildcard user tokens remain supported
10. Broad script CDN Confirmed unrestricted jsDelivr script source, without a source-code consumer Remove the broad script source; retain required captcha/analytics sources and nonce

The additional withNitroStaff code and its tests mentioned in the supplied port do not exist in this checkout; they were not assumed to have been reviewed or imported.

Evidence and limits

  • Regression tests exercise authorization before I/O, actual file decoding, SVG/error response headers, token-boundary exports, token abilities, forged derived-IP headers across consumers, locale values, and mail header control characters.
  • An updated pnpm audit --json reported zero known advisories. This is a dependency database result, not proof that application code has no vulnerabilities.
  • Next.js treats exported Server Actions as public endpoints; unused actions can also be removed by the compiler. The email refactor removes the action boundary entirely instead of relying on whether a specific build exports an unused helper. See Next.js data security.
  • The framework also has its own Server Action body limit. The logo defect was absence of application-level file validation, not evidence of literally unlimited bytes through every deployment layer.
  • No live database, user accounts, uploaded files, or gateway configuration were modified during verification. These changes do not constitute a penetration test or an audit of the emulator, host, or all CMS endpoints.
  • No nginx/Traefik ingress configuration is versioned here. The deployment guide records the forwarding-header trust requirement. That external boundary remains unverified.

Follow-up identified during verification

The separate comment review is now implemented: both the website form and REST API use one submission service, require a published article whose publication time is due, apply the same moderation, and share a five-attempt/30-second per-user quota. The publication check locks the current article in the insertion transaction. Regression tests cover both entrypoints; real MariaDB/Redis coverage includes publication eligibility, word filtering and alternating submissions. Moderation retains its existing fail-open behavior on service outages. Form input beyond 255 characters is now rejected instead of truncated, and temporary API storage failures return 503. Real integration execution remains a required CI check.