# Business Rules — U3 HTTP Security Headers & CSP --- ## Decision Logic ```mermaid graph TD start["Response starting"] exists{"Header already set
by something else ?"} leave["Leave it untouched"] dev{"Environment is Development
and header is HSTS ?"} skiphsts["Skip HSTS"] always{"Header is nosniff or HSTS ?"} apply["Apply"] html{"Content type is HTML ?"} skiphtml["Skip: not an HTML response"] start --> exists exists -->|yes| leave exists -->|no| dev dev -->|yes| skiphsts dev -->|no| always always -->|yes| apply always -->|no| html html -->|yes| apply html -->|no| skiphtml classDef entry fill:#f6e05e,stroke:#c05621,stroke-width:1px,color:#000; classDef decision fill:#90cdf4,stroke:#2b6cb0,stroke-width:1px,color:#000; classDef good fill:#9ae6b4,stroke:#2f855a,stroke-width:1px,color:#000; classDef neutral fill:#e2e8f0,stroke:#4a5568,stroke-width:1px,color:#000; class start entry; class exists,dev,always,html decision; class apply good; class leave,skiphsts,skiphtml neutral; ``` Text alternative: an already-present header is never overwritten; HSTS is skipped in Development; `nosniff` and HSTS apply to every response while the remaining three apply only to HTML responses. --- ## Applicability Rules | ID | Rule | |---|---| | **BR-U3-01** | `X-Content-Type-Options: nosniff` is applied to **every** response. | | **BR-U3-02** | `Strict-Transport-Security` is applied to **every** response **except in Development**. | | **BR-U3-03** | `Content-Security-Policy`, `X-Frame-Options` and `Referrer-Policy` are applied **only** to responses whose content type is HTML. | | **BR-U3-04** | A header already present on the response is never overwritten. | | **BR-U3-05** | Headers are written at response start, not before the pipeline continues, because the content type is unknown earlier. | | **BR-U3-06** | The middleware is registered **before** static-file middleware, which short-circuits the pipeline. | | **BR-U3-07** | Header application never throws into the response path. A configuration error is a startup failure, not a per-request one. | | **BR-U3-08** | Every header except HSTS applies in Development, so a CSP violation surfaces during development. | | **BR-U3-09** | Headers apply to error responses too — the middleware sits inside the exception handler. | **Rationale for BR-U3-01**: `nosniff` exists to stop a browser guessing the type of a **non-HTML** resource. An uploaded `.txt` or `.svg` interpreted as HTML or JavaScript is the attack it prevents, so restricting it to HTML would remove it precisely where it works. **Rationale for BR-U3-04**: a component that deliberately set a header — a download endpoint setting its own `Content-Disposition`-adjacent policy, for example — knows something this middleware does not. Overwriting would be silently destructive. --- ## Policy Content Rules | ID | Rule | |---|---| | **BR-U3-10** | Exactly two policies exist, defined **in code**: `Strict` and `Relaxed`. | | **BR-U3-11** | Path-to-policy assignment and allowed origins come from **configuration**, so a path or origin can be added without a code change. | | **BR-U3-12** | `Strict` applies to `/admin`, `/api/v1` and `/health`. `Relaxed` is the default for everything else. | | **BR-U3-13** | `Strict` sets `script-src 'self'` — **no `'unsafe-inline'` and no `'unsafe-eval'`**. This must not be relaxed. | | **BR-U3-14** | `Strict` sets `style-src 'self' 'unsafe-inline'`. Documented exception, unavoidable — see below. | | **BR-U3-15** | `Relaxed` is **enforcing**, not report-only, so SECURITY-04 is satisfied on every HTML-serving path. | | **BR-U3-16** | `Relaxed` permits inline scripts and styles, and images, fonts and frames from any HTTPS origin — but **not** external script origins. | | **BR-U3-17** | Both policies set `object-src 'none'` and `base-uri 'self'`. | | **BR-U3-18** | Policy strings are composed once at startup and reused. | | **BR-U3-19** | `X-Frame-Options` is `DENY` under `Strict` and `SAMEORIGIN` under `Relaxed`, with `frame-ancestors` set to match. | **Rationale for BR-U3-14 — the one exception, and why it is not negotiable**: Radix UI positions dropdowns, dialogs and selects using inline `style` attributes whose values are recomputed per click, viewport and scroll position. CSP nonces apply only to `