@slashgear/gdpr-cookie-scanner 1.3.0 → 2.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.
Files changed (78) hide show
  1. package/.dockerignore +13 -0
  2. package/.github/workflows/ci.yml +8 -2
  3. package/.github/workflows/docker.yml +49 -0
  4. package/.github/workflows/pages.yml +40 -0
  5. package/.github/workflows/release.yml +1 -1
  6. package/.nvmrc +1 -0
  7. package/CHANGELOG.md +87 -0
  8. package/Dockerfile +36 -0
  9. package/README.md +44 -63
  10. package/dist/cli.js +21 -3
  11. package/dist/cli.js.map +1 -1
  12. package/dist/report/generator.d.ts +1 -4
  13. package/dist/report/generator.d.ts.map +1 -1
  14. package/dist/report/generator.js +45 -23
  15. package/dist/report/generator.js.map +1 -1
  16. package/dist/report/html.d.ts +3 -0
  17. package/dist/report/html.d.ts.map +1 -0
  18. package/dist/report/html.js +766 -0
  19. package/dist/report/html.js.map +1 -0
  20. package/dist/types.d.ts +2 -0
  21. package/dist/types.d.ts.map +1 -1
  22. package/docs/index.html +314 -0
  23. package/docs/reports/github.com/after-accept.png +0 -0
  24. package/docs/reports/github.com/after-reject.png +0 -0
  25. package/docs/reports/github.com/gdpr-checklist-github.com-2026-02-22.md +44 -0
  26. package/docs/reports/github.com/gdpr-cookies-github.com-2026-02-22.md +29 -0
  27. package/docs/reports/github.com/gdpr-report-github.com-2026-02-22.md +102 -0
  28. package/docs/reports/github.com/gdpr-report-github.com-2026-02-22.pdf +0 -0
  29. package/docs/reports/gitlab.com/after-accept.png +0 -0
  30. package/docs/reports/gitlab.com/after-reject.png +0 -0
  31. package/docs/reports/gitlab.com/gdpr-checklist-gitlab.com-2026-02-22.md +44 -0
  32. package/docs/reports/gitlab.com/gdpr-cookies-gitlab.com-2026-02-22.md +55 -0
  33. package/docs/reports/gitlab.com/gdpr-report-gitlab.com-2026-02-22.md +200 -0
  34. package/docs/reports/gitlab.com/gdpr-report-gitlab.com-2026-02-22.pdf +0 -0
  35. package/docs/reports/gitlab.com/modal-initial.png +0 -0
  36. package/docs/reports/npmjs.com/after-accept.png +0 -0
  37. package/docs/reports/npmjs.com/after-reject.png +0 -0
  38. package/docs/reports/npmjs.com/gdpr-checklist-npmjs.com-2026-02-22.md +44 -0
  39. package/docs/reports/npmjs.com/gdpr-cookies-npmjs.com-2026-02-22.md +25 -0
  40. package/docs/reports/npmjs.com/gdpr-report-npmjs.com-2026-02-22.md +88 -0
  41. package/docs/reports/npmjs.com/gdpr-report-npmjs.com-2026-02-22.pdf +0 -0
  42. package/docs/reports/reddit.com/after-accept.png +0 -0
  43. package/docs/reports/reddit.com/after-reject.png +0 -0
  44. package/docs/reports/reddit.com/gdpr-checklist-reddit.com-2026-02-22.md +44 -0
  45. package/docs/reports/reddit.com/gdpr-cookies-reddit.com-2026-02-22.md +33 -0
  46. package/docs/reports/reddit.com/gdpr-report-reddit.com-2026-02-22.md +148 -0
  47. package/docs/reports/reddit.com/gdpr-report-reddit.com-2026-02-22.pdf +0 -0
  48. package/docs/reports/reddit.com/modal-initial.png +0 -0
  49. package/docs/reports/stackoverflow.com/after-accept.png +0 -0
  50. package/docs/reports/stackoverflow.com/after-reject.png +0 -0
  51. package/docs/reports/stackoverflow.com/gdpr-checklist-stackoverflow.com-2026-02-22.md +44 -0
  52. package/docs/reports/stackoverflow.com/gdpr-cookies-stackoverflow.com-2026-02-22.md +67 -0
  53. package/docs/reports/stackoverflow.com/gdpr-report-stackoverflow.com-2026-02-22.md +206 -0
  54. package/docs/reports/stackoverflow.com/gdpr-report-stackoverflow.com-2026-02-22.pdf +0 -0
  55. package/docs/reports/stackoverflow.com/modal-initial.png +0 -0
  56. package/docs/reports/www.afp.com/after-accept.png +0 -0
  57. package/docs/reports/www.afp.com/after-reject.png +0 -0
  58. package/docs/reports/www.afp.com/gdpr-checklist-afp.com-2026-02-22.md +44 -0
  59. package/docs/reports/www.afp.com/gdpr-cookies-afp.com-2026-02-22.md +42 -0
  60. package/docs/reports/www.afp.com/gdpr-report-afp.com-2026-02-22.md +202 -0
  61. package/docs/reports/www.afp.com/gdpr-report-afp.com-2026-02-22.pdf +0 -0
  62. package/docs/reports/www.afp.com/modal-initial.png +0 -0
  63. package/docs/style.css +439 -0
  64. package/package.json +10 -7
  65. package/src/cli.ts +28 -4
  66. package/src/report/generator.ts +54 -29
  67. package/src/report/html.ts +940 -0
  68. package/src/types.ts +3 -0
  69. package/tests/e2e/fixtures/compliant-site.html +80 -0
  70. package/tests/e2e/fixtures/no-modal-site.html +17 -0
  71. package/tests/e2e/fixtures/non-compliant-site.html +54 -0
  72. package/tests/e2e/scanner.test.ts +135 -0
  73. package/tests/helpers/test-server.ts +57 -0
  74. package/tests/unit/compliance.test.ts +460 -0
  75. package/tests/unit/cookie-classifier.test.ts +192 -0
  76. package/tests/unit/network-classifier.test.ts +91 -0
  77. package/tests/unit/wording.test.ts +162 -0
  78. package/vitest.config.ts +9 -0
