Skip to content

CSP support: nonce for inline scripts and CspPolicy helper - #2

Merged
stromcom merged 2 commits into
masterfrom
feature/csp-support
Aug 7, 2026
Merged

CSP support: nonce for inline scripts and CspPolicy helper#2
stromcom merged 2 commits into
masterfrom
feature/csp-support

Conversation

@stromcom

@stromcom stromcom commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Řeší část stromcom/www.stromcom.cz#10 týkající se PHP balíčku.

Co přibylo

Nonce pro inline <script>SnippetCode::getHTML() dosud vracel <script> bez atributů, takže integrátor s script-src 'self' musel mít 'unsafe-inline'. Nonce se nastaví jednou na SnippetClient / SnippetClientFactory::create() a propíše se do všech generovaných SnippetCode. Validace na jednom místě (NonceValidator), atribut escapovaný, nová CspException.

CspPolicy — poskládá direktivy, které musí hostitelská stránka povolit: getDirectives(), getHeaderName(), getHeaderValue(), getMetaTag(). SnippetClient::csp() vrátí politiku předdrátovanou environmentem a nonce klienta.

OriginAwareEnvironmentInterface — rozšiřuje EnvironmentInterface o getApiUrl() a getApplicationUrl(); Environment enum nese reálné hodnoty, CustomEnvironment je bere volitelně v konstruktoru. Když origin nikdo nedodá, letí CspException — origins se zásadně nehádají, protože špatný odhad by widget tiše zablokoval.

Zpětná kompatibilita

EnvironmentInterface beze změny, nonce je poslední (volitelný) parametr, bez nonce je výstup bajt po bajtu stejný jako dosud — pokryto testem.

Ověřeno

Seznam direktiv není odvozený od stolu — je ověřený Playwright testem proti stránce s reálně vynucenou striktní CSP (default-src 'none'), včetně negativní kontroly, že test výpadek direktivy chytí.

composer ca:   [OK] No errors (PHPStan level 8, php-cs-fixer 0/45)
composer test: OK (132 tests, 216 assertions)

Integrators embedding the snippet had to allow 'unsafe-inline' because
getHTML() rendered a bare <script> tag, and they had no authoritative
list of the directives the widget needs.

- SnippetCode accepts an optional nonce and renders <script nonce="...">;
  getCode() and the no-nonce output are unchanged
- The nonce is configured once on SnippetClient / SnippetClientFactory
  and propagates to every generated tag; NonceValidator rejects values
  outside the base64 alphabet with a CspException
- CspPolicy derives script-src, connect-src, style-src, img-src and
  frame-src from the environment's loader URL and exposes them as an
  array, a ready header or a meta tag; SnippetClient::csp() pre-fills it
  with the client's environment and nonce
Deriving the API and application origins from the CDN hostname produced a
silently wrong policy whenever the deployment did not follow the assumed
host layout — the widget then breaks on a CSP block, which is miserable to
debug. The layout also is not a rule: production needs www.<zone> while
staging needs the bare <zone>.

- OriginAwareEnvironmentInterface extends EnvironmentInterface with
  getApiUrl() and getApplicationUrl(); plain EnvironmentInterface
  implementations keep working
- Environment carries the real values per case, CustomEnvironment takes
  them as optional constructor arguments
- CspPolicy reads them from the environment or from its own apiUrl /
  applicationUrl arguments and throws a CspException naming the directive
  and the argument when neither knows them; only cdnOrigin is still read
  from the loader URL, which does contain it
@stromcom
stromcom merged commit 888a234 into master Aug 7, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant