DOKIMASecurity review report

Overview

SPA index document is served with no Content-Security-Policy because the mux CSP middleware never runs for the NotFoundHandler

low Fully Valid high confidence

DOK-100004-FILEBROWSER-HTTP-ROUTER-MIDDLE-SPA-INDEX-HTML-DOC · filebrowser · http-router-middleware

Status

Fully Valid

Canonical record for the SPA-index CSP coverage gap. The middleware-bypass root cause is proven end-to-end in the gorilla/mux dependency source (matched routes get the CSP; NotFoundHandler does not), so the control is definitively absent — fully_valid. Severity is low because it is defense-in-depth: no concrete injection vector into the auto-escaped document was demonstrated. DOK-100010 is the same issue re-observed from the static-frontend component.

Repository / Component

Repository
filebrowser
Component
http-router-middleware

Plain-English Description

The main application page is delivered without the Content-Security-Policy protection the app intends to apply. The policy is added by middleware that only runs for matched routes, but the main page is served through the router's not-found handler, which that middleware never touches.

Description of the Underlying Issue

The intended CSP is set only inside the r.Use middleware closure (http/http.go:30-35), which sets Content-Security-Policy: default-src 'self'; style-src 'unsafe-inline';. The SPA HTML document is served by r.NotFoundHandler = index (http/http.go:44). In gorilla/mux v1.8.1, the middleware chain is applied only when a route matches with MatchErr == nil (mux.go:141-146); the NotFoundHandler branch (mux.go:159-163) assigns match.Handler with no middleware applied. The index handler itself sets only x-xss-protection (http/static.go:107-114) and the globalHeaders map contains only Cache-Control (http/headers.go:6-8) — neither adds a CSP. Therefore GET / and every unmatched SPA route are served with no Content-Security-Policy header at all, even though matched routes such as GET /static/* do receive the middleware CSP. Admin-set branding/ReCaptcha configuration is injected into this same document via template.JS (http/static.go:83-88).

Potential Attack

This is a defense-in-depth coverage gap rather than a demonstrated injection. If any future or overlooked vector allowed attacker-controlled content into the index document (for example a mishandled branding/config value not caught by html/template auto-escaping), the absence of a CSP on that document would remove the layer intended to constrain script execution and resource loading, making an XSS materially easier to exploit. No concrete injection vector into the auto-escaped document was proven.

Outcomes of Potential Attack

With no CSP on the primary application document, any script-injection foothold in index.html would execute without policy constraints on script sources, inline execution, or connect/frame targets — increasing the blast radius of a hypothetical XSS. Absent an injection vector, the direct outcome is a missing hardening layer on the app's most security-sensitive page.

Affected Scope

GET / and every unmatched SPA route (served by r.NotFoundHandler=index); the main app HTML document into which admin-set branding/ReCaptcha config is injected via template.JS.

Suggested Fix (plain english)

Set the Content-Security-Policy header inside the index/not-found handler itself rather than relying on route middleware, and/or add a CSP meta tag to index.

Suggested Fix (detailed)

Do not rely on the mux r.Use route middleware for the CSP on the SPA document, since gorilla/mux v1.8.1 does not run route middleware for NotFoundHandler (mux.go:159-163). Set Content-Security-Policy directly inside the index handler (http/static.go:107-114) or wrap r.NotFoundHandler in an explicit CSP-setting handler (http/http.go:44). Consider also adding globalHeaders/CSP coverage in headers.go and/or a <meta http-equiv="Content-Security-Policy"> tag in index.html as belt-and-braces. Verify by requesting GET / and confirming a Content-Security-Policy header is present, matching what matched routes like GET /static/* already return. This record is the canonical version of the same coverage gap tracked from the static-frontend component as DOK-100010. Set the CSP header inside the index/NotFoundHandler itself (or wrap NotFoundHandler explicitly) instead of relying on mux route middleware, and/or add a CSP meta http-equiv tag to index.html.

Validation

Canonical record for the SPA-index CSP coverage gap. The middleware-bypass root cause is proven end-to-end in the gorilla/mux dependency source (matched routes get the CSP; NotFoundHandler does not), so the control is definitively absent — fully_valid. Severity is low because it is defense-in-depth: no concrete injection vector into the auto-escaped document was demonstrated. DOK-100010 is the same issue re-observed from the static-frontend component.

first_opinion

Full Evidence

http/http.go:30-35 CSP is set only inside the r.Use middleware closure
http/http.go:44 r.NotFoundHandler=index serves the SPA HTML
gorilla/mux@v1.8.1 mux.go:141-146 builds the middleware chain ONLY when a route matches with MatchErr==nil
gorilla/mux@v1.8.1 mux.go:159-163 NotFoundHandler branch assigns match.Handler with NO middleware applied
http/static.go:107-114 index handler sets only x-xss-protection; http/headers.go:6-8 globalHeaders is only Cache-Control
http/static.go:83-88 admin-set config injected into this document via template.JS
Proven fact: The intended CSP control (r.Use middleware) demonstrably does not cover the NotFoundHandler in gorilla/mux v1.8.1 — verified in the dependency source on disk
Proven fact: The index handler itself sets no Content-Security-Policy header
Proven fact: Therefore GET / is served with no CSP whatsoever
Unvalidated fact: No concrete stored/reflected XSS injection vector into index.html was proven; html/template auto-escapes and the Json blob uses template.JS, so this is a defense-in-depth coverage gap rather than an exploited XSS
http/http.go:30-35 — CSP is set only inside the r.Use middleware closure (default-src 'self'; style-src 'unsafe-inline';)
http/http.go:44 — r.NotFoundHandler = index serves the SPA HTML
gorilla/mux@v1.8.1 mux.go:141-146 — the middleware chain is built only when a route matches with MatchErr == nil
gorilla/mux@v1.8.1 mux.go:159-163 — the NotFoundHandler branch assigns match.Handler with NO middleware applied
http/static.go:107-114 — the index handler sets only x-xss-protection; http/headers.go:6-8 — globalHeaders is only Cache-Control
http/static.go:83-88 — admin-set config is injected into this document via template.JS
Proven fact: The intended CSP control (r.Use middleware) demonstrably does not cover the NotFoundHandler in gorilla/mux v1.8.1 — verified in the dependency source on disk (mux.go:141-146 vs 159-163).
Proven fact: The index handler itself sets no Content-Security-Policy header (only x-xss-protection), and globalHeaders adds only Cache-Control.
Proven fact: Therefore GET / and unmatched SPA routes are served with no CSP whatsoever, while matched routes do receive the middleware CSP.
Unvalidated fact: No concrete stored/reflected XSS injection vector into index.html was proven; html/template auto-escapes and the injected config blob uses template.JS, so this is a defense-in-depth coverage gap rather than an exploited XSS.
http/http.go:30-35 CSP set only inside r.Use middleware closure
http/http.go:44 r.NotFoundHandler = index (SPA served here)
gorilla/mux v1.8.1 applies r.Use middleware only to matched routes (Match wraps match.Handler when MatchErr==nil); NotFoundHandler bypasses the middleware chain
http/static.go:107-114 index handler sets only x-xss-protection; http/headers.go:7 sets only Cache-Control
frontend/index.html & frontend/public/index.html contain no CSP meta http-equiv
http/static.go:88 config incl. ReCaptchaKey/branding injected via html/template template.JS into the same document