<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bcrypto</title>
    <description>The latest articles on DEV Community by Bcrypto (@bcrypto).</description>
    <link>https://dev.to/bcrypto</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4072173%2F63169890-757e-481d-b854-6a5cf1eed2a6.png</url>
      <title>DEV Community: Bcrypto</title>
      <link>https://dev.to/bcrypto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bcrypto"/>
    <language>en</language>
    <item>
      <title>My monitor said "all clear" while it was completely blind</title>
      <dc:creator>Bcrypto</dc:creator>
      <pubDate>Thu, 13 Aug 2026 03:09:15 +0000</pubDate>
      <link>https://dev.to/bcrypto/my-monitor-said-all-clear-while-it-was-completely-blind-25ni</link>
      <guid>https://dev.to/bcrypto/my-monitor-said-all-clear-while-it-was-completely-blind-25ni</guid>
      <description>&lt;p&gt;I have a small watcher script. Its whole job is to notice when one specific web&lt;br&gt;
page changes — a rewards program that, when it announces, opens a claim window&lt;br&gt;
that closes in about 90 days. Miss the announcement, miss the window, miss the&lt;br&gt;
money. So the watcher runs daily and tells me: changed, or no action needed.&lt;/p&gt;

&lt;p&gt;One morning I read the logs and found this, the day before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INCOME WATCHERS — no change
No action needed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reassuring. Except the line above it, from a different run, was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠ fetch failed — fetch failed
⚠ fetch failed — fetch failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both of the watcher's fetches had failed that day. And it had still printed&lt;br&gt;
&lt;strong&gt;"No action needed"&lt;/strong&gt; and exited &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why that's the worst possible bug for this program
&lt;/h2&gt;

&lt;p&gt;A monitor has exactly one promise: &lt;em&gt;if something happened, I'll tell you.&lt;/em&gt; The&lt;br&gt;
one output it must never produce is a confident all-clear while blind.&lt;/p&gt;

&lt;p&gt;Mine did. "I checked and nothing changed" and "I couldn't check at all" came out&lt;br&gt;
identical — same message, same exit code, same green. If the announcement I'm&lt;br&gt;
watching for had landed on a day my flaky connection dropped two requests, the&lt;br&gt;
log would have said "no action needed" and the 90-day clock would have started&lt;br&gt;
without me.&lt;/p&gt;

