Skip to main content

v0.0.144

Released Oct 30, 2026


Release Notes

squirrel audit --max-duration stops at a wall-clock budget and writes a partial report instead of running on, --include-pages adds every crawled page with its status and rule count to the JSON report, and info checks now appear in the JSON and LLM reports (--include-info). The local technology scan reads stylesheet URLs and knows 24 more stacks, query-string copies of a page are counted with their canonical page, rules see controls inside declarative shadow roots, and a successful but empty page is audited and scored. A rule that throws is reported once as not evaluated, a rule that runs past its time budget is listed as skipped, and the docs now keep --offline and --passive apart. Thanks to William for the empty-page fix (#1314, #1330).

Added

  • squirrel audit --max-duration <seconds> puts a wall-clock budget on a whole audit. A slow or throttling site used to keep an audit running for minutes, and a batch job that killed it at its own timeout got no report and lost every page already crawled. At the limit the audit stops launching new work, finishes the pages in flight, runs the rules on what it fetched and writes the report with status: "partial", reason code max_duration and a maxDuration object that counts the pages discovered but never fetched, the fetched pages whose rules never ran, and whether the site-wide checks finished. It stops its own work within max(10% of the budget, 15 s) of the limit: link and resource checks are cut inside that margin and so is the rules phase. A partial report exits 0 like any finished audit (a --fail-on gate still decides pass or fail), squirrel report shows a stored one the same way, and a run the limit did not cut is a normal completed report. The budget counts from when the command starts, so the reachability check is inside it, and discovery (robots.txt, sitemaps, root files) may use at most 30% of it before the first page. [crawler] max_duration sets it in squirrel.toml, the flag overrides it, unset means no limit, 7 days is the ceiling, and a limit reached with no page fetched fails with exit 1. The MCP audit_website tool takes maxDuration. The margin covers waiting, not computation: one multi-megabyte page that takes longer than the margin to parse runs to completion. The flag is in the bash, zsh and fish completions. See Time limit. (#1271)
  • --include-pages adds the crawled pages to the JSON report. A pipeline that tracks which pages a run really covered had to stream-parse the entity map and guess. With squirrel audit -f json --include-pages (and squirrel report -f json --include-pages for a stored audit) the report gets pages: one { url, status, ruleCount } entry per crawled page, the HTTP status it answered with and how many distinct rules reported on it. It is off by default, so existing reports and their size do not change. The list is cut at 2,000 pages, with pagesTotal for the real count and pagesTruncated: true when entries were dropped. Every JSON report now carries meta.schemaVersion (1.1, the version that added pages; a report without it is 1.0). Pages that never produced a stored response (connection errors, server errors that survived retries, rate-limited requests) are not listed. See Pages list. (#1260)
  • Info checks are in the JSON and LLM reports. Notes a rule records with status info (for example redundant-noindex, or "No dialog elements found") were in the local database but never in -f json or -f llm. The JSON report now has a top-level infoChecks list (rule, check, message, up to 10 pages and the true page count), and the LLM report an <info-checks> block with --include-info (off by default, to keep the output small). They are not findings and never change the score. (#669)
  • Technology detection finds more of a site's stack in a logged-out audit. The free local scan now also reads stylesheet and preload <link href> URLs (up to 40 a page, platform paths such as /wp-content/plugins/ first, listed after the scripts), so a signal that lives in a stylesheet path, such as WooCommerce's plugin CSS, is no longer missed when the page also has scripts; the cloud payload is built the same way. It adds 23 first-party fingerprints: consent tools (Borlabs Cookie, Complianz, Real Cookie Banner, Cookie Notice, consentmanager, Klaro, Axeptio, CookieFirst, Cookie Information, Cookie-Script), tag managers (Cloudflare Zaraz, Matomo Tag Manager, Piwik PRO Tag Manager, Commanders Act), CMS and shop systems (Lightspeed eCom, Contao, SilverStripe, Plone, Grav, Odoo, Liferay, Pimcore) and Azure Front Door. The Technologies section now says whether it came from the local scan or the cloud scan (technologies.source in JSON, and on the llm and xml tags), and a local one shows where the list came from instead of a "no changes since last scan" line, or first-scan and added or removed counts, it cannot know. The docs explain what each scan reads and why a result can show little more than headers. (#1263)
  • A rule that ran past its time budget is listed in skippedChecks with reason rule-timed-out, so the report no longer reads that rule as clean on the pages it did not finish. (#518)

Fixed

  • A successful HTML page with a zero-byte body is no longer omitted from rule results. It is audited as an empty document, appears in the report, counts toward coverage and scoring, and receives findings such as missing title and H1. (#1314)
  • Query-string variants of a page are counted with their canonical page. /contact and /contact?subject=Billing used to be two affected pages for the same template finding, and two pages in the page total. A crawled page whose rel=canonical is another crawled page of the same path is now counted with it: a finding on both lists one page, the page total, the scan line and the structure counts agree, and the report says how many variants were folded (meta.canonicalVariants, a note on the scan line, canonical-variants in the LLM output). Variants with their own canonical (pagination, search, filters) stay separate, every variant is still fetched and checked so scores do not change, and a site with no variants reports exactly as before. See Query-string variants. (#858)
  • The Markdown report lists every page that carries a broken link. A broken link found on more than three pages showed the first three and "+N more pages", with no way to see the rest in the report. The Markdown report now lists all of them in the collapsed item block; the text, console and LLM reports keep the three-page preview. (#837)
  • Rules now see controls inside declarative shadow roots. A page built with web components can put a button, link, image or heading inside <template shadowrootmode="open"> (or "closed"). A browser renders it, but the parser kept it inside the template's inert content, so no rule saw it and a11y/button-name missed an unnamed button there. The content is now placed where the template was before the rules run; a plain <template> stays inert. See Rules. (#922)
  • A finding that carries pages from an earlier audit is no longer shown as a share of a page region. A smart audit keeps findings for pages it did not fetch again, and a region line such as "In the site footer, on 49 of 53 pages" could count those pages against this audit's own region totals. Like the page-type line (#857), the region line now steps aside when any of a finding's pages are carried, in every output format, and the finding keeps its page rows. Findings on fresh pages read as before and no score changes. (#860)
  • A rule that throws is reported once as not evaluated, not as a failure on every page. A rule that threw on every page of a 30-page site showed 30 failed checks and lowered the score for one defect in squirrel. It is now a skipped <rule>-error check (not evaluated, like a rule that ran out of time) on each page, plus one <rule>-errors summary with the first error and the page count, the score is unchanged, and the report prints a Not checked: line saying it is a squirrelscan defect. A rule that throws on every page still scores as if it passed, so --fail-on on warnings or severity counts it as one warning and the JSON notChecked lists its first pages. See Rules. (#1291)
  • The docs and --offline help now keep offline and passive apart. --offline is the network scope (no cloud, no other site) and --passive turns the probe rules off; they are independent, so -C surface --offline still sends the GraphQL and form-action probe requests to the audited site. A new "Offline and passive are separate" section in the audit docs has the four combinations and says exactly what a passive offline run still sends (the 28 discovery probes unless --disable-discovery-probes, internal link checks, sitemap path guesses). The passive banner now reads passive · no probe rules. Behaviour is unchanged. (#1225)
  • Blocked-page detection follow-ups. A seed that redirects to a WAF challenge is one blocked page: wafBlocked.total counts it once, and the URL you typed is no longer listed under wafBlocked.pages beside the destination when a sitemap or link names it again. A stored refusal that reads like a known provider's but carries none of its block markers (a bare "Access Denied" behind Cloudflare) is now noted instead of passing silently: the console, text, Markdown and LLM reports say its findings may describe the provider's page (a site's own short 403 behind a CDN reads the same, so it is a pointer, not a verdict), and the JSON report lists it under wafSuspect (total, up to 50 pages with url, status, provider, providerId and signal, and providers). The page is audited as before. Wordfence is attributed only when the page also carries the footer's generation timestamp or its inline document.write(new Date().toUTCString()) script, so a 403 that copies the "Your access to this site has been limited" wording is no longer called a Wordfence block. Netlify and Fastly Next-Gen WAF stay unrecognised, now with the reason in the docs: Netlify answers a firewall block with its ordinary branded 404, and Fastly's default block is a 406 with no documented body. See blocked by a WAF. (#1258)

Update to this version

squirrelscan will auto-update, or run this command to update now:

$

Downloads

macOS

Detected
Apple Silicon (M-series)
squirrel-0.0.144-darwin-arm64 · 101.9 MB
Download
SHA-256
Intel
squirrel-0.0.144-darwin-x64 · 110.5 MB
Download
SHA-256

Linux

x64 (musl/Alpine)
squirrel-0.0.144-linux-x64-musl · 114.7 MB
Download
SHA-256
ARM64 (musl/Alpine)
squirrel-0.0.144-linux-arm64-musl · 113.6 MB
Download
SHA-256
ARM64
squirrel-0.0.144-linux-arm64 · 120.9 MB
Download
SHA-256
x64
squirrel-0.0.144-linux-x64 · 120.8 MB
Download
SHA-256

Windows

x64 (Intel/AMD)
squirrel-0.0.144-windows-x64.exe · 126.7 MB
Download
SHA-256

Audit your site in one command

SEO, performance, security, accessibility and agent experience issues, with exact fixes for your coding agent.

Install
$

No account needed for the CLI. Cloud audits include free monthly credits.