@slashgear/gdpr-cookie-scanner 3.2.2 → 3.4.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/.github/workflows/ci.yml +20 -1
- package/.github/workflows/release.yml +1 -1
- package/CHANGELOG.md +159 -0
- package/NEXT_STEPS.md +0 -35
- package/README.md +58 -12
- package/dist/classifiers/cookie-classifier.d.ts +1 -1
- package/dist/classifiers/cookie-classifier.d.ts.map +1 -1
- package/dist/classifiers/cookie-classifier.js +3 -2
- package/dist/classifiers/cookie-classifier.js.map +1 -1
- package/dist/classifiers/network-classifier.d.ts +1 -1
- package/dist/classifiers/network-classifier.d.ts.map +1 -1
- package/dist/classifiers/network-classifier.js +5 -3
- package/dist/classifiers/network-classifier.js.map +1 -1
- package/dist/classifiers/tracker-list.d.ts.map +1 -1
- package/dist/classifiers/tracker-list.js +29 -1
- package/dist/classifiers/tracker-list.js.map +1 -1
- package/dist/cli.js +37 -4
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/scanner/browser.d.ts.map +1 -1
- package/dist/scanner/browser.js +17 -7
- package/dist/scanner/browser.js.map +1 -1
- package/dist/scanner/consent-modal.d.ts +14 -1
- package/dist/scanner/consent-modal.d.ts.map +1 -1
- package/dist/scanner/consent-modal.js +148 -32
- package/dist/scanner/consent-modal.js.map +1 -1
- package/dist/scanner/cookies.d.ts +1 -1
- package/dist/scanner/cookies.d.ts.map +1 -1
- package/dist/scanner/cookies.js +2 -2
- package/dist/scanner/cookies.js.map +1 -1
- package/dist/scanner/index.d.ts.map +1 -1
- package/dist/scanner/index.js +17 -8
- package/dist/scanner/index.js.map +1 -1
- package/dist/scanner/network.d.ts +1 -1
- package/dist/scanner/network.d.ts.map +1 -1
- package/dist/scanner/network.js +2 -2
- package/dist/scanner/network.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/classifiers/cookie-classifier.ts +8 -2
- package/src/classifiers/network-classifier.ts +9 -3
- package/src/classifiers/tracker-list.ts +29 -1
- package/src/cli.ts +60 -5
- package/src/index.ts +11 -2
- package/src/scanner/browser.ts +23 -9
- package/src/scanner/consent-modal.ts +169 -32
- package/src/scanner/cookies.ts +2 -1
- package/src/scanner/index.ts +41 -8
- package/src/scanner/network.ts +2 -2
- package/src/types.ts +8 -0
- package/tests/scanner/button-classification.test.ts +241 -0
- package/tests/scanner/contrast-ratio.test.ts +172 -0
package/.github/workflows/ci.yml
CHANGED
|
@@ -43,4 +43,23 @@ jobs:
|
|
|
43
43
|
run: pnpm exec playwright install chromium --with-deps
|
|
44
44
|
|
|
45
45
|
- name: Test
|
|
46
|
-
|
|
46
|
+
id: test
|
|
47
|
+
run: |
|
|
48
|
+
start=$(date +%s)
|
|
49
|
+
pnpm test
|
|
50
|
+
echo "duration=$(( $(date +%s) - start ))" >> "$GITHUB_OUTPUT"
|
|
51
|
+
|
|
52
|
+
- name: Record test suite duration
|
|
53
|
+
if: always()
|
|
54
|
+
run: |
|
|
55
|
+
duration="${{ steps.test.outputs.duration }}"
|
|
56
|
+
echo "## Test suite duration" >> "$GITHUB_STEP_SUMMARY"
|
|
57
|
+
echo "" >> "$GITHUB_STEP_SUMMARY"
|
|
58
|
+
echo "| Suite | Duration |" >> "$GITHUB_STEP_SUMMARY"
|
|
59
|
+
echo "| ----- | -------- |" >> "$GITHUB_STEP_SUMMARY"
|
|
60
|
+
echo "| unit + e2e | ${duration}s |" >> "$GITHUB_STEP_SUMMARY"
|
|
61
|
+
if [ "${duration:-0}" -gt 300 ]; then
|
|
62
|
+
echo "" >> "$GITHUB_STEP_SUMMARY"
|
|
63
|
+
echo "> [!WARNING]" >> "$GITHUB_STEP_SUMMARY"
|
|
64
|
+
echo "> Suite exceeded 300 s — possible performance regression." >> "$GITHUB_STEP_SUMMARY"
|
|
65
|
+
fi
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,164 @@
|
|
|
1
1
|
# @slashgear/gdpr-cookie-scanner
|
|
2
2
|
|
|
3
|
+
## 3.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 39794dc: Crop consent modal screenshot to the element; make after-reject/accept screenshots opt-in.
|
|
8
|
+
|
|
9
|
+
Two behaviour changes:
|
|
10
|
+
|
|
11
|
+
**Modal screenshot is now always captured (cropped)**
|
|
12
|
+
The consent modal screenshot (`modal-initial.png`) is taken whenever a modal
|
|
13
|
+
is detected and `outputDir` is set, regardless of the `--screenshots` flag.
|
|
14
|
+
The screenshot is clipped to the bounding box of the modal element instead of
|
|
15
|
+
capturing the full viewport, giving a tighter, more readable image. If the
|
|
16
|
+
bounding box cannot be determined (rare), it falls back to the viewport
|
|
17
|
+
screenshot.
|
|
18
|
+
|
|
19
|
+
**`--no-screenshots` replaced by `--screenshots`**
|
|
20
|
+
Previously all three screenshots were enabled by default and `--no-screenshots`
|
|
21
|
+
opted out. Now only the modal screenshot is taken by default; passing
|
|
22
|
+
`--screenshots` additionally captures `after-reject.png` and `after-accept.png`
|
|
23
|
+
(full viewport, as before). The `screenshots` field in `ScanOptions` / the
|
|
24
|
+
programmatic API retains the same type (`boolean`) with updated semantics:
|
|
25
|
+
`false` (default) = modal only; `true` = modal + after-reject + after-accept.
|
|
26
|
+
|
|
27
|
+
- 6a71a18: Add multi-language consent button detection (de, es, it, nl, pl, pt).
|
|
28
|
+
|
|
29
|
+
Previously, button classification only covered French and English, causing
|
|
30
|
+
false "no reject button" findings on sites served in other EU locales.
|
|
31
|
+
|
|
32
|
+
The fix has two parts:
|
|
33
|
+
|
|
34
|
+
1. **Locale-aware pattern map** — `ACCEPT_PATTERNS` / `REJECT_PATTERNS` /
|
|
35
|
+
`PREFERENCES_PATTERNS` are replaced by a `PATTERNS_BY_LOCALE` map keyed by
|
|
36
|
+
BCP 47 primary subtag, covering `en`, `fr`, `de`, `es`, `it`, `nl`, `pl`,
|
|
37
|
+
`pt`. Polish patterns use a negative lookbehind instead of `\b` because
|
|
38
|
+
several Polish words end in non-ASCII characters (ć, ę, ó) that fall
|
|
39
|
+
outside JS `\w`.
|
|
40
|
+
|
|
41
|
+
2. **`<html lang>` detection** — `detectConsentModal` now reads the page's
|
|
42
|
+
declared language from `document.documentElement.lang` and normalises it to
|
|
43
|
+
a primary subtag (e.g. `"de-DE"` → `"de"`). When the language is
|
|
44
|
+
recognised, only that locale's patterns plus English (universal fallback)
|
|
45
|
+
are tested. When the language is missing or unsupported, all available
|
|
46
|
+
patterns are tried — preserving the previous behaviour for unknown pages.
|
|
47
|
+
|
|
48
|
+
The public export `classifyButtonText(text, lang)` is added for testing and
|
|
49
|
+
programmatic use; 56 new unit tests cover every supported locale.
|
|
50
|
+
|
|
51
|
+
### Patch Changes
|
|
52
|
+
|
|
53
|
+
- ceed240: Add unit tests for `computeContrastRatio`, `parseRgb`, and `relativeLuminance`.
|
|
54
|
+
|
|
55
|
+
These three pure functions in `consent-modal.ts` were previously only exercised
|
|
56
|
+
indirectly through the E2E suite. The new test file
|
|
57
|
+
(`tests/scanner/contrast-ratio.test.ts`) covers the happy path, edge cases
|
|
58
|
+
(identical colours, fully transparent rgba, non-integer ratios), and documents
|
|
59
|
+
the known limitations — named colours (`white`, `black`) and hex values (`#fff`)
|
|
60
|
+
return `null` until the parser is extended.
|
|
61
|
+
|
|
62
|
+
The functions are now exported so they can be imported by the test suite without
|
|
63
|
+
moving them to a separate module.
|
|
64
|
+
|
|
65
|
+
- df24a36: Fix consent modal detection for CMPs that start hidden (e.g. Axeptio).
|
|
66
|
+
|
|
67
|
+
`MODAL_SELECTORS` was a single flat list where every candidate was required to
|
|
68
|
+
pass `isVisible()`. CMPs such as Axeptio inject their overlay as `display:none`
|
|
69
|
+
during initialisation and reveal it via JS animation a few hundred milliseconds
|
|
70
|
+
later. The visibility check caused the scanner to skip `#axeptio_overlay` and
|
|
71
|
+
fall through to the first matching generic heuristic (e.g. `[id*='consent']`),
|
|
72
|
+
which could be a completely unrelated element.
|
|
73
|
+
|
|
74
|
+
The list is now split into two:
|
|
75
|
+
|
|
76
|
+
- **`CMP_SELECTORS`** — precise, platform-specific identifiers. DOM presence
|
|
77
|
+
alone is treated as a reliable signal. Once the element is found the scanner
|
|
78
|
+
waits up to 3 s for it to become visible (so button extraction sees an
|
|
79
|
+
interactive state) but proceeds regardless, preventing a slow CMP from
|
|
80
|
+
silently falling back to a wrong heuristic.
|
|
81
|
+
- **`HEURISTIC_SELECTORS`** — broad patterns that could match unrelated
|
|
82
|
+
elements. Visibility is still required to avoid false positives.
|
|
83
|
+
|
|
84
|
+
- f9efe0b: Normalise button text whitespace before classification.
|
|
85
|
+
|
|
86
|
+
`classifyButtonType` previously received raw `textContent` that had only been
|
|
87
|
+
`.trim()`-ed. CMP HTML templates frequently embed ` ` (U+00A0), newlines,
|
|
88
|
+
or tabs inside button labels, causing pattern matching to silently fail.
|
|
89
|
+
|
|
90
|
+
A `normalizeText` helper now collapses any whitespace sequence (including
|
|
91
|
+
U+00A0 and all Unicode spaces covered by JS `\s`) into a single ASCII space
|
|
92
|
+
before the regex is tested. The normalisation is applied in two places:
|
|
93
|
+
|
|
94
|
+
- `classifyButtonText` (public export) — defensive normalisation of any caller-
|
|
95
|
+
provided string.
|
|
96
|
+
- `extractButtons` — the raw `el.textContent()` result is normalised before
|
|
97
|
+
being stored in `ConsentButton.text` and before classification, so the
|
|
98
|
+
report also shows the cleaned label.
|
|
99
|
+
|
|
100
|
+
## 3.3.0
|
|
101
|
+
|
|
102
|
+
### Minor Changes
|
|
103
|
+
|
|
104
|
+
- 7c128b9: Add `--json-summary` flag to emit a machine-readable JSON line on stdout after the scan.
|
|
105
|
+
|
|
106
|
+
CI pipelines that use `--fail-on` previously had to parse the full report file to extract
|
|
107
|
+
score, grade, and issue details programmatically. With `--json-summary`, a single JSON line
|
|
108
|
+
is written to stdout at the end of every scan (pass or fail), containing the URL, score,
|
|
109
|
+
grade, pass/fail status, threshold, score breakdown, and issue list.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
gdpr-scan scan https://example.com --fail-on B --json-summary \
|
|
113
|
+
| grep '^{' | jq '{grade: .grade, passed: .passed}'
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
- faa6da8: Add `--strict` flag: treat unrecognised cookies and unknown third-party requests as requiring consent.
|
|
117
|
+
|
|
118
|
+
By default, cookies and network requests that do not match any known pattern are assumed
|
|
119
|
+
to be first-party and consent-free (conservative). This avoids false positives but lets
|
|
120
|
+
obfuscated or emerging tracking cookies slip through undetected.
|
|
121
|
+
|
|
122
|
+
With `--strict`, the classifier falls back to `requiresConsent: true` for anything
|
|
123
|
+
unrecognised, making the scan more aggressive. Use this when auditing sites where you
|
|
124
|
+
suspect custom tracking solutions not yet in the pattern database.
|
|
125
|
+
|
|
126
|
+
CLI: `gdpr-scan scan https://example.com --strict`
|
|
127
|
+
|
|
128
|
+
Programmatic API: `await scan(url, { strict: true })`
|
|
129
|
+
|
|
130
|
+
- df30f31: Add `--viewport` option to scan with desktop, tablet, or mobile browser dimensions.
|
|
131
|
+
|
|
132
|
+
All scans previously used a fixed 1280×900 desktop viewport. Many consent banners
|
|
133
|
+
have different layouts on mobile (bottom sheets, full-screen overlays) that can only
|
|
134
|
+
be tested with the correct viewport and user-agent.
|
|
135
|
+
|
|
136
|
+
Three presets are available:
|
|
137
|
+
|
|
138
|
+
| Preset | Dimensions | User-agent |
|
|
139
|
+
| --------- | ---------- | ---------------- |
|
|
140
|
+
| `desktop` | 1280×900 | Chrome on macOS |
|
|
141
|
+
| `tablet` | 768×1024 | Safari on iPad |
|
|
142
|
+
| `mobile` | 390×844 | Safari on iPhone |
|
|
143
|
+
|
|
144
|
+
CLI: `gdpr-scan scan https://example.com --viewport mobile`
|
|
145
|
+
|
|
146
|
+
Programmatic API: `await scan(url, { viewport: 'mobile' })`
|
|
147
|
+
|
|
148
|
+
Default is `desktop` — no change to existing behaviour.
|
|
149
|
+
|
|
150
|
+
### Patch Changes
|
|
151
|
+
|
|
152
|
+
- 6eb4c3d: Add Fathom, Simple Analytics, Cabin, and Pirsch to the consent-exempt analytics list.
|
|
153
|
+
|
|
154
|
+
These tools are cookieless, collect no personal data, and do not cross-reference data
|
|
155
|
+
with other processing — meeting the CNIL conditions for analytics exempt from the
|
|
156
|
+
ePrivacy consent requirement. Only Plausible was previously listed; sites using any
|
|
157
|
+
of these four tools were incorrectly flagged as loading trackers before consent.
|
|
158
|
+
|
|
159
|
+
Domains added: `cdn.usefathom.com`, `scripts.simpleanalyticscdn.com`,
|
|
160
|
+
`api.simpleanalytics.io`, `scripts.withcabin.com`, `api.pirsch.io`.
|
|
161
|
+
|
|
3
162
|
## 3.2.2
|
|
4
163
|
|
|
5
164
|
### Patch Changes
|
package/NEXT_STEPS.md
CHANGED
|
@@ -14,25 +14,6 @@ Ideas and improvement areas for `gdpr-cookie-scanner`. Not a roadmap — pick wh
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
-
## Language support
|
|
18
|
-
|
|
19
|
-
Currently only French and English patterns are covered. A lot of EU sites served in other locales will get false "no reject button" results.
|
|
20
|
-
|
|
21
|
-
Languages to add at minimum (ordered by EU population / GDPR enforcement activity):
|
|
22
|
-
|
|
23
|
-
| Locale | Reject examples | Accept examples |
|
|
24
|
-
| ------- | ------------------------ | -------------------------------- |
|
|
25
|
-
| `de-DE` | Ablehnen, Alle ablehnen | Alle akzeptieren, Zustimmen |
|
|
26
|
-
| `es-ES` | Rechazar, Rechazar todo | Aceptar, Aceptar todo |
|
|
27
|
-
| `it-IT` | Rifiuta, Rifiuta tutto | Accetta, Accetta tutto |
|
|
28
|
-
| `nl-NL` | Weigeren, Alles weigeren | Accepteren, Alles accepteren |
|
|
29
|
-
| `pl-PL` | Odrzuć, Odrzuć wszystkie | Zaakceptuj, Zaakceptuj wszystkie |
|
|
30
|
-
| `pt-PT` | Rejeitar, Rejeitar tudo | Aceitar, Aceitar tudo |
|
|
31
|
-
|
|
32
|
-
The patterns live in `src/scanner/consent-modal.ts`. A locale-aware pattern map keyed by BCP 47 tag would be cleaner than one giant regex.
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
17
|
## Dark pattern detection gaps
|
|
37
18
|
|
|
38
19
|
Patterns that are explicitly listed in CNIL/EDPB guidelines but not yet detected:
|
|
@@ -53,8 +34,6 @@ Patterns that are explicitly listed in CNIL/EDPB guidelines but not yet detected
|
|
|
53
34
|
|
|
54
35
|
- **Configurable wait times** — the `waitForTimeout(2000)` / `waitForTimeout(3000)` values in `scanner/index.ts` are hardcoded. Some pages need more time (heavy SPAs); others waste time. Expose them as `ScanOptions` fields with sensible defaults.
|
|
55
36
|
|
|
56
|
-
- **Mobile viewport** — all scans use a 1280×900 desktop viewport. Many consent banners have different layouts on mobile (bottom sheets, full-screen overlays). A `--viewport mobile|tablet|desktop` option would let users test the responsive behaviour.
|
|
57
|
-
|
|
58
37
|
- **Multi-page scanning** — run the scan across several URLs of the same domain (home page + one inner page) and merge results. Useful for detecting banners that only appear on the first visit, or trackers that fire on specific pages.
|
|
59
38
|
|
|
60
39
|
- **Batch mode** — accept a list of URLs (file or `--url` repeated) and produce a summary report alongside individual reports. Useful for agencies auditing multiple client sites.
|
|
@@ -67,10 +46,6 @@ Patterns that are explicitly listed in CNIL/EDPB guidelines but not yet detected
|
|
|
67
46
|
|
|
68
47
|
- **Tracker database size** — the hardcoded list (`src/classifiers/tracker-list.ts`) currently has ~55 entries. The monthly auto-update workflow merges Disconnect.me and DuckDuckGo Tracker Radar entries but the result needs review before merge. Consider auto-approving updates that only add entries (no removals or category changes).
|
|
69
48
|
|
|
70
|
-
- **Cookieless analytics exemptions** — only Plausible is marked `consentRequired: false`. Fathom, Simple Analytics, Cabin, and Pirsch have the same privacy-by-design model and should be listed.
|
|
71
|
-
|
|
72
|
-
- **Unknown first-party cookies default** — unknown cookies default to `requiresConsent: false` (conservative). This means new or obfuscated tracking cookies slip through. Consider a stricter mode (`--strict`) that flips the default to `requiresConsent: true` for unrecognised cookies.
|
|
73
|
-
|
|
74
49
|
---
|
|
75
50
|
|
|
76
51
|
## Report improvements
|
|
@@ -81,24 +56,14 @@ Patterns that are explicitly listed in CNIL/EDPB guidelines but not yet detected
|
|
|
81
56
|
|
|
82
57
|
- **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.
|
|
83
58
|
|
|
84
|
-
- **Structured exit summary** — when `--fail-on` causes an exit 1, emit a machine-readable summary to stdout (JSON lines) so CI systems can parse it without reading the full report file.
|
|
85
|
-
|
|
86
59
|
---
|
|
87
60
|
|
|
88
61
|
## Testing
|
|
89
62
|
|
|
90
63
|
- **Report output tests** — no tests currently validate the content of generated Markdown, HTML, or JSON files. Add snapshot tests for at least the JSON output and spot-checks for key sections in HTML.
|
|
91
64
|
|
|
92
|
-
- **Contrast ratio unit tests** — `computeContrastRatio` is pure logic but only exercised through E2E tests. Deserves a dedicated unit test file covering edge cases (identical colours, transparent backgrounds, named/hex inputs once supported).
|
|
93
|
-
|
|
94
|
-
- **Locale-specific button pattern tests** — once multi-language patterns land, add a test case per locale to prevent regressions.
|
|
95
|
-
|
|
96
|
-
- **Performance baseline** — the E2E suite takes ~2 minutes. As features grow, tracking suite duration in CI would help catch regressions early.
|
|
97
|
-
|
|
98
65
|
---
|
|
99
66
|
|
|
100
67
|
## Infrastructure
|
|
101
68
|
|
|
102
|
-
- **Node version consistency** — CI runs Node 24 but the release workflow runs Node 22. Align to a single version across all workflows.
|
|
103
|
-
|
|
104
69
|
- **Pre-built GitHub Action** — a dedicated `uses: slashgear/gdpr-cookie-scanner-action@v1` action would make the GitHub Actions integration one step instead of running the Docker container manually. The action wrapper would handle artifact upload and PR comments automatically.
|
package/README.md
CHANGED
|
@@ -63,15 +63,18 @@ gdpr-scan scan <url> [options]
|
|
|
63
63
|
|
|
64
64
|
### Options
|
|
65
65
|
|
|
66
|
-
| Option | Default | Description
|
|
67
|
-
| ------------------------ | ---------------- |
|
|
68
|
-
| `-o, --output <dir>` | `./gdpr-reports` | Output directory for the report
|
|
69
|
-
| `-t, --timeout <ms>` | `30000` | Navigation timeout
|
|
70
|
-
| `-f, --format <formats>` | `html` | Output formats: `md`, `html`, `json`, `pdf` (comma-separated)
|
|
71
|
-
| `--
|
|
72
|
-
| `--
|
|
73
|
-
|
|
|
74
|
-
|
|
|
66
|
+
| Option | Default | Description |
|
|
67
|
+
| ------------------------ | ---------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
68
|
+
| `-o, --output <dir>` | `./gdpr-reports` | Output directory for the report |
|
|
69
|
+
| `-t, --timeout <ms>` | `30000` | Navigation timeout |
|
|
70
|
+
| `-f, --format <formats>` | `html` | Output formats: `md`, `html`, `json`, `pdf` (comma-separated) |
|
|
71
|
+
| `--viewport <preset>` | `desktop` | Viewport preset: `desktop` (1280×900), `tablet` (768×1024), `mobile` (390×844) |
|
|
72
|
+
| `--fail-on <threshold>` | `F` | Exit with code 1 if grade is below this letter (`A`/`B`/`C`/`D`/`F`) or score is below this number (`0–100`) |
|
|
73
|
+
| `--json-summary` | — | Emit a machine-readable JSON line to stdout after the scan (parseable by `jq`) |
|
|
74
|
+
| `--strict` | — | Treat unrecognised cookies and unknown third-party requests as requiring consent |
|
|
75
|
+
| `--screenshots` | — | Also capture full-page screenshots after reject and accept interactions (the consent modal is always screenshotted when detected) |
|
|
76
|
+
| `-l, --locale <locale>` | `fr-FR` | Browser locale |
|
|
77
|
+
| `-v, --verbose` | — | Show full stack trace on error |
|
|
75
78
|
|
|
76
79
|
### Examples
|
|
77
80
|
|
|
@@ -82,8 +85,8 @@ gdpr-scan scan https://example.com
|
|
|
82
85
|
# With custom output directory
|
|
83
86
|
gdpr-scan scan https://example.com -o ./reports
|
|
84
87
|
|
|
85
|
-
# Scan in English
|
|
86
|
-
gdpr-scan scan https://example.com --locale en-US --
|
|
88
|
+
# Scan in English with full interaction screenshots (reject + accept)
|
|
89
|
+
gdpr-scan scan https://example.com --locale en-US --screenshots
|
|
87
90
|
|
|
88
91
|
# Generate a Markdown report instead
|
|
89
92
|
gdpr-scan scan https://example.com -f md
|
|
@@ -91,6 +94,12 @@ gdpr-scan scan https://example.com -f md
|
|
|
91
94
|
# Generate all formats at once
|
|
92
95
|
gdpr-scan scan https://example.com -f md,html,json,pdf
|
|
93
96
|
|
|
97
|
+
# Scan with a mobile viewport (390×844, iPhone UA)
|
|
98
|
+
gdpr-scan scan https://example.com --viewport mobile
|
|
99
|
+
|
|
100
|
+
# Scan with a tablet viewport (768×1024, iPad UA)
|
|
101
|
+
gdpr-scan scan https://example.com --viewport tablet
|
|
102
|
+
|
|
94
103
|
# Fail (exit 1) if the site is graded below B — useful in CI
|
|
95
104
|
gdpr-scan scan https://example.com --fail-on B
|
|
96
105
|
|
|
@@ -159,6 +168,42 @@ gdpr-scan:
|
|
|
159
168
|
- if: $CI_PIPELINE_SOURCE == "web"
|
|
160
169
|
```
|
|
161
170
|
|
|
171
|
+
### Machine-readable output
|
|
172
|
+
|
|
173
|
+
Add `--json-summary` to get a single JSON line on stdout after the scan — useful when
|
|
174
|
+
you need to parse results in a script or post them to a webhook without reading the full
|
|
175
|
+
report file.
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
gdpr-scan scan https://example.com --fail-on B --json-summary 2>/dev/null \
|
|
179
|
+
| grep '^{' | jq '{grade: .grade, score: .score, passed: .passed}'
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The JSON line is always emitted (pass or fail) and contains:
|
|
183
|
+
|
|
184
|
+
```jsonc
|
|
185
|
+
{
|
|
186
|
+
"url": "https://example.com",
|
|
187
|
+
"scanDate": "2026-01-01T00:00:00.000Z",
|
|
188
|
+
"score": 62,
|
|
189
|
+
"grade": "C",
|
|
190
|
+
"passed": false,
|
|
191
|
+
"threshold": "B",
|
|
192
|
+
"breakdown": {
|
|
193
|
+
"consentValidity": 20,
|
|
194
|
+
"easyRefusal": 12,
|
|
195
|
+
"transparency": 15,
|
|
196
|
+
"cookieBehavior": 15,
|
|
197
|
+
},
|
|
198
|
+
"issues": {
|
|
199
|
+
"total": 4,
|
|
200
|
+
"critical": 2,
|
|
201
|
+
"items": [{ "type": "buried-reject", "severity": "critical", "description": "..." }],
|
|
202
|
+
},
|
|
203
|
+
"reportPaths": { "html": "./gdpr-reports/example.com/gdpr-report-example.com-2026-01-01.html" },
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
162
207
|
### Threshold reference
|
|
163
208
|
|
|
164
209
|
| `--fail-on` value | Fails when grade is… | Fails when score is… |
|
|
@@ -198,9 +243,10 @@ All fields of `ScanResult` — cookies, network requests, modal analysis, compli
|
|
|
198
243
|
const result = await scan("https://example.com", {
|
|
199
244
|
locale: "fr-FR", // browser locale, also controls report language
|
|
200
245
|
timeout: 60_000, // navigation timeout in ms (default: 30 000)
|
|
201
|
-
screenshots: true, // capture screenshots (
|
|
246
|
+
screenshots: true, // also capture after-reject and after-accept screenshots (modal is always screenshotted)
|
|
202
247
|
outputDir: "./reports", // where to save screenshots
|
|
203
248
|
verbose: false, // log scanner phases to stdout
|
|
249
|
+
viewport: "mobile", // 'desktop' (default) | 'tablet' | 'mobile'
|
|
204
250
|
});
|
|
205
251
|
```
|
|
206
252
|
|
|
@@ -3,6 +3,6 @@ interface CookieClassification {
|
|
|
3
3
|
category: CookieCategory;
|
|
4
4
|
requiresConsent: boolean;
|
|
5
5
|
}
|
|
6
|
-
export declare function classifyCookie(name: string, domain: string, value: string): CookieClassification;
|
|
6
|
+
export declare function classifyCookie(name: string, domain: string, value: string, strict?: boolean): CookieClassification;
|
|
7
7
|
export {};
|
|
8
8
|
//# sourceMappingURL=cookie-classifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie-classifier.d.ts","sourceRoot":"","sources":["../../src/classifiers/cookie-classifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;CAC1B;AA2GD,wBAAgB,cAAc,
|
|
1
|
+
{"version":3,"file":"cookie-classifier.d.ts","sourceRoot":"","sources":["../../src/classifiers/cookie-classifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,cAAc,CAAC;IACzB,eAAe,EAAE,OAAO,CAAC;CAC1B;AA2GD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,UAAQ,GACb,oBAAoB,CActB"}
|
|
@@ -94,7 +94,7 @@ const COOKIE_PATTERNS = [
|
|
|
94
94
|
requiresConsent: true,
|
|
95
95
|
},
|
|
96
96
|
];
|
|
97
|
-
export function classifyCookie(name, domain, value) {
|
|
97
|
+
export function classifyCookie(name, domain, value, strict = false) {
|
|
98
98
|
for (const { pattern, category, requiresConsent } of COOKIE_PATTERNS) {
|
|
99
99
|
if (pattern.test(name)) {
|
|
100
100
|
return { category, requiresConsent };
|
|
@@ -104,6 +104,7 @@ export function classifyCookie(name, domain, value) {
|
|
|
104
104
|
if (name.length <= 4 && !value.includes("=")) {
|
|
105
105
|
return { category: "unknown", requiresConsent: true };
|
|
106
106
|
}
|
|
107
|
-
|
|
107
|
+
// In strict mode, unrecognised cookies are assumed to require consent
|
|
108
|
+
return { category: "unknown", requiresConsent: strict };
|
|
108
109
|
}
|
|
109
110
|
//# sourceMappingURL=cookie-classifier.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookie-classifier.js","sourceRoot":"","sources":["../../src/classifiers/cookie-classifier.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cookie-classifier.js","sourceRoot":"","sources":["../../src/classifiers/cookie-classifier.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,6 @@ interface NetworkClassification {
|
|
|
5
5
|
trackerName: string | null;
|
|
6
6
|
requiresConsent: boolean;
|
|
7
7
|
}
|
|
8
|
-
export declare function classifyNetworkRequest(url: string, resourceType: string): NetworkClassification;
|
|
8
|
+
export declare function classifyNetworkRequest(url: string, resourceType: string, strict?: boolean): NetworkClassification;
|
|
9
9
|
export {};
|
|
10
10
|
//# sourceMappingURL=network-classifier.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-classifier.d.ts","sourceRoot":"","sources":["../../src/classifiers/network-classifier.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,UAAU,qBAAqB;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"network-classifier.d.ts","sourceRoot":"","sources":["../../src/classifiers/network-classifier.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,UAAU,qBAAqB;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,EACX,YAAY,EAAE,MAAM,EACpB,MAAM,UAAQ,GACb,qBAAqB,CAsDvB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TRACKER_DB, PIXEL_PATTERNS } from "./tracker-list.js";
|
|
2
|
-
export function classifyNetworkRequest(url, resourceType) {
|
|
2
|
+
export function classifyNetworkRequest(url, resourceType, strict = false) {
|
|
3
3
|
let hostname;
|
|
4
4
|
try {
|
|
5
5
|
hostname = new URL(url).hostname.replace(/^www\./, "");
|
|
@@ -41,11 +41,13 @@ export function classifyNetworkRequest(url, resourceType) {
|
|
|
41
41
|
requiresConsent: true,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
// In strict mode, unrecognised third-party requests are assumed to require consent
|
|
45
|
+
const isThirdParty = strict;
|
|
44
46
|
return {
|
|
45
|
-
isThirdParty
|
|
47
|
+
isThirdParty,
|
|
46
48
|
trackerCategory: null,
|
|
47
49
|
trackerName: null,
|
|
48
|
-
requiresConsent:
|
|
50
|
+
requiresConsent: strict,
|
|
49
51
|
};
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"network-classifier.js","sourceRoot":"","sources":["../../src/classifiers/network-classifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAU/D,MAAM,UAAU,sBAAsB,
|
|
1
|
+
{"version":3,"file":"network-classifier.js","sourceRoot":"","sources":["../../src/classifiers/network-classifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAU/D,MAAM,UAAU,sBAAsB,CACpC,GAAW,EACX,YAAoB,EACpB,MAAM,GAAG,KAAK;IAEd,IAAI,QAAgB,CAAC;IAErB,IAAI,CAAC;QACH,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACzD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,YAAY,EAAE,KAAK;YACnB,eAAe,EAAE,IAAI;YACrB,WAAW,EAAE,IAAI;YACjB,eAAe,EAAE,KAAK;SACvB,CAAC;IACJ,CAAC;IAED,uDAAuD;IACvD,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QACzD,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,MAAM,EAAE,CAAC,EAAE,CAAC;YAC3D,OAAO;gBACL,YAAY,EAAE,IAAI;gBAClB,eAAe,EAAE,KAAK,CAAC,QAAQ;gBAC/B,WAAW,EAAE,KAAK,CAAC,IAAI;gBACvB,eAAe,EAAE,KAAK,CAAC,eAAe,KAAK,KAAK,IAAI,KAAK,CAAC,QAAQ,KAAK,KAAK;aAC7E,CAAC;QACJ,CAAC;IACH,CAAC;IAED,qCAAqC;IACrC,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5C,OAAO;YACL,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,OAAO;YACxB,WAAW,EAAE,gBAAgB;YAC7B,eAAe,EAAE,IAAI;SACtB,CAAC;IACJ,CAAC;IAED,2BAA2B;IAC3B,IAAI,YAAY,KAAK,OAAO,IAAI,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;QACnD,OAAO;YACL,YAAY,EAAE,IAAI;YAClB,eAAe,EAAE,OAAO;YACxB,WAAW,EAAE,wBAAwB;YACrC,eAAe,EAAE,IAAI;SACtB,CAAC;IACJ,CAAC;IAED,mFAAmF;IACnF,MAAM,YAAY,GAAG,MAAM,CAAC;IAC5B,OAAO;QACL,YAAY;QACZ,eAAe,EAAE,IAAI;QACrB,WAAW,EAAE,IAAI;QACjB,eAAe,EAAE,MAAM;KACxB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,GAAW;IAChC,MAAM,CAAC,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAC5B,OAAO,CACL,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC1C,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC;QACf,4CAA4C,CAAC,IAAI,CAAC,GAAG,CAAC,CACvD,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker-list.d.ts","sourceRoot":"","sources":["../../src/classifiers/tracker-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"tracker-list.d.ts","sourceRoot":"","sources":["../../src/classifiers/tracker-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,eAAe,CAAC;IAC1B,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAmHnD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,EAAE,MAAM,EAQlC,CAAC"}
|
|
@@ -70,12 +70,40 @@ export const TRACKER_DB = {
|
|
|
70
70
|
"optimizely.com": { name: "Optimizely", category: "analytics" },
|
|
71
71
|
"vwo.com": { name: "VWO", category: "analytics" },
|
|
72
72
|
"app.convert.com": { name: "Convert", category: "analytics" },
|
|
73
|
-
// ── Consent-exempt analytics
|
|
73
|
+
// ── Consent-exempt analytics ──────────────────────────────────
|
|
74
|
+
// These tools are cookieless, collect no personal data, and do not
|
|
75
|
+
// cross-reference with other processing — meeting the CNIL conditions
|
|
76
|
+
// for analytics tools exempt from the ePrivacy consent requirement.
|
|
74
77
|
"plausible.io": {
|
|
75
78
|
name: "Plausible Analytics",
|
|
76
79
|
category: "analytics",
|
|
77
80
|
consentRequired: false,
|
|
78
81
|
},
|
|
82
|
+
"cdn.usefathom.com": {
|
|
83
|
+
name: "Fathom Analytics",
|
|
84
|
+
category: "analytics",
|
|
85
|
+
consentRequired: false,
|
|
86
|
+
},
|
|
87
|
+
"scripts.simpleanalyticscdn.com": {
|
|
88
|
+
name: "Simple Analytics",
|
|
89
|
+
category: "analytics",
|
|
90
|
+
consentRequired: false,
|
|
91
|
+
},
|
|
92
|
+
"api.simpleanalytics.io": {
|
|
93
|
+
name: "Simple Analytics",
|
|
94
|
+
category: "analytics",
|
|
95
|
+
consentRequired: false,
|
|
96
|
+
},
|
|
97
|
+
"scripts.withcabin.com": {
|
|
98
|
+
name: "Cabin Analytics",
|
|
99
|
+
category: "analytics",
|
|
100
|
+
consentRequired: false,
|
|
101
|
+
},
|
|
102
|
+
"api.pirsch.io": {
|
|
103
|
+
name: "Pirsch Analytics",
|
|
104
|
+
category: "analytics",
|
|
105
|
+
consentRequired: false,
|
|
106
|
+
},
|
|
79
107
|
};
|
|
80
108
|
/**
|
|
81
109
|
* Patterns for detecting tracking pixels and beacons by URL shape.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracker-list.js","sourceRoot":"","sources":["../../src/classifiers/tracker-list.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"tracker-list.js","sourceRoot":"","sources":["../../src/classifiers/tracker-list.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -16,11 +16,14 @@ program
|
|
|
16
16
|
.argument("<url>", "URL of the website to scan")
|
|
17
17
|
.option("-o, --output <dir>", "Output directory for the report", "./gdpr-reports")
|
|
18
18
|
.option("-t, --timeout <ms>", "Navigation timeout in milliseconds", "30000")
|
|
19
|
-
.option("--
|
|
19
|
+
.option("--screenshots", "Capture full-page screenshots after reject and accept interactions (the consent modal is always screenshotted when detected)")
|
|
20
20
|
.option("-l, --locale <locale>", "Browser locale for language detection", "fr-FR")
|
|
21
21
|
.option("-v, --verbose", "Show detailed output", false)
|
|
22
22
|
.option("-f, --format <formats>", "Output formats: md, html, json, pdf (comma-separated)", "html")
|
|
23
|
+
.option("--viewport <preset>", "Viewport preset: desktop (1280×900), tablet (768×1024), mobile (390×844)", "desktop")
|
|
23
24
|
.option("--fail-on <threshold>", "Exit with code 1 if grade is below this letter (A/B/C/D/F) or score is below this number", "F")
|
|
25
|
+
.option("--json-summary", "Emit a JSON summary line to stdout after the scan (machine-readable)", false)
|
|
26
|
+
.option("--strict", "Treat unrecognised cookies and unknown third-party requests as requiring consent", false)
|
|
24
27
|
.action(async (url, opts) => {
|
|
25
28
|
console.log();
|
|
26
29
|
console.log(styleText(["bold", "blue"], " GDPR Cookie Scanner"));
|
|
@@ -28,8 +31,15 @@ program
|
|
|
28
31
|
const normalizedUrl = normalizeUrl(url);
|
|
29
32
|
const hostname = new URL(normalizedUrl).hostname;
|
|
30
33
|
const outputDir = join(resolve(opts.output), hostname);
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
const validViewports = new Set(["desktop", "tablet", "mobile"]);
|
|
35
|
+
const viewport = opts.viewport.toLowerCase();
|
|
36
|
+
if (!validViewports.has(viewport)) {
|
|
37
|
+
console.error(styleText("red", " Invalid --viewport value. Valid options: desktop, tablet, mobile"));
|
|
38
|
+
process.exit(2);
|
|
39
|
+
}
|
|
40
|
+
console.log(styleText("gray", ` Target : ${url}`));
|
|
41
|
+
console.log(styleText("gray", ` Output : ${outputDir}`));
|
|
42
|
+
console.log(styleText("gray", ` Viewport : ${viewport}`));
|
|
33
43
|
console.log();
|
|
34
44
|
const validFormats = new Set(["md", "html", "json", "pdf"]);
|
|
35
45
|
const formats = opts.format
|
|
@@ -44,10 +54,12 @@ program
|
|
|
44
54
|
url: normalizedUrl,
|
|
45
55
|
outputDir,
|
|
46
56
|
timeout: parseInt(opts.timeout, 10),
|
|
47
|
-
screenshots: opts.screenshots
|
|
57
|
+
screenshots: Boolean(opts.screenshots),
|
|
48
58
|
locale: opts.locale,
|
|
49
59
|
verbose: opts.verbose,
|
|
50
60
|
formats,
|
|
61
|
+
viewport: viewport,
|
|
62
|
+
strict: opts.strict,
|
|
51
63
|
};
|
|
52
64
|
const spinner = createSpinner("Launching browser...").start();
|
|
53
65
|
try {
|
|
@@ -89,6 +101,27 @@ program
|
|
|
89
101
|
console.log(styleText("red", ` Failed threshold: score ${result.compliance.total}/100 (grade ${result.compliance.grade}) is below --fail-on ${threshold.toUpperCase()}`));
|
|
90
102
|
console.log();
|
|
91
103
|
}
|
|
104
|
+
if (opts.jsonSummary) {
|
|
105
|
+
process.stdout.write(JSON.stringify({
|
|
106
|
+
url: result.url,
|
|
107
|
+
scanDate: result.scanDate,
|
|
108
|
+
score: result.compliance.total,
|
|
109
|
+
grade: result.compliance.grade,
|
|
110
|
+
passed: !failed,
|
|
111
|
+
threshold: threshold.toUpperCase(),
|
|
112
|
+
breakdown: result.compliance.breakdown,
|
|
113
|
+
issues: {
|
|
114
|
+
total: result.compliance.issues.length,
|
|
115
|
+
critical: result.compliance.issues.filter((i) => i.severity === "critical").length,
|
|
116
|
+
items: result.compliance.issues.map((i) => ({
|
|
117
|
+
type: i.type,
|
|
118
|
+
severity: i.severity,
|
|
119
|
+
description: i.description,
|
|
120
|
+
})),
|
|
121
|
+
},
|
|
122
|
+
reportPaths: paths,
|
|
123
|
+
}) + "\n");
|
|
124
|
+
}
|
|
92
125
|
process.exit(failed ? 1 : 0);
|
|
93
126
|
}
|
|
94
127
|
catch (err) {
|