v0.2Fingerprinting detection + iframe CMP automation (Sourcepoint, TrustArc)

The scanner that proves
your consent banner
actually works.

Crumb drives a real Chromium browser through four passes — pre-consent, accept-all, reject-all, and GPC-enabled — then diffs them against UK PECR, EU ePrivacy + GDPR, and CCPA/CPRA. With evidence per finding.

$ crumb scan --matrix --with-gpc https://hartwell.co
No JS tag Real Chromium Per-jurisdiction evaluation Signed evidence
scan_0418·hartwell.co/blog/budget-update13 findings
vendor
PRE
ACPT
RJCT
GPC
rule
statgoogle-analytics.com
uk-pecr-r7
markgoogletagmanager.com
uk-pecr-r1
markconnect.facebook.net
uk-pecr-r1
stathotjar.com
marklinkedin.com/insight
us-ca-r1
statstatic.hotjar.com
uk-pecr-r3
fingfp.fingerprint.com
uk-pecr-r9
nececdn.cookielaw.org
UK · EU · US-CA evaluated13 findings · 4 high · 2 medium
The problem

You declared your cookies in the banner.
Has anyone proven they match?

Most CMPs publish what they think the site does. Marketing ships a pixel on Friday, a new chat widget lands Monday, and by Wednesday the consent banner is honestly lying. A quarterly scanner producing a 40-page PDF won't catch it. A scanner that doesn't actually click the Reject button can't see what survives.

Avg. days between tracker changes
2.4
across mid-market sites
Quarterly scanner latency
38d
industry baseline
Crumb notification SLA
<1h
from scan complete
Capabilities

Built like an inspector tool, not a marketing scanner.

Consent matrix scanning

Four passes per URL — pre-consent, accept-all, reject-all, and (optional) GPC-enabled — each in a fresh incognito context. The diff is the finding.

$ crumb scan --matrix --with-gpc URL

JS call-stack attribution

Every cookie and storage write reports the exact script URL + V8 stack that caused it, including frame ID. No more 'set by document.cookie'.

initiator.stack[0] = gtm.js:234

CMP automation, 10 vendors

Drives Accept/Reject via vendor SDK first, DOM-selector fallback. Iframe-entry for cross-origin CMPs (Sourcepoint, TrustArc).

OneTrust · Cookiebot · Didomi · …

Fingerprinting detection

Wraps canvas, WebGL, audio, and font APIs at page-init. A script touching ≥2 distinct APIs fires r9 with stack-level evidence.

canvas.toDataURL · webgl.getParameter

Dark-pattern inspection

Opens the CMP's preferences panel and snapshots every toggle's default state. Pre-checked non-essential = r8 violation.

OneTrust · Cookiebot · Didomi · UC

Per-jurisdiction rule sets

One scan evaluated against N jurisdictions independently. UK PECR · EU ePrivacy + GDPR · CCPA/CPRA today. Quebec Law 25 + LGPD queued.

comply --jur=UK,EU,US-CA scan.json

Signed evidence manifests

Every scan is a hash chain. Every finding carries Evidence objects pointing at the exact cookie / request / storage write / CMP action.

scan_0418.attestation.sig

Scan tiers + parallelism

Quick (1 page) · Standard (~15 stratified) · Full (every page). Worker fleet scales horizontally — 50 workers, $0.04/h each.

tier=standard parallelism=10
The headline feature

Four passes.
One verdict.

Other scanners load the page once and count cookies. Crumb runs four full browser sessions — fresh incognito each time — and compares them. A tracker that fires after Reject is a different finding than one that fires before consent. Both matter. Both are surfaced with rule-level precision.

  • PRE-CONSENTNo buttons clicked. What does the page do to a visitor who hasn't decided yet?
  • ACCEPT-ALLWe click Accept via the CMP's SDK. What does 'consent granted' actually unlock?
  • REJECT-ALLWe click Reject. Anything still firing is a violation — and we know which vendor.
  • GPC-ENABLEDSec-GPC header + navigator.globalPrivacyControl. Sale/sharing vendors should stop.
