Creating security audit script generators, code generation issues write-up | AI Web Page SEO
Transparency report

Website Security Audit Tool: Headers, TLS, DNS and Email

A full write-up of what we checked, what we found, and what we did, in plain English or full technical detail.

Two tools, the site audit and the server test

This report is about two things, and only two things: our on-page security audit (run from a web address) and our server security script (run on the machine itself). They check different layers — here's which is which.

1 · The server test

server_audit.py — a program that runs on the machine itself. It reads the actual files, their permissions, which programs are running and with what power, and how the server is logged into. Nothing outside the server can see any of this — no online scanner, and not us without direct access. Most of the fixes in this report came from this test.

2 · The security audit

Our on-page security audit — run against any web address, the way a visitor's browser loads a page. It checks whether a site is actually secure: the settings that decide whether it can be broken into, tampered with, or spoofed.

Checked against an independent benchmark

We didn't just trust our own tool. We cross-checked our security audit against Mozilla's HTTP Observatory, a widely used independent security scanner. Where Observatory flagged something our tool had missed — a Content-Security-Policy that only looked present — we fixed our tool to match its stricter logic (see Parts 4–5). Our audit then goes further than Observatory does, also giving you a real security score across:

  • HTTP security headers, plus strict Content-Security-Policy and Subresource-Integrity checks
  • TLS configuration and cookie flags
  • Mixed content and server fingerprinting
  • Known-vulnerability (CVE) lookups
  • Mail-security records — SPF, DMARC, DKIM — that stop your domain being spoofed
  • security.txt and CMS detection

And behind the public audit sits our server test — the inside-the-machine check no URL-based tool can ever perform. Don't take our word for it: run your site through both and compare what each one reports.

Tested for real — not just on our own site

The two tools were tested against different sets of sites, because they check different layers. Our on-page security audit was run against two real financial-institution websites, Gateway Bank (scored 63/100) and BankCFCU (scored 82/100). Two was deliberate: the point wasn't to survey the whole banking sector, it was to prove the tool differentiates, two real, independent sites returning two clearly different but plausible scores shows it genuinely inspects each site, rather than rubber-stamping everything as safe (which would score them identically) or failing everything (which would too). Both are real, publicly-operating institutions, checked only the way any visitor's browser sees a page. Separately, from our own server, we ran a passive sweep of the same checks across the official sites of 10 major website platforms:

WordPressShopifyWixSquarespaceWebflowDrupalGhostBigCommerceHubSpotJoomla

And the server test — the inside-the-machine check no outside tool can reach — was run across every site on our own server:

aiwebpageseo.comaiwebpageseo.shopaipageseo.comaipageseo.co.ukgrabzies.comgrabzies.co.ukformyfamilyprotection.comrecycleplanet.co.ukoneshopcctv.com

This report is about the tool. Run the audit on your own site, check any URL's security headers, TLS, cookies, CVEs, mail and DNS in one pass.

Run the Security Audit →
01

New feature, planned, not yet built

Confirmed safe

What the feature was meant to be

Not everything in this write-up is a fix, this part is about a feature that was planned but deliberately not built this time, and it's included for the sake of a complete, honest record rather than to claim credit for work that didn't happen. The idea was a public tool where someone types in something awkward or rude that was said to them, picks a tone, funny, blunt, classy, and so on, and gets back a set of AI-generated comebacks. The results would be stored in a way that search engines and AI assistants could later find and reference, so the feature would also bring in search visibility over time, not just serve the one person using it in the moment.

1 · Doing the homework first, looking at what already exists

Before writing a line of it, we looked at four existing tools that already do something similar. The point of that wasn't to copy them; it was to learn from what they get right and, more importantly, to spot the ways they go wrong so we could design against those from the very start rather than discovering them the hard way after launch. Studying the failure modes of comparable tools is cheaper and safer than making the same mistakes yourself in public.

2 · The genuinely concerning thing we found

One of those existing tools, during our testing, did something that matters a great deal for a feature like this: when a real person's name appeared in the test input, it generated a fabricated, made-up personal insult aimed at that named individual. That is a serious failure mode. There's a clear and important line between helping someone respond wittily to a remark that was actually made to them, and a tool that will invent damaging personal claims about a real, named person on request, the latter is a harassment risk, and it's the kind of thing that turns a light-hearted feature into something genuinely harmful.

3 · The design rule that came out of it

So this shaped a firm design rule for any version we would build, decided up front rather than bolted on later: the tool must always respond to what was actually said, the remark itself, and must never invent personal claims or attacks about a real, named individual. Respond to the words, never target the person. Building that boundary in from the first line of the design, rather than trying to patch it in after something goes wrong, is the difference between a feature that's fun and one that's a liability. Finding this in someone else's tool, before building our own, is exactly what the homework was for.

4 · How it would be controlled and paid for

We also worked out how the feature would be gated, so it isn't relevant to note it wasn't a loose end left unconsidered. It would use the same credit-based system already used elsewhere on the site, the existing mechanism that controls access to the other tools, rather than inventing a new one. Reusing the established pattern keeps the feature consistent with everything else and avoids a whole new set of things that could go wrong.

5 · Why it wasn't built, and why that's the right call

In the end, this feature was deliberately set aside this time in favour of the security work that makes up the rest of this write-up. That was a conscious priority decision, not something forgotten: with real, concrete security issues found on the server, fixing those came first, ahead of building a new public-facing feature. It's an honest reflection of where the effort actually went, and putting known security problems ahead of a nice-to-have new feature is the right order to do things in.

6 · Status

Planned, researched, and scoped, including its most important safety boundary, but not built this session. It's recorded here so the write-up reflects everything that was actually worked on, including the things that were thought through and then, sensibly, deferred.

Key points

  • A planned public feature, type in a rude remark, pick a tone, get AI comebacks, recorded here as planned, not built.
  • We studied four existing tools first, to learn their mistakes rather than repeat them.
  • One of them invented a personal insult about a real named person, a serious harassment failure mode.
  • That set a firm rule up front: respond to the remark, never invent claims about a real individual.
  • It was deliberately set aside this session so the security work could come first.

How to plan a risky new feature responsibly

  1. Before building anything like this, study the tools that already exist, especially how they fail.
  2. Decide the safety boundary first: respond only to what was said, never target a real, named person.
  3. Build that boundary in from the start, rather than trying to patch it after something goes wrong.

How to prioritise a new feature against existing work

  1. Reuse the access and payment controls you already have rather than inventing new ones.
  2. Be honest about priority, put fixing known problems ahead of building nice-to-have features.

Questions & answers

Why look at other tools before building your own? To learn from their mistakes cheaply. Spotting a harmful failure in someone else's tool beforehand is far better than discovering the same failure in your own after it's live.

What was the concerning behaviour you found? One tool, given a real person's name, made up a personal insult aimed at that named person. That's the exact line a feature like this must never cross.

What rule did that lead to? Respond to the remark that was actually made, and never invent personal claims or attacks about a real, named individual, decided before any building began.

Why wasn't it built? Because real security issues on the server took priority. Fixing those came first, ahead of adding a new public feature, a deliberate choice.

Technical detail

Scope

Planned public feature ("Retort Q&A"): user submits a remark and selects a tone from a set of options; receives AI-generated retort candidates; results persisted as searchable Q:/A: text pairs for AI-search citability. Included here as a not-built item for a complete record.

1 · Competitor research before build

Reviewed four existing comparable tools to learn desirable behaviour and, critically, to surface failure modes to design against pre-emptively rather than post-launch.

