Skip to content

Repository files navigation

weaveeye-scan

Fail your build when a tracker sends data before consent.

A pre-consent tracking gate for CI. It loads your site in a real browser, detects and accepts the consent banner, and records every third party that fired before consent was given. If a consent-requiring tracker fired first, the build fails — with a millisecond-timestamped list of exactly which ones and who owns them.

This is the enforcement layer for the thing regulators and plaintiffs actually go after: pre-consent tracking under GDPR/ePrivacy in the EU and CIPA-style wiretapping claims in the US. A CMP tells you that you have a banner. This tells you whether the banner is actually holding trackers back — and keeps the next deploy from silently regressing it.

The WEAVEEYE project

WEAVEEYE checks whether a website loads trackers before the user consents — the pre-consent tracking that GDPR / ePrivacy and US CIPA lawsuits target. It has two halves, across three repos:

A scanner — point it at any URL and see, to the millisecond, what data went to third-party trackers before consent. The "see the problem" half.

A CI gate — the same check in your pipeline, failing the build when a tracker sends data before consent. The "stop the problem" half.

  • weaveeye-ci — this repo: the GitHub Action, published to npm as the CLI weaveeye-scan. (Action = mohitgauniyal/weaveeye-ci; CLI = npx weaveeye-scan. The Action does not require the npm package.)

Both halves share one detection engine (classification, CMP handling, verdict logic), so a finding in the scanner matches the CI gate. How classification works and what is / isn't claimed: METHODOLOGY. A sample scan of real sites: FINDINGS.

You're reading the docs for the CI gate (repo weaveeye-ci, npm weaveeye-scan).


Quick start

npx weaveeye-scan https://staging.example.com
WEAVEEYE consent scan — example.com

✗ NON-COMPLIANT  3 third parties received data before consent was given.
CMP: OneTrust   consent accepted at 4.2s   14 before / 22 after

Data sent to third parties before consent:
  FIRED  CATEGORY     DOMAIN                          OWNER              DATA
  340ms  Advertising  securepubads.g.doubleclick.net  Google / Alphabet  269KB
  520ms  Analytics    static.chartbeat.com            Chartbeat          100KB
  890ms  Data Broker  api.rlcdn.com                   LiveRamp           —

  Classification: 100% of these domains identified from curated data or public
  tracker databases (Disconnect.me, DuckDuckGo). Unknown domains are never flagged.

NON-COMPLIANT means "does not pass the policy you configured" (e.g. nothing before consent) — a check against your rule, not a legal ruling. See METHODOLOGY.md for exactly what is and isn't claimed.

Exit code is 0 if clean, 1 on a violation, 2 on an error — so it drops straight into any pipeline.


GitHub Action

# .github/workflows/consent.yml
on: pull_request
permissions:
  contents: read
  pull-requests: write

jobs:
  consent:
    runs-on: ubuntu-latest
    steps:
      - uses: mohitgauniyal/weaveeye-ci@v0
        with:
          url: https://your-preview-deploy.example.com

On a pull request it posts (and updates in place) a comment with the verdict and the full table of pre-consent trackers, and writes the same to the job summary. See .github/workflows/example-consent-gate.yml.

Action inputs

Input Default Description
url — (required) URL to scan — typically your preview deployment.
policy auto Path to a policy file. .weaveeye.yml is picked up automatically.
fail-on Advertising,Analytics,Data Broker Categories that fail the build.
allow Comma-separated domains to exempt.
inconclusive warn Treat INCONCLUSIVE as fail, warn, or pass.
comment true Post/update a PR comment.
github-token ${{ github.token }} Token used for the comment.

Outputs: passed, verdict, violations.


Policy

Drop a .weaveeye.yml at your repo root (see the annotated .weaveeye.yml here). Everything is optional.

fail_on_categories: [Advertising, Analytics, Data Broker]
allow:
  - onetrust.com          # exempt your CMP or anything with a lawful basis
verdicts:
  non_compliant: fail
  no_banner_detected: fail
  inconclusive: warn      # don't fail a build just because we couldn't click the banner
  compliant: pass

Verdicts

Verdict Meaning Default action
COMPLIANT Banner accepted, nothing consent-requiring fired first. pass
NON_COMPLIANT Banner accepted, but trackers fired before it. fail
NO_BANNER_DETECTED No consent mechanism; trackers fired anyway. fail
INCONCLUSIVE A banner was present but couldn't be operated. warn

INCONCLUSIVE defaults to a warning on purpose: the scanner failing to click a banner is not proof the site is broken, and a gate that fails on it would cry wolf. Set it to fail once you trust detection on your stack.


CLI

weaveeye-scan <url> [options]

  --policy <path>        Policy file (.yml/.yaml/.json). Default: .weaveeye.yml if present.
  --format <fmt>         terminal | json | markdown       (default: terminal)
  --output <path>        Also write the formatted report to a file.
  --json-out <path>      Write the machine-readable JSON result to a file.
  --allow <domains>      Comma-separated domains to exempt.
  --fail-on <categories> Comma-separated categories that fail the build.
  --inconclusive <act>   fail | warn | pass.
  --timeout <ms>         Navigation timeout (default: 30000).
  --no-color             Disable ANSI colour.
  --headful              Show the browser (debugging).

Programmatic API

import { consentScan, evaluate, loadPolicy } from "weaveeye-scan";

const scan = await consentScan("https://staging.example.com");
const result = evaluate(scan, loadPolicy(".weaveeye.yml"));
if (!result.passed) process.exit(1);

How it works, and its limits

  • Real browser. Playwright loads the page like a visitor would; it does not just parse HTML or read the cookie jar.
  • Before vs after. Third parties are timestamped and split at the moment consent is accepted. The "before" set, filtered to consent-requiring categories, is the finding.
  • Ownership. Trackers are attributed to their parent company using a bundled snapshot of Disconnect.me + DuckDuckGo Tracker Radar data plus a curated map. Regenerate it with npm run build:snapshot.

Geography matters. Consent banners are shown based on the visitor's region. To meaningfully test EU behaviour the job must run from an EU IP; for US/CIPA, a US IP. A runner in the wrong region will see NO_BANNER_DETECTED where a real user sees a banner. Choose your runner region deliberately.

This is not legal advice. It reports what the browser did, with timestamps and a traceable source for every domain flagged. Whether a given data flow is lawful depends on the site's legal basis and jurisdiction — a call for your counsel. The evidence is here to inform that, not replace it. Full details of how classification works and what is and isn't claimed: METHODOLOGY.md.


Development

npm install
npx playwright install chromium
npm test                  # unit tests (node:test) — no browser or network
npm run build:snapshot    # refresh the bundled tracker data
node src/cli.js example.com

License

MIT

About

Fail your build when a tracker sends data before consent. A pre-consent tracking gate for CI - GitHub Action + npx weaveeye-scan. GDPR / ePrivacy / CIPA.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages