Move PDF viewer wrapper to html/template — fix stored XSS (C-2, L-6) #97
No reviewers
Labels
No labels
Compat/Breaking
Kind/Bug
Kind/Documentation
Kind/Enhancement
Kind/Feature
Kind/Security
Kind/Testing
Priority
Critical
Priority
High
Priority
Low
Priority
Medium
Reviewed
Confirmed
Reviewed
Duplicate
Reviewed
Invalid
Reviewed
Won't Fix
Status
Abandoned
Status
Blocked
Status
Need More Info
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
qwc-open/asiakirjat!97
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/pdf-viewer-xss"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Workstream item #2. Audit findings C-2 (Critical) stored XSS in the PDF viewer wrapper and L-6 hand-formatted HTML — both fixed by the same change.
servePDFViewerbuilt the wrapper page withfmt.Fprintf, interpolating the project name and version tag directly into HTML. Project names are free-form text set by editors/admins; an editor could set a name likeand any viewer opening the PDF would execute the script in their session, same-origin to
/admin.Changes
internal/templates/overlay/pdf_viewer.html, parsed via the existinghtml/templateengine — all string fields auto-escape.templates.PDFViewerDataandEngine.RenderPDFViewer; the pre-rendered overlay HTML is passed in astemplate.HTMLso it still emits verbatim.servePDFViewerreplaced from ~40 lines of hand-formatted HTML with atemplates.RenderPDFViewercall.TestPDFViewerEscapesProjectNamecreates a project named</title><script>...</script>, fetches the viewer wrapper, asserts the raw payload is absent and an escaped form is present.Version tags are already constrained by the C-1 validation regex (PR #96), so the project-name path was the remaining XSS sink.
Test plan
go test ./...passes🤖 Generated with Claude Code
Audit findings C-2 (Critical, stored XSS) and L-6 (hand-formatted HTML fragile to maintain). servePDFViewer built the wrapper page with fmt.Fprintf, interpolating the project name and version tag directly into HTML. Project names are free-form text set by editors/admins; version tags are now constrained by the C-1 validation regex but the project name path remained: an editor could set a project name like </title><script>fetch('/admin/...')</script> and any viewer (including admins) opening the PDF would execute the script in their session, same-origin to /admin. Moves the wrapper to internal/templates/overlay/pdf_viewer.html, parsed via the existing html/template engine; all string fields are now auto-escaped. The pre-rendered overlay HTML is passed in as template.HTML so it still emits verbatim. Adds RenderPDFViewer on Engine that writes directly to the response. Regression test creates a project whose name contains the </title><script> payload, fetches the viewer wrapper, and asserts the raw payload is absent and an escaped form is present. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>