You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

5.0 KiB

CSP Report Review (PROD, 2026-08-03)

Review of Content-Security-Policy-Report-Only violations from PNSPS PROD (https://pnsps.gld.gov.hk), for deciding Apache CSP updates.

Source logs (external):

  • PNSPS PROD CSP Report 2026-08-03.txt
  • summary.txt
  • CSP Issue 1.txt (script-src / eval samples)

Related app docs:


Policy in effect at report time

default-src 'self';
base-uri 'self';
object-src 'none';
frame-ancestors 'none';
form-action 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
style-src-elem 'self' 'unsafe-inline';
img-src 'self' data: https://www.w3.org https://w3.org;
media-src 'self' blob:;
font-src 'self' data:;
connect-src 'self';
upgrade-insecure-requests;
report-uri https://pnsps.gld.gov.hk/api/csp-report

Notes:

  • Disposition was report (Report-Only); nothing was enforced.
  • No frame-src → framing falls back to default-src 'self'.

Summary of findings

Most reports are browser / extension noise. Only one directive change is required for app functionality: add frame-src.

Approx. volume Directive Blocked Verdict
~3000 frame-src empty / data: (URI often stripped) App — PDF preview iframes
~1500 script-src wasm-eval Noise — chrome-extension
~1400 font-src fonts.gstatic.com (Inter / Nunito) Noise — not app fonts
~500 img-src https://www.w3.org/WAI/wcag2AA Already allowed; mostly doc 404 noise
~240 font-src at.alicdn.com, cdn.yiban.io, Perplexity CDN Noise — toolbars / extensions
~180 style-src-elem fonts.googleapis.com Noise — not loaded by app
~70 script-src eval (line ~67) Noise — no app eval
rest various Kaspersky, Youdao, Quark, todesktop-internal, etc. Noise

App fonts are self-hosted (@fontsource Public Sans / Noto; see public/index.html and src/assets/fonts.css). Google Fonts (Roboto / Inter / Nunito) are not part of the app and must not be allowlisted.


App-owned issue: frame-src

Cause

Proof upload preview in src/pages/Proof/Create_FromApp/UploadFileTable.js:

  1. FileReader.readAsDataURL(...) builds a data: URL
  2. window.open("") then document.write an <iframe src="data:...">

With no frame-src, CSP uses default-src 'self', which blocks data: frames. Browsers often report blocked-uri as empty ("") for privacy.

Almost all of these hits are on /proof/create/..., source static/js/4608.*.chunk.js.

Apache fix

frame-src 'self' data: blob:;

Optional follow-up (frontend)

Prefer URL.createObjectURL + blob: for preview (still needs frame-src ... blob:).


Optional: media-src and data:

Small volume of media-src / data on login and related pages.

Captcha audio already uses blob: (CustomFormWizard.js and similar). Adding data: is low risk:

media-src 'self' blob: data:;

Already covered / not a CSP gap

WCAG badge (img-src / www.w3.org)

AuthFooter.js loads https://www.w3.org/WAI/wcag2AA. Policy already allows https://www.w3.org and https://w3.org. Many reports had document status-code: 404 (SPA shell). No Apache change required.

Optional hardening: host the badge image locally.

Payment gateway CSS (epaygateway1.gcis.gov.hk)

Single style-src-elem hit on /paymentPage/callback with gateway referrer. Card payment uses window.location.assign(redirecturl), not embedded gateway CSS. Do not allowlist unless a real UI break is confirmed.


Do not add to CSP

Requested by reports Why not
'unsafe-eval' / 'wasm-unsafe-eval' Extension / injected; app has no eval
fonts.googleapis.com / fonts.gstatic.com App uses self-hosted fonts
Kaspersky (*.kis.v2.scr.kaspersky-labs.com) AV injection
Perplexity, Youdao, Quark, alicdn, yiban, NetEase CDN Third-party client tooling
todesktop-internal Desktop wrapper noise
epaygateway1.gcis.gov.hk (styles) Not required for current payment flow

Use the snippets in csp-apache.conf.md.

Changes vs the 2026-08-03 deployed policy:

  1. Add frame-src 'self' data: blob:;
  2. Change media-src to 'self' blob: data:;

Rollout

  1. Deploy updated Report-Only header.
  2. Watch /api/csp-report for a few days — expect frame-src volume to drop; remaining extension noise is OK.
  3. Promote the same policy to enforcing when app-owned directives look clean.

Adjust report-uri host for UAT vs PROD:

  • UAT: https://pnspsuat.gld.gov.hk/api/csp-report
  • PROD: https://pnsps.gld.gov.hk/api/csp-report

Backend reference

  • Report endpoint: POST /csp-report (context path → /api/csp-report)
  • Spring Security also sets a short API CSP on API responses (default-src 'self'; script-src 'self'; frame-ancestors 'self'). Frontend/Apache CSP above is what browsers enforce for the SPA document.