@slashgear/gdpr-cookie-scanner 3.6.0 → 3.8.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/.dockerignore +3 -0
- package/.gitattributes +1 -0
- package/.github/workflows/website.yml +80 -0
- package/CHANGELOG.md +52 -0
- package/CLAUDE.md +12 -1
- package/CONTRIBUTING.md +32 -4
- package/NEXT_STEPS.md +37 -3
- package/README.md +23 -0
- package/dist/analyzers/colour.d.ts +36 -0
- package/dist/analyzers/colour.d.ts.map +1 -0
- package/dist/analyzers/colour.js +75 -0
- package/dist/analyzers/colour.js.map +1 -0
- package/dist/analyzers/compliance.d.ts.map +1 -1
- package/dist/analyzers/compliance.js +24 -6
- package/dist/analyzers/compliance.js.map +1 -1
- package/dist/analyzers/tcf-decoder.d.ts +9 -0
- package/dist/analyzers/tcf-decoder.d.ts.map +1 -0
- package/dist/analyzers/tcf-decoder.js +123 -0
- package/dist/analyzers/tcf-decoder.js.map +1 -0
- package/dist/analyzers/wording.d.ts +1 -0
- package/dist/analyzers/wording.d.ts.map +1 -1
- package/dist/analyzers/wording.js +39 -0
- package/dist/analyzers/wording.js.map +1 -1
- package/dist/report/generator.d.ts +1 -0
- package/dist/report/generator.d.ts.map +1 -1
- package/dist/report/generator.js +71 -1
- package/dist/report/generator.js.map +1 -1
- package/dist/report/html.d.ts.map +1 -1
- package/dist/report/html.js +123 -0
- package/dist/report/html.js.map +1 -1
- package/dist/scanner/consent-modal.d.ts.map +1 -1
- package/dist/scanner/consent-modal.js +4 -2
- package/dist/scanner/consent-modal.js.map +1 -1
- package/dist/scanner/index.d.ts.map +1 -1
- package/dist/scanner/index.js +4 -0
- package/dist/scanner/index.js.map +1 -1
- package/dist/scanner/tcf.d.ts +9 -0
- package/dist/scanner/tcf.d.ts.map +1 -0
- package/dist/scanner/tcf.js +72 -0
- package/dist/scanner/tcf.js.map +1 -0
- package/dist/types.d.ts +26 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +7 -3
- package/pnpm-workspace.yaml +3 -0
- package/scripts/build-showcase.mjs +113 -0
- package/src/analyzers/colour.ts +89 -0
- package/src/analyzers/compliance.ts +35 -10
- package/src/analyzers/tcf-decoder.ts +130 -0
- package/src/analyzers/wording.ts +44 -0
- package/src/report/generator.ts +83 -1
- package/src/report/html.ts +146 -0
- package/src/scanner/consent-modal.ts +3 -1
- package/src/scanner/index.ts +5 -0
- package/src/scanner/tcf.ts +80 -0
- package/src/types.ts +29 -0
- package/tests/analyzers/colour.test.ts +187 -0
- package/tests/analyzers/compliance.test.ts +102 -0
- package/tests/analyzers/tcf-decoder.test.ts +292 -0
- package/tests/analyzers/wording.test.ts +38 -0
- package/tests/scanner/button-classification.test.ts +32 -0
- package/website/Dockerfile +55 -0
- package/website/node_modules/.bin/oxfmt +21 -0
- package/website/node_modules/.bin/oxlint +21 -0
- package/website/node_modules/.bin/tsc +21 -0
- package/website/node_modules/.bin/tsserver +21 -0
- package/website/node_modules/.bin/tsx +21 -0
- package/website/package.json +29 -0
- package/{docs → website/public}/index.html +88 -50
- package/website/public/reports/www.20minutes.fr/after-accept.png +3 -0
- package/website/public/reports/www.20minutes.fr/after-reject.png +3 -0
- package/website/public/reports/www.20minutes.fr/gdpr-report-20minutes.fr-2026-02-22.html +907 -0
- package/website/public/reports/www.20minutes.fr/modal-initial.png +3 -0
- package/website/public/reports/www.arte.tv/after-accept.png +3 -0
- package/website/public/reports/www.arte.tv/after-reject.png +3 -0
- package/website/public/reports/www.arte.tv/gdpr-report-arte.tv-2026-02-24.html +998 -0
- package/website/public/reports/www.arte.tv/modal-initial.png +3 -0
- package/website/public/reports/www.backmarket.fr/after-accept.png +3 -0
- package/website/public/reports/www.backmarket.fr/after-reject.png +3 -0
- package/website/public/reports/www.backmarket.fr/gdpr-report-backmarket.fr-2026-02-24.html +1530 -0
- package/website/public/reports/www.backmarket.fr/modal-initial.png +3 -0
- package/website/public/reports/www.deezer.com/after-accept.png +3 -0
- package/website/public/reports/www.deezer.com/after-reject.png +3 -0
- package/website/public/reports/www.deezer.com/gdpr-report-deezer.com-2026-02-22.html +1668 -0
- package/website/public/reports/www.deezer.com/modal-initial.png +3 -0
- package/website/public/reports/www.france.tv/after-accept.png +3 -0
- package/website/public/reports/www.france.tv/after-reject.png +3 -0
- package/website/public/reports/www.france.tv/gdpr-report-france.tv-2026-02-23.html +977 -0
- package/website/public/reports/www.france.tv/modal-initial.png +3 -0
- package/website/public/reports/www.m6.fr/after-accept.png +3 -0
- package/website/public/reports/www.m6.fr/after-reject.png +3 -0
- package/website/public/reports/www.m6.fr/gdpr-report-m6.fr-2026-02-28.html +1862 -0
- package/website/public/reports/www.m6.fr/modal-initial.png +3 -0
- package/website/public/reports/www.netflix.com/after-accept.png +3 -0
- package/website/public/reports/www.netflix.com/after-reject.png +3 -0
- package/website/public/reports/www.netflix.com/gdpr-report-netflix.com-2026-02-23.html +1051 -0
- package/website/public/reports/www.netflix.com/modal-initial.png +3 -0
- package/website/public/reports/www.radiofrance.fr/after-accept.png +3 -0
- package/website/public/reports/www.radiofrance.fr/after-reject.png +3 -0
- package/website/public/reports/www.radiofrance.fr/gdpr-report-radiofrance.fr-2026-02-24.html +1146 -0
- package/website/public/reports/www.radiofrance.fr/modal-initial.png +3 -0
- package/website/public/reports/www.tf1.fr/after-accept.png +3 -0
- package/website/public/reports/www.tf1.fr/after-reject.png +3 -0
- package/website/public/reports/www.tf1.fr/gdpr-report-tf1.fr-2026-02-23.html +1512 -0
- package/website/public/reports/www.tf1.fr/modal-initial.png +3 -0
- package/website/src/index.ts +15 -0
- package/website/src/security.ts +26 -0
- package/website/tsconfig.json +14 -0
- package/.github/workflows/pages.yml +0 -40
- package/docs/reports/github.com/after-accept.png +0 -0
- package/docs/reports/github.com/after-reject.png +0 -0
- package/docs/reports/github.com/gdpr-checklist-github.com-2026-02-22.md +0 -44
- package/docs/reports/github.com/gdpr-cookies-github.com-2026-02-22.md +0 -29
- package/docs/reports/github.com/gdpr-report-github.com-2026-02-22.md +0 -102
- package/docs/reports/github.com/gdpr-report-github.com-2026-02-22.pdf +0 -0
- package/docs/reports/gitlab.com/after-accept.png +0 -0
- package/docs/reports/gitlab.com/after-reject.png +0 -0
- package/docs/reports/gitlab.com/gdpr-checklist-gitlab.com-2026-02-22.md +0 -44
- package/docs/reports/gitlab.com/gdpr-cookies-gitlab.com-2026-02-22.md +0 -55
- package/docs/reports/gitlab.com/gdpr-report-gitlab.com-2026-02-22.md +0 -200
- package/docs/reports/gitlab.com/gdpr-report-gitlab.com-2026-02-22.pdf +0 -0
- package/docs/reports/gitlab.com/modal-initial.png +0 -0
- package/docs/reports/npmjs.com/after-accept.png +0 -0
- package/docs/reports/npmjs.com/after-reject.png +0 -0
- package/docs/reports/npmjs.com/gdpr-checklist-npmjs.com-2026-02-22.md +0 -44
- package/docs/reports/npmjs.com/gdpr-cookies-npmjs.com-2026-02-22.md +0 -25
- package/docs/reports/npmjs.com/gdpr-report-npmjs.com-2026-02-22.md +0 -88
- package/docs/reports/npmjs.com/gdpr-report-npmjs.com-2026-02-22.pdf +0 -0
- package/docs/reports/reddit.com/after-accept.png +0 -0
- package/docs/reports/reddit.com/after-reject.png +0 -0
- package/docs/reports/reddit.com/gdpr-checklist-reddit.com-2026-02-22.md +0 -44
- package/docs/reports/reddit.com/gdpr-cookies-reddit.com-2026-02-22.md +0 -33
- package/docs/reports/reddit.com/gdpr-report-reddit.com-2026-02-22.md +0 -148
- package/docs/reports/reddit.com/gdpr-report-reddit.com-2026-02-22.pdf +0 -0
- package/docs/reports/reddit.com/modal-initial.png +0 -0
- package/docs/reports/stackoverflow.com/after-accept.png +0 -0
- package/docs/reports/stackoverflow.com/after-reject.png +0 -0
- package/docs/reports/stackoverflow.com/gdpr-checklist-stackoverflow.com-2026-02-22.md +0 -44
- package/docs/reports/stackoverflow.com/gdpr-cookies-stackoverflow.com-2026-02-22.md +0 -67
- package/docs/reports/stackoverflow.com/gdpr-report-stackoverflow.com-2026-02-22.md +0 -206
- package/docs/reports/stackoverflow.com/gdpr-report-stackoverflow.com-2026-02-22.pdf +0 -0
- package/docs/reports/stackoverflow.com/modal-initial.png +0 -0
- package/docs/reports/www.afp.com/after-accept.png +0 -0
- package/docs/reports/www.afp.com/after-reject.png +0 -0
- package/docs/reports/www.afp.com/gdpr-checklist-afp.com-2026-02-22.md +0 -44
- package/docs/reports/www.afp.com/gdpr-cookies-afp.com-2026-02-22.md +0 -42
- package/docs/reports/www.afp.com/gdpr-report-afp.com-2026-02-22.md +0 -202
- package/docs/reports/www.afp.com/gdpr-report-afp.com-2026-02-22.pdf +0 -0
- package/docs/reports/www.afp.com/modal-initial.png +0 -0
- /package/{docs → website/public}/style.css +0 -0
package/.dockerignore
CHANGED
package/.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
website/public/reports/**/*.png filter=lfs diff=lfs merge=lfs -text
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: Website
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- "website/**"
|
|
8
|
+
- "pnpm-workspace.yaml"
|
|
9
|
+
- ".github/workflows/website.yml"
|
|
10
|
+
|
|
11
|
+
env:
|
|
12
|
+
SCW_DEFAULT_REGION: fr-par
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
build-and-push:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
packages: write
|
|
20
|
+
|
|
21
|
+
outputs:
|
|
22
|
+
image: ${{ steps.meta.outputs.image }}
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v6
|
|
26
|
+
with:
|
|
27
|
+
lfs: true
|
|
28
|
+
|
|
29
|
+
- name: Log in to GitHub Container Registry
|
|
30
|
+
uses: docker/login-action@v3
|
|
31
|
+
with:
|
|
32
|
+
registry: ghcr.io
|
|
33
|
+
username: ${{ github.actor }}
|
|
34
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
35
|
+
|
|
36
|
+
- name: Set up QEMU
|
|
37
|
+
uses: docker/setup-qemu-action@v3
|
|
38
|
+
|
|
39
|
+
- name: Set up Docker Buildx
|
|
40
|
+
uses: docker/setup-buildx-action@v3
|
|
41
|
+
|
|
42
|
+
- name: Compute image name
|
|
43
|
+
id: meta
|
|
44
|
+
run: |
|
|
45
|
+
echo "image=ghcr.io/$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')-website" >> "$GITHUB_OUTPUT"
|
|
46
|
+
|
|
47
|
+
- name: Build and push
|
|
48
|
+
uses: docker/build-push-action@v6
|
|
49
|
+
with:
|
|
50
|
+
context: .
|
|
51
|
+
file: website/Dockerfile
|
|
52
|
+
push: true
|
|
53
|
+
platforms: linux/amd64,linux/arm64
|
|
54
|
+
tags: |
|
|
55
|
+
${{ steps.meta.outputs.image }}:latest
|
|
56
|
+
${{ steps.meta.outputs.image }}:${{ github.sha }}
|
|
57
|
+
cache-from: type=gha
|
|
58
|
+
cache-to: type=gha,mode=max
|
|
59
|
+
|
|
60
|
+
deploy:
|
|
61
|
+
needs: build-and-push
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
environment: production
|
|
64
|
+
|
|
65
|
+
steps:
|
|
66
|
+
- name: Install Scaleway CLI
|
|
67
|
+
run: |
|
|
68
|
+
curl -fsSL https://raw.githubusercontent.com/scaleway/scaleway-cli/master/scripts/get.sh | sh
|
|
69
|
+
|
|
70
|
+
- name: Deploy to Scaleway Serverless Container
|
|
71
|
+
run: |
|
|
72
|
+
scw container container update ${{ secrets.SCW_CONTAINER_ID }} \
|
|
73
|
+
registry-image=${{ needs.build-and-push.outputs.image }}:${{ github.sha }} \
|
|
74
|
+
region=${{ env.SCW_DEFAULT_REGION }} \
|
|
75
|
+
redeploy=true
|
|
76
|
+
env:
|
|
77
|
+
SCW_ACCESS_KEY: ${{ secrets.SCW_ACCESS_KEY }}
|
|
78
|
+
SCW_SECRET_KEY: ${{ secrets.SCW_SECRET_KEY }}
|
|
79
|
+
SCW_DEFAULT_ORGANIZATION_ID: ${{ secrets.SCW_ORGANIZATION_ID }}
|
|
80
|
+
SCW_DEFAULT_PROJECT_ID: ${{ secrets.SCW_PROJECT_ID }}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,57 @@
|
|
|
1
1
|
# @slashgear/gdpr-cookie-scanner
|
|
2
2
|
|
|
3
|
+
## 3.8.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 88d944f: Introduce pnpm workspace monorepo and `website` package.
|
|
8
|
+
|
|
9
|
+
Converts the repo to a pnpm workspace and adds a new private `website` package (`@slashgear/gdpr-website`) built with Hono on Node.js. The package exposes a Docker image (`ghcr.io/slashgear/gdpr-website`) intended for deployment as a Scaleway Serverless Container at `gdpr.slashgear.dev`. A dedicated GitHub Actions workflow (`website.yml`) builds and pushes the image on every push to `main` that touches `website/**`, then redeploys the container via the Scaleway CLI.
|
|
10
|
+
|
|
11
|
+
## 3.7.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- 439a24d: feat: detect colour nudging dark pattern (green accept + grey/red reject)
|
|
16
|
+
|
|
17
|
+
Using a "positive" colour (green) on the accept button while the reject button
|
|
18
|
+
is visually de-emphasised (grey or red) is a documented dark pattern per
|
|
19
|
+
EDPB Guidelines 03/2022 § 3.3.3. The easyRefusal score now deducts 5 points
|
|
20
|
+
and surfaces a `nudging` warning when this pattern is detected.
|
|
21
|
+
|
|
22
|
+
A new `src/analyzers/colour.ts` module implements RGB→HSL conversion, perceptual
|
|
23
|
+
hue classification (green / red / grey / blue / neutral), and the nudging check.
|
|
24
|
+
32 unit tests cover the colour math and the compliance integration.
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- ee162de: fix: classify reject before accept to handle "continuer sans accepter" dark pattern
|
|
29
|
+
|
|
30
|
+
Buttons like "Continuer sans accepter" or "Continue without accepting" contain the
|
|
31
|
+
word "accept/accepter" and were being incorrectly classified as accept buttons because
|
|
32
|
+
accept patterns were tested first. Swapping the check order (reject → accept) ensures
|
|
33
|
+
these rejection-phrased labels are correctly identified, preventing false negatives on
|
|
34
|
+
a very common dark pattern used by French and other European sites.
|
|
35
|
+
|
|
36
|
+
- 00c926b: fix: deduct points for indirect reject button labels in easyRefusal score
|
|
37
|
+
|
|
38
|
+
Buttons like "Continuer sans accepter" or "Continue without accepting" are technically
|
|
39
|
+
reject buttons but use indirect wording that makes the refusal non-obvious to users.
|
|
40
|
+
This is a dark pattern covered by EDPB Guidelines 03/2022 (§ 3.3.3 — hiding choices).
|
|
41
|
+
|
|
42
|
+
The easyRefusal score now deducts 5 points when such indirect wording is detected,
|
|
43
|
+
and a warning issue is surfaced in the report alongside the existing detection logic.
|
|
44
|
+
|
|
45
|
+
- 9f69e31: feat: add IAB TCF detection and consent string decoding
|
|
46
|
+
|
|
47
|
+
The scanner now detects TCF (Transparency & Consent Framework) implementations on scanned pages.
|
|
48
|
+
It checks for the `__tcfapi` JavaScript API, the `__tcfapiLocator` iframe, and `euconsent-v2`/`euconsent` cookies.
|
|
49
|
+
When a consent string is found, it is decoded (TCF v1 and v2 core segments) to extract CMP identity,
|
|
50
|
+
declared purposes, legitimate interests, and special features opt-ins.
|
|
51
|
+
|
|
52
|
+
This data is purely informational and does not affect the compliance score.
|
|
53
|
+
It appears as a new section in both the HTML and Markdown reports.
|
|
54
|
+
|
|
3
55
|
## 3.6.0
|
|
4
56
|
|
|
5
57
|
### Minor Changes
|
package/CLAUDE.md
CHANGED
|
@@ -19,7 +19,18 @@ node dist/cli.js scan <url> -o ./reports --locale fr-FR --verbose
|
|
|
19
19
|
node dist/cli.js list-trackers
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
## Tests
|
|
23
|
+
|
|
24
|
+
Use [Vitest](https://vitest.dev/) for unit tests. Test files live in `tests/` and mirror the source structure (`tests/analyzers/`, `tests/classifiers/`, `tests/scanner/`, `tests/unit/`).
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pnpm test # run all tests
|
|
28
|
+
pnpm test:watch # watch mode
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**When to write tests:** every new feature or bug fix must include tests. Pure functions (decoders, classifiers, analyzers) are the primary targets. Browser-dependent code (scanner phases) is not unit-tested.
|
|
32
|
+
|
|
33
|
+
**When to update the README:** every new user-facing feature must be documented in `README.md` — CLI flags, output formats, new report sections, etc.
|
|
23
34
|
|
|
24
35
|
## Commit checklist
|
|
25
36
|
|
package/CONTRIBUTING.md
CHANGED
|
@@ -2,20 +2,32 @@
|
|
|
2
2
|
|
|
3
3
|
## Prerequisites
|
|
4
4
|
|
|
5
|
-
- Node.js ≥
|
|
5
|
+
- Node.js ≥ 24
|
|
6
6
|
- pnpm
|
|
7
|
-
- Playwright: `
|
|
7
|
+
- Playwright: `pnpm exec playwright install chromium`
|
|
8
|
+
|
|
9
|
+
## Repository structure
|
|
10
|
+
|
|
11
|
+
This is a **pnpm workspace monorepo** with two packages:
|
|
12
|
+
|
|
13
|
+
| Package | Path | Description |
|
|
14
|
+
| -------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------ |
|
|
15
|
+
| `@slashgear/gdpr-cookie-scanner` | `/` (root) | Published CLI tool |
|
|
16
|
+
| `@slashgear/gdpr-website` | `website/` | Hono web app — not published, deployed as a Docker image to [gdpr.slashgear.dev](https://gdpr.slashgear.dev) |
|
|
8
17
|
|
|
9
18
|
## Getting started
|
|
10
19
|
|
|
11
20
|
```bash
|
|
12
21
|
git clone https://github.com/Slashgear/gdpr-report.git
|
|
13
22
|
cd gdpr-report
|
|
14
|
-
pnpm install
|
|
15
|
-
pnpm build
|
|
23
|
+
pnpm install # installs all workspace packages
|
|
24
|
+
pnpm build # builds the CLI
|
|
16
25
|
|
|
17
26
|
# Run the CLI locally
|
|
18
27
|
node dist/cli.js scan https://example.com
|
|
28
|
+
|
|
29
|
+
# Run the website locally (http://localhost:8080)
|
|
30
|
+
pnpm website:dev
|
|
19
31
|
```
|
|
20
32
|
|
|
21
33
|
## Workflow
|
|
@@ -28,6 +40,7 @@ node dist/cli.js scan https://example.com
|
|
|
28
40
|
pnpm lint
|
|
29
41
|
pnpm typecheck
|
|
30
42
|
pnpm build
|
|
43
|
+
pnpm website:typecheck
|
|
31
44
|
```
|
|
32
45
|
4. Open a Pull Request targeting `main`
|
|
33
46
|
|
|
@@ -37,6 +50,21 @@ node dist/cli.js scan https://example.com
|
|
|
37
50
|
- **Consent modal detection** — improve CMP detection in `src/scanner/consent-modal.ts`
|
|
38
51
|
- **Compliance rules** — refine scoring rules in `src/analyzers/compliance.ts`
|
|
39
52
|
- **Report** — improve report rendering in `src/report/generator.ts`
|
|
53
|
+
- **Website** — Hono server and landing page in `website/src/` and `website/public/`
|
|
54
|
+
|
|
55
|
+
## Website — live reports showcase
|
|
56
|
+
|
|
57
|
+
The `website/public/reports/` directory contains pre-generated HTML reports (grades A–D only).
|
|
58
|
+
After adding new reports, run:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pnpm build:showcase # regenerates the report cards in website/public/index.html
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The script reads `website/public/reports/` and injects sorted report cards between the
|
|
65
|
+
`<!-- ── REPORTS_START ── -->` / `<!-- ── REPORTS_END ── -->` markers.
|
|
66
|
+
|
|
67
|
+
Report screenshots (`.png`) are tracked via **Git LFS** — make sure `git lfs` is installed before cloning.
|
|
40
68
|
|
|
41
69
|
## Releasing
|
|
42
70
|
|
package/NEXT_STEPS.md
CHANGED
|
@@ -20,13 +20,15 @@ Patterns that are explicitly listed in CNIL/EDPB guidelines but not yet detected
|
|
|
20
20
|
|
|
21
21
|
- **Cookie wall** — detect when the page content is blurred, hidden, or inaccessible before consent is given. Requires comparing DOM structure / visible content area before and after interaction.
|
|
22
22
|
|
|
23
|
-
- **Colour nudging** — accept button in green / reject button in grey or red is a documented dark pattern. Detect hue of each button's background colour and flag when accept is visually "positive" and reject is "negative".
|
|
23
|
+
- **Colour nudging** — accept button in green / reject button in grey or red is a documented dark pattern. Detect hue of each button's background colour and flag when accept is visually "positive" (green hue) and reject is "negative" (grey or red hue). Deduct from `easyRefusal`.
|
|
24
24
|
|
|
25
25
|
- **Scroll- or navigation-as-consent** — some sites display a banner and treat scrolling as acceptance. Would require simulating a scroll event and checking whether the banner disappears without an explicit click.
|
|
26
26
|
|
|
27
27
|
- **Bundled opt-outs** — some CMPs only provide "reject analytics + marketing" as a single checkbox rather than granular controls. Could be inferred from `hasGranularControls` combined with a single reject-all path.
|
|
28
28
|
|
|
29
|
-
- **
|
|
29
|
+
- **Legitimate interest abuse** — sites often declare legitimate interest for purposes 2–10 (advertising, profiling), bypassing consent entirely. When TCF is detected, flag when `purposesLegitimateInterest` includes ad-related IAB purposes (2, 3, 4, 7, 8, 9, 10).
|
|
30
|
+
|
|
31
|
+
- **Consent fatigue / re-prompting** — detect if the consent banner reappears on page reload after rejection. Requires a 5th scan phase: reload after reject and check for modal re-appearance.
|
|
30
32
|
|
|
31
33
|
---
|
|
32
34
|
|
|
@@ -40,6 +42,8 @@ Patterns that are explicitly listed in CNIL/EDPB guidelines but not yet detected
|
|
|
40
42
|
|
|
41
43
|
- **Firefox / WebKit** — Playwright supports both. Firefox in particular is relevant because some CMPs behave differently across engines. A `--browser chromium|firefox|webkit` flag would be low-effort to wire up.
|
|
42
44
|
|
|
45
|
+
- **Screenshot capture** — capture a screenshot of the consent modal at detection time and embed it in the HTML report as visual evidence. Useful for audit trails and regulatory submissions.
|
|
46
|
+
|
|
43
47
|
---
|
|
44
48
|
|
|
45
49
|
## Tracker & cookie classification
|
|
@@ -50,12 +54,42 @@ Patterns that are explicitly listed in CNIL/EDPB guidelines but not yet detected
|
|
|
50
54
|
|
|
51
55
|
## Report improvements
|
|
52
56
|
|
|
53
|
-
- **
|
|
57
|
+
- **JSON output format** — export the full `ScanResult` as a `gdpr-report-*.json` file. Enables programmatic consumption in CI pipelines, dashboards, and historical comparisons without parsing Markdown or HTML.
|
|
58
|
+
|
|
59
|
+
- **CMP fingerprinting** — identify which CMP is in use (OneTrust, Didomi, Axeptio, Cookiebot, TrustArc, Usercentrics…) from script URLs, global objects (`window.Didomi`, `window.OneTrust`…), or CSS class names. Enrich the report and correlate compliance scores with specific vendors.
|
|
54
60
|
|
|
55
61
|
- **Report localisation** — recommendations and section headings are hardcoded in French even when `--locale en-US` is used. The report language should follow `--locale`.
|
|
56
62
|
|
|
57
63
|
- **Historical comparison** — if a previous JSON report for the same hostname exists in the output directory, surface a diff (score delta, issues resolved/introduced). Useful for tracking progress over time.
|
|
58
64
|
|
|
65
|
+
- **TCF consent string cross-check** — after rejection, re-read the TCF consent string and verify `purposesConsent` is empty. Currently we capture the string before interaction only; a mismatch between the declared rejection and a non-empty consent string would be a critical finding.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Website (`docs/`)
|
|
70
|
+
|
|
71
|
+
The site is served as GitHub Pages from the `docs/` directory. Reports and cards are maintained manually.
|
|
72
|
+
|
|
73
|
+
- **HTML reports on the site** — generate `.html` reports into `docs/reports/<host>/` and point the "Live Reports" cards directly at the GitHub Pages URL instead of GitHub's Markdown renderer. Visitors stay on the site and get the full styled report experience.
|
|
74
|
+
|
|
75
|
+
- **Per-report `<title>` and `<meta description>`** — each HTML report should carry a specific page title (`"github.com — Grade F (15/100) | gdpr-cookie-scanner"`) and a meta description summarising the scan result. Currently the generator uses a generic title.
|
|
76
|
+
|
|
77
|
+
- **Open Graph / Twitter Card tags** — add `og:title`, `og:description`, `og:url` (and ideally `og:image` with a grade badge) to each report page so social shares display a meaningful preview card.
|
|
78
|
+
|
|
79
|
+
- **`sitemap.xml`** — auto-generated file listing the home page + every report page. Critical for search engine discovery of individual report pages. Can be a simple script that reads `docs/reports/` and writes the XML.
|
|
80
|
+
|
|
81
|
+
- **JSON-LD structured data** — `SoftwareApplication` schema on the home page; a `Dataset` or `Article` schema on each report page with `name`, `url`, `datePublished`, and a custom `score` property. Enables Google rich snippets.
|
|
82
|
+
|
|
83
|
+
- **`robots.txt`** — confirm (or add) a `robots.txt` that allows full crawling of `docs/` including report sub-directories.
|
|
84
|
+
|
|
85
|
+
- **Screenshots in the HTML report** — `after-reject.png` and `after-accept.png` are already captured alongside each report. Embedding them in the HTML report as visual evidence would make reports significantly more compelling.
|
|
86
|
+
|
|
87
|
+
- **Fix outdated copy** — the "What it checks" feature card still says "3 Markdown reports". Update to reflect the current single-file output and the HTML format.
|
|
88
|
+
|
|
89
|
+
- **"Submit a site" workflow** — a GitHub Issues template that lets users request a scan. A workflow picks it up, runs the scanner, commits the HTML report to `docs/reports/`, and posts a link back in the issue.
|
|
90
|
+
|
|
91
|
+
- **Dark mode** — `style.css` has no `@media (prefers-color-scheme: dark)` support. The design is simple enough that adding a dark palette would be straightforward.
|
|
92
|
+
|
|
59
93
|
---
|
|
60
94
|
|
|
61
95
|
## Testing
|
package/README.md
CHANGED
|
@@ -317,9 +317,32 @@ All formats contain:
|
|
|
317
317
|
- Detected dark patterns (missing reject button, visual asymmetry, pre-ticked boxes, misleading wording…)
|
|
318
318
|
- Cookie table before interaction, after reject, after accept
|
|
319
319
|
- Network tracker requests by phase
|
|
320
|
+
- **IAB TCF detection** — see below
|
|
320
321
|
- Targeted recommendations
|
|
321
322
|
- Legal references (RGPD, ePrivacy directive, CEPD guidelines, CNIL 2022)
|
|
322
323
|
|
|
324
|
+
### IAB TCF detection
|
|
325
|
+
|
|
326
|
+
When a site uses the [IAB Transparency & Consent Framework](https://iabeurope.eu/tcf-2-0/), the scanner automatically detects and decodes it. This is **informational only** — it does not affect the compliance score.
|
|
327
|
+
|
|
328
|
+
Detected signals:
|
|
329
|
+
|
|
330
|
+
- `window.__tcfapi` (TCF v2) or `window.__cmp` (TCF v1) JavaScript API
|
|
331
|
+
- `__tcfapiLocator` iframe
|
|
332
|
+
- `euconsent-v2` / `euconsent` cookie
|
|
333
|
+
|
|
334
|
+
When a consent string is found (via API or cookie), it is decoded to expose:
|
|
335
|
+
|
|
336
|
+
| Field | Description |
|
|
337
|
+
| --------------------------------- | ----------------------------------------------- |
|
|
338
|
+
| CMP ID & version | Which Consent Management Platform is in use |
|
|
339
|
+
| Consent language | Language the consent was collected in |
|
|
340
|
+
| Vendor list version | IAB GVL version at time of consent |
|
|
341
|
+
| Purposes with consent | IAB purposes explicitly consented to (IDs 1–11) |
|
|
342
|
+
| Purposes with legitimate interest | IAB purposes claimed under legitimate interest |
|
|
343
|
+
| Special features opted in | Geolocation, device scanning (IDs 1–2) |
|
|
344
|
+
| Publisher country | Country code of the publisher |
|
|
345
|
+
|
|
323
346
|
## Scoring
|
|
324
347
|
|
|
325
348
|
The score is made up of 4 criteria (25 points each):
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour nudging detection — EDPB Guidelines 03/2022 § 3.3.3.
|
|
3
|
+
*
|
|
4
|
+
* A "positive" colour (green = go, approve) on the accept button combined with
|
|
5
|
+
* a "negative" or neutral colour (grey, red) on the reject button steers users
|
|
6
|
+
* toward consent without technically hiding the refusal option.
|
|
7
|
+
*/
|
|
8
|
+
/** Returns [hue 0–360, saturation 0–100, lightness 0–100]. */
|
|
9
|
+
export declare function rgbToHsl(r: number, g: number, b: number): [number, number, number];
|
|
10
|
+
export type ButtonHue = "green" | "red" | "grey" | "blue" | "neutral";
|
|
11
|
+
/**
|
|
12
|
+
* Classify the perceptual "valence" of a colour:
|
|
13
|
+
* - green → positive, approval (h 80–165, s ≥ 25)
|
|
14
|
+
* - red → negative, danger (h ≤ 20 or ≥ 340, s ≥ 25)
|
|
15
|
+
* - grey → neutral / muted (s < 20)
|
|
16
|
+
* - blue → informational (h 195–265, s ≥ 25)
|
|
17
|
+
* - neutral → anything else
|
|
18
|
+
*
|
|
19
|
+
* Very dark (<10 L) or very light (>93 L) colours are treated as neutral
|
|
20
|
+
* because their hue carries little visual weight in a button context.
|
|
21
|
+
*/
|
|
22
|
+
export declare function classifyHue(r: number, g: number, b: number): ButtonHue;
|
|
23
|
+
export interface ColourNudgingResult {
|
|
24
|
+
acceptHue: ButtonHue | null;
|
|
25
|
+
rejectHue: ButtonHue | null;
|
|
26
|
+
/** True when accept is green and reject is grey or red. */
|
|
27
|
+
isNudging: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Detect colour nudging between the accept and reject buttons.
|
|
31
|
+
*
|
|
32
|
+
* Returns `isNudging: true` when the accept button has a "positive" hue (green)
|
|
33
|
+
* while the reject button has a "negative" or neutral hue (grey or red).
|
|
34
|
+
*/
|
|
35
|
+
export declare function detectColourNudging(acceptBg: string | null | undefined, rejectBg: string | null | undefined): ColourNudgingResult;
|
|
36
|
+
//# sourceMappingURL=colour.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colour.d.ts","sourceRoot":"","sources":["../../src/analyzers/colour.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,8DAA8D;AAC9D,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAsBlF;AAED,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAQtE;AAED,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,2DAA2D;IAC3D,SAAS,EAAE,OAAO,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAClC,mBAAmB,CAUrB"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour nudging detection — EDPB Guidelines 03/2022 § 3.3.3.
|
|
3
|
+
*
|
|
4
|
+
* A "positive" colour (green = go, approve) on the accept button combined with
|
|
5
|
+
* a "negative" or neutral colour (grey, red) on the reject button steers users
|
|
6
|
+
* toward consent without technically hiding the refusal option.
|
|
7
|
+
*/
|
|
8
|
+
function parseRgb(css) {
|
|
9
|
+
const m = css.match(/rgba?\(\s*(\d+),\s*(\d+),\s*(\d+)/);
|
|
10
|
+
if (!m)
|
|
11
|
+
return null;
|
|
12
|
+
return [parseInt(m[1], 10), parseInt(m[2], 10), parseInt(m[3], 10)];
|
|
13
|
+
}
|
|
14
|
+
/** Returns [hue 0–360, saturation 0–100, lightness 0–100]. */
|
|
15
|
+
export function rgbToHsl(r, g, b) {
|
|
16
|
+
const rr = r / 255, gg = g / 255, bb = b / 255;
|
|
17
|
+
const max = Math.max(rr, gg, bb), min = Math.min(rr, gg, bb);
|
|
18
|
+
const l = (max + min) / 2;
|
|
19
|
+
if (max === min)
|
|
20
|
+
return [0, 0, Math.round(l * 100)];
|
|
21
|
+
const d = max - min;
|
|
22
|
+
const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
23
|
+
let h;
|
|
24
|
+
switch (max) {
|
|
25
|
+
case rr:
|
|
26
|
+
h = (gg - bb) / d + (gg < bb ? 6 : 0);
|
|
27
|
+
break;
|
|
28
|
+
case gg:
|
|
29
|
+
h = (bb - rr) / d + 2;
|
|
30
|
+
break;
|
|
31
|
+
default:
|
|
32
|
+
h = (rr - gg) / d + 4;
|
|
33
|
+
}
|
|
34
|
+
return [Math.round(h * 60), Math.round(s * 100), Math.round(l * 100)];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Classify the perceptual "valence" of a colour:
|
|
38
|
+
* - green → positive, approval (h 80–165, s ≥ 25)
|
|
39
|
+
* - red → negative, danger (h ≤ 20 or ≥ 340, s ≥ 25)
|
|
40
|
+
* - grey → neutral / muted (s < 20)
|
|
41
|
+
* - blue → informational (h 195–265, s ≥ 25)
|
|
42
|
+
* - neutral → anything else
|
|
43
|
+
*
|
|
44
|
+
* Very dark (<10 L) or very light (>93 L) colours are treated as neutral
|
|
45
|
+
* because their hue carries little visual weight in a button context.
|
|
46
|
+
*/
|
|
47
|
+
export function classifyHue(r, g, b) {
|
|
48
|
+
const [h, s, l] = rgbToHsl(r, g, b);
|
|
49
|
+
if (l < 10 || l > 93)
|
|
50
|
+
return "neutral";
|
|
51
|
+
if (s < 20)
|
|
52
|
+
return "grey";
|
|
53
|
+
if (h >= 80 && h <= 165)
|
|
54
|
+
return "green";
|
|
55
|
+
if (h <= 20 || h >= 340)
|
|
56
|
+
return "red";
|
|
57
|
+
if (h >= 195 && h <= 265)
|
|
58
|
+
return "blue";
|
|
59
|
+
return "neutral";
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Detect colour nudging between the accept and reject buttons.
|
|
63
|
+
*
|
|
64
|
+
* Returns `isNudging: true` when the accept button has a "positive" hue (green)
|
|
65
|
+
* while the reject button has a "negative" or neutral hue (grey or red).
|
|
66
|
+
*/
|
|
67
|
+
export function detectColourNudging(acceptBg, rejectBg) {
|
|
68
|
+
const acceptRgb = acceptBg ? parseRgb(acceptBg) : null;
|
|
69
|
+
const rejectRgb = rejectBg ? parseRgb(rejectBg) : null;
|
|
70
|
+
const acceptHue = acceptRgb ? classifyHue(...acceptRgb) : null;
|
|
71
|
+
const rejectHue = rejectRgb ? classifyHue(...rejectRgb) : null;
|
|
72
|
+
const isNudging = acceptHue === "green" && (rejectHue === "grey" || rejectHue === "red");
|
|
73
|
+
return { acceptHue, rejectHue, isNudging };
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=colour.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"colour.js","sourceRoot":"","sources":["../../src/analyzers/colour.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +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;
|
|
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;AAIrB,UAAU,eAAe;IACvB,KAAK,EAAE,YAAY,CAAC;IACpB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,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,CAwQzE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { analyzeButtonWording, analyzeModalText } from "./wording.js";
|
|
2
|
+
import { detectColourNudging } from "./colour.js";
|
|
2
3
|
export function analyzeCompliance(input) {
|
|
3
4
|
const issues = [];
|
|
4
5
|
// Determine whether a consent mechanism is actually required
|
|
@@ -11,6 +12,9 @@ export function analyzeCompliance(input) {
|
|
|
11
12
|
...input.networkAfterAccept,
|
|
12
13
|
].some((r) => r.requiresConsent);
|
|
13
14
|
const consentRequired = hasNonEssentialCookies || hasNonEssentialTrackers;
|
|
15
|
+
// Run wording analysis once — modal may not be detected, so these can be null
|
|
16
|
+
const wordingResult = input.modal.detected ? analyzeButtonWording(input.modal.buttons) : null;
|
|
17
|
+
const textResult = input.modal.detected ? analyzeModalText(input.modal.text) : null;
|
|
14
18
|
// ── A. Consent validity (0-25) ────────────────────────────────
|
|
15
19
|
let consentValidity = 25;
|
|
16
20
|
if (!input.modal.detected && consentRequired) {
|
|
@@ -23,9 +27,7 @@ export function analyzeCompliance(input) {
|
|
|
23
27
|
consentValidity = 0;
|
|
24
28
|
}
|
|
25
29
|
else if (input.modal.detected) {
|
|
26
|
-
// Wording analysis
|
|
27
|
-
const wordingResult = analyzeButtonWording(input.modal.buttons);
|
|
28
|
-
const textResult = analyzeModalText(input.modal.text);
|
|
30
|
+
// Wording analysis (wordingResult / textResult hoisted above)
|
|
29
31
|
issues.push(...wordingResult.issues, ...textResult.issues);
|
|
30
32
|
// Pre-ticked checkboxes
|
|
31
33
|
const preTicked = input.modal.checkboxes.filter((c) => c.isCheckedByDefault);
|
|
@@ -86,6 +88,23 @@ export function analyzeCompliance(input) {
|
|
|
86
88
|
easyRefusal -= 5;
|
|
87
89
|
}
|
|
88
90
|
}
|
|
91
|
+
// Indirect reject label ("continuer sans accepter", "continue without accepting"…)
|
|
92
|
+
if (wordingResult?.hasIndirectRejectLabel) {
|
|
93
|
+
easyRefusal -= 5;
|
|
94
|
+
}
|
|
95
|
+
// Colour nudging: green accept + grey/red reject
|
|
96
|
+
if (acceptButton && rejectButton) {
|
|
97
|
+
const { isNudging, acceptHue, rejectHue } = detectColourNudging(acceptButton.backgroundColor, rejectButton.backgroundColor);
|
|
98
|
+
if (isNudging) {
|
|
99
|
+
issues.push({
|
|
100
|
+
type: "nudging",
|
|
101
|
+
severity: "warning",
|
|
102
|
+
description: 'Accept button uses a "positive" colour (green) while reject is visually de-emphasised',
|
|
103
|
+
evidence: `Accept: ${acceptButton.backgroundColor} (${acceptHue}), Reject: ${rejectButton.backgroundColor} (${rejectHue}) — EDPB Guidelines 03/2022 § 3.3.3`,
|
|
104
|
+
});
|
|
105
|
+
easyRefusal -= 5;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
89
108
|
// Font size asymmetry
|
|
90
109
|
if (acceptButton?.fontSize && rejectButton?.fontSize) {
|
|
91
110
|
if (acceptButton.fontSize > rejectButton.fontSize * 1.3) {
|
|
@@ -142,9 +161,8 @@ export function analyzeCompliance(input) {
|
|
|
142
161
|
transparency -= 10;
|
|
143
162
|
}
|
|
144
163
|
// Already deducted in consentValidity for missing info
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
transparency -= wordingResult.missingInfo.length * 3;
|
|
164
|
+
if (textResult.missingInfo.length > 0) {
|
|
165
|
+
transparency -= textResult.missingInfo.length * 3;
|
|
148
166
|
}
|
|
149
167
|
// No privacy policy link in the modal
|
|
150
168
|
if (!input.modal.privacyPolicyUrl) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compliance.js","sourceRoot":"","sources":["../../src/analyzers/compliance.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"compliance.js","sourceRoot":"","sources":["../../src/analyzers/compliance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { TcfConsentString } from "../types.js";
|
|
2
|
+
export declare const IAB_PURPOSES: Record<number, string>;
|
|
3
|
+
export declare const IAB_SPECIAL_FEATURES: Record<number, string>;
|
|
4
|
+
/**
|
|
5
|
+
* Decode a TCF v1 or v2 consent string (core segment only).
|
|
6
|
+
* Returns null if decoding fails or if the version is not 1 or 2.
|
|
7
|
+
*/
|
|
8
|
+
export declare function decodeTcfConsentString(raw: string): TcfConsentString | null;
|
|
9
|
+
//# sourceMappingURL=tcf-decoder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tcf-decoder.d.ts","sourceRoot":"","sources":["../../src/analyzers/tcf-decoder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAEpD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAY/C,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAGvD,CAAC;AAwCF;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAkE3E"}
|