ConsentDiff · scan_0418JSON
{
  "url": "https://hartwell.co/blog/budget-update",
  "passes": ["pre-consent","accept-all","reject-all","gpc-enabled"],
  "diff": {
    "pre_consent_trackers": [
      { "vendor": "Meta Pixel",  "categories": ["marketing"], "first": "gtm.js:234" },
      { "vendor": "Hotjar",      "categories": ["statistics"] },
      { "vendor": "LinkedIn Ads","categories": ["marketing"] }
    ],
    "post_reject_trackers": [
      { "vendor": "Google Analytics", "categories": ["statistics"] }
    ],
    "unconditional_trackers": [
      { "vendor": "FingerprintJS", "categories": ["fingerprint"] }
    ],
    "gpc_ignored_trackers": [
      { "vendor": "LinkedIn Ads", "categories": ["marketing"] }
    ]
  },
  "findings": [
    { "rule": "uk-pecr-r1", "severity": "high", "vendor": "Meta Pixel" },
    { "rule": "uk-pecr-r7", "severity": "high", "vendor": "FingerprintJS" },
    { "rule": "us-ca-r1",   "severity": "high", "vendor": "LinkedIn Ads" }
  ]
}
Pricing

Three tiers, three levers.
You pay for coverage, not pages.

Tier sets what gets scanned. Parallelism sets how fast. Multi-region is an optional accuracy add-on for sites that vary by geography. Jurisdiction evaluations are free and unlimited — one scan, every rule set.

Quick
$19/site /month

Daily homepage check. Catches globally-loaded trackers.

Pages scanned1 URL
Parallelism1 worker
Coverage~60%
Wall-clock~2 min
RECOMMENDED
Standard
$89/site /month

10–20 page stratified sample. Default for paid customers.

Pages scanned~15 URLs
Parallelismup to 10
Coverage~95%
Wall-clock~3 min
Full
$320/site /month

Every discovered URL. Enterprise / regulator-grade.

Pages scannedAll discovered
Parallelismup to 50
Coverage100%
Wall-clock30 min – 4 h
Enterprise
Custom

Volume domains, multi-region, custom retention, on-prem workers.

Pages scannedAll + custom
ParallelismFleet-wide
Coverage100%
Wall-clockYour SLA
Multi-region scanning

Geo-variant sites only.

OPT-IN

A US-only flow that skips the consent UI for non-EU IPs needs a EU-region scan to catch it. Pick the regions your site actually serves differently from. Jurisdictions are evaluated independently — they cost nothing extra.

eu-west-1
Dublin
default
eu-central
Frankfurt
default
us-east-1
N. Virginia
+$40/mo
us-west-2
Oregon
+$40/mo
ap-southeast-1
Singapore
+$60/mo
sa-east-1
São Paulo
+$60/mo
Included in every tier
  • Unlimited jurisdiction evaluations
  • Unlimited subdomains
  • Signed evidence manifests
  • Webhook + Slack + email routing
  • 5-year scan retention
  • Annotations + suppression rules
  • CLI access (open source)
  • SSO (Okta, Azure AD, Google)
Jurisdictions

One scan. Every rule set.

Adding a country means adding a rules/{code}.go file. No scanner work, no re-scanning.

CodeJurisdictionLegal basisRulesStatus
UKUnited KingdomPECR + UK GDPR9LIVE
EUEuropean UnionePrivacy + GDPR9LIVE
US-CACaliforniaCCPA + CPRA3LIVE
CA-QCQuebecLaw 25QUEUED
BRBrazilLGPDQUEUED
US-VAVirginiaVCDPAQUEUED
US-COColoradoCPAQUEUED
FRFrance (CNIL)EU + CNIL guidanceQUEUED