2 · The harassment failure mode found

One competitor, given test input containing a real person's name, generated a fabricated targeted personal attack on that named individual. This is the key risk for a generative "comeback" feature: the gap between responding to a remark and generating defamatory/harassing content about a named real person.

3 · The resulting prompt-design constraint

Hard rule for any implementation, fixed at design time: the generator must respond only to the submitted remark and never fabricate character claims or personal attacks about a named real individual. Enforced in the system prompt and reinforced with input handling around detected names, designed in from the start, not retrofitted.

4 · Gating / cost model

Access gated through the site's existing credit-based middleware (the same requireTool() / requireApiCall() pattern used by other tools), rather than a bespoke mechanism, consistent access control and billing with the rest of the platform.

5 · Reusable storage pattern noted

During the earlier code review, a public GET /api/qa endpoint backed by a qa_items table was found, a close structural match to what this feature needs, meaning the storage layer could likely be reused rather than built from scratch.

6 · Status

Researched, scoped, and safety-bounded, but deprioritised this session in favour of the security audit work. Not built.

Key points

  • Planned public "Retort Q&A" feature: remark + tone → AI retorts, stored as searchable Q:/A: pairs.
  • Competitor research surfaced a real harassment failure mode: fabricated attacks on a named real person.
  • Design rule fixed up front: respond to the remark, never fabricate claims about a named individual.
  • Gating via existing credit middleware; storage likely reusable from an existing qa_items table.
  • Not built this session, deliberately deprioritised behind the security work.

How you'd approach this yourself

# 1. study comparable tools for failure modes BEFORE building
# 2. encode the safety boundary in the system prompt from line one:
#    "Respond only to the submitted remark. Never fabricate claims
#     or attacks about any named real individual."
# 3. reuse existing access control rather than inventing new gating
# 4. reuse existing storage (qa_items) if its shape already fits

Questions & answers

Why research competitors before building? To inherit their hard-won lessons cheaply, especially failure modes. Finding a harassment flaw in someone else's tool is far better than shipping the same flaw in your own.

What exactly was the risky behaviour? A competitor, given a real name, invented a personal attack on that named person. A comeback tool must respond to the remark, not manufacture defamatory content about an individual.

Why reuse existing gating and storage? Consistency and safety: the credit middleware and qa_items table are already proven on the site, so reusing them avoids a new surface of bugs and keeps behaviour uniform.

Why wasn't it built? Concrete server-security findings took priority. Fixing real, live security issues comes before shipping a new public feature, a deliberate ordering, not an oversight.

02

Testing our own security checker against real sites

Confirmed safe

Why a tool that always says "fine" is worse than useless

A security-checking tool is only worth anything if it tells the truth even when the truth is uncomfortable. The failure to worry about isn't a tool that's occasionally too harsh, it's one that quietly rubber-stamps everything as fine regardless of what it's actually looking at, because that gives false confidence exactly where it's most dangerous. So before relying on our own public tool for anything, we needed to prove it doesn't do that: that it genuinely inspects a site and reports real, varying results rather than always returning a comfortable pass.

1 · Choosing a fair, real-world test

The honest way to test that is to point the tool at real websites that we don't control and have no reason to score in any particular way, and ideally ones where you'd expect a serious security posture, so a weak result would clearly mean something. We chose two real, well-known businesses' public websites for exactly this. Running the tool against real sites, rather than only our own, removes any suspicion that it's tuned to flatter a site it already knows.

2 · What "a working tool" would actually look like

Before seeing the results, it's worth being clear about what would count as the tool working versus failing, because otherwise any result can be rationalised after the fact. A broken tool tends to fail in one of two obvious ways: it scores everything as perfect (so it isn't really checking anything), or it fails everything (so it's just throwing errors and flagging problems that aren't there). A genuinely working tool does neither: it produces different, believable scores for different sites, reflecting the real differences in how well each one is actually protected.

3 · The results, real, and meaningfully different

That's exactly what happened. The two sites scored clearly differently from each other, and both scores were plausible for what they were. One site was missing several important protections and scored notably lower as a result. The other had a number of protections properly in place, but was still missing some others, and scored better, though not perfect. Two real sites, two different, sensible scores, each one traceable to concrete things the site did or didn't have. Neither an all-pass nor an all-fail.

4 · Why that specific pattern is the reassuring one

The differentiation is the whole point, and it's worth spelling out why. If both real sites had scored a perfect 100, that would have been a warning sign that the tool wasn't genuinely testing anything, a rubber stamp. If both had scored zero, that would have suggested it was broken and flagging phantom problems. Two different, defensible scores, each tied to real, nameable gaps, is precisely the signature of a tool that is actually inspecting each site on its own merits and reporting honestly. It's the good outcome, not just an outcome.

5 · Outcome

The test confirmed the tool produces credible, real-world-plausible results rather than a fixed or broken score, which is what gave us the confidence to go on and use it for the broader testing that follows in this write-up. A checker you haven't proven is honest is one you can't safely rely on; this is the step that earned that reliance.

Key points

  • A checker that always says "fine" is worse than useless, it gives false confidence where it's most dangerous.
  • We tested our tool against two real, well-known businesses' sites, ones we don't control.
  • A working tool gives different, believable scores for different sites, not all-pass, not all-fail.
  • The two sites scored clearly differently, each traceable to real, nameable protections they had or lacked.
  • That pattern is the reassuring one: it proves the tool genuinely inspects each site rather than rubber-stamping.

How to test a security checker against real sites

  1. Point any checker you rely on at real sites you don't control, not just your own.
  2. Decide in advance what "working" looks like: different, sensible scores, not everything perfect.
  3. Run it against at least two different sites and compare.

How to confirm a checker's scores are trustworthy

  1. For each score, check it traces back to real, concrete things the site does or doesn't have.
  2. Only trust the tool for real decisions once it's shown it can tell good and bad apart honestly.

Questions & answers

Why not just test it on your own site? A tool might be unconsciously set up to give your own site a good result. Testing real sites you don't control is a fairer, more honest check of whether it really works.

Why would two perfect scores be a bad sign? Because real websites genuinely differ in how well they're protected. If everything scores perfectly, the tool probably isn't really checking anything.

What made the results trustworthy? The two sites got clearly different scores, and each score matched real, specific protections the site had or was missing, not a random or fixed number.

Why do this before the other testing? Everything that follows depends on the tool being honest. This is the step that proved it, before we relied on it further.

Technical detail

Purpose

Validation of the live public security-audit tool: confirm it produces genuine, differentiated scoring rather than a constant pass (untested) or constant fail (broken). Tested against two real third-party sites we don't control.

1 · Method

Ran the live tool against two real, public business websites, chosen as independent, uncontrolled targets where a weak result would be meaningful. Passive HTTP/TLS/DNS checks only, identical to any user-initiated scan.

2 · Results

# Site A
63/100, missing Content-Security-Policy, HSTS, X-Frame-Options,
         X-Content-Type-Options, Referrer-Policy, Permissions-Policy; no DMARC/DKIM
# Site B
82/100, HSTS present, full mail security (SPF/DMARC/DKIM);
         still missing CSP, Referrer-Policy, Permissions-Policy, cookie SameSite

3 · Interpretation

Each score is traceable to concrete, nameable header/DNS facts, not an opaque number. The two sites differ by 19 points on real differences (mail security and HSTS present on B, absent on A). This is the signature of genuine per-site inspection.

4 · Why the pattern validates the tool

