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
+2 -2
View File
@@ -157,11 +157,11 @@ function render(rec) {
(bullets ? '<ul>' + bullets + '</ul>' : '') +
'<div class="cta"><p>' + esc(c.closing || 'Take a look and see what you think.') + '</p>' +
'<a class="btn btn-primary" href="' + esc(link) + '">See how it works →</a>' +
'<div class="qr" id="ppQr"></div><div class="micro" style="color:var(--muted)">or scan · ' + esc(link.replace(/^https:\/\//, '')) + '</div></div>' +
'<div class="qr" id="ppQr" data-link="' + esc(link) + '"></div><div class="micro" style="color:var(--muted)">or scan · ' + esc(link.replace(/^https:\/\//, '')) + '</div></div>' +
'<div class="foot">Independent team resource shared by an individual member. Participation takes real effort and involves cryptocurrency risk, including risk of total loss. No income is guaranteed. ' +
'<a href="/disclaimer" style="color:var(--teal)">Disclaimers</a> · <a href="/contract" style="color:var(--teal)">How the contract works</a></div>' +
'</main>' +
'<script src="/qrlib.js"></script><script>(function(){try{var q=qrcode(0,"M");q.addData(' + JSON.stringify(link) + ');q.make();document.getElementById("ppQr").innerHTML=q.createSvgTag({cellSize:4,margin:2,scalable:true});}catch(e){}})();</script>' +
'<script src="/qrlib.js"></script><script src="/page-qr.js"></script>' +
'<script src="/chat.js" defer></script><script src="/translate.js" defer></script>' +
'</body></html>';
}