&lt;p&gt;Here's the code that did it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`  ⚠ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: fetch failed — &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// &amp;lt;-- skip this surface and carry on&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;continue&lt;/code&gt; skips a failed surface, and the run ends by reporting on the surfaces&lt;br&gt;
it &lt;em&gt;did&lt;/em&gt; read. Zero surfaces read, zero changes found, "no action needed." The&lt;br&gt;
logic is technically correct and completely useless.&lt;/p&gt;
&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Two ideas. First, a transient failure isn't a verdict — retry before believing&lt;br&gt;
it. Second, and the real point: &lt;strong&gt;"could not check" is a distinct state from&lt;br&gt;
"nothing changed," and must never render as the second.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Retry: transient DNS/TLS blips must not be mistaken for "nothing changed".&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastErr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="nx"&gt;lastErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;lastErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;consecutiveFailures&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;consecutiveFailures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fails&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;lastErr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;consecutive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fails&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`  ⚠ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: COULD NOT CHECK (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fails&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;x) — &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastErr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the output now refuses to say all-clear when any surface went unchecked —&lt;br&gt;
even in &lt;code&gt;--quiet&lt;/code&gt; mode, because silence there is the whole bug:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;  ⚠ DEGRADED — could not check &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; surface(s).&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;    This is NOT "nothing changed". An announcement could have landed unseen.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worst&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;failures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;consecutive&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;worst&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;    🚨 &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;worst&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; runs in a row failed. The watcher is effectively OFF.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// distinct from 10 (real change) so a cron can tell them apart&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three states now, three exit codes: &lt;code&gt;0&lt;/code&gt; checked-and-clear, &lt;code&gt;10&lt;/code&gt; something&lt;br&gt;
changed, &lt;code&gt;20&lt;/code&gt; couldn't check. A consecutive-failure counter means a watcher&lt;br&gt;
that's been quietly dead for days announces it instead of blending into the&lt;br&gt;
green.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lesson
&lt;/h2&gt;

&lt;p&gt;Absence of an alert can mean two very different things: &lt;em&gt;nothing happened&lt;/em&gt;, or&lt;br&gt;
&lt;em&gt;I'm not watching anymore.&lt;/em&gt; Any monitor that renders both as the same calm&lt;br&gt;
"all clear" isn't reassuring — it's the most dangerous kind of broken, because&lt;br&gt;
it fails in the one direction you'll never think to check. Make "I couldn't&lt;br&gt;
look" loud, and never let it wear the same face as "I looked, we're fine."&lt;/p&gt;




&lt;p&gt;&lt;em&gt;From the little automation stack behind&lt;br&gt;
&lt;a href="https://preflightscan.vercel.app" rel="noopener noreferrer"&gt;a project of mine&lt;/a&gt;. The watcher is boring.&lt;br&gt;
Boring is the point — right up until it lies to you.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bugsmash</category>
      <category>monitoring</category>
      <category>javascript</category>
      <category>reliability</category>
    </item>
    <item>
      <title>My security scanner cried wolf on Vercel and Linear. Here's the bug.</title>
      <dc:creator>Bcrypto</dc:creator>
      <pubDate>Thu, 13 Aug 2026 03:09:10 +0000</pubDate>
      <link>https://dev.to/bcrypto/my-security-scanner-cried-wolf-on-vercel-and-linear-heres-the-bug-3999</link>
      <guid>https://dev.to/bcrypto/my-security-scanner-cried-wolf-on-vercel-and-linear-heres-the-bug-3999</guid>
      <description>&lt;p&gt;I built a scanner that checks a live site for exposed secrets and files —&lt;br&gt;
leaked API keys in JS bundles, a reachable &lt;code&gt;.env&lt;/code&gt;, a published &lt;code&gt;.git/config&lt;/code&gt;.&lt;br&gt;
Before showing it to anyone, I ran it across a batch of well-known sites as a&lt;br&gt;
sanity check.&lt;/p&gt;

&lt;p&gt;It reported &lt;strong&gt;two CRITICAL findings on vercel.com. And two on linear.app.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both companies employ people who do security for a living. My first thought was&lt;br&gt;
the honest one: it's not them, it's my scanner. This is the story of why it was&lt;br&gt;
wrong, because the failure mode is one every "does this file exist" check walks&lt;br&gt;
into.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it claimed
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;🔴 CRITICAL — Sensitive file exposed: /.git/config&lt;br&gt;
🔴 CRITICAL — Sensitive file exposed: /.DS_Store&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If true, that's a real leak: a served &lt;code&gt;.git/config&lt;/code&gt; can expose repository&lt;br&gt;
internals. So I checked by hand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://vercel.com/.git/config | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; 200
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;title&amp;gt;&lt;/span&gt;Vercel&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's not a git config. That's the &lt;strong&gt;homepage&lt;/strong&gt;. Served with &lt;code&gt;200 OK&lt;/code&gt;, for a&lt;br&gt;
path that does not exist.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bug
&lt;/h2&gt;

&lt;p&gt;Single-page apps route on the client. Ask the server for &lt;code&gt;/.git/config&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;/.env&lt;/code&gt;, &lt;code&gt;/anything-at-all&lt;/code&gt;, and it can't know that's not a real app route — so&lt;br&gt;
it returns &lt;code&gt;200&lt;/code&gt; and the app shell, letting the client-side router sort it out.&lt;/p&gt;

&lt;p&gt;My probe was, in effect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.git/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// treat as exposed&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a SPA, &lt;code&gt;status === 200&lt;/code&gt; is true for &lt;strong&gt;every&lt;/strong&gt; path. So the scanner reported&lt;br&gt;
&lt;code&gt;.git/config&lt;/code&gt; exposed on every well-built SPA on the internet — which is to say,&lt;br&gt;
precisely the sites whose engineers would notice and never trust it again.&lt;/p&gt;

&lt;p&gt;Then it got worse. Looking at the older code, the "is this real" guard had this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;looksReal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.git/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="sr"&gt;core&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;{[&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.git/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;   &lt;span class="c1"&gt;// &amp;lt;-- unconditionally true&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's a second &lt;code&gt;/.git/config&lt;/code&gt; clause with no body check. Any 200 response for&lt;br&gt;
that path matched. The one file whose content I most needed to verify was the&lt;br&gt;
one I'd accidentally waved through.&lt;/p&gt;
&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;A 200 proves the server answered. It proves nothing about &lt;em&gt;what&lt;/em&gt; it answered.&lt;br&gt;
So verify the content is actually the file, and treat any HTML response as the&lt;br&gt;
catch-all it almost always is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SENSITIVE_FILES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.env&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="na"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;(?:&lt;/span&gt;&lt;span class="sr"&gt;#|&lt;/span&gt;&lt;span class="se"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;A-Z&lt;/span&gt;&lt;span class="se"&gt;][&lt;/span&gt;&lt;span class="sr"&gt;A-Z0-9_&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*=&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.git/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\[&lt;/span&gt;&lt;span class="sr"&gt;core&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="sr"&gt;/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.DS_Store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bud1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// binary magic bytes&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;sig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;SENSITIVE_FILES&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;safeFetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ctype&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ctype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c1"&gt;// SPA/404 catch-all&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&amp;lt;!doctype html|^&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;*&amp;lt;html/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// mislabeled catch-all&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                            &lt;span class="c1"&gt;// 200, but not the real file&lt;/span&gt;
  &lt;span class="nf"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;critical&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Sensitive file exposed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three gates now: not HTML by content-type, not HTML by sniffing the body (some&lt;br&gt;
servers mislabel), and a signature that matches the real file — &lt;code&gt;[core]&lt;/code&gt; for a&lt;br&gt;
git config, the &lt;code&gt;Bud1&lt;/code&gt; magic string for a &lt;code&gt;.DS_Store&lt;/code&gt;, &lt;code&gt;KEY=value&lt;/code&gt; for a&lt;br&gt;
&lt;code&gt;.env&lt;/code&gt;. A real exposed file passes all three; a SPA shell fails the first.&lt;/p&gt;
&lt;h2&gt;
  
  
  Proof it works both ways
&lt;/h2&gt;

&lt;p&gt;The part I actually care about — a fix that stops false positives by also&lt;br&gt;
suppressing real ones is worthless. So I tested both directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;vercel.com, linear.app, github.com, stripe.com: &lt;strong&gt;0 criticals&lt;/strong&gt; (was 2 each on
the first two)&lt;/li&gt;
&lt;li&gt;A local server serving a genuine &lt;code&gt;.env&lt;/code&gt; with &lt;code&gt;SECRET_KEY=...&lt;/code&gt;: &lt;strong&gt;still caught&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I locked it in with tests, because this is the highest-consequence logic in&lt;br&gt;
the whole tool:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.git/config rejects an HTML app shell&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SPA_SHELL&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nf"&gt;t&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/.git/config accepts a real git config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[core]&lt;/span&gt;&lt;span class="se"&gt;\n\t&lt;/span&gt;&lt;span class="s1"&gt;repositoryformatversion = 0&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The lesson
&lt;/h2&gt;

&lt;p&gt;A scanner's credibility is entirely downside. Miss a real issue and you're no&lt;br&gt;
worse than not scanning. Cry wolf on a well-run site &lt;em&gt;once&lt;/em&gt; and everyone who saw&lt;br&gt;
it stops believing every finding you'll ever produce. For a security tool the&lt;br&gt;
false positive isn't a smaller bug than the false negative — in reputation terms&lt;br&gt;
it's the larger one.&lt;/p&gt;

&lt;p&gt;"HTTP 200 means the file exists" is an assumption almost everything on the web&lt;br&gt;
quietly breaks. I'm glad I pointed the thing at Vercel before I pointed it at a&lt;br&gt;
front page.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This was in &lt;a href="https://preflightscan.vercel.app" rel="noopener noreferrer"&gt;Preflight&lt;/a&gt;, a free scanner for&lt;br&gt;
exactly these exposures. It grades itself, and after this fix it earns the A.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bugsmash</category>
      <category>security</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I have 591 pages. Google indexed one. Here's how I'm debugging it.</title>
      <dc:creator>Bcrypto</dc:creator>
      <pubDate>Tue, 11 Aug 2026 19:00:50 +0000</pubDate>
      <link>https://dev.to/bcrypto/i-have-591-pages-google-indexed-one-heres-how-im-debugging-it-5cel</link>
      <guid>https://dev.to/bcrypto/i-have-591-pages-google-indexed-one-heres-how-im-debugging-it-5cel</guid>
      <description>&lt;p&gt;I run a directory site with 2,499 real vendor listings across Beirut, Dubai, Riyadh, Doha, Cairo and Casablanca. It has 591 programmatic landing pages and 1,182 URLs in its sitemap.&lt;/p&gt;

&lt;p&gt;I checked what Google had actually indexed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The homepage. That was it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not 591. Not 200. One.&lt;/p&gt;

&lt;p&gt;This post is what I checked, what I ruled out, and the one diagnostic that actually decides what to do about it — because I got that last part wrong at first, and I suspect a lot of people do.&lt;/p&gt;

&lt;h2&gt;
  
  
  First instinct: something's broken on the site
&lt;/h2&gt;

&lt;p&gt;That's where I went, and it's usually where everyone goes. So I worked the checklist.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;robots.txt&lt;/strong&gt; — correct. Allows everything, references the sitemap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sitemap&lt;/strong&gt; — valid XML, 1,182 URLs, auto-generated, submitted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hub page&lt;/strong&gt; — present. Something links into the page set; they aren't orphans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internal linking&lt;/strong&gt; — 62 cross-links per page. Not a dead end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured data&lt;/strong&gt; — JSON-LD on every page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-rendered content&lt;/strong&gt; — real listings in the initial HTML. Not a JS shell.&lt;/p&gt;

&lt;p&gt;Here's the fastest way to check that last one, and it's the one people skip:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://yoursite.com/some-page | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that number is small, that's roughly what a crawler sees. Your framework hydrating beautifully in a browser is irrelevant to a bot that doesn't run JavaScript. I've measured client sites with &lt;strong&gt;10 words&lt;/strong&gt; in the initial HTML that looked perfect to a human.&lt;/p&gt;

&lt;p&gt;All six checks passed. Which meant the problem wasn't on the page.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wrong conclusion I nearly published
&lt;/h2&gt;

&lt;p&gt;My first explanation was: the site has no inbound links and no brand mentions anywhere, so Google has no reason to crawl it deeply. Authority problem. Go get links.&lt;/p&gt;

&lt;p&gt;That's a coherent story. It might even be right. But I nearly wrote it up as fact, and it turns out there's a specific piece of data that decides it — and until you look at that data, you're guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The diagnostic that actually matters
&lt;/h2&gt;

&lt;p&gt;Google Search Console → &lt;strong&gt;Indexing → Pages&lt;/strong&gt; → "Why pages aren't indexed".&lt;/p&gt;

&lt;p&gt;Two statuses look similar and mean opposite things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Discovered – currently not indexed&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Google knows the URL exists but hasn't spent crawl budget on it. This is the authority/crawl-scheduling case. Links and internal-linking improvements genuinely help here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Crawled – currently not indexed&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Google fetched the page, looked at it, and decided not to index it. This is a &lt;strong&gt;quality judgement&lt;/strong&gt;. For programmatic pages — city × category templates where only a couple of fields change — this usually means it read them as thin or near-duplicate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If it's the second one, backlinks will not fix it.&lt;/strong&gt; You can build links for six months and stay at one indexed page, because the pages themselves are what got rejected. The fix is consolidation: fewer, substantially different pages with real unique content, rather than more thin ones.&lt;/p&gt;

&lt;p&gt;Same symptom. Opposite remedies. The export is free and takes two minutes, and I was about to skip it in favour of a narrative that felt right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap in programmatic SEO
&lt;/h2&gt;

&lt;p&gt;Programmatic SEO makes the satisfying work and the useful work feel identical.&lt;/p&gt;

&lt;p&gt;Generating 600 pages is a fun afternoon. It produces a number that goes up. It looks like progress. And if your pages are being &lt;em&gt;rejected&lt;/em&gt; rather than &lt;em&gt;not reached&lt;/em&gt;, generating another 600 moves you from one indexed page to one indexed page.&lt;/p&gt;

&lt;p&gt;I could have shipped a second batch and felt productive the whole time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd check on any site, in order
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;site:yourdomain.com&lt;/code&gt; in Google.&lt;/strong&gt; Compare the count to how many pages you think you have. The gap is the actual story, and most people never look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View source with JS disabled.&lt;/strong&gt; If there's a loading spinner where your content should be, that's what you've published.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GSC → Indexing → Pages.&lt;/strong&gt; Get the real status before choosing a fix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check whether you're blocking AI crawlers.&lt;/strong&gt; Plenty of &lt;code&gt;robots.txt&lt;/code&gt; files disallow &lt;code&gt;GPTBot&lt;/code&gt;, &lt;code&gt;ClaudeBot&lt;/code&gt; or &lt;code&gt;PerplexityBot&lt;/code&gt; inherited from a template, and then people wonder why assistants never cite them:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://yoursite.com/robots.txt | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s2"&gt;"gptbot|claudebot|perplexity"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Grep your bundles for keys.&lt;/strong&gt; Unrelated to indexing, but while you're in there — anything starting &lt;code&gt;sk_live&lt;/code&gt;, &lt;code&gt;AKIA&lt;/code&gt;, or a service-role JWT that reached the browser is public regardless of what your backend does.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Where I've got to
&lt;/h2&gt;

&lt;p&gt;The GSC export is the next thing I do, and it decides whether the next month is spent earning citations or consolidating pages. I'm writing this before I have that answer deliberately, because the interesting part isn't the resolution — it's that the symptom looked obvious and pointed at two incompatible fixes.&lt;/p&gt;

&lt;p&gt;If you've got a programmatic site sitting at a fraction of its expected index coverage, check which status you have before you spend a month on the wrong one.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I automated the on-page half of these checks into a free scanner while debugging this — &lt;a href="https://preflightscan.vercel.app" rel="noopener noreferrer"&gt;preflightscan.vercel.app&lt;/a&gt;, no signup. It won't tell you your GSC status; nothing can except GSC.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>debugging</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
