Vendor disclosure
each loaded vendor must be named in the policy by name, company alias ("Facebook" for Meta Pixel) or domain. Unknown third-party domains must be named too.
GitHub Packages · MIT · Node ≥ 18
Compare the trackers and third parties your built site actually loads with what its privacy policy says, and fail CI when they drift apart.
1 Point the @mrlouiswright scope at GitHub Packages in .npmrc
@mrlouiswright:registry=https://npm.pkg.github.com2 Install
npm i -D @mrlouiswright/policy-drift-wardenPublished 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.
$ npx policy-drift-warden dist --base-url https://acme.examplepolicy-drift-warden — https://acme.example3 page(s); 3 third parties; policy: privacy-policy.html HIGH index.html Meta Pixel (Meta) [advertising] is loaded on 1 page(s) but not named in the policy [policy-undisclosed-vendor] ↳ fix: Name Meta Pixel / Meta, its purpose and legal basis in the privacy policy, or remove it.HIGH index.html Hotjar (Hotjar) [session-replay] is loaded on 1 page(s) but not named in the policy [policy-undisclosed-vendor] ↳ fix: Name Hotjar / Hotjar, its purpose and legal basis in the privacy policy, or remove it.HIGH privacy-policy.html Vendors that sell/share data (Meta Pixel) are used, but the policy does not mention selling, sharing or targeted advertising [policy-sale-share-undisclosed] ↳ fix: Disclose sale/sharing for targeted advertising and the opt-out (CCPA/CPRA, GDPR Art. 13).MEDIUM index.html 2 page(s) do not link to the privacy policy, e.g. /, /shop.html [policy-not-linked]
Privacy policies are written once and sites keep changing. Marketing adds a Meta pixel, product adds Hotjar, and the policy still only mentions Google Analytics. Regulators and complainants check exactly this gap. policy-drift-warden builds the third-party inventory from your built HTML (the same detection as tracker-warden), reads your privacy policy page or file, and reports every vendor, category or sale/share practice that is not disclosed, 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.
each loaded vendor must be named in the policy by name, company alias ("Facebook" for Meta Pixel) or domain. Unknown third-party domains must be named too.
advertising, session recording/heatmaps, analytics and social plugins must be described.
if vendors that sell or share data for advertising are present, the policy must mention selling, sharing or targeted advertising.
tracking vendors named in the policy that the site no longer loads.
"Last updated" / effective date present (ISO, 3 March 2025, January 5, 2024, 01.02.2023) and not older than --max-age-days (365).
every page links to the policy.
# Example
npx policy-drift-warden dist# Example
npx policy-drift-warden dist --policy legal/privacy.md --fail-on medium# Example
npx policy-drift-warden public --trackers-only --max-age-days 180# Example
npx policy-drift-warden out --policy-pages "/legal/privacy/**" --format sarif > policy.sarifpermissions:
contents: read
security-events: write # only needed for the SARIF upload
jobs:
policy-drift-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/policy-drift-warden@v1
with:
path: dist
fail-on: high
sarif: policy-drift-warden.sarif
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: policy-drift-warden.sarif }
| Flag | Description |
|---|---|
| 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-urls | Pages are served without .html (about.html -> /about) |
| Policy | |
--policy <file> | Privacy policy file (HTML, Markdown or text) instead of a page in the build |
--policy-pages <globs> | Pages that make up the policy (default: privacy*, cookie-policy, datenschutz, ...) |
--max-age-days <n> | Report the policy as outdated after n days (default 365) |
--trackers-only | Only require disclosure of tracking vendors (not fonts/CDN/video/payments) |
| Common | |
-c, --config <file> | JSON config (default: .policy-drift-warden.json or "policyDriftWarden" 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-rules | Print rules and default severities |
--no-fixes | Hide fix suggestions in text output |
--no-color | Disable colours |
Full reference, config file format and API in the README.
Support this project
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