Merge pull request 'docs(website): document the SPA Content-Security-Policy' (#26) from docs/csp-security-headers into main
Reviewed-on: #26 Reviewed-by: Colby Whitlock <whitlocktech@gmail.com>
This commit is contained in:
@@ -409,7 +409,19 @@ who"; `activity_log` provides the history feed.
|
|||||||
- **bcrypt** hashing (cost 10+); plaintext passwords never stored, logged, or returned.
|
- **bcrypt** hashing (cost 10+); plaintext passwords never stored, logged, or returned.
|
||||||
- **Rate limiting** (`express-rate-limit`) on `/auth/login` and `/public/contact`.
|
- **Rate limiting** (`express-rate-limit`) on `/auth/login` and `/public/contact`.
|
||||||
- **Validation** (`express-validator`) on all writes; centralized error handler.
|
- **Validation** (`express-validator`) on all writes; centralized error handler.
|
||||||
- **helmet** with a CSP suited to the SPA (self + inline styles as needed; image sources for uploads/hero).
|
- **helmet** with a Content-Security-Policy tuned for the built React SPA (see `server/src/app.js`):
|
||||||
|
`default-src 'self'`; `script-src 'self'` (the Vite build emits only external module chunks — the
|
||||||
|
inline module-preload polyfill is disabled in `client/vite.config.js` to keep this valid);
|
||||||
|
`style-src 'self' 'unsafe-inline' https://fonts.googleapis.com` (React's pervasive inline
|
||||||
|
`style={{…}}` attributes can't be nonce'd, plus the Google Fonts stylesheet); `font-src 'self'
|
||||||
|
https://fonts.gstatic.com` (Cinzel); `img-src 'self' data: https:` (same-origin uploads, plus
|
||||||
|
external https images embedded in wiki/news bodies or `BRAND_*` logo/hero/favicon); `connect-src
|
||||||
|
'self'` (REST + SSE are same-origin); `frame-ancestors 'self'`; `object-src 'none'`; `base-uri
|
||||||
|
'self'`. `upgrade-insecure-requests` is intentionally **not** set (TLS terminates at the proxy, there
|
||||||
|
are no mixed-content subresources, and it would break a local `npm start` over plain http). The
|
||||||
|
`/api/docs` Swagger UI route gets a **looser** policy that additionally allows inline script/style,
|
||||||
|
since swagger-ui-express injects an inline bootstrap. helmet also strips `X-Powered-By`; the two
|
||||||
|
internal-only listeners (`internalApp.js`, `bot/src/app.js`) disable it explicitly too.
|
||||||
- **Admin not indexed**: `X-Robots-Tag: noindex, nofollow` on `/api/v1/admin` and the admin SPA routes; `robots.txt` disallows `/admin`.
|
- **Admin not indexed**: `X-Robots-Tag: noindex, nofollow` on `/api/v1/admin` and the admin SPA routes; `robots.txt` disallows `/admin`.
|
||||||
- **No directory browsing** (express.static doesn't list; no `serve-index`).
|
- **No directory browsing** (express.static doesn't list; no `serve-index`).
|
||||||
- **No hardcoded credentials**: first admin via `seed.js` reading `ADMIN_USERNAME`/`ADMIN_PASSWORD` from env (created only if no users exist); `.env` git-ignored, `.env.example` committed.
|
- **No hardcoded credentials**: first admin via `seed.js` reading `ADMIN_USERNAME`/`ADMIN_PASSWORD` from env (created only if no users exist); `.env` git-ignored, `.env.example` committed.
|
||||||
|
|||||||
Reference in New Issue
Block a user