Two failure signatures were being ruled out: uniform 100 (not actually testing) and uniform 0/errors (broken, false positives). Differentiated, defensible scores tied to specific missing controls rule out both.

5 · Outcome

Confirmed genuine, differentiated, plausible scoring. This validation is what justified using the tool for the subsequent multi-platform testing.

Key points

  • Goal: prove the tool isn't a rubber stamp (constant pass) or broken (constant fail).
  • Tested against two real, uncontrolled third-party sites, passive checks only.
  • Results: 63/100 and 82/100, a 19-point spread on real differences (HSTS + mail security).
  • Every point of difference traces to concrete missing headers/DNS records, not an opaque number.
  • Differentiated, defensible scores rule out both failure signatures, the tool inspects genuinely.

How to do this yourself

# validate any scanner against real, uncontrolled targets before trusting it:
# 1. run it against 2+ real sites you don't own
# 2. expect DIFFERENT, defensible scores, not all-100, not all-0
# 3. trace each score to concrete facts, e.g.:
$ curl -sI https://<site>/ | grep -iE "strict-transport|content-security|x-frame"
$ dig +short TXT _dmarc.<domain>    # mail security present or not

Questions & answers

Why test against sites you don't own? A tool can be accidentally tuned to flatter sites it knows. Uncontrolled real targets remove that bias, the scores reflect the sites, not the tool's expectations.

Why is a 100/100 on a real site a bad sign here? Uniform perfect scores across different real sites suggest the tool isn't actually inspecting anything. Real sites vary, so honest results should vary too.

What makes the two scores "believable"? Each one decomposes into concrete facts, B has HSTS and full mail security, A has neither, so the 19-point gap is explained by real, checkable differences, not a random number.

Why does this matter before the rest of the testing? Everything downstream relies on the tool being honest. Proving it produces differentiated, defensible results is what earns the right to trust its later verdicts.

03

Built a tool to test 10 major website platforms

Confirmed safe

Why build a second tool at all

Our public security audit checks one website at a time, the way a visitor's browser would. That's exactly right for a member of the public checking their own site, but it makes testing the checking logic across many sites slow and manual: you'd have to sit and run it by hand, one site after another, and copy down each result. So we built a separate companion tool that runs the same kind of safe, passive checks, the ones that only look at a page the way any browser sees it, but automatically down a whole list of websites in one pass, straight from our own server. Same checks, many sites, no manual clicking.

It's worth being clear this is a genuinely separate program from the public audit, not a change to it. The public tool on the website was never touched; this is a standalone script we wrote for our own testing, so we could put the checking logic through its paces against a wide, varied range of real sites quickly.

1 · Keeping it strictly to safe, passive checks

The tool does only passive checks, the exact same category of thing a normal web browser does when it loads a page and reads the response. It sends no attacks, tries no break-ins, and does nothing a site owner could object to; it simply looks at what every visitor's browser already sees: the security-related settings a site sends back with its pages. That restraint is deliberate and important, it's what makes running it against websites we don't own completely legitimate, exactly as looking at any public page in a browser is legitimate.

2 · Choosing a deliberately varied set of test sites

To really put the checking logic through its paces, we needed sites that are genuinely different from one another, not ten near-identical sites that would all pass or fail together and prove nothing. So we chose the official websites of 10 of the biggest website-building platforms in the world. These are run by large, sophisticated companies with real security teams, and they're built on very different underlying technology from one another, which makes them an excellent, demanding, and fair stress test: if the logic handles this much variety correctly, it's robust.

3 · What the results showed, real variation, not uniformity

The results varied genuinely and sensibly from platform to platform, which is exactly what you want to see. Some scored very well, with barely any issues. Others, even these big, capable companies, had real, specific, identifiable gaps in their security settings: things like missing protections on the small pieces of data a site stores in your browser, or missing security-related instructions in the pages they serve. A few even openly advertised, in their pages, exactly which platform they were built on, a small information leak in itself. The point isn't to single any of them out; it's that the tool found real, different, nameable things on different sites, which is precisely how you know it's genuinely inspecting each one rather than producing a canned answer.

4 · Why this was a meaningful test of the logic

This mattered because it proved the checking logic behaves correctly and consistently across a wide range of real, very different websites, not just the one site we started from. It's easy for a checker to work on the site it was built and tested against, and then behave strangely the first time it meets a site built differently. Running it across ten genuinely different, professionally-built platforms and getting sensible, varied, defensible results from each is strong evidence that the logic is sound in general, not just narrowly tuned to our own site.

5 · Outcome

The companion tool confirmed the underlying checking logic is correct and consistent across a broad, diverse set of real sites, giving real confidence in the checks themselves. And it did so using only the same safe, passive approach as the public audit, run entirely from our own machine against publicly-available pages.

Key points

  • A separate companion tool runs the same safe, passive checks as the public audit, but down a whole list of sites in one pass.
  • It does only what a browser does on page load, no attacks, which makes checking sites we don't own legitimate.
  • We chose 10 of the biggest, most different website platforms on purpose, as a demanding, fair stress test.
  • Results varied sensibly, some near-perfect, others with real, nameable gaps, proving genuine per-site inspection.
  • It confirmed the checking logic works across many very different real sites, not just our own.

How to test a checker across many platforms

  1. If you rely on a checker, test its logic against many sites, not just the one it was built on.
  2. Keep the checks strictly passive (only what a browser does on page load), so testing others' public pages is safe.
  3. Deliberately pick sites that differ from one another, so a real bug has a chance to show.

How to read multi-site test results

  1. Look for varied, sensible results, some good, some with specific gaps, rather than everything identical.
  2. Treat that variation as the good sign: it means the tool is genuinely inspecting each site.

Questions & answers

Is this your public audit tool? No, it's a separate program we built just for testing. The public tool on the website was never changed; this one simply reuses the same safe checks to try them across many sites at once.

Is it OK to run this against sites you don't own? Yes, it only looks at a page the way any visitor's browser does. It sends nothing harmful and tries no break-ins, so it's the same as viewing a public page.

Why test 10 different platforms instead of 10 similar sites? Because difference is the whole point. Ten near-identical sites would all behave the same and prove little; ten very different ones that each give sensible results show the tool really works in general.

What did "varied results" actually look like? Some platforms scored very well; others had real, specific gaps, and a few even advertised which platform they were built on. Different, nameable findings on different sites is exactly how you know it's inspecting each one properly.

Technical detail

Purpose

Build a server-run, multi-target harness that exercises the live tool's passive check categories across many domains in one pass, to validate the check logic against a diverse real-world sample without manual, one-at-a-time runs. Distinct program from the public audit; the live product was untouched.

1 · What it is

A standalone script (site_check.py) replicating the live tool's passive categories, HTTP security headers, TLS config, cookie flags, mixed content, server fingerprint, CMS detection, mail DNS (SPF/DMARC/DKIM), robots/security.txt, looped over a domain list, driven by curl / openssl / dig. Purely outbound, read-only requests; nothing touches local files, no sudo needed.

$ python3 site_check.py            # loops the domain list, passive checks only
# per site: curl -sI (headers) · openssl s_client (TLS) · dig TXT (SPF/DMARC/DKIM)

2 · Passive-only, by design

Every check is what a browser already performs on page load, no active payloads, no port scans, no auth attempts. That scoping is what makes running it against third-party public sites legitimate (identical to viewing the page and reading response headers).

3 · Target set, chosen for diversity