package/.dockerignore ADDED
@@ -0,0 +1,13 @@
1
+ .git
2
+ .github
3
+ .claude
4
+ .changeset
5
+ node_modules/
6
+ dist/
7
+ reports/
8
+ gdpr-reports/
9
+ tests/
10
+ *.md
11
+ *.log
12
+ .DS_Store
13
+ .nvmrc
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
 
13
13
  steps:
14
- - uses: actions/checkout@v4
14
+ - uses: actions/checkout@v6
15
15
 
16
16
  - uses: pnpm/action-setup@v4
17
17
  with:
@@ -19,7 +19,7 @@ jobs:
19
19
 
20
20
  - uses: actions/setup-node@v4
21
21
  with:
22
- node-version: 22
22
+ node-version: 24
23
23
  cache: pnpm
24
24
 
25
25
  - name: Install dependencies
@@ -36,3 +36,9 @@ jobs:
36
36
 
37
37
  - name: Build
38
38
  run: pnpm build
39
+
40
+ - name: Install Playwright browsers
41
+ run: pnpm exec playwright install chromium --with-deps
42
+
43
+ - name: Test
44
+ run: pnpm test
@@ -0,0 +1,49 @@
1
+ name: Docker
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ docker:
9
+ runs-on: ubuntu-latest
10
+ permissions:
11
+ contents: read
12
+ packages: write
13
+
14
+ steps:
15
+ - uses: actions/checkout@v6
16
+
17
+ - name: Set up QEMU
18
+ uses: docker/setup-qemu-action@v3
19
+
20
+ - name: Set up Docker Buildx
21
+ uses: docker/setup-buildx-action@v3
22
+
23
+ - name: Log in to GitHub Container Registry
24
+ uses: docker/login-action@v3
25
+ with:
26
+ registry: ghcr.io
27
+ username: ${{ github.actor }}
28
+ password: ${{ secrets.GITHUB_TOKEN }}
29
+
30
+ - name: Extract metadata
31
+ id: meta
32
+ uses: docker/metadata-action@v5
33
+ with:
34
+ images: ghcr.io/slashgear/gdpr-cookie-scanner
35
+ tags: |
36
+ type=semver,pattern={{version}}
37
+ type=semver,pattern={{major}}.{{minor}}
38
+ type=raw,value=latest
39
+
40
+ - name: Build and push
41
+ uses: docker/build-push-action@v6
42
+ with:
43
+ context: .
44
+ platforms: linux/amd64,linux/arm64
45
+ push: true
46
+ tags: ${{ steps.meta.outputs.tags }}
47
+ labels: ${{ steps.meta.outputs.labels }}
48
+ cache-from: type=gha
49
+ cache-to: type=gha,mode=max
@@ -0,0 +1,40 @@
1
+ name: Deploy GitHub Pages
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "docs/**"
9
+ workflow_dispatch:
10
+
11
+ permissions:
12
+ contents: read
13
+ pages: write
14
+ id-token: write
15
+
16
+ concurrency:
17
+ group: pages
18
+ cancel-in-progress: false
19
+
20
+ jobs:
21
+ deploy:
22
+ environment:
23
+ name: github-pages
24
+ url: ${{ steps.deployment.outputs.page_url }}
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - name: Checkout
28
+ uses: actions/checkout@v4
29
+
30
+ - name: Configure Pages
31
+ uses: actions/configure-pages@v4
32
+
33
+ - name: Upload artifact
34
+ uses: actions/upload-pages-artifact@v3
35
+ with:
36
+ path: docs/
37
+
38
+ - name: Deploy to GitHub Pages
39
+ id: deployment
40
+ uses: actions/deploy-pages@v4
@@ -12,7 +12,7 @@ jobs:
12
12
  pull-requests: write
