@slashgear/gdpr-cookie-scanner 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +44 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +26 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +24 -0
- package/.github/workflows/ci.yml +38 -0
- package/.github/workflows/release.yml +57 -0
- package/.idea/gdpr-report.iml +8 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/CHANGELOG.md +7 -0
- package/CLAUDE.md +75 -0
- package/CODE_OF_CONDUCT.md +41 -0
- package/CONTRIBUTING.md +79 -0
- package/LICENSE +21 -0
- package/README.md +127 -0
- package/SECURITY.md +15 -0
- package/dist/analyzers/compliance.d.ts +13 -0
- package/dist/analyzers/compliance.d.ts.map +1 -0
- package/dist/analyzers/compliance.js +171 -0
- package/dist/analyzers/compliance.js.map +1 -0
- package/dist/analyzers/wording.d.ts +13 -0
- package/dist/analyzers/wording.d.ts.map +1 -0
- package/dist/analyzers/wording.js +91 -0
- package/dist/analyzers/wording.js.map +1 -0
- package/dist/classifiers/cookie-classifier.d.ts +8 -0
- package/dist/classifiers/cookie-classifier.d.ts.map +1 -0
- package/dist/classifiers/cookie-classifier.js +108 -0
- package/dist/classifiers/cookie-classifier.js.map +1 -0
- package/dist/classifiers/network-classifier.d.ts +9 -0
- package/dist/classifiers/network-classifier.d.ts.map +1 -0
- package/dist/classifiers/network-classifier.js +51 -0
- package/dist/classifiers/network-classifier.js.map +1 -0
- package/dist/classifiers/tracker-list.d.ts +16 -0
- package/dist/classifiers/tracker-list.d.ts.map +1 -0
- package/dist/classifiers/tracker-list.js +86 -0
- package/dist/classifiers/tracker-list.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +110 -0
- package/dist/cli.js.map +1 -0
- package/dist/report/generator.d.ts +19 -0
- package/dist/report/generator.d.ts.map +1 -0
- package/dist/report/generator.js +552 -0
- package/dist/report/generator.js.map +1 -0
- package/dist/scanner/browser.d.ts +11 -0
- package/dist/scanner/browser.d.ts.map +1 -0
- package/dist/scanner/browser.js +38 -0
- package/dist/scanner/browser.js.map +1 -0
- package/dist/scanner/consent-modal.d.ts +5 -0
- package/dist/scanner/consent-modal.d.ts.map +1 -0
- package/dist/scanner/consent-modal.js +244 -0
- package/dist/scanner/consent-modal.js.map +1 -0
- package/dist/scanner/cookies.d.ts +11 -0
- package/dist/scanner/cookies.d.ts.map +1 -0
- package/dist/scanner/cookies.js +30 -0
- package/dist/scanner/cookies.js.map +1 -0
- package/dist/scanner/index.d.ts +9 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +146 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/network.d.ts +8 -0
- package/dist/scanner/network.d.ts.map +1 -0
- package/dist/scanner/network.js +41 -0
- package/dist/scanner/network.js.map +1 -0
- package/dist/types.d.ts +105 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +52 -0
- package/renovate.json +17 -0
- package/src/analyzers/compliance.ts +203 -0
- package/src/analyzers/wording.ts +112 -0
- package/src/classifiers/cookie-classifier.ts +125 -0
- package/src/classifiers/network-classifier.ts +65 -0
- package/src/classifiers/tracker-list.ts +105 -0
- package/src/cli.ts +134 -0
- package/src/report/generator.ts +703 -0
- package/src/scanner/browser.ts +52 -0
- package/src/scanner/consent-modal.ts +276 -0
- package/src/scanner/cookies.ts +43 -0
- package/src/scanner/index.ts +163 -0
- package/src/scanner/network.ts +51 -0
- package/src/types.ts +134 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changesets
|
|
2
|
+
|
|
3
|
+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
4
|
+
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
5
|
+
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
6
|
+
|
|
7
|
+
We have a quick list of common questions to get you started engaging with this project in
|
|
8
|
+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
|
|
3
|
+
"changelog": "@changesets/cli/changelog",
|
|
4
|
+
"commit": false,
|
|
5
|
+
"fixed": [],
|
|
6
|
+
"linked": [],
|
|
7
|
+
"access": "public",
|
|
8
|
+
"baseBranch": "main",
|
|
9
|
+
"updateInternalDependencies": "patch",
|
|
10
|
+
"ignore": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a problem with the scanner
|
|
3
|
+
labels: [bug]
|
|
4
|
+
body:
|
|
5
|
+
- type: input
|
|
6
|
+
id: url
|
|
7
|
+
attributes:
|
|
8
|
+
label: Scanned URL
|
|
9
|
+
description: The URL that caused the problem (optional if sensitive)
|
|
10
|
+
validations:
|
|
11
|
+
required: false
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: description
|
|
15
|
+
attributes:
|
|
16
|
+
label: Problem description
|
|
17
|
+
description: What happened vs what was expected
|
|
18
|
+
validations:
|
|
19
|
+
required: true
|
|
20
|
+
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: reproduce
|
|
23
|
+
attributes:
|
|
24
|
+
label: Command used
|
|
25
|
+
render: bash
|
|
26
|
+
placeholder: node dist/cli.js scan https://...
|
|
27
|
+
validations:
|
|
28
|
+
required: true
|
|
29
|
+
|
|
30
|
+
- type: textarea
|
|
31
|
+
id: output
|
|
32
|
+
attributes:
|
|
33
|
+
label: Output / error
|
|
34
|
+
render: text
|
|
35
|
+
validations:
|
|
36
|
+
required: false
|
|
37
|
+
|
|
38
|
+
- type: input
|
|
39
|
+
id: version
|
|
40
|
+
attributes:
|
|
41
|
+
label: Node.js version
|
|
42
|
+
placeholder: "e.g. 22.0.0"
|
|
43
|
+
validations:
|
|
44
|
+
required: true
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an improvement or a new RGPD rule
|
|
3
|
+
labels: [enhancement]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem or uncovered case
|
|
9
|
+
description: What situation does the scanner not currently handle?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
|
|
13
|
+
- type: textarea
|
|
14
|
+
id: solution
|
|
15
|
+
attributes:
|
|
16
|
+
label: Proposed solution
|
|
17
|
+
validations:
|
|
18
|
+
required: true
|
|
19
|
+
|
|
20
|
+
- type: input
|
|
21
|
+
id: reference
|
|
22
|
+
attributes:
|
|
23
|
+
label: Legal reference (if applicable)
|
|
24
|
+
placeholder: "e.g. CNIL 2022, RGPD Art. 7, CEPD 03/2022"
|
|
25
|
+
validations:
|
|
26
|
+
required: false
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
## Description
|
|
2
|
+
|
|
3
|
+
<!-- Describe the changes made -->
|
|
4
|
+
|
|
5
|
+
## Type of change
|
|
6
|
+
|
|
7
|
+
- [ ] Bug fix
|
|
8
|
+
- [ ] New feature
|
|
9
|
+
- [ ] New tracker / cookie pattern
|
|
10
|
+
- [ ] CMP detection improvement
|
|
11
|
+
- [ ] Refactoring / performance
|
|
12
|
+
- [ ] Documentation
|
|
13
|
+
|
|
14
|
+
## Checklist
|
|
15
|
+
|
|
16
|
+
- [ ] `pnpm format:check` passes
|
|
17
|
+
- [ ] `pnpm lint` passes
|
|
18
|
+
- [ ] `pnpm typecheck` passes
|
|
19
|
+
- [ ] `pnpm build` passes
|
|
20
|
+
- [ ] Manually tested on at least one site
|
|
21
|
+
|
|
22
|
+
## Legal reference (if new rule)
|
|
23
|
+
|
|
24
|
+
<!-- E.g. CNIL 2022, RGPD Art. 7, CEPD 03/2022 -->
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
ci:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
|
|
16
|
+
- uses: pnpm/action-setup@v4
|
|
17
|
+
with:
|
|
18
|
+
version: latest
|
|
19
|
+
|
|
20
|
+
- uses: actions/setup-node@v4
|
|
21
|
+
with:
|
|
22
|
+
node-version: 22
|
|
23
|
+
cache: pnpm
|
|
24
|
+
|
|
25
|
+
- name: Install dependencies
|
|
26
|
+
run: pnpm install --frozen-lockfile
|
|
27
|
+
|
|
28
|
+
- name: Format check
|
|
29
|
+
run: pnpm format:check
|
|
30
|
+
|
|
31
|
+
- name: Lint
|
|
32
|
+
run: pnpm lint
|
|
33
|
+
|
|
34
|
+
- name: Type check
|
|
35
|
+
run: pnpm typecheck
|
|
36
|
+
|
|
37
|
+
- name: Build
|
|
38
|
+
run: pnpm build
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
release:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
permissions:
|
|
11
|
+
contents: write
|
|
12
|
+
pull-requests: write
|
|
13
|
+
packages: write
|
|
14
|
+
id-token: write # required for npm trusted publishing (OIDC)
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: pnpm/action-setup@v4
|
|
20
|
+
with:
|
|
21
|
+
version: latest
|
|
22
|
+
|
|
23
|
+
- uses: actions/setup-node@v4
|
|
24
|
+
with:
|
|
25
|
+
node-version: 22
|
|
26
|
+
cache: pnpm
|
|
27
|
+
registry-url: https://registry.npmjs.org
|
|
28
|
+
|
|
29
|
+
- name: Install dependencies
|
|
30
|
+
run: pnpm install --frozen-lockfile
|
|
31
|
+
|
|
32
|
+
- name: Build
|
|
33
|
+
run: pnpm build
|
|
34
|
+
|
|
35
|
+
- name: Create release PR or publish to npm
|
|
36
|
+
id: changesets
|
|
37
|
+
uses: changesets/action@v1
|
|
38
|
+
with:
|
|
39
|
+
publish: pnpm changeset publish
|
|
40
|
+
title: "chore: release new version"
|
|
41
|
+
commit: "chore: release new version"
|
|
42
|
+
env:
|
|
43
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
44
|
+
NPM_CONFIG_PROVENANCE: "true"
|
|
45
|
+
|
|
46
|
+
- name: Configure registry for GitHub Packages
|
|
47
|
+
if: steps.changesets.outputs.published == 'true'
|
|
48
|
+
uses: actions/setup-node@v4
|
|
49
|
+
with:
|
|
50
|
+
node-version: 22
|
|
51
|
+
registry-url: https://npm.pkg.github.com
|
|
52
|
+
|
|
53
|
+
- name: Publish to GitHub Packages
|
|
54
|
+
if: steps.changesets.outputs.published == 'true'
|
|
55
|
+
run: npm publish
|
|
56
|
+
env:
|
|
57
|
+
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="WEB_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$" />
|
|
5
|
+
<orderEntry type="inheritedJdk" />
|
|
6
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
7
|
+
</component>
|
|
8
|
+
</module>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/gdpr-report.iml" filepath="$PROJECT_DIR$/.idea/gdpr-report.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
package/.idea/vcs.xml
ADDED
package/CHANGELOG.md
ADDED
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm build # Compile TypeScript → dist/
|
|
9
|
+
pnpm dev # Watch mode compilation
|
|
10
|
+
pnpm typecheck # Type-check without emitting
|
|
11
|
+
pnpm lint # oxlint
|
|
12
|
+
pnpm lint:fix # oxlint --fix
|
|
13
|
+
pnpm format # oxfmt (auto-format)
|
|
14
|
+
pnpm format:check # oxfmt --check (used in CI)
|
|
15
|
+
|
|
16
|
+
# Run the CLI after building
|
|
17
|
+
node dist/cli.js scan <url>
|
|
18
|
+
node dist/cli.js scan <url> -o ./reports --locale fr-FR --verbose
|
|
19
|
+
node dist/cli.js list-trackers
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
There are no tests currently.
|
|
23
|
+
|
|
24
|
+
## Release process
|
|
25
|
+
|
|
26
|
+
This project uses [Changesets](https://github.com/changesets/changesets).
|
|
27
|
+
|
|
28
|
+
- **Contributors**: run `pnpm changeset` before opening a PR to document the change (patch/minor/major + summary). Commit the generated `.changeset/*.md` file with your PR. Skip for docs/CI-only changes.
|
|
29
|
+
- **Maintainers**: merging changesets into `main` triggers the release workflow, which opens a "chore: release new version" PR (bumps version + updates `CHANGELOG.md`). Merging that PR publishes to GitHub Packages and creates the GitHub Release automatically.
|
|
30
|
+
|
|
31
|
+
Commits must follow [Conventional Commits](https://www.conventionalcommits.org/) (`feat:`, `fix:`, `chore:`, `docs:`, etc.).
|
|
32
|
+
|
|
33
|
+
## Architecture
|
|
34
|
+
|
|
35
|
+
This is a TypeScript CLI tool (`gdpr-scan`) that audits websites for GDPR/RGPD cookie consent compliance using Playwright. It produces a Markdown report in French.
|
|
36
|
+
|
|
37
|
+
### Scan pipeline (`src/scanner/index.ts`)
|
|
38
|
+
|
|
39
|
+
The scanner runs **4 sequential phases** using real Chromium browsers:
|
|
40
|
+
|
|
41
|
+
1. **Phase 1** — Load page with no interaction; capture cookies + network requests (`before-interaction`)
|
|
42
|
+
2. **Phase 2** — Detect the consent modal/banner (CSS selectors + heuristics)
|
|
43
|
+
3. **Phase 3** — Click the reject button in the same session; capture state (`after-reject`)
|
|
44
|
+
4. **Phase 4** — Fresh browser session, load page, click accept; capture state (`after-accept`)
|
|
45
|
+
|
|
46
|
+
Phase 3 and 4 require two separate browser sessions so cookie state is fully isolated.
|
|
47
|
+
|
|
48
|
+
### Module responsibilities
|
|
49
|
+
|
|
50
|
+
- **`src/scanner/browser.ts`** — Playwright browser/context lifecycle helpers
|
|
51
|
+
- **`src/scanner/cookies.ts`** — Extract and classify cookies from Playwright context
|
|
52
|
+
- **`src/scanner/network.ts`** — Intercept and classify network requests via Playwright events
|
|
53
|
+
- **`src/scanner/consent-modal.ts`** — Detect consent modal by trying known CMP selectors (`MODAL_SELECTORS`) then falling back to DOM heuristics; extracts buttons/checkboxes with visual properties (font size, bounding box, contrast ratio) needed for dark-pattern detection
|
|
54
|
+
- **`src/classifiers/cookie-classifier.ts`** — Pattern-match cookie names against a static list to assign `CookieCategory` and `requiresConsent`
|
|
55
|
+
- **`src/classifiers/network-classifier.ts`** — Look up request hostnames in `TRACKER_DB` and match URL patterns against `PIXEL_PATTERNS`
|
|
56
|
+
- **`src/classifiers/tracker-list.ts`** — Static database of tracker domains by category
|
|
57
|
+
- **`src/analyzers/compliance.ts`** — Scores 4 dimensions (0–25 each) and surfaces `DarkPatternIssue` objects: consent validity, easy refusal, transparency, cookie behavior
|
|
58
|
+
- **`src/analyzers/wording.ts`** — Text analysis of button labels and modal copy
|
|
59
|
+
- **`src/report/generator.ts`** — Renders a Markdown report and checklist from `ScanResult`; runs `oxfmt` on generated files
|
|
60
|
+
- **`src/types.ts`** — All shared TypeScript interfaces (`ScanResult`, `ScanOptions`, `ComplianceScore`, `ConsentModal`, etc.)
|
|
61
|
+
|
|
62
|
+
### Compliance scoring
|
|
63
|
+
|
|
64
|
+
Each of the 4 score dimensions starts at 25 and gets deducted based on detected issues:
|
|
65
|
+
|
|
66
|
+
- `consentValidity` — pre-ticked boxes, misleading wording, missing info
|
|
67
|
+
- `easyRefusal` — absent/buried reject button, click asymmetry, visual asymmetry
|
|
68
|
+
- `transparency` — no granular controls, missing info fields
|
|
69
|
+
- `cookieBehavior` — non-essential cookies/trackers before consent, consent-requiring cookies persisting after reject
|
|
70
|
+
|
|
71
|
+
Grade thresholds: A ≥ 90, B ≥ 75, C ≥ 55, D ≥ 35, F < 35. Exit code 1 on grade F.
|
|
72
|
+
|
|
73
|
+
### Module system
|
|
74
|
+
|
|
75
|
+
The project uses `"type": "module"` with `"moduleResolution": "NodeNext"`. All local imports **must** include the `.js` extension even for `.ts` source files.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
|
2
|
+
|
|
3
|
+
## Our Pledge
|
|
4
|
+
|
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, caste, color, religion, or sexual identity and orientation.
|
|
6
|
+
|
|
7
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
|
8
|
+
|
|
9
|
+
## Our Standards
|
|
10
|
+
|
|
11
|
+
Examples of behavior that contributes to a positive environment:
|
|
12
|
+
|
|
13
|
+
- Demonstrating empathy and kindness toward other people
|
|
14
|
+
- Being respectful of differing opinions, viewpoints, and experiences
|
|
15
|
+
- Giving and gracefully accepting constructive feedback
|
|
16
|
+
- Accepting responsibility and apologizing to those affected by our mistakes
|
|
17
|
+
- Focusing on what is best not just for us as individuals, but for the overall community
|
|
18
|
+
|
|
19
|
+
Examples of unacceptable behavior:
|
|
20
|
+
|
|
21
|
+
- The use of sexualized language or imagery, and sexual attention or advances of any kind
|
|
22
|
+
- Trolling, insulting or derogatory comments, and personal or political attacks
|
|
23
|
+
- Public or private harassment
|
|
24
|
+
- Publishing others' private information without their explicit permission
|
|
25
|
+
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
26
|
+
|
|
27
|
+
## Enforcement Responsibilities
|
|
28
|
+
|
|
29
|
+
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
|
30
|
+
|
|
31
|
+
## Scope
|
|
32
|
+
|
|
33
|
+
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces.
|
|
34
|
+
|
|
35
|
+
## Enforcement
|
|
36
|
+
|
|
37
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening a [GitHub Security Advisory](https://github.com/Slashgear/gdpr-report/security/advisories/new). All complaints will be reviewed and investigated promptly and fairly.
|
|
38
|
+
|
|
39
|
+
## Attribution
|
|
40
|
+
|
|
41
|
+
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.1.
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Contributing to gdpr-cookie-scanner
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
- Node.js ≥ 20
|
|
6
|
+
- pnpm
|
|
7
|
+
- Playwright: `npx playwright install chromium`
|
|
8
|
+
|
|
9
|
+
## Getting started
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
git clone https://github.com/Slashgear/gdpr-report.git
|
|
13
|
+
cd gdpr-report
|
|
14
|
+
pnpm install
|
|
15
|
+
pnpm build
|
|
16
|
+
|
|
17
|
+
# Run the CLI locally
|
|
18
|
+
node dist/cli.js scan https://example.com
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Workflow
|
|
22
|
+
|
|
23
|
+
1. Fork the repository and create a branch from `main`
|
|
24
|
+
2. Make your changes
|
|
25
|
+
3. Verify that CI passes locally:
|
|
26
|
+
```bash
|
|
27
|
+
pnpm format:check
|
|
28
|
+
pnpm lint
|
|
29
|
+
pnpm typecheck
|
|
30
|
+
pnpm build
|
|
31
|
+
```
|
|
32
|
+
4. Open a Pull Request targeting `main`
|
|
33
|
+
|
|
34
|
+
## Areas to contribute
|
|
35
|
+
|
|
36
|
+
- **Classifiers** — add cookie patterns or tracker domains in `src/classifiers/`
|
|
37
|
+
- **Consent modal detection** — improve CMP detection in `src/scanner/consent-modal.ts`
|
|
38
|
+
- **Compliance rules** — refine scoring rules in `src/analyzers/compliance.ts`
|
|
39
|
+
- **Report** — improve report rendering in `src/report/generator.ts`
|
|
40
|
+
|
|
41
|
+
## Releasing
|
|
42
|
+
|
|
43
|
+
This project uses [Changesets](https://github.com/changesets/changesets) to manage versions and changelogs.
|
|
44
|
+
|
|
45
|
+
### As a contributor — document your change
|
|
46
|
+
|
|
47
|
+
Every PR that changes behaviour (bug fix, new feature, breaking change) must include a changeset:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pnpm changeset
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The interactive prompt asks:
|
|
54
|
+
|
|
55
|
+
- **Which packages** are affected (only one here: `@slashgear/gdpr-cookie-scanner`)
|
|
56
|
+
- **Bump type**: `patch` (bug fix) · `minor` (new feature) · `major` (breaking change)
|
|
57
|
+
- **Summary**: one-line description that will appear in `CHANGELOG.md`
|
|
58
|
+
|
|
59
|
+
This creates a file in `.changeset/` — commit it alongside your changes.
|
|
60
|
+
|
|
61
|
+
> PRs without a changeset are fine for docs, tests, or CI changes that don't affect the published package.
|
|
62
|
+
|
|
63
|
+
### As a maintainer — publish a release
|
|
64
|
+
|
|
65
|
+
When changesets are merged into `main`, the [release workflow](.github/workflows/release.yml) automatically opens a **"chore: release new version"** PR that:
|
|
66
|
+
|
|
67
|
+
- Bumps `package.json` version
|
|
68
|
+
- Aggregates all changeset summaries into `CHANGELOG.md`
|
|
69
|
+
|
|
70
|
+
Merging that PR triggers the workflow again, which:
|
|
71
|
+
|
|
72
|
+
1. Publishes `@slashgear/gdpr-cookie-scanner` to [GitHub Packages](https://github.com/Slashgear/gdpr-report/pkgs/npm/gdpr-cookie-scanner)
|
|
73
|
+
2. Creates the corresponding GitHub Release with the generated changelog
|
|
74
|
+
|
|
75
|
+
## Conventions
|
|
76
|
+
|
|
77
|
+
- Local imports must include the `.js` extension (ESM NodeNext module)
|
|
78
|
+
- Formatting is handled by oxfmt (`pnpm format`), do not format manually
|
|
79
|
+
- Commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/) convention
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 gdpr-cookie-scanner contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# gdpr-cookie-scanner
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Slashgear/gdpr-report/actions/workflows/ci.yml)
|
|
4
|
+
[](CODE_OF_CONDUCT.md)
|
|
5
|
+
|
|
6
|
+
CLI tool to automatically audit the GDPR cookie consent compliance of a website: consent modal, dark patterns, cookies set before/after interaction, network trackers. Generates a detailed Markdown report.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install -g @slashgear/gdpr-cookie-scanner
|
|
12
|
+
npx playwright install chromium
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Or run without installing:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npx @slashgear/gdpr-cookie-scanner gdpr-scan scan https://example.com
|
|
19
|
+
# Playwright is still required the first time:
|
|
20
|
+
npx playwright install chromium
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
gdpr-scan scan <url> [options]
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Options
|
|
30
|
+
|
|
31
|
+
| Option | Default | Description |
|
|
32
|
+
| ----------------------- | ---------------- | ------------------------------- |
|
|
33
|
+
| `-o, --output <dir>` | `./gdpr-reports` | Output directory for the report |
|
|
34
|
+
| `-t, --timeout <ms>` | `30000` | Navigation timeout |
|
|
35
|
+
| `--no-screenshots` | — | Disable screenshot capture |
|
|
36
|
+
| `-l, --locale <locale>` | `fr-FR` | Browser locale |
|
|
37
|
+
| `-v, --verbose` | — | Show full stack trace on error |
|
|
38
|
+
|
|
39
|
+
### Examples
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Basic scan
|
|
43
|
+
gdpr-scan scan https://example.com
|
|
44
|
+
|
|
45
|
+
# With custom output directory
|
|
46
|
+
gdpr-scan scan https://example.com -o ./reports
|
|
47
|
+
|
|
48
|
+
# Scan in English, without screenshots
|
|
49
|
+
gdpr-scan scan https://example.com --locale en-US --no-screenshots
|
|
50
|
+
|
|
51
|
+
# Show the built-in tracker database
|
|
52
|
+
gdpr-scan list-trackers
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## How it works
|
|
56
|
+
|
|
57
|
+
The scanner runs **4 phases** using a real Chromium browser (Playwright):
|
|
58
|
+
|
|
59
|
+
1. **Initial load** — The page is loaded without any interaction. All cookies and network requests are captured (`before-interaction`).
|
|
60
|
+
2. **Modal analysis** — The consent banner is detected (CSS selectors of known CMPs + DOM heuristics). Buttons are extracted with their visual properties (size, color, contrast ratio).
|
|
61
|
+
3. **Reject test** — The "Reject" button is clicked. Cookies and requests are captured (`after-reject`).
|
|
62
|
+
4. **Accept test** — A new browser session (clean state) loads the page and clicks "Accept". Cookies and requests are captured (`after-accept`).
|
|
63
|
+
|
|
64
|
+
## Generated report
|
|
65
|
+
|
|
66
|
+
The Markdown report contains:
|
|
67
|
+
|
|
68
|
+
- **Global score** (0–100) and **grade** A/B/C/D/F
|
|
69
|
+
- Executive summary
|
|
70
|
+
- Modal analysis: buttons, checkboxes, font size, screenshots
|
|
71
|
+
- Detected dark patterns (missing reject button, visual asymmetry, pre-ticked boxes, misleading wording…)
|
|
72
|
+
- Cookie table before interaction, after reject, after accept
|
|
73
|
+
- Network tracker requests by phase
|
|
74
|
+
- Targeted recommendations
|
|
75
|
+
- Legal references (RGPD, ePrivacy directive, CEPD guidelines, CNIL 2022)
|
|
76
|
+
|
|
77
|
+
The file is created at: `<output-dir>/gdpr-report-<domain>-<date>.md`
|
|
78
|
+
|
|
79
|
+
## Scoring
|
|
80
|
+
|
|
81
|
+
The score is made up of 4 criteria (25 points each):
|
|
82
|
+
|
|
83
|
+
| Criterion | What is evaluated |
|
|
84
|
+
| -------------------- | ---------------------------------------------------------------------------------------------- |
|
|
85
|
+
| **Consent validity** | Pre-ticked boxes, ambiguous wording, missing information |
|
|
86
|
+
| **Easy refusal** | Missing or buried reject button, click asymmetry, visual asymmetry |
|
|
87
|
+
| **Transparency** | Granular controls, mention of purposes / third parties / duration / right to withdraw |
|
|
88
|
+
| **Cookie behavior** | Non-essential cookies before consent, cookies persisting after reject, trackers before consent |
|
|
89
|
+
|
|
90
|
+
**Grade scale:** A ≥ 90 · B ≥ 75 · C ≥ 55 · D ≥ 35 · F < 35
|
|
91
|
+
|
|
92
|
+
The process exits with code `1` if the grade is F, `2` on scan error.
|
|
93
|
+
|
|
94
|
+
## Detected dark patterns
|
|
95
|
+
|
|
96
|
+
| Type | Severity | Description |
|
|
97
|
+
| ----------------------- | ---------------- | ----------------------------------------------------- |
|
|
98
|
+
| `no-reject-button` | Critical | No reject option in the modal |
|
|
99
|
+
| `buried-reject` | Critical | Reject button not present at the first layer |
|
|
100
|
+
| `click-asymmetry` | Critical | Rejecting requires more clicks than accepting |
|
|
101
|
+
| `pre-ticked` | Critical | Pre-ticked checkboxes (invalid under RGPD Recital 32) |
|
|
102
|
+
| `auto-consent` | Critical | Non-essential cookies/trackers before any consent |
|
|
103
|
+
| `asymmetric-prominence` | Warning | Accept button significantly larger than reject |
|
|
104
|
+
| `nudging` | Warning | Accept button font larger than reject button font |
|
|
105
|
+
| `misleading-wording` | Warning/Critical | Ambiguous labels ("OK", "Continue"…) |
|
|
106
|
+
| `missing-info` | Warning | Mandatory information absent from the text |
|
|
107
|
+
|
|
108
|
+
## Automatically recognised CMPs
|
|
109
|
+
|
|
110
|
+
Axeptio, Cookiebot, OneTrust, Didomi, Tarteaucitron, Usercentrics, and about twenty others via their specific CSS selectors. A heuristic fallback (fixed/sticky element with cookie-related text) covers custom banners.
|
|
111
|
+
|
|
112
|
+
## Development
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
pnpm dev # Watch-mode compilation
|
|
116
|
+
pnpm typecheck # Type-check without compiling
|
|
117
|
+
pnpm lint # oxlint
|
|
118
|
+
pnpm format # oxfmt
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Release
|
|
122
|
+
|
|
123
|
+
Releases are published automatically to [npm](https://www.npmjs.com/package/@slashgear/gdpr-cookie-scanner) and [GitHub Packages](https://github.com/Slashgear/gdpr-report/pkgs/npm/gdpr-cookie-scanner) via [Changesets](https://github.com/changesets/changesets). See [CONTRIBUTING.md](CONTRIBUTING.md) for the release process.
|
|
124
|
+
|
|
125
|
+
## Contributing
|
|
126
|
+
|
|
127
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). This project follows the [Contributor Covenant](CODE_OF_CONDUCT.md) code of conduct.
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
If you discover a security vulnerability, **do not open a public issue**.
|
|
6
|
+
|
|
7
|
+
Send a report via [GitHub Security Advisories](https://github.com/Slashgear/gdpr-report/security/advisories/new) including:
|
|
8
|
+
|
|
9
|
+
- A description of the vulnerability
|
|
10
|
+
- Steps to reproduce it
|
|
11
|
+
- The potential impact
|
|
12
|
+
|
|
13
|
+
## Scope
|
|
14
|
+
|
|
15
|
+
This tool runs a controlled Chromium browser against URLs provided by the user. It is designed to be run locally or in a controlled CI environment — do not expose the CLI as a web service without appropriate isolation.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ComplianceScore, ConsentModal, ScannedCookie, NetworkRequest } from "../types.js";
|
|
2
|
+
interface ComplianceInput {
|
|
3
|
+
modal: ConsentModal;
|
|
4
|
+
cookiesBeforeInteraction: ScannedCookie[];
|
|
5
|
+
cookiesAfterAccept: ScannedCookie[];
|
|
6
|
+
cookiesAfterReject: ScannedCookie[];
|
|
7
|
+
networkBeforeInteraction: NetworkRequest[];
|
|
8
|
+
networkAfterAccept: NetworkRequest[];
|
|
9
|
+
networkAfterReject: NetworkRequest[];
|
|
10
|
+
}
|
|
11
|
+
export declare function analyzeCompliance(input: ComplianceInput): ComplianceScore;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=compliance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compliance.d.ts","sourceRoot":"","sources":["../../src/analyzers/compliance.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,YAAY,EAEZ,aAAa,EACb,cAAc,EACf,MAAM,aAAa,CAAC;AAGrB,UAAU,eAAe;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,wBAAwB,EAAE,aAAa,EAAE,CAAC;IAC1C,kBAAkB,EAAE,aAAa,EAAE,CAAC;IACpC,kBAAkB,EAAE,aAAa,EAAE,CAAC;IACpC,wBAAwB,EAAE,cAAc,EAAE,CAAC;IAC3C,kBAAkB,EAAE,cAAc,EAAE,CAAC;IACrC,kBAAkB,EAAE,cAAc,EAAE,CAAC;CACtC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,eAAe,GAAG,eAAe,CA+KzE"}
|