consent-guard
Skill0 starsChanged 4 months agoMITView on GitHub
Audits whether tracking scripts, pixels, and third-party analytics/ad tags actually stop firing until real consent is recorded — as opposed to a cookie banner that's cosmetic while GA, Meta Pixel, GTM tags, or a session-replay SDK fire on page load regardless of the visitor's choice. Reviews the code, not the banner UI: script-injection ordering versus Google Consent Mode defaults, whether gating covers the SDK's own automatic network calls or only your app's later API calls into it, SPA route-change re-initialization, server-side/CAPI paths that bypass client consent entirely, and whether a missing/failed consent lookup fails open (tracks) or closed (doesn't). Use when adding or reviewing a consent banner/CMP integration, when someone asks "does our cookie banner actually block trackers", "GTM tags fire before the user clicks accept", "add Google Consent Mode", "audit our tracking scripts for consent", or when a privacy/legal team asks whether the code actually enforces what the banner promises.
In this repository46 other files it gives its agents.
- atomic-ioSkill
- backfill-pilotSkill
- backpressure-guardSkill
- blast-guardSkill
- changelogSkill
- cipher-guardSkill
- clipboard-guardSkill
- clock-sweepSkill
- codebase-tourSkill
- devlogSkill
- dormancy-guardSkill
- env-detectiveSkill
- erasure-guardSkill
- export-guardSkill
- failover-guardSkill
- git-rescueSkill
- import-guardSkill
- job-wardenSkill
- migration-guardSkill
- overwrite-guardSkill
- pagination-guardSkill
- portability-auditSkill
- pref-guardSkill
- proration-guardSkill
- push-guardSkill
- quote-guardSkill
- reaccept-guardSkill
- readme-forgeSkill
- recall-guardSkill
- residency-guardSkill
- restore-guardSkill
- resync-guardSkill
- rollout-guardSkill
- rotation-guardSkill
- seat-guardSkill
- secret-spillSkill
- security-sweepSkill
- selectall-guardSkill
- ship-itSkill
- skew-checkSkill
- stale-guardSkill
- storm-guardSkill
- sunset-guardSkill
- throttle-guardSkill
- tombstoneSkill
- trail-guardSkill
The file
Files
consent-guard
A consent banner is a UI component. Whether it does anything is a separate, purely code-level question, and the two drift apart constantly — not from bad intent, but because the banner ships first (it's the visible, reviewable part) and the actual gating gets bolted on afterward, incompletely, by whoever wires up the analytics SDKs. The bug this skill exists to catch is never "there's no banner" — that's obvious on the first page load. It's the banner that visibly works (clicking "Reject" dismisses it, the choice is remembered) while GA4, Meta Pixel, and three ad-retargeting tags already fired their first network call before the visitor had a chance to click anything, because the <script> tag sits unconditionally in <head> and the "consent check" only guards a gtag('event', ...) call your own code makes after the SDK already loaded and beaconed a pageview on its own.
This is a code-review question, not a legal one, and it needs a different lens than a marketing team's GTM-container audit (which works from exports and screenshots and can't see the source). Trace it the way you'd trace any other correctness property: find every place a tracking mechanism can start, then prove it can't run ahead of a real "granted" signal.
Step 1: enumerate every tracking initiation point
List every way a third-party script, pixel, or SDK can start running: a <script src> tag in the HTML template, a script injected via document.createElement('script') or a tag-manager loader snippet, an npm-packaged SDK's own init()/load() call (Segment analytics.js, react-ga, the Meta Pixel fbq('init', ...) bootstrap, a session-replay or heatmap SDK), and a Google Tag Manager container itself — GTM is not one tracker, it's a loader for however many tags are configured inside it, each with its own trigger. Don't stop at the ones a grep for "pixel" finds; ad-tech and support-widget SDKs (chat widgets, A/B testing tools, heatmaps) often carry their own tracking and get added by a different team than whoever built the consent banner.
Then separate the two things that get conflated: the SDK's own automatic behavior (many pixels fire a PageView beacon the instant their script executes, with no call from your code at all) versus your app's calls into the SDK (gtag('event', 'purchase', ...), fbq('track', ...)). A codebase can gate every one of its own calls behind a consent check and still leak data, because the SDK bootstrap itself — the part that fires without being asked — was never gated.
Step 2: invariant A — nothing fires before a real "granted" signal, at the actual load site
For each initiation point from step 1, find where it's placed relative to the consent check, not where the banner code lives:
- Unconditional script tags. A
<script src="https://www.googletagmanager.com/gtm.js">sitting directly in the HTML with no surrounding condition runs on every page load regardless of what the banner shows. The fix isn't removing the tag — it's injecting it dynamically, only after consent is granted (or, for GTM specifically, after Consent Mode defaults are set — see below). - Consent Mode ordering. If Google Consent Mode is in use,
gtag('consent', 'default', {...})has to execute beforegtm.jsloads — that's the only way GTM starts with the right default instead of briefly running with an implicit "granted" state for however long it takes the default call to catch up. Check the actual<head>order (or the equivalent in whatever templating renders it), not just that both calls exist somewhere in the codebase. - Consent-gating that only covers your own event calls. Grep for the SDK's init/bootstrap call specifically, separately from your app's tracking calls. If the SDK script loads immediately but only
fbq('track', 'Purchase')is wrapped inif (hasConsent), the automatic PageView/init beacon already went out — the gate is on the wrong call. - SPA route-change re-initialization. A client-side router that re-mounts an analytics component on every navigation can re-run an SDK's init call without re-checking current consent state, especially if the check was only evaluated once at app boot and cached in a variable that never gets invalidated when the user changes their choice mid-session.
- Server-side / Conversions API paths. Meta CAPI, server-side GTM, or a backend job that forwards events to an ad platform directly bypasses browser-side consent checks entirely, because there's no client script to gate — if one exists, confirm the server code checks a stored consent flag before forwarding, not just that the client-side pixel is gated.
Step 3: invariant B — a missing or failed consent lookup fails closed, not open
Find what happens when the consent check itself can't run cleanly: the CMP's cookie/localStorage read throws, the consent-state variable is undefined because the banner component hasn't mounted yet, or a cached consent record predates a category the site added since. The dangerous direction is the same shape as pref-guard's inverted-default finding: a lookup that defaults to "granted" when it can't determine otherwise silently tracks people who never consented, and it's invisible in normal testing because the failure only shows up in edge cases (ad blockers interfering with the CMP script, a race between the banner's async config fetch and the tracking scripts). Flag any consent check whose failure path defaults to "track anyway" as a finding regardless of how rare the failure condition is.
Also check the expiry/re-consent path: if the CMP's own documentation specifies consent expires after N months, confirm the stored consent record actually has a checked timestamp and the code re-prompts rather than treating an old "granted" cookie as permanent.
Step 4: report what's actually gated, not what's intended
Per tracking mechanism found in step 1: how it's initiated (static tag, dynamic injection, SDK call), what consent check (if any) gates that specific initiation point versus just a later call into it, the ordering relative to Consent Mode defaults where applicable, and whether a failed/missing lookup fails open or closed. Lead with anything that fires before consent — that's the finding that turns a passing legal review into a real regulatory exposure, not a code-quality nit.
Boundaries
- Not a legal or compliance certification. Whether the banner's copy, categories, and reject-button prominence satisfy GDPR/ePrivacy/CCPA is a legal and UX question this skill doesn't answer — it verifies whether the code enforces whatever consent state the banner records, nothing about whether the banner itself is compliant or free of dark patterns (pre-ticked boxes, a buried reject option). Flag an obviously fail-open default; don't claim the banner clears any legal bar.
- Can't verify a third-party SDK's own server-side behavior. If a pixel's script executes and sends a beacon, this skill can find that in code and flag it — it can't verify what the ad platform does with the payload once received, or whether a "consent granted" signal is actually honored on their end.
- If tracking is wired through a CMP or tag-manager configuration this codebase doesn't own (a marketing team's GTM container edited outside the repo), this skill can verify the app's side — is the container script itself gated, are Consent Mode defaults set correctly and in order — but can't audit tag-level triggers configured only inside GTM's UI; say that's the boundary rather than assuming the container is configured correctly.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