Ran against 10 major CMS/site-builder platforms' own official sites: WordPress, Shopify, Wix, Squarespace, Webflow, Drupal, Ghost, BigCommerce, HubSpot, Joomla. Large, professionally-run, and built on very different stacks, a demanding correctness sample.

4 · Representative results

Ghost / Webflow      near-perfect , strong headers, cookie flags, TLS
Wix / BigCommerce /
  Squarespace        real gaps   , missing cookie SameSite / security headers
Drupal / Joomla / Wixinfo leak   , CMS generator meta tag exposed (platform advertised)

5 · Why it validates the logic

Uniform results across a diverse set would signal the logic is insensitive (not really inspecting). Instead, results decomposed into concrete, per-site, per-category facts, varied and defensible, confirming the check logic generalises beyond the site it was built on.

6 · Outcome

Check logic verified correct and consistent across a diverse real-world sample, via passive-only, server-run requests. Public audit untouched.

Key points

  • Separate server-run harness (site_check.py), same passive categories as the live tool, many domains per pass.
  • Passive-only (curl/openssl/dig), legitimate against third-party public sites; live product untouched.
  • Target set chosen for diversity: 10 major platforms on very different stacks.
  • Results varied per site/category (near-perfect → real cookie/header gaps → exposed generator meta), not uniform.
  • Varied, decomposable results prove the check logic generalises beyond the origin site.

How to do this yourself

# passive, per-domain, in a loop, same shape as the harness:
$ for d in site1 site2 site3; do
    echo "== $d =="
    curl -sI "https://$d/" | grep -iE "strict-transport|content-security|x-frame|set-cookie"
    dig +short TXT "_dmarc.$d"
  done

Questions & answers

Is this the same as the public audit tool? No, it's a separate script that reuses the same passive check categories. The live product was never modified; this exists purely to test the logic at scale.

Is it legal to run against sites you don't own? Yes, it only does what a browser does on page load (reads response headers, checks TLS, looks up public DNS). No active payloads, no port scans, no auth attempts.

Why 10 different platforms rather than 10 similar sites? Diversity is the test. Ten near-identical sites would pass or fail together and prove little; ten different stacks that each yield sensible, distinct results show the logic generalises.

What does an "exposed generator meta tag" mean? Some sites include a tag in their HTML naming the platform they're built on. It's a minor information leak, it tells an attacker exactly what software (and therefore what known weaknesses) to target.

04

Real bug found in our own security tool

Fixed

The moment of doubt, and why we welcomed it

Our own security-checking tool gave the main site a perfect 100 out of 100. That should feel good, but a perfect score is exactly the kind of result that deserves suspicion rather than celebration, because the most dangerous failure a checking tool can have is telling you everything's fine when it isn't. So instead of taking the win, we did the honest thing and cross-checked it: we ran the very same site through a completely independent, well-known security checker built by a major technology company, to see whether it agreed.

It didn't. The independent checker scored the site 80 out of 100, and it pointed to one real, specific weakness in a particular security setting, a weakness our own tool had missed entirely and papered over with full marks. That gap between "100 from us" and "80 from them, with a named problem" is the whole story of this section: a real bug in our own tool, caught precisely because we didn't trust our own perfect score.

1 · Not dismissing the disagreement

The easy, comfortable response to that disagreement would have been to wave it away, to assume the other tool was being fussy and ours was right. We deliberately didn't. When a trusted independent tool disagrees with yours and names a concrete problem, the honest assumption is that yours might be wrong, and the only way to know is to look. So rather than defend our score, we went and read our own tool's actual underlying code to find out exactly why it had given full marks where a respected checker saw a real fault.

2 · What the code was actually doing

Reading the code revealed the bug plainly, and it was a genuinely instructive one. Our check for this particular security setting was only checking whether the setting was present at all, not what it actually said. It's the difference between checking that a door has a lock fitted and checking whether that lock is any good or left wide open. Because of that, a site could have the weakest, loosest, most permissive possible version of this setting, one that effectively cancels out the protection the setting is supposed to provide, and still sail through our check with full marks, purely because something was there. Present-but-useless was being scored identically to present-and-strong.

3 · Fixing it against the real standard

We rewrote the check properly. Instead of merely asking "does this setting exist?", it now inspects what the setting actually contains and judges whether it's genuinely strong, using the real, well-established rule for what makes this setting strong (that it must not include the self-defeating options that cancel its protection). To be clear about what we did and didn't do: we did not change our tool to produce the same score as the independent one. The two use completely different scoring scales, and we're not claiming an 80 or trying to reproduce it. The independent tool didn't set our score, it simply helped us spot that our own check was wrong. All we changed was the check itself, so it now judges this setting correctly instead of rubber-stamping it.

4 · Testing the fix three ways before trusting it

