Review of Content-Security-Policy-Report-Only violations from PNSPS PROD
(https://pnsps.gld.gov.hk).
Source logs (external):
All_CSP.txt — 196 reports, 2026-08-27 to 2026-09-17Related app docs:
No app-side CSP concern in this dump. Almost every line is a browser
extension, not PNSPS code. /proof/reply/{id} is just a busy authenticated
page, so the same extension noise shows up there a lot.
CSP is still Report-Only (disposition: report) — the browser logged these
hits and did not block the page.
The 2026-08-14 app-owned issue (proof PDF preview frame-src) is gone from
this dump. Live PROD now includes frame-src 'self' data: blob:;.
PROD — add nothing required. Remove nothing.
Keep script-src 'self'. Do not add 'unsafe-inline', 'unsafe-eval', or
'wasm-unsafe-eval'.
Optional (low priority, already in csp-apache.conf.md):
media-src 'self' blob: data:;
Live PROD is still media-src 'self' blob: (no data:). This dump has only
2 matching hits in three weeks.
Do not add: 'wasm-unsafe-eval', Perplexity CDN, Youdao, NetEase CDN,
Google Fonts.
This is the live header that generated every report in All_CSP.txt. It matches
original-policy in all 196 violations.
Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
base-uri 'self'; \
object-src 'none'; \
frame-ancestors 'none'; \
frame-src 'self' data: blob:; \
form-action 'self'; \
script-src 'self'; \
style-src 'self' 'unsafe-inline'; \
style-src-elem 'self' 'unsafe-inline'; \
img-src 'self' data:; \
media-src 'self' blob:; \
font-src 'self' data:; \
connect-src 'self'; \
upgrade-insecure-requests; \
report-uri https://pnsps.gld.gov.hk/api/csp-report"
Changes vs the 2026-08-14 live header:
frame-src 'self' data: blob:; is now present.img-src no longer allowlists https://www.w3.org / https://w3.org
(badge is hosted locally).style-src 'unsafe-inline' is expected for MUI and is the only real policy
weakness. These reports do not show a new XSS issue.
| Count | Directive | Blocked | Verdict |
|---|---|---|---|
| 192 | script-src |
wasm-eval |
Noise — Chrome extension |
| 2 | media-src |
data |
Optional leftover — data: not in live media-src |
| 1 | font-src |
frontend-cdn.perplexity.ai … FKGroteskNeue.woff2 |
Noise — Perplexity sidebar |
| 1 | img-src |
ydlunacommon-cdn.nosdn.127.net … .svg |
Noise — Youdao / NetEase plugin |
No frame-src reports. No app eval. No Google Fonts.
wasm-eval volume is high192 of 196 reports (98%) are script-src / wasm-eval.
| Count | source-file |
Notes |
|---|---|---|
| 103 | chrome-extension |
Every one with a location is line 23, column 39316 — the same injected script |
| 89 | (none) | Chrome often sends a second report for the same violation without source-file |
About 46 timestamps have 2+ reports in the same second (paired duplicates). That is why the log looks twice as large as the number of real events.
Password managers, translators, Grammarly-style tools, and similar extensions
compile WebAssembly into the page. script-src 'self' (without
'wasm-unsafe-eval') reports that. Do not loosen script-src to silence
it.
If quieter logs are needed, filter blocked-uri=wasm-eval plus
source-file=chrome-extension (and the paired empty-source twin).
/proof/reply reports| Count | Page group |
|---|---|
| 90 | /login |
| 38 | /proof/reply/{id} |
| 23 | /publicNotice/{id} |
| 15 | /publicNotice/apply |
| 10 | /publicNotice |
| 10 | /proof/search |
| 4 | /paymentPage/* |
| 3 | /user/changePassword |
| 2 | /registerFromOrganization |
| 1 | /verify/* |
The 38 Reply Proof hits span 12 proof IDs (29518, 29948, 30112,
30114, 30134, 30166, 30190, 30266, 30306, 30326, 30484,
30958). They are the same wasm-eval + Chrome extension pattern as login,
apply, and notice detail.
Reply Proof is a long, authenticated screen. Anyone with a WASM extension triggers a report on every visit. No Reply Proof code change is required.
| Count | Page | Directive | Blocked | Why ignore |
|---|---|---|---|---|
| 192 | many (login 90, reply 38, …) | script-src |
wasm-eval |
chrome-extension (line 23, col 39316) or paired empty source |
| 1 | /login |
font-src |
Perplexity FKGroteskNeue.woff2 |
Sidebar / extension |
| 1 | /login |
img-src |
NetEase ydlunacommon-cdn.nosdn.127.net |
Youdao-style translator |
| 2 | /publicNotice/apply, /proof/reply/30166 |
media-src |
data |
Optional data: gap only; 2 hits in three weeks |
status-code: 404 on 166 reports is typical SPA client-route noise, not a
broken document.
media-src and data:Live policy: media-src 'self' blob:;
Captcha audio already uses blob: (CustomFormWizard.js and similar). Adding
data: is low risk and already documented:
media-src 'self' blob: data:;
Not required for this dump.
frame-src deploy from
2026-08-14 looks effective (zero frame-src hits here).script-src 'self'. Do not add 'unsafe-eval' or
'wasm-unsafe-eval'.data: to media-src to match
csp-apache.conf.md.wasm-eval + chrome-extension so
real app regressions stay visible.No application code change is required.
POST /csp-report (context path → /api/csp-report)CspReportController in PNSPS-backend