Reference: Security
Every page on this site is served with a Content-Security-Policy, the header
that tells a browser which origins a page may load code from. Ours carries no
unsafe-inline, with one exception this post will get to. Writing
that policy took an afternoon. I want to talk about the part that is not the
afternoon: how it stays true while a hundred pages get edited around it. That
is a maintenance problem, and maintenance problems on this repository get the
same treatment as engine claims. Something greps.
The tripwire
CI runs chained greps over src/web/: an inline
<script>, an inline <style> or a
style= attribute anywhere in the served pages fails the
build. Plain POSIX grep, no lookahead, because the check must run
identically everywhere. The one exemption is
application/ld+json, and the reasoning is worth keeping. A
JSON-LD block is structured data for search engines, not code: the browser
never executes it. So the strict CSP neither blocks it nor needs to cover it,
and rich results get to keep quoting it.
The tripwire is what makes the policy a property of the repository rather than a property of the day it was written. An inline style is never added maliciously. It is added at 11pm, on a page nobody will review for punctuation, by someone fixing one margin. The grep does not care how reasonable the margin was.
The default that falsified a privacy claim
Here is the embarrassing find, kept because it is the useful one. This policy
is assembled through helmet, a standard Node library for security headers.
Helmet's default for fonts is font-src 'self' https: data:. That
is every HTTPS origin on earth.
Our policy object simply never mentioned fonts, so that default is what production served. Meanwhile the privacy page said, in writing, that nothing on any page loads a script, font, image or stylesheet from another domain, and that the CSP forbids it. On fonts, the policy permitted exactly what the sentence swore it forbade.
Nothing on the site wants a remote font. The stack is system-ui. So
font-src is pinned to 'self', without
data:. No page inlines a font either, and "just in case" would
reopen a narrower version of the same gap.
The lesson generalises. A policy you did not state is a policy someone else chose. That someone else was optimising for not breaking anybody's site, which is the opposite of a promise.
The one exception, and how it is prevented from spreading
/docs is Swagger UI, the interactive API-reference viewer. It
renders markdown at runtime and needs
style-src 'unsafe-inline'. It gets that one directive relaxed
and nothing else. The route's policy is derived from the shared
cspDirectives() function with two keys overridden, rather than
hand-written.
That is not tidiness. The hand-written version is how /docs once
shipped missing three directives every other route had. A copied string does
not receive updates.
The derived version is also narrower than the site policy in one respect. The Turnstile origins the checker pages need, which is Cloudflare's bot check, are dropped: a docs page has no use for them. Inheriting privileges you do not use is its own small rot.
What the policy ends up designing
The fun of a strict CSP is that it becomes an architecture critic with commit access. A few of its verdicts around here:
- The owner stats page has no charting library, because the CSP would block one. Four rectangles did not need a dependency anyway. Bar widths are set through the CSSOM, the JavaScript view of a page's styles, which CSP does not restrict. It is the same technique the quota bar has always used.
-
The blog covers, including the one above this article, are inline SVG
coloured entirely with
var(--token)andcurrentColor. A cover cannot carry astyle=attribute, because the index generator refuses it, because the CI grep would refuse the index. The constraint that began as security is also what makes one drawing correct in both colour themes. -
The Product Hunt badge is self-hosted as two local SVGs, with the live
vote count trimmed off. Hotlinking it would widen
img-srcand quietly falsify the same privacy sentence the fonts almost did.
None of this asks for trust. curl -D- against any page shows
the header. The privacy and security pages state the policy in prose, and
the MVSP self-assessment, which is the Minimum Viable Secure Product
checklist buyers send, names the tripwires. A security property you can
verify from outside is worth more than a longer list of properties you
cannot. That is the same argument this product makes about document
forensics, applied to itself.
Check it yourself
Run curl -D- https://tamperlens.com/ -o /dev/null and read
the policy line. Then, if you like, try to find an inline style in the
served HTML of any page. The build system has been trying every commit.
The security page documents the headers, the hardening and the receipt every production response signs. It is written for the reviewer who checks rather than the one who skims.