A fix to a checking tool has to be tested as carefully as anything else, because a check that's wrong in a new direction is no better than the old one. So before putting it live we tested it three ways: we confirmed it correctly failed the site's real, currently-weak setup (the thing the independent tool caught); confirmed it correctly passed a properly strict, secure setup (so it isn't just failing everything now); and confirmed it correctly failed when the setting was missing entirely. Fails the weak case, passes the strong case, fails the absent case, all three, so we know it's discriminating correctly rather than just flipped to always-fail.

5 · Outcome

Once deployed and re-tested on the real live site, the tool now correctly and honestly reports this as a genuine issue needing attention, with a clear, accurate explanation of exactly what's wrong, instead of hiding a real problem behind a false, reassuring 100/100. The tool is now more honest than it was, which for a security checker is the most important quality it can have. And it was our own willingness to distrust a flattering result that surfaced the bug in the first place.

Key points

  • Our tool scored the site a perfect 100; an independent, trusted checker scored it 80 and named a real weakness ours missed.
  • We cross-checked rather than celebrated, a perfect score is the one that most deserves suspicion.
  • The bug: our check only asked whether a setting existed, never whether it was any good.
  • We rewrote it to judge the setting's real content, not to change our score to match the other tool (different scales); it just helped us find the bug.
  • We tested the fix three ways (weak fails, strong passes, missing fails) before putting it live.

How to catch a bug in your own security tool

  1. Treat a perfect score from your own tool as a reason to double-check, not to relax.
  2. Cross-check the same thing with an independent, well-regarded tool and see if it agrees.
  3. If it disagrees and names a problem, assume yours might be wrong and go read your own logic.

How to fix and verify a checker bug

  1. Make sure your checks judge what a setting actually says, not just whether it's present.
  2. Test any fix on a weak case, a strong case, and a missing case before trusting it.

Questions & answers

Why not trust your own 100/100? Because a perfect score is exactly what a broken checker would also produce. The safest response to your own tool saying "all perfect" is to have an independent tool check the same thing.

What was actually wrong? Our check only looked at whether a security setting was there at all, not whether it was strong. So a site with the weakest possible version of that setting still got full marks.

How did you fix it? We rewrote the check to look at what the setting actually says and decide whether it's genuinely strong. We didn't change our tool to output the other tool's number, the scales differ, we just stopped it scoring a self-defeating setting as safe.

How do you know the fix is right? We tested it three ways: it now correctly fails the weak real setup, correctly passes a properly strict one, and correctly fails when the setting is missing, then verified it on the live site.

Technical detail

The trigger

Our tool scored the main site 100/100. Cross-checked against an independent tool (Mozilla HTTP Observatory), which scored it 80/100 and flagged 'unsafe-inline' / 'unsafe-eval' in the Content-Security-Policy header's script-src, both of which defeat CSP's core XSS protection. A perfect score from our tool alongside a named real fault from a trusted one meant our tool was wrong.

1 · Root cause, presence-only check

Read the live tool's backend (audit-handler.js). The CSP check was:

csp: { pass: !!h['content-security-policy'], weight: 8 }

Presence-only: any CSP header at all scored a pass, regardless of content. A policy with 'unsafe-inline' 'unsafe-eval', i.e. one that neuters its own protection, passed identically to a strict one.

2 · The fix, evaluate the directive against the real CSP rule

Added a strictness check using the actual CSP rule (a policy is only strong if the relevant directive contains neither 'unsafe-inline' nor 'unsafe-eval'). This does not tune our score to the independent tool's number, the two scales differ; it fixes what our check evaluates:

function _saCspIsStrict(csp) {
  if (!csp) return false;
  const scriptSrc = csp.match(/script-src\s+([^;]+)/i);
  const defaultSrc = csp.match(/default-src\s+([^;]+)/i);
  const relevant = scriptSrc ? scriptSrc[1] : (defaultSrc ? defaultSrc[1] : '');
  if (!relevant) return false;
  if (/'unsafe-inline'/i.test(relevant)) return false;
  if (/'unsafe-eval'/i.test(relevant)) return false;
  return true;
}

Falls back to default-src when script-src is absent (as the spec does), and fails on either unsafe token.

3 · Three-case validation before deploy

real live CSP (has unsafe-inline/eval)  -> FAIL  # the real fault, now caught
genuinely strict CSP                    -> PASS  # not just failing everything
empty / missing CSP                     -> FAIL  # absence still fails

4 · Deploy

Uploaded, reset ownership/permissions (chown/chmod), pm2 restart. Verified live: site now reports 21/22 checks passed, CSP correctly failing with an accurate custom message. Note this is the on-page audit's own header score, separate from the site's AI-readiness score, which lives elsewhere in the codebase.

5 · Outcome

A false 100/100 replaced by an honest, accurate result driven by real directive analysis and validated across fail/pass/absent cases. Our tool keeps its own scoring (checks passed / total), we did not re-style it to emit the independent tool's 80. The checker is now correct where it was previously blind.

Key points

  • Our tool said 100/100; independent Mozilla HTTP Observatory said 80/100 and named a real CSP fault.
  • Root cause: CSP check was presence-only (!!h['content-security-policy']), never read the directive.
  • A policy with 'unsafe-inline'/'unsafe-eval' (self-defeating) passed identically to a strict one.
  • Fix: _saCspIsStrict() inspects script-src/default-src and fails on unsafe tokens, matching the reference tool's logic.
  • Validated FAIL/PASS/FAIL across weak/strict/absent, then deployed; live now 21/22 with CSP correctly failing.

How to do this yourself

# never trust a self-scored 100, cross-check against an independent tool
# and evaluate directive CONTENT, not mere presence:
$ curl -sI https://<site>/ | grep -i content-security-policy
# a 'pass' should require: header present AND no 'unsafe-inline'/'unsafe-eval'
# in script-src (or default-src as fallback)

Questions & answers

Why cross-check a perfect score? A 100 from your own tool is the least trustworthy result it can give, it's indistinguishable from a tool that isn't checking properly. An independent second opinion is how you catch that.

What was the actual bug? The CSP check used !!h['content-security-policy'], true if any CSP header exists. It never parsed the policy, so a self-defeating 'unsafe-inline' policy passed exactly like a strict one.

Why use the real CSP rule rather than invent your own? Because "strong CSP = no 'unsafe-inline'/'unsafe-eval'" is a well-established rule, so encoding it is simply correct. We did not tune our tool to reproduce the other tool's 80, the scales differ, we just stopped scoring a self-defeating policy as safe.

Why test the strict-CSP pass case, not just the fail? To prove the fix discriminates rather than merely flipping to always-fail. A check that fails everything is as useless as one that passes everything.

05

Added a check that was missing entirely

Fixed

A gap, not a bug, something we simply weren't checking

The previous section was about a check that was wrong. This one is about a check that was missing entirely, a genuine blind spot rather than a mistake. When we cross-checked our tool against the independent one, it tested one thing ours didn't look at all: whether the scripts and stylesheets a website loads from other websites carry a special safety verification. Being honest about a missing check is just as important as fixing a broken one; an incomplete checker gives false confidence in a quieter way.

Here's the real-world risk this addresses, in plain terms. Most websites pull in some code from outside sources, a font here, a script from another company there. Normally your browser just trusts and runs whatever those outside sources send. The safety verification in question lets a website say, in advance, "this outside file should look exactly like this, and if it doesn't, don't run it." That protects visitors if one of those outside sources is ever hacked or tampered with: instead of every visitor's browser silently running the altered, malicious code, the browser notices it doesn't match and refuses. It's a genuinely valuable protection, and our tool wasn't checking for it.

1 · Building the missing check

So we built and added it ourselves. The check reads through a page, finds every script and stylesheet that's loaded from outside the site's own address, and works out whether each one carries that safety verification or not, flagging the ones that don't. We built it on top of the same reliable page-reading component the tool already used elsewhere, rather than bolting on something new and untested, so it fits cleanly with how the rest of the tool works.

2 · The second, separate problem, it worked but didn't show

Once the check was working correctly behind the scenes, a second and quite different problem appeared: the results page that visitors and admins actually see didn't display the new check anywhere, even though it was running and scoring correctly out of sight. This is a good example of how two parts of a system have to agree with each other. The part that does the checking now knew about the new check, but the part that displays the results keeps its own separate list of checks it knows how to show, and that list hadn't been told the new check existed. So the check ran, scored, and then vanished before anyone could see it.

3 · Fixing the display side too

The fix was to add the new check to that display list as well, matching exactly the format every other check already used, so it slots in looking and behaving like a native part of the page rather than an afterthought. It's a small change, but it's the difference between a check that quietly works where nobody can see it and one that actually informs the person reading the report. A check that runs but never surfaces is, from the reader's point of view, not really there.

4 · The honest final score

With both parts fixed and live, the check itself, and its display, the new check now appears correctly on the results page with a clear explanation, and the main site now scores 93 out of 100, correctly passing 22 of 23 checks in total. The one remaining issue is the real CSP weakness described in the previous section, now accurately reported rather than hidden. That's the honest picture: a genuine, nearly-complete score with the one real outstanding item shown plainly, which is far more useful than a flattering but false perfect mark.

5 · Outcome

A real protective check that was entirely absent is now present, working, and visible, closing the last gap between our tool and the trusted independent one, and giving anyone who runs the audit a fuller, more honest picture of a site's security than before.

Key points

  • This was a missing check, not a broken one, a genuine blind spot the independent tool exposed.
  • It checks whether outside scripts/styles carry a safety verification so a tampered file won't run in a visitor's browser.
  • We built the check on the tool's existing, proven page-reading component.
  • A second problem: it worked but didn't show, the display side kept its own list and didn't know the new check existed.
  • Fixing both parts, the site now scores 93/100 (22/23), with only the real CSP issue correctly still flagged.

How to find a missing check in your tool

  1. Compare your checker against a trusted independent one and note anything it tests that yours doesn't.
  2. For a genuine gap, build the missing check rather than ignoring it, a missing check is false confidence.

How to add a new check correctly

  1. Reuse your tool's existing, proven building blocks rather than bolting on something new.
  2. Remember the display side: a new check that scores correctly but never appears is no use.
  3. Prefer an honest, slightly-imperfect score that shows real issues over a flattering perfect one.

Questions & answers

What does this new check protect against? If a website loads code from an outside source and that source is ever hacked, this protection lets the browser notice the code has changed and refuse to run it, protecting every visitor instead of silently running the tampered version.

Why did the check work but not show up? The part that runs checks and the part that displays them keep separate lists. The checking part knew about the new one; the display part didn't, so it ran but never appeared until we updated the display side too.

Why does the score drop from 100 to 93? Because 93 is the honest number. The old 100 hid a real problem; 93 reflects a near-complete, genuine result with the one real outstanding issue shown clearly.

Is one remaining issue a bad sign? No, it's an honest one, and it's the CSP weakness from the previous section, now correctly reported. A tool that shows real remaining issues is doing its job.

Technical detail

The gap

Added Subresource Integrity (SRI) checking, previously absent entirely, and the one remaining real gap versus the independent Mozilla checker. SRI lets a page pin a cryptographic hash on cross-origin <script>/<link> resources so the browser refuses to execute a tampered file (e.g. a compromised CDN).

1 · Backend check

Implemented _saCheckSri() using the cheerio parser already present in the file, parse the HTML, find cross-origin script/stylesheet references lacking an integrity attribute, and report them:

function _saCheckSri(html, baseUrl) {
  const $ = cheerio.load(html);
  const missing = [];
  $('script[src]').each((_i, el) => {
    const src = $(el).attr('src');
    if (isCrossOrigin(src) && !$(el).attr('integrity'))
      missing.push({ tag: 'script', url: src });
  });
  // same pattern for <link rel="stylesheet">
  return { applicable: true, missing };
}

2 · The frontend disconnect

The check scored correctly server-side but rendered nothing on the results page. Cause: the frontend keeps a separate hardcoded CHECK_META map of every known check's label/description, and it had no sri entry, so a correctly-scored result had no display metadata and was silently dropped from the rendered list.

# CHECK_META had no 'sri' key -> result computed but never displayed

3 · Frontend fix

Added the sri entry to CHECK_META, matching the existing label/description formatting exactly so it renders identically to native checks:

sri: { label: 'Subresource Integrity',
       desc: 'Cross-origin scripts/styles pinned with integrity hashes' }

4 · Deploy & verify

Deployed both changes; verified live: the SRI row now displays with a PASS status. Combined with the CSP fix from the previous section, the site scores 93/100, 22/23 checks passed, the single remaining fail being the (correctly-reported) CSP.

5 · Outcome

SRI coverage added end-to-end (compute + display), closing the last gap against the reference checker. Backend and frontend check registries are now consistent for this check.

Key points

  • Added SRI checking, previously missing; the last real gap vs the independent checker.
  • SRI pins cross-origin scripts/styles so the browser rejects a tampered file (e.g. hacked CDN).
  • Backend _saCheckSri() reused the existing cheerio parser, flags cross-origin refs lacking integrity.
  • Second bug: correct score didn't render, frontend CHECK_META had no sri entry, so it was dropped.
  • Added the display entry; live now 93/100, 22/23, only the CSP correctly still failing.

How to do this yourself

# find cross-origin scripts/styles missing an integrity attribute:
$ curl -s https://<site>/ \
   | grep -oE '<(script|link)[^>]+(src|href)="https?://[^"]+"[^>]*' \
   | grep -v 'integrity='
# and remember: a new backend check also needs a frontend display entry

Questions & answers

What does SRI actually protect against? A compromised third-party source (e.g. a hacked CDN) serving altered code. With an integrity hash pinned, the browser refuses to run a file that doesn't match, so tampering fails closed instead of silently executing.

Why did a correct check show nothing on the page? The frontend has its own list of known checks with display labels. The new check scored fine but had no entry there, so the renderer had nothing to show and dropped it.

Why reuse cheerio rather than a new parser? It was already in the file and proven; reusing it keeps the new check consistent with existing ones and avoids adding an untested dependency for the same job.

Why is 93/100 a better result than the old 100? Because it's true. It reflects a real, near-complete posture with the one genuine outstanding item (CSP) shown plainly, instead of a false perfect that hid a real problem.

06

The 'award winning' search phrase investigation

Confirmed safe

Not a security issue, a mystery worth ruling out properly

This last item isn't a security fix at all; it's an honest investigation into something that looked odd and turned out to be harmless, included because ruling things out carefully is part of the same disciplined mindset as fixing real problems. A search-performance report showed a specific phrase describing the business favourably suddenly appearing in Google's results far more often than the week before, a genuinely large jump. The phrase didn't sound familiar, and an unfamiliar claim about your own business showing up in search is exactly the sort of thing you should chase down rather than either celebrate or ignore. Where was it coming from? Had something been added to the site without our knowledge? We investigated properly rather than assuming.

1 · Searching every file on the whole server

We started with the most thorough possible check: we searched every single file on the entire server, every domain, every file type, no exceptions, for that exact phrase. The only place it appeared was inside files belonging to completely unrelated third-party software that the site uses behind the scenes (marketing text in another product's bundled documentation), with nothing to do with this business's own content at all. So it wasn't in anything we'd written or published.

2 · Checking the files meant for AI and search engines

Next we checked the files specifically meant to be read directly by AI tools and search engines, the ones that describe a site to automated systems. If the phrase had been planted somewhere to influence how machines describe the business, this is where it would be. It wasn't there either. Nothing in any of those files contained it.

3 · Widening the net, and following the trail to its real source

We then broadened the search to catch near-variations, different spacing, hyphens, word order, in case it was present in a slightly different form. That turned up some old, leftover working files sitting in a private folder that isn't reachable from the internet at all. Digging into those, we traced the actual source: the phrase had been accidentally captured as sample test data by a separate tool used for checking lots of different websites at once, and it originally belonged to a completely different, unrelated real company. It had nothing to do with this business, it was just incidental data left over in a working folder. We also confirmed that folder returns a "not found" response from the internet, so nothing in it can be reached or read by any search engine or visitor.

4 · Checking everywhere else the business appears

To be genuinely thorough, we also checked everywhere off the site that the business appears. Every business directory listing was inspected, all of them plain factual entries (address, phone, category), with no promotional wording. Every social-media profile was checked too; several of those platforms deliberately block automated checking tools, so those were checked personally, by hand, rather than skipped. Nowhere did the phrase appear.

5 · The actual, boring, reassuring explanation

So the phrase genuinely does not exist anywhere connected to this business, not on the website, not in any file meant for AI tools, not in any directory listing, not on any social profile. The explanation turns out to be completely normal, well-documented behaviour: search engines can show a page as relevant to a search without that search's exact wording appearing anywhere on the page. They increasingly match on what a page is broadly about, its general topic and meaning, rather than on literal word-for-word matching. In other words, Google decided the site was relevant to that favourable phrase based on the site's overall subject matter, not because anyone wrote those words anywhere. We confirmed this is standard behaviour via the search engine's own official help community and several independent industry sources.

6 · Conclusion, nothing to fix, and that's the finding

There was nothing to fix, because there was genuinely nothing wrong or hidden anywhere to begin with. That is itself a worthwhile result: a surprising signal was chased all the way to the ground and shown to be benign, with certainty rather than a shrug. Sometimes the honest, valuable outcome of an investigation is a confident "there is no problem here", and being able to say that, having actually checked everything rather than assumed, is worth just as much as a fix.

Key points

  • A favourable phrase spiked in Google's results; it didn't sound familiar, so we investigated rather than assumed.
  • A whole-server search (exact and near-variants) found it only in unrelated third-party files and private, unreachable test data, never in the site's own content.
  • Files meant for AI/search engines, every directory listing, and every social profile were all checked, some by hand.
  • The cause is normal, documented behaviour: search engines rank a page for a phrase's topic, not its exact words.
  • Nothing to fix, a confident "no problem here", reached by actually checking everything.

How to trace a strange phrase across your server

  1. When a strange phrase appears in your search stats, search your whole server for it before assuming anything.
  2. Search for near-variations too, different spacing, hyphens and word order.
  3. Check the files meant for AI tools and search engines specifically, not just your normal pages.

How to confirm whether a found phrase is exposed

  1. For any match, confirm whether it's actually reachable from the internet or just a private leftover.
  2. Check off-site too, directory listings and social profiles, by hand where automated tools can't reach.

Questions & answers

How can Google show a page for a phrase that isn't on it? Search engines increasingly match on what a page is broadly about, not just its exact words. So a page can be shown for a phrase it doesn't literally contain, based on its overall topic.

Was something added to the site without your knowledge? No. We searched every file on the whole server and the phrase only appeared in unrelated third-party software and in private files that aren't reachable from the internet, nothing in the business's own content.

Why check social profiles and directories by hand? Some of those platforms block automated checking tools, so an automated-only search would miss them. Checking personally makes sure nothing was overlooked.

If nothing was wrong, why does this count as a result? Because a surprising signal was chased all the way down and shown to be harmless with certainty. A confident "there's genuinely nothing wrong here" is a real and valuable outcome.

Technical detail

The signal

Google Search Console showed a query (a favourable "award winning" phrase) jumping 11 → 61 impressions week-on-week (+455%), all attributed to the homepage. The phrase wasn't known to be anywhere on the site, investigated rather than assumed.

1 · Exact-phrase sweep, whole server

$ grep -rlin "award winning" /var/www/vhosts/
# only match: an unrelated third-party npm package's own README.md
# (marketing copy for a different product), nothing in the site's content

2 · AI/crawler-facing files

Checked llms.txt, JSON, and .md across all public folders specifically, empty result. Nothing planted where machines read.

3 · Broadened variant search → traced to source

$ grep -rlinE "award[- ]?winning|winning.{0,10}award|award.{0,15}win" /var/www/vhosts/
# 8 CSV files in a PRIVATE, non-web-accessible working folder
# traced matched text -> an unrelated real company's own site content,
# captured as incidental test data by a bulk domain-crawling tool
$ curl -s -o /dev/null -w '%{http_code}' https://<site>/<that-folder>/
404   # folder not web-accessible -> no path onto the live site, no crawler can read it

4 · Off-site presence

Directory listings (Hotfrog, FreeIndex, find-open.co.uk) fetched directly, bare factual entries, no promotional copy. Social profiles: several blocked automated fetching (robots/bot-detection), so were checked directly by the site owner and confirmed clean. General web search tying the phrase to the business/domain, no connecting results.

5 · Root cause

Confirmed via Google's Search Central community and independent SEO sources: this is standard, documented Search Console behaviour, impressions for a query do not require that query's literal wording on the ranked page. Google's ranking matches on broader topical/semantic relevance, so a page can accrue impressions for a phrase it never contains.

6 · Outcome

No fix required or possible: nothing associated with the site or business produces the phrase. A surprising metric was run to ground and shown benign with certainty.

Key points

  • GSC query jumped 11 → 61 impressions (+455%) on the homepage for a phrase not known to be on the site.
  • Whole-server exact grep: only match was an unrelated npm package's README, not the site's content.
  • AI-facing files (llms.txt/JSON/.md): nothing.
  • Variant regex found the phrase only in private, 404 (non-web-accessible) CSV test data from an unrelated company.
  • Root cause: normal GSC semantic matching, impressions don't require the literal phrase on the page. No fix needed.

How to do this yourself

# 1. exact sweep across every vhost, case-insensitive:
$ grep -rlin "the phrase" /var/www/vhosts/
# 2. broaden to spacing/hyphen/order variants:
$ grep -rlinE "award[- ]?winning|winning.{0,10}award" /var/www/vhosts/
# 3. prove any hit is unreachable from the web:
$ curl -s -o /dev/null -w '%{http_code}' https://<site>/<path>/   # expect 404

Questions & answers

How can a page rank for a phrase it doesn't contain? Modern search matches on topic and meaning, not just literal words. Google can judge a page relevant to a phrase based on what it's broadly about, so impressions don't require the exact wording on the page.

Was the phrase planted or injected anywhere? No. A whole-server sweep (exact and variant) found it only in unrelated third-party files and in private, non-web-accessible test data, never in the site's own served content.

Why did the 404 check matter? It proves the private folder holding the incidental match can't be reached from the internet, so no crawler or visitor can read it, ruling it out as the cause.

Why check social/directory listings by hand? Some platforms block automated fetching, so an automated-only check would have blind spots. Checking them directly closes those gaps and confirms the phrase is genuinely nowhere.

07

Rebuilt the audit to 54 checks, then tested it on 200 real sites

Confirmed safe

Making the audit more complete, then proving it holds up

The earlier testing showed the security audit was sound, but sound isn't the same as complete. A checklist that only covers some of what matters can still miss real things, so we went through the audit properly and added every meaningful check it was lacking, taking it from forty-four checks to fifty-four. Then, crucially, we didn't just trust that the new, bigger version worked, we put it through the hardest test we could: running it against two hundred different real websites and inspecting not only the results it calculated, but what it actually displayed on the page, to be certain the two always agreed.

1 · Adding the missing checks, honestly

We added a full set of checks the audit didn't previously make: modern browser-isolation settings, a safe test for an old and risky web method, certificate breadth, mail-branding records, and several others. The guiding rule throughout was honesty, not box-ticking. Many of the new items are genuinely optional, a site is not insecure for lacking them, so those are shown as neutral information, never as a red failure. A deprecated setting that modern browsers ignore is labelled as exactly that, with a note that any scanner still demanding it is out of date. The point of a bigger audit is to see more, not to invent more things to fail people on.

2 · Testing the results and the display together

A subtle but important point: a security tool can calculate the right answer and still show the wrong thing. The number it works out internally and the list of results a person actually reads on screen have to match exactly, or the report misleads even when the underlying logic is correct. So we tested both layers at once, the calculated result and the rendered page, and checked on every single site that the totals reconciled, that every result shown was accounted for, and that nothing extra was quietly displayed that the summary didn't count.

3 · The first hundred sites, and a real bug caught

We ran the audit against one hundred major websites and it behaved correctly on ninety-nine of them. The hundredth, a large government site, revealed a genuine display fault: because that site had a number of known software vulnerabilities, the page listed each one as if it were a separate check, inflating the visible list beyond the number the summary counted. The calculation was right, the vulnerabilities were real and correctly scored once, but the way they were shown didn't line up with the count. This is exactly the kind of fault that only appears on a site with that specific characteristic, which is precisely why testing across many varied real sites matters.

4 · Fixing it the honest way

The fix kept every vulnerability fully visible, hiding them would have been dishonest and defeated the point, but showed them as supporting detail beneath the single check that counts them, rather than as separate checks in their own right. The finding is reported once and counted once; the detail of exactly which vulnerabilities were found is shown in full underneath. Visible and honest, but no longer distorting the count.

5 · A second hundred, completely different sites

To be sure the fix worked and that the tool wasn't merely tuned to the first hundred, we ran it again against a different hundred sites, deliberately chosen to be varied: international sites, publishing sites built on common software (the exact kind likely to have the vulnerability pattern that caught us the first time), online shops, developer services, forums and government sites. This time all one hundred passed cleanly, results and display in perfect agreement on every one, including the sites that did have known vulnerabilities, which now displayed correctly. Two hundred different real sites in total, both the calculation and the on-screen report checked on each, every one reconciling.

6 · Outcome

The audit is now both broader and proven: fifty-four honest checks, validated on two hundred real websites at both the calculation and display level, with the one fault that testing uncovered found and fixed. A bigger checklist is only an improvement if it's still accurate; this is the work that confirmed it is.

Key points

  • The audit was expanded from 44 to 54 checks, adding every meaningful check it lacked.
  • New optional items are shown as neutral information, never as failures, a bigger audit should see more, not fail people on more.
  • We tested the calculated result and the displayed page together on every site, so the two can never disagree.
  • Testing on 100 sites caught a real display fault (a site's known vulnerabilities inflating the visible list); it was fixed so they show as detail, fully visible but counted once.
  • A second, different 100 sites then passed cleanly, 200 real sites in total, every one reconciling at both layers.

How to properly test a tool you rely on

  1. Test the result it calculates and the result it displays together, they must always match.
  2. Run it across many genuinely varied real sites, not a handful of similar ones, some faults only show on specific kinds of site.
  3. When a fault appears, fix it without hiding information, keep everything visible, just show it correctly.

How to add checks honestly

  1. Mark genuinely optional items as neutral information, never as a red failure.
  2. Label deprecated settings as deprecated, don't demand things modern browsers ignore.
  3. Count each finding once; show supporting detail beneath it, not as extra checks.

Questions & answers

Does adding more checks just mean failing sites on more things? No, that would be the wrong kind of tool. Many of the added checks are genuinely optional and are shown as neutral information, not failures. The aim is to see more accurately, not to manufacture problems.

Why test what's displayed as well as what's calculated? Because a tool can work out the right answer and still show a misleading list on screen. If the number it counts and the results a person reads don't match, the report misleads even when the logic is sound, so both have to be checked.

Was hiding the vulnerabilities the fix? No. Hiding real findings would be dishonest and defeat the point of an audit. Every vulnerability stays fully visible; the fix simply shows them as detail under the single check that counts them, so the finding is reported once and counted once.

Why run a second, different hundred sites? To prove the tool isn't merely tuned to the first set and that the fix genuinely worked, especially on the kinds of site that had triggered the fault. Passing a fresh, varied hundred is far stronger evidence than re-running the same list.

Technical detail

Purpose

Expand the passive security audit from 44 to 54 checks, then validate the larger tool at scale across both layers, the API response (backend scoring/reconciliation) and the rendered DOM (frontend), against 200 unique real sites in two independent 100-site runs.

1 · The 54-check expansion

Added: coop, coep, x_dns_prefetch, clear_site_data, expect_ct (all weight 0, informational, rendered as a distinct INFO state, never a fail), http_methods (a safe TLS-socket HTTP TRACE probe for Cross-Site Tracing, weight 5), cert_scope (SAN breadth), bimi + DMARC-policy capture, cms_detected (informational), and refined referrer to grade policy strength and dkim to probe 20 selectors and warn (not hard-fail) on a miss, since a custom selector can't be detected passively. A new info status was added so weight-0 optional checks never render as red failures on any site.

2 · Both-layer test harness

Backend: POST each URL to /api/security-audit-free, assert pass+warn+fail+na+info === total and that any na check also has pass:true (so the score loop can't wrongly deduct). Frontend: render the live page in headless Chromium (Playwright), read the DOM, and assert rendered .check-row count equals the summary total, with no orphan rows, then cross-check API total === DOM rows.

3 · Run 1, 100 sites, one real fault

# 99/100 clean. The one failure:
irs.gov: 74 rows vs summary 54   # 20 phantom rows
# cause: the frontend injected one .check-row per NVD CVE match
# (irs.gov matched 20 CVEs) -> visible but NOT in the counted set.
# backend was correct: cve_none already scores the finding ONCE (weight 20).

4 · The fix

Rewrote the CVE render: each CVE now emits a .cve-detail-row (not a .check-row), fully HTML-escaped (NVD text is external), shown as detail beneath the single counted cve_none check. All CVEs stay visible; the check count is unaffected.

# re-verified live on irs.gov, headless browser:
check-rows: 54 | cve-detail-rows: 20 | summary: 54 checks · 19 passed · 13 failed · 9 warnings · 7 N/A · 6 info

5 · Run 2, a different 100 sites, both layers

# fresh list: international, WordPress/CMS news, e-comm, dev/SaaS, forums, gov
# each site: API reconcile + headless render + cross-check (api_total == dom_rows)
=== 100 tested, 100 fully clean (backend+frontend), 0 with issues ===
# CVE fix confirmed on new sites:
techcrunch.com  API:54/YES DOM:54(+20cve)/YES   # 20 CVEs, still reconciles
engadget.com    API:54/YES DOM:54(+6cve)/YES

6 · Outcome

Two independent 100-site validations (200 unique sites), both API and rendered DOM checked on each, all clean; the single fault found in run 1 was fixed and re-verified in run 2 on the exact class of site (CVE-matching) that had triggered it. Calibration held across both populations (top scores gov/security-focused sites, bottom the header-bare giants), so the expanded tool is broad, consistent, and not overfit.

Key points

  • Expanded 44 → 54 checks; added an info status so optional/weight-0 checks never render as a red fail.
  • Tested both layers: API reconciliation (pass+warn+fail+na+info===total, na implies pass) and rendered DOM (rows === summary, no orphans, API total === DOM rows).
  • Run 1 (100 sites): 99 clean; irs.gov exposed a CVE-injection display fault (20 phantom .check-rows), backend was correct.
  • Fix: CVEs render as escaped .cve-detail-row detail under the single counted cve_none check, fully visible, count unaffected.
  • Run 2 (a different 100 sites): 100/100 clean both layers; CVE-matching sites (techcrunch +20, engadget +6) confirmed the fix. 200 unique sites total.

How to do this yourself

# 1. assert the backend reconciles, per site:
#    pass + warn + fail + na + info === total, and every na check is also pass
# 2. render the real page headless and assert the DOM matches:
$ node -e "const{chromium}=require('playwright'); ..." # rows === summary total, no orphans
# 3. cross-check api_total === dom_rows, and run a SECOND, different list to rule out overfit

Questions & answers

Why does na have to imply pass:true? The score loop only skips a check when pass is true. An na ("not applicable") check that wasn't also pass:true would be silently deducted from the score, so every na must carry pass:true to be score-safe.

Why render in a real browser rather than just checking the API? The CVE fault was invisible at the API level, the JSON was correct. It only appeared in the rendered DOM, where extra rows were injected. Testing the actual rendered page is the only way to catch that class of bug.

Why is a weight-0 "info" check not a failure? Because the audit deducts by real risk. Genuinely optional headers (cross-origin isolation, DNS prefetch control) aren't vulnerabilities, so their absence is shown as neutral information, never a red fail, on any site scanned.

What does the second run prove that the first didn't? That the tool generalises. Passing a fresh, deliberately varied 100 sites, including the CMS/CVE-heavy kind that caught us, shows the result isn't tuned to one list and the fix works in the wild, not just on the site where the bug was found.

Seen enough? Put your own site through the same audit. It runs the exact checks in this report against any URL, free to try, no sign-up.

Run the Security Audit →