13
13
 
14
14
  steps:
15
- - uses: actions/checkout@v4
15
+ - uses: actions/checkout@v6
16
16
 
17
17
  - uses: pnpm/action-setup@v4
18
18
  with:
package/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ 24
package/CHANGELOG.md CHANGED
@@ -1,5 +1,92 @@
1
1
  # @slashgear/gdpr-cookie-scanner
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 98880ac: feat!: require Node.js 24 LTS (breaking change for Node 20/22 users)
8
+
9
+ Node.js 24 became Active LTS in October 2025. This is a breaking change for
10
+ users running Node 20 or 22. A `.nvmrc` file is provided so `nvm use`
11
+ automatically selects the correct version when entering the project directory.
12
+
13
+ ### Minor Changes
14
+
15
+ - bd769d6: Add GitHub Pages landing page with live GDPR reports
16
+
17
+ Adds a static landing page (`docs/`) hosted on GitHub Pages that showcases the tool
18
+ with real scan results for 6 tech sites (reddit.com, dev.to, github.com, gitlab.com,
19
+ stackoverflow.com, npmjs.com). The page is vanilla HTML/CSS with dark mode support
20
+ and links directly to the rendered Markdown reports on GitHub.
21
+
22
+ Also adds `.github/workflows/pages.yml` to automatically deploy `docs/` on every
23
+ push to `main` that touches that directory.
24
+
25
+ - cf9c42a: feat: add Docker image for containerised usage
26
+
27
+ A `Dockerfile` is now included at the root of the repository, enabling
28
+ `gdpr-scan` to be run in any environment that supports Docker without
29
+ needing a local Node.js or Playwright installation.
30
+
31
+ The image uses a two-stage build:
32
+
33
+ - **Builder** (`node:24-slim`) — compiles TypeScript to `dist/`
34
+ - **Runtime** (`node:24-slim` + `playwright install chromium --with-deps`) —
35
+ installs only Chromium and its system dependencies instead of the full
36
+ official Playwright image (which bundles Chromium, Firefox and WebKit).
37
+ This keeps the final image around **400–600 MB** vs ~1.5–1.8 GB for the
38
+ unoptimised approach.
39
+
40
+ A `.dockerignore` is also included to exclude source files, Git history,
41
+ and dev artefacts from the build context.
42
+
43
+ Usage:
44
+
45
+ ```bash
46
+ # Build
47
+ docker build -t gdpr-scan .
48
+
49
+ # Run a scan (mount a local directory to retrieve the reports)
50
+ docker run --rm -v $(pwd)/reports:/reports gdpr-scan scan https://example.com -o /reports
51
+ ```
52
+
53
+ - 5cba8a8: feat: add multi-format report output and a standalone HTML report
54
+
55
+ The CLI now accepts a `-f, --format <formats>` option (comma-separated, defaults
56
+ to `md,pdf`) that controls which report files are produced. Supported values:
57
+ `md`, `html`, `json`, `pdf`.
58
+
59
+ The new `html` format generates a self-contained, styled HTML report directly from
60
+ the scan result — no external dependencies, no network requests. It includes a
61
+ colour-coded grade badge, per-dimension score cards with progress bars,
62
+ dark-pattern issue cards, modal details, cookies by phase, network tracker tables,
63
+ recommendations, and a compliance checklist.
64
+
65
+ This makes the report easier to share (single file, opens in any browser) and
66
+ provides a better reading experience than the Markdown version.
67
+
68
+ ## 1.4.0
69
+
70
+ ### Minor Changes
71
+
72
+ - 5af8678: test: add Vitest unit and E2E test suite
73
+
74
+ Introduces a comprehensive test suite using Vitest:
75
+
76
+ - **Unit tests** (76 tests) covering the three pure-logic modules:
77
+ `cookie-classifier`, `network-classifier`, `wording` analyzer, and
78
+ `compliance` analyzer — verifying scoring rules, dark pattern detection,
79
+ and cookie/tracker classification across French and English content.
80
+
81
+ - **E2E tests** (14 tests) that spin up a local HTTP server serving three
82
+ HTML fixtures (compliant banner, non-compliant banner, no modal) and run
83
+ the full Playwright scanner against them, asserting detected modals,
84
+ cookie behavior, and compliance grades.
85
+
86
+ - **CI integration**: the GitHub Actions workflow now installs Playwright's
87
+ Chromium browser and runs `pnpm test` after every build, blocking merges
88
+ on test failures.
89
+
3
90
  ## 1.3.0
