GitHub Packages · MIT · Node ≥ 18

gpc-warden

Check that your built site honours Global Privacy Control: it reads the signal and suppresses sale/share trackers when it is on.

Check your built site honours Global Privacy Control: code reading navigator.globalPrivacyControl / Sec-GPC, sale/share advertising trackers that load unconditionally, /.well-known/gpc.json validity and policy disclosure. CCPA/CPRA, Colorado, Connecticut. SARIF, allowlist with expiry, GitHub Action. Zero deps.

  1. 1 Point the @mrlouiswright scope at GitHub Packages in .npmrc

    @mrlouiswright:registry=https://npm.pkg.github.com
  2. 2 Install

    npm i -D @mrlouiswright/gpc-warden
Installing from GitHub Packages needs a token

Published to GitHub Packages, not npmjs.com. GitHub Packages needs a GitHub token with read:packages to install, even for public packages: add //npm.pkg.github.com/:_authToken=${GITHUB_TOKEN} to .npmrc or run npm login --registry=https://npm.pkg.github.com.

~/project — zsh
$ npx gpc-warden dist --base-url https://acme.examplegpc-warden — https://acme.example2 page(s); 2 sale/share vendor(s); GPC handling not found HIGH     Sale/share vendors (Meta Pixel, LinkedIn Insight Tag) are used but no code checks navigator.globalPrivacyControl or Sec-GPC [gpc-not-detected]         ↳ fix: Read navigator.globalPrivacyControl (and the Sec-GPC request header server-side) and treat it as an opt-out of sale/sharing (CCPA/CPRA, Colorado, Connecticut).HIGH     index.html:1 Meta Pixel (script) loads unconditionally on / [gpc-tracker-not-suppressed]         ↳ fix: Load it from a script that first checks navigator.globalPrivacyControl (and consent), or tag it for your CMP so it is suppressed when GPC is on.HIGH     index.html:1 LinkedIn Insight Tag (pixel) loads unconditionally on / [gpc-tracker-not-suppressed]         ↳ fix: Load it from a script that first checks navigator.globalPrivacyControl (and consent), or tag it for your CMP so it is suppressed when GPC is on.MEDIUM   .well-known/gpc.json /.well-known/gpc.json: "gpc" must be true or false [gpc-wellknown-invalid]
Sample output from the gpc-warden README.

Why it exists

Global Privacy Control (“Sec-GPC: 1” and navigator.globalPrivacyControl) is a legally binding opt-out of sale and sharing under the CCPA/CPRA and in Colorado, Connecticut and other US states, and California has fined companies for ignoring it. Honouring it means two things: some code has to read the signal, and advertising tags must load in a way that lets that code stop them. gpc-warden checks both in your built site, plus the /.well-known/gpc.json declaration and your privacy policy, and reports each gap with a concrete fix.

CLI, library and GitHub Action. Node.js 18+, zero runtime dependencies. Works on any static build output (Astro, Next.js “output: 'export'”, Eleventy, Hugo, Jekyll, Docusaurus, VitePress, plain HTML) and runs offline: point it at the folder your build writes. By Louis Wright.

Features

Signal handling

looks for navigator.globalPrivacyControl / Sec-GPC in inline scripts, the site’s own JS files and edge/serverless functions in the build (functions/, middleware). Do Not Track-only handling is called out.

Suppression

advertising vendors that sell or share data (Meta, Google Ads, TikTok, LinkedIn, X, Pinterest, Snap, Microsoft, Criteo, Taboola, AddThis, ...) must not load unconditionally: they need a GPC-aware loader or CMP tagging (type="text/plain" + category).

CMPs

OneTrust, Osano, Usercentrics, Didomi, Termly, Ketch, TrustArc, Transcend can honour GPC; confirm it is enabled with --cmp-honours-gpc.

/.well-known/gpc.json

present, valid ({"gpc": true, "lastUpdate": "YYYY-MM-DD"}) and not false while selling/sharing.

Disclosure

the privacy policy mentions GPC / opt-out preference signals.

Usage

# Example

 npx gpc-warden dist

# Example

 npx gpc-warden dist --fail-on medium

# Example

 npx gpc-warden public --cmp-honours-gpc

# Example

 npx gpc-warden out --format sarif > gpc.sarif

CI / GitHub Action

permissions:
  contents: read
  security-events: write   # only needed for the SARIF upload
jobs:
  gpc-warden:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
        with: { persist-credentials: false }
      - run: npm ci && npm run build   # produce the static site first
      - uses: MrLouisWright/gpc-warden@v1
        with:
          path: dist
          fail-on: high
          sarif: gpc-warden.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with: { sarif_file: gpc-warden.sarif }

Options & flags

FlagDescription
Site
--base-url <url>Public URL of the site, e.g. https://example.com (default: inferred from canonical/og:url)
--include <globs>Only check these pages (file or URL path globs, comma-separated)
--exclude <globs>Skip these pages
--clean-urlsPages are served without .html (about.html -> /about)
GPC
--cmp-honours-gpcYour CMP (OneTrust, Osano, Usercentrics, ...) is configured to honour GPC
Common
-c, --config <file>JSON config (default: .gpc-warden.json or "gpcWarden" in package.json)
-f, --format <fmt>text (default) | json | sarif | markdown
--json <file>Also write a JSON report
--sarif <file>Also write SARIF 2.1.0
--markdown <file>Also write Markdown (e.g. for a job summary)
--fail-on <sev>critical | high | medium | low | info | none (default: high)
--list-rulesPrint rules and default severities
--no-fixesHide fix suggestions in text output
--no-colorDisable colours

Full reference, config file format and API in the README.

Support this project

Support open-source security tools

I build free, open-source tools that help developers ship safer websites. Every coffee helps keep them maintained, documented and free for everyone.

Buy me a coffee (opens Stripe in a new tab)

Secure checkout by Stripe

Portrait of Louis Wright

About the author

Louis Wright

Cyber security researcher and software engineer based in the UK.

More tools by Louis Wright

58 tools

Sentinels security scanners & CI gates · 20