Fix missing QR on every member page — blocked by our own CSP

The QR on /p/<id> and /p/<id>/<slug> was drawn by an inline <script>, but the
site sends `script-src 'self'` with no 'unsafe-inline'. The browser silently
refused to run it, so the QR box rendered as an empty white square on every
member page, not just Funnel Factory ones.

Moved the bootstrap to /page-qr.js and pass the link via a data attribute, so
no page data is interpolated into executable script and the CSP stays as
strict as it is. Loosening script-src to fix this would have traded a
site-wide security property for one widget.

This matters more than it looks: these pages go on printed flyers and ad
destinations, where an unscannable QR is discovered by the person holding the
paper.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
martbost
2026-08-28 11:28:48 -05:00
parent 7ca89f25ee
commit 1d52597c1c
2 changed files with 33 additions and 2 deletions
+31
View File
@@ -0,0 +1,31 @@
// Draws the QR on a member's hosted page (/p/<id> and /p/<id>/<slug>).
//
// This lives in its own file rather than inline in the rendered HTML for one
// reason: the site's CSP is `script-src 'self'` with no 'unsafe-inline', so an
// inline bootstrap is silently blocked and the QR box renders as an empty white
// square. Nothing errors visibly — the page just quietly loses its QR, which is
// exactly the sort of failure nobody notices until a printed flyer or an ad
// lands somewhere and cannot be scanned.
//
// The link comes from a data attribute so no page data is ever interpolated
// into executable script.
(function () {
'use strict';
function draw() {
var el = document.getElementById('ppQr');
if (!el) return;
var link = el.getAttribute('data-link');
if (!link || typeof qrcode !== 'function') return;
try {
var q = qrcode(0, 'M');
q.addData(link);
q.make();
el.innerHTML = q.createSvgTag({ cellSize: 4, margin: 2, scalable: true });
} catch (e) { /* leave the box empty rather than break the page */ }
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', draw);
} else {
draw();
}
})();