4
91
 
5
92
  ### Minor Changes
package/Dockerfile ADDED
@@ -0,0 +1,36 @@
1
+ # ── Stage 1: build ────────────────────────────────────────────────────────────
2
+ FROM node:24-slim AS builder
3
+ WORKDIR /app
4
+
5
+ RUN npm install -g pnpm@latest
6
+
7
+ COPY package.json pnpm-lock.yaml ./
8
+ RUN pnpm install --frozen-lockfile
9
+
10
+ COPY tsconfig.json ./
11
+ COPY src ./src
12
+ RUN pnpm build
13
+
14
+ # ── Stage 2: runtime ──────────────────────────────────────────────────────────
15
+ # node:24-slim + Chromium only (~400-600 MB) vs the full Playwright image
16
+ # (~1.5-1.8 GB with all 3 browser stacks).
17
+ FROM node:24-slim
18
+ WORKDIR /app
19
+ ENV NODE_ENV=production
20
+
21
+ RUN npm install -g pnpm@latest
22
+
23
+ # Install production dependencies first — playwright CLI is needed for browser install
24
+ COPY package.json pnpm-lock.yaml ./
25
+ RUN pnpm install --prod --frozen-lockfile
26
+
27
+ # Install Chromium + its system dependencies, then clean up apt caches
28
+ RUN pnpm exec playwright install chromium --with-deps \
29
+ && rm -rf /var/lib/apt/lists/*
30
+
31
+ COPY --from=builder /app/dist ./dist
32
+
33
+ VOLUME ["/reports"]
34
+
35
+ ENTRYPOINT ["node", "dist/cli.js"]
36
+ CMD ["--help"]
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # gdpr-cookie-scanner
2
2
 
3
- [![CI](https://github.com/Slashgear/gdpr-report/actions/workflows/ci.yml/badge.svg)](https://github.com/Slashgear/gdpr-report/actions/workflows/ci.yml)
3
+ [![CI](https://github.com/Slashgear/gdpr-cookie-scanner/actions/workflows/ci.yml/badge.svg)](https://github.com/Slashgear/gdpr-cookie-scanner/actions/workflows/ci.yml)
4
4
  [![npm version](https://img.shields.io/npm/v/@slashgear/gdpr-cookie-scanner?logo=npm)](https://www.npmjs.com/package/@slashgear/gdpr-cookie-scanner)
5
5
  [![npm downloads](https://img.shields.io/npm/dw/@slashgear/gdpr-cookie-scanner?logo=npm&label=downloads%2Fweek)](https://www.npmjs.com/package/@slashgear/gdpr-cookie-scanner)
6
6
  [![License: MIT](https://img.shields.io/npm/l/@slashgear/gdpr-cookie-scanner)](LICENSE)
7
- [![Node ≥ 20](https://img.shields.io/node/v/@slashgear/gdpr-cookie-scanner?logo=node.js)](https://nodejs.org)
7
+ [![Node ≥ 24](https://img.shields.io/node/v/@slashgear/gdpr-cookie-scanner?logo=node.js)](https://nodejs.org)
8
8
  [![TypeScript](https://img.shields.io/badge/TypeScript-5-3178c6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
9
9
  [![Playwright](https://img.shields.io/badge/Playwright-✓-45ba4b?logo=playwright&logoColor=white)](https://playwright.dev)
10
10
  [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
@@ -35,6 +35,20 @@ npx @slashgear/gdpr-cookie-scanner gdpr-scan scan https://example.com
35
35
  npx playwright install chromium
36
36
  ```
37
37
 
38
+ ## Docker
39
+
40
+ No Node.js required — pull and run directly:
41
+
42
+ ```bash
43
+ docker run --rm \
44
+ -v $(pwd)/reports:/reports \
45
+ ghcr.io/slashgear/gdpr-cookie-scanner \
46
+ scan https://example.com -o /reports
47
+ ```
48
+
49
+ The image is published to [GitHub Container Registry](https://ghcr.io/slashgear/gdpr-cookie-scanner)
50
+ on every release and supports `linux/amd64` and `linux/arm64`.
51
+
38
52
  ## Usage
39
53
 
40
54
  ```bash
@@ -43,13 +57,14 @@ gdpr-scan scan <url> [options]
43
57
 
44
58
  ### Options
45
59
 
46
- | Option | Default | Description |
47
- | ----------------------- | ---------------- | ------------------------------- |
48
- | `-o, --output <dir>` | `./gdpr-reports` | Output directory for the report |
49
- | `-t, --timeout <ms>` | `30000` | Navigation timeout |
50
- | `--no-screenshots` | | Disable screenshot capture |
51
- | `-l, --locale <locale>` | `fr-FR` | Browser locale |
52
- | `-v, --verbose` | | Show full stack trace on error |
60
+ | Option | Default | Description |
61
+ | ------------------------ | ---------------- | ------------------------------------------------------------- |
62
+ | `-o, --output <dir>` | `./gdpr-reports` | Output directory for the report |
63
+ | `-t, --timeout <ms>` | `30000` | Navigation timeout |
64
+ | `-f, --format <formats>` | `md,pdf` | Output formats: `md`, `html`, `json`, `pdf` (comma-separated) |
65
+ | `--no-screenshots` | — | Disable screenshot capture |
66
+ | `-l, --locale <locale>` | `fr-FR` | Browser locale |
67
+ | `-v, --verbose` | — | Show full stack trace on error |
53
68
 
54
69
  ### Examples
55
70
 
@@ -63,71 +78,39 @@ gdpr-scan scan https://example.com -o ./reports
63
78
  # Scan in English, without screenshots
64
79
  gdpr-scan scan https://example.com --locale en-US --no-screenshots
65
80
 
81
+ # Generate only an HTML report
82
+ gdpr-scan scan https://example.com -f html
83
+
84
+ # Generate all formats at once
85
+ gdpr-scan scan https://example.com -f md,html,json,pdf
86
+
66
87
  # Show the built-in tracker database
67
88
  gdpr-scan list-trackers
68
89
  ```
69
90
 
70
91
  ## How it works
71
92
 
72
- The scanner runs **4 phases** using a real Chromium browser (Playwright):
73
-
74
- 1. **Initial load** — The page is loaded without any interaction. All cookies and network requests are captured (`before-interaction`).
75
- 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).
76
- 3. **Reject test** — The "Reject" button is clicked. Cookies and requests are captured (`after-reject`).
77
- 4. **Accept test** — A new browser session (clean state) loads the page and clicks "Accept". Cookies and requests are captured (`after-accept`).
78
-
79
- ## Architecture
80
-
81
93
  ```mermaid
82
- flowchart TD
83
- CLI["⌨️ gdpr-scan CLI\n─────────────────\nURL · options"]
84
-
85
- CLI --> B
86
-
87
- subgraph B["🌐 Chromium browser — 4 sequential phases"]
88
- direction TB
89
- P1["Phase 1 — Load page\nCapture cookies & network requests\n(before-interaction)"]
90
- P2["Phase 2 — Detect consent modal\nCSS selectors · DOM heuristics\nExtract buttons, checkboxes, screenshots"]
91
- P3["Phase 3 — Click Reject (same session)\nCapture state (after-reject)"]
92
- P4["Phase 4 — Fresh session · Click Accept\nCapture state (after-accept)"]
93
- P1 --> P2 --> P3 --> P4
94
- end
95
-
96
- B --> C
97
-
98
- subgraph C["🔎 Classifiers"]
99
- direction LR
100
- CK["Cookie classifier\n─────────────\nPattern matching → category\n(analytics, ads, strictly-necessary…)"]
101
- NK["Network classifier\n─────────────\nTracker DB lookup\nPixel pattern matching"]
102
- end
103
-
104
- C --> A
105
-
106
- subgraph A["⚖️ Analyzers"]
107
- direction LR
108
- SC["Compliance scorer\n─────────────\n4 dimensions × 25 pts\n→ score 0–100, grade A–F"]
109
- DP["Dark pattern detector\n─────────────\nPre-ticked boxes · asymmetry\nMissing reject · misleading wording"]
110
- end
111
-
112
- A --> R
113
-
114
- subgraph R["📄 Report generator"]
115
- direction LR
116
- MD1["gdpr-report-*.md\nMain compliance report"]
117
- MD2["gdpr-checklist-*.md\nPer-rule checklist\nwith legal references"]
118
- MD3["gdpr-cookies-*.md\nDeduplicated cookie\ninventory"]
119
- PDF["gdpr-report-*.pdf\nMerged PDF with TOC\n& embedded screenshots"]
120
- end
94
+ flowchart LR
95
+ URL([URL]) --> Chromium[Chromium] --> Classify[Classify] --> Score[Score] --> Report[Report]
121
96
  ```
122
97
 
123
- The tool runs a **real Chromium browser** (via Playwright) through 4 isolated phases to capture the site's behaviour before any interaction, on modal detection, after rejection, and after acceptance. Raw data is then classified (cookies by name pattern, network requests against a tracker database), scored across 4 compliance dimensions, and rendered into 3 Markdown files plus a self-contained PDF.
98
+ A real Chromium browser loads the page, interacts with the consent modal (reject then accept in a fresh session), and captures cookies and network requests at each step. Results are classified, scored across 4 compliance dimensions, and rendered into one or more report files depending on `--format`.
124
99
 
125
- ## Generated report
100
+ ## Generated reports
126
101
 
127
- The Markdown report contains:
102
+ Each scan produces up to 4 file types in `<output-dir>/<hostname>/`:
103
+
104
+ | Format | Files | Description |
105
+ | ------ | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
106
+ | `md` | `gdpr-report-*.md`, `gdpr-checklist-*.md`, `gdpr-cookies-*.md` | Main compliance report, per-rule checklist with legal references, and deduplicated cookie inventory |
107
+ | `html` | `gdpr-report-*.html` | Self-contained styled report — grade badge, score cards, dark-pattern issues, cookie and tracker tables. Opens in any browser, no dependencies |
108
+ | `json` | `gdpr-report-*.json` | Full raw scan result for programmatic processing or CI integration |
109
+ | `pdf` | `gdpr-report-*.pdf` | PDF built from the Markdown reports via Playwright |
110
+
111
+ All formats contain:
128
112
 
129
113
  - **Global score** (0–100) and **grade** A/B/C/D/F
130
- - Executive summary
131
114
  - Modal analysis: buttons, checkboxes, font size, screenshots
132
115
  - Detected dark patterns (missing reject button, visual asymmetry, pre-ticked boxes, misleading wording…)
133
116
  - Cookie table before interaction, after reject, after accept
@@ -135,8 +118,6 @@ The Markdown report contains:
135
118
  - Targeted recommendations
136
119
  - Legal references (RGPD, ePrivacy directive, CEPD guidelines, CNIL 2022)
137
120
 
138
- The file is created at: `<output-dir>/gdpr-report-<domain>-<date>.md`
139
-
140
121
  ## Scoring
141
122
 
142
123
  The score is made up of 4 criteria (25 points each):
package/dist/cli.js CHANGED
@@ -19,6 +19,7 @@ program
19
19
  .option("--no-screenshots", "Disable screenshot capture")
20
20
  .option("-l, --locale <locale>", "Browser locale for language detection", "fr-FR")
21
21
  .option("-v, --verbose", "Show detailed output", false)
22
+ .option("-f, --format <formats>", "Output formats: md, html, json, pdf (comma-separated)", "md,pdf")
22
23
  .action(async (url, opts) => {
23
24
  console.log();
24
25
  console.log(chalk.bold.blue(" GDPR Cookie Scanner"));
@@ -29,6 +30,15 @@ program
29
30
  console.log(chalk.gray(` Target : ${url}`));
30
31
  console.log(chalk.gray(` Output : ${outputDir}`));
31
32
  console.log();
33
+ const validFormats = new Set(["md", "html", "json", "pdf"]);
34
+ const formats = opts.format
35
+ .split(",")
36
+ .map((f) => f.trim().toLowerCase())
37
+ .filter((f) => validFormats.has(f));
38
+ if (formats.length === 0) {
39
+ console.error(chalk.red(" Invalid --format value. Valid options: md, html, json, pdf"));
40
+ process.exit(2);
41
+ }
32
42
  const options = {
33
43
  url: normalizedUrl,
34
44
  outputDir,
@@ -36,6 +46,7 @@ program
36
46
  screenshots: opts.screenshots !== false,
37
47
  locale: opts.locale,
38
48
  verbose: opts.verbose,
49
+ formats,
39
50
  };
40
51
  const spinner = ora("Launching browser...").start();
41
52
  try {
@@ -47,7 +58,7 @@ program
47
58
  spinner.succeed("Scan complete");
48
59
  console.log();
49
60
  const generator = new ReportGenerator(options);
50
- const { reportPath, pdfPath } = await generator.generate(result);
61
+ const paths = await generator.generate(result);
51
62
  console.log(chalk.bold(` Compliance score: ${formatScore(result.compliance.total)} ${result.compliance.grade}`));
52
63
  console.log();
53
64
  if (result.compliance.issues.length > 0) {
@@ -61,8 +72,15 @@ program
61
72
  }
62
73
  console.log();
63
74
  }
64
- console.log(chalk.green(` Report saved: ${reportPath}`));
65
- console.log(chalk.green(` PDF saved: ${pdfPath}`));
75
+ const labels = {
76
+ md: "Markdown",
77
+ html: "HTML",
78
+ json: "JSON",
79
+ pdf: "PDF",
80
+ };
81
+ for (const [fmt, path] of Object.entries(paths)) {
82
+ console.log(chalk.green(` ${(labels[fmt] ?? fmt).padEnd(8)} ${path}`));
83
+ }
66
84
  console.log();
67
85
  process.exit(result.compliance.grade === "F" ? 1 : 0);
68
86
  }
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -3,10 +3,7 @@ import type { ScanOptions } from "../types.js";
3
3
  export declare class ReportGenerator {
4
4
  private readonly options;
5
5
  constructor(options: ScanOptions);
6
- generate(result: ScanResult): Promise<{
7
- reportPath: string;
8
- pdfPath: string;
9
- }>;
6
+ generate(result: ScanResult): Promise<Record<string, string>>;
10
7
  private buildHtmlBody;
11
8
  private inlineImages;
12
9
  private wrapHtml;
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,UAAU,EAKX,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,WAAW;IAE3C,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;YAmCtE,aAAa;YAkDb,YAAY;IAmC1B,OAAO,CAAC,QAAQ;IAgDhB,OAAO,CAAC,aAAa;IA6ErB,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,qBAAqB;IAkD7B,OAAO,CAAC,iBAAiB;IA2EzB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,iBAAiB;IA4BzB,OAAO,CAAC,8BAA8B;IAiBtC,OAAO,CAAC,mBAAmB;IAgD3B,OAAO,CAAC,oBAAoB;IA2D5B,OAAO,CAAC,qBAAqB;IA6H7B,OAAO,CAAC,cAAc;CAmRvB"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/report/generator.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,UAAU,EAKX,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,WAAW;IAE3C,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YA2DrD,aAAa;YAkDb,YAAY;IAmC1B,OAAO,CAAC,QAAQ;IAgDhB,OAAO,CAAC,aAAa;IA6ErB,OAAO,CAAC,eAAe;IAmBvB,OAAO,CAAC,qBAAqB;IAkD7B,OAAO,CAAC,iBAAiB;IA2EzB,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,kBAAkB;IAqC1B,OAAO,CAAC,iBAAiB;IA4BzB,OAAO,CAAC,8BAA8B;IAiBtC,OAAO,CAAC,mBAAmB;IAgD3B,OAAO,CAAC,oBAAoB;IA2D5B,OAAO,CAAC,qBAAqB;IA6H7B,OAAO,CAAC,cAAc;CAmRvB"}
@@ -5,6 +5,7 @@ import { promisify } from "util";
5
5
  import { fileURLToPath } from "url";
6
6
  import { Marked } from "marked";
7
7
  import { generatePdf } from "./pdf.js";
8
+ import { generateHtmlReport } from "./html.js";
8
9
  const execFileAsync = promisify(execFile);
9
10
  const oxfmtBin = join(dirname(fileURLToPath(import.meta.url)), "../../node_modules/.bin/oxfmt");
10
11
  export class ReportGenerator {
@@ -16,29 +17,50 @@ export class ReportGenerator {
16
17
  await mkdir(this.options.outputDir, { recursive: true });
17
18
  const hostname = new URL(result.url).hostname.replace(/^www\./, "");
18
19
  const date = new Date(result.scanDate).toISOString().split("T")[0];
19
- const filename = `gdpr-report-${hostname}-${date}.md`;
20
- const outputPath = join(this.options.outputDir, filename);
21
- const markdown = this.buildMarkdown(result);
22
- await writeFile(outputPath, markdown, "utf-8");
23
- await execFileAsync(oxfmtBin, [outputPath]).catch(() => { });
24
- const checklistFilename = `gdpr-checklist-${hostname}-${date}.md`;
25
- const checklistPath = join(this.options.outputDir, checklistFilename);
26
- const checklist = this.buildChecklist(result);
27
- await writeFile(checklistPath, checklist, "utf-8");
28
- await execFileAsync(oxfmtBin, [checklistPath]).catch(() => { });
29
- const cookiesFilename = `gdpr-cookies-${hostname}-${date}.md`;
30
- const cookiesPath = join(this.options.outputDir, cookiesFilename);
31
- const cookiesInventory = this.buildCookiesInventory(result);
32
- await writeFile(cookiesPath, cookiesInventory, "utf-8");
33
- await execFileAsync(oxfmtBin, [cookiesPath]).catch(() => { });
34
- const combined = [markdown, checklist, cookiesInventory].join("\n\n---\n\n");
35
- const rawBody = await this.buildHtmlBody(combined);
36
- const body = await this.inlineImages(rawBody, this.options.outputDir);
37
- const html = this.wrapHtml(body, hostname);
38
- const pdfFilename = `gdpr-report-${hostname}-${date}.pdf`;
39
- const pdfPath = join(this.options.outputDir, pdfFilename);
40
- await generatePdf(html, pdfPath);
41
- return { reportPath: outputPath, pdfPath };
20
+ const base = `gdpr-report-${hostname}-${date}`;
21
+ const formats = this.options.formats;
22
+ const paths = {};
23
+ // ── Markdown ──────────────────────────────────────────────────
24
+ if (formats.includes("md")) {
25
+ const mdPath = join(this.options.outputDir, `${base}.md`);
26
+ await writeFile(mdPath, this.buildMarkdown(result), "utf-8");
27
+ await execFileAsync(oxfmtBin, [mdPath]).catch(() => { });
28
+ paths.md = mdPath;
29
+ const checklistPath = join(this.options.outputDir, `gdpr-checklist-${hostname}-${date}.md`);
30
+ await writeFile(checklistPath, this.buildChecklist(result), "utf-8");
31
+ await execFileAsync(oxfmtBin, [checklistPath]).catch(() => { });
32
+ const cookiesPath = join(this.options.outputDir, `gdpr-cookies-${hostname}-${date}.md`);
33
+ await writeFile(cookiesPath, this.buildCookiesInventory(result), "utf-8");
34
+ await execFileAsync(oxfmtBin, [cookiesPath]).catch(() => { });
35
+ }
36
+ // ── HTML ──────────────────────────────────────────────────────
37
+ if (formats.includes("html")) {
38
+ const htmlPath = join(this.options.outputDir, `${base}.html`);
39
+ await writeFile(htmlPath, generateHtmlReport(result), "utf-8");
40
+ paths.html = htmlPath;
41
+ }
42
+ // ── JSON ──────────────────────────────────────────────────────
43
+ if (formats.includes("json")) {
44
+ const jsonPath = join(this.options.outputDir, `${base}.json`);
45
+ await writeFile(jsonPath, JSON.stringify(result, null, 2), "utf-8");
46
+ paths.json = jsonPath;
47
+ }
48
+ // ── PDF (via Markdown → HTML → Playwright) ────────────────────
49
+ if (formats.includes("pdf")) {
50
+ const markdown = paths.md
51
+ ? await import("fs/promises").then((m) => m.readFile(paths.md, "utf-8"))
52
+ : this.buildMarkdown(result);
53
+ const checklist = this.buildChecklist(result);
54
+ const cookiesInventory = this.buildCookiesInventory(result);
55
+ const combined = [markdown, checklist, cookiesInventory].join("\n\n---\n\n");
56
+ const rawBody = await this.buildHtmlBody(combined);
57
+ const body = await this.inlineImages(rawBody, this.options.outputDir);
58
+ const html = this.wrapHtml(body, hostname);
59
+ const pdfPath = join(this.options.outputDir, `${base}.pdf`);
60
+ await generatePdf(html, pdfPath);
61
+ paths.pdf = pdfPath;
62
+ }
63
+ return paths;
42
64
  }
43
65
  async buildHtmlBody(markdown) {
44
66
  const entries = [];