privalint 0.1.1 → 0.3.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/README.de.md +117 -0
  2. package/README.md +55 -1
  3. package/dist/assessment.d.ts +6 -0
  4. package/dist/assessment.js +79 -0
  5. package/dist/assessment.js.map +1 -0
  6. package/dist/capture.d.ts +5 -0
  7. package/dist/capture.js +155 -0
  8. package/dist/capture.js.map +1 -0
  9. package/dist/cli.js +12 -3
  10. package/dist/cli.js.map +1 -1
  11. package/dist/consent.d.ts +3 -0
  12. package/dist/consent.js +13 -6
  13. package/dist/consent.js.map +1 -1
  14. package/dist/diagnostics.d.ts +27 -0
  15. package/dist/diagnostics.js +187 -0
  16. package/dist/diagnostics.js.map +1 -0
  17. package/dist/i18n.d.ts +5 -0
  18. package/dist/i18n.js +3 -0
  19. package/dist/i18n.js.map +1 -0
  20. package/dist/index.d.ts +5 -0
  21. package/dist/index.js +5 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/legal.d.ts +10 -0
  24. package/dist/legal.js +99 -0
  25. package/dist/legal.js.map +1 -0
  26. package/dist/network.d.ts +67 -0
  27. package/dist/network.js +235 -0
  28. package/dist/network.js.map +1 -0
  29. package/dist/observations.d.ts +3 -0
  30. package/dist/observations.js +4 -0
  31. package/dist/observations.js.map +1 -0
  32. package/dist/reporters/capture.d.ts +5 -0
  33. package/dist/reporters/capture.js +35 -0
  34. package/dist/reporters/capture.js.map +1 -0
  35. package/dist/reporters/markdown.js +21 -12
  36. package/dist/reporters/markdown.js.map +1 -1
  37. package/dist/reporters/messages.d.ts +21 -0
  38. package/dist/reporters/messages.js +68 -0
  39. package/dist/reporters/messages.js.map +1 -0
  40. package/dist/reporters/sarif.js +31 -1
  41. package/dist/reporters/sarif.js.map +1 -1
  42. package/dist/reporters/shared.d.ts +1 -3
  43. package/dist/reporters/shared.js +0 -17
  44. package/dist/reporters/shared.js.map +1 -1
  45. package/dist/reporters/text.js +23 -15
  46. package/dist/reporters/text.js.map +1 -1
  47. package/dist/rules/google-fonts.js +28 -10
  48. package/dist/rules/google-fonts.js.map +1 -1
  49. package/dist/rules/impressum-link.js +21 -8
  50. package/dist/rules/impressum-link.js.map +1 -1
  51. package/dist/rules/links.d.ts +2 -0
  52. package/dist/rules/links.js +2 -0
  53. package/dist/rules/links.js.map +1 -1
  54. package/dist/rules/pre-consent-trackers.js +20 -7
  55. package/dist/rules/pre-consent-trackers.js.map +1 -1
  56. package/dist/rules/privacy-policy-link.js +12 -5
  57. package/dist/rules/privacy-policy-link.js.map +1 -1
  58. package/dist/rules/reject-option.js +28 -8
  59. package/dist/rules/reject-option.js.map +1 -1
  60. package/dist/rules/third-party-cdns.js +10 -3
  61. package/dist/rules/third-party-cdns.js.map +1 -1
  62. package/dist/rules/third-party-embeds.js +10 -3
  63. package/dist/rules/third-party-embeds.js.map +1 -1
  64. package/dist/rules/third-party-hits.js +2 -1
  65. package/dist/rules/third-party-hits.js.map +1 -1
  66. package/dist/rules/tracker-hits.d.ts +4 -2
  67. package/dist/rules/tracker-hits.js +16 -4
  68. package/dist/rules/tracker-hits.js.map +1 -1
  69. package/dist/rules/trackers-after-reject.js +24 -12
  70. package/dist/rules/trackers-after-reject.js.map +1 -1
  71. package/dist/scan-options.d.ts +39 -0
  72. package/dist/scan-options.js +2 -0
  73. package/dist/scan-options.js.map +1 -0
  74. package/dist/scanner.d.ts +4 -32
  75. package/dist/scanner.js +87 -104
  76. package/dist/scanner.js.map +1 -1
  77. package/dist/types.d.ts +70 -0
  78. package/package.json +11 -6
@@ -1 +1 @@
1
- {"version":3,"file":"tracker-hits.js","sourceRoot":"","sources":["../../src/rules/tracker-hits.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAgB,MAAM,qBAAqB,CAAC;AAG1F,MAAM,YAAY,GAAG,CAAC,CAAC;AAQvB,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAC7B,QAAoC,EACpC,OAAkC;IAElC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAuD,CAAC;IAC5E,MAAM,MAAM,GAAG,CAAC,OAAgB,EAAE,QAAgB,EAAE,EAAE;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;QACjF,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,OAAO,EAAE,WAAW,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC5C,CAAC;IACH,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GACX,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC;YAChC,gBAAgB,CAAC,WAAW,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QACnE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,OAAO,EAAE,UAAU,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACxD,OAAO;QACP,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC;KAC/C,CAAC,CAAC,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"tracker-hits.js","sourceRoot":"","sources":["../../src/rules/tracker-hits.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,gBAAgB,GAGjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAa,GAAG,EAAa,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,MAAM,YAAY,GAAG,CAAC,CAAC;AAQvB,gFAAgF;AAChF,MAAM,UAAU,eAAe,CAC7B,QAAoC,EACpC,OAAkC,EAClC,IAAU;IAEV,MAAM,YAAY,GAAG,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IACjE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAuD,CAAC;IAC5E,MAAM,MAAM,GAAG,CAAC,OAAgB,EAAE,QAAgB,EAAE,EAAE;QACpD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAU,EAAE,CAAC;QACjF,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC3B,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAChC,CAAC,CAAC;IAEF,KAAK,MAAM,OAAO,IAAI,gBAAgB,CAAC,QAAQ,CAAC,EAAE,CAAC;QACjD,MAAM,OAAO,GAAG,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,OAAO,EAAE,GAAG,YAAY,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,OAAO,GACX,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC;YAChC,gBAAgB,CAAC,WAAW,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;QACnE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,MAAM,CAAC,OAAO,EAAE,UAAU,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QACxD,OAAO;QACP,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC;KAC/C,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,eAAe,GAAkC;IACrD,SAAS,EAAE,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE;IAC7C,WAAW,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,SAAS,EAAE;IACjD,gBAAgB,EAAE,EAAE,EAAE,EAAE,gBAAgB,EAAE,EAAE,EAAE,gBAAgB,EAAE;IAChE,MAAM,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,cAAc,EAAE;IAC5C,sBAAsB,EAAE,EAAE,EAAE,EAAE,sBAAsB,EAAE,EAAE,EAAE,2BAA2B,EAAE;IACvF,aAAa,EAAE,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,aAAa,EAAE;CACxD,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAyB,EAAE,IAAU,EAAE,EAAE,CACrE,GAAG,CAAC,IAAI,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC"}
@@ -1,4 +1,5 @@
1
- import { findTrackerHits } from './tracker-hits.js';
1
+ import { say } from '../i18n.js';
2
+ import { categoryLabel, findTrackerHits } from './tracker-hits.js';
2
3
  const ID = 'trackers-after-reject';
3
4
  export const trackersAfterReject = {
4
5
  id: ID,
@@ -7,21 +8,32 @@ export const trackersAfterReject = {
7
8
  'page and records what still loads. Anything on the tracker list that loads or sets new ' +
8
9
  'cookies after that ignores the visitor’s explicit refusal.',
9
10
  references: ['§ 25 Abs. 1 TDDDG', 'Art. 6(1)(a) GDPR', 'Art. 7(3) GDPR'],
10
- check({ page }) {
11
+ check({ page, lang }) {
11
12
  const after = page.consent?.afterReject;
12
13
  if (after === undefined) {
13
14
  return [];
14
15
  }
15
- return findTrackerHits(after.requests, after.cookies).map(({ tracker, evidence }) => ({
16
- ruleId: ID,
17
- // Same reasoning as pre-consent-trackers: the container alone may be configured correctly.
18
- severity: tracker.category === 'tag-manager' ? 'warning' : 'error',
19
- message: `${tracker.vendor} (${tracker.category}) still runs after clicking "${page.consent?.rejectLabel ?? 'reject'}".`,
20
- pageUrl: page.finalUrl,
21
- subject: tracker.vendor,
22
- evidence,
23
- fix: `Check that ${tracker.vendor} is assigned to a consent category in your consent manager and that the category is blocked when the visitor rejects.`,
24
- }));
16
+ const button = page.consent?.rejectLabel ?? say(lang, { en: 'reject', de: 'Ablehnen' });
17
+ return findTrackerHits(after.requests, after.cookies, lang).map(({ tracker, evidence }) => {
18
+ const { vendor } = tracker;
19
+ const category = categoryLabel(tracker.category, lang);
20
+ return {
21
+ ruleId: ID,
22
+ // Same reasoning as pre-consent-trackers: the container alone may be configured correctly.
23
+ severity: tracker.category === 'tag-manager' ? 'warning' : 'error',
24
+ message: say(lang, {
25
+ en: `${vendor} (${category}) still runs after clicking "${button}".`,
26
+ de: `${vendor} (${category}) läuft weiter, obwohl „${button}“ geklickt wurde.`,
27
+ }),
28
+ pageUrl: page.finalUrl,
29
+ subject: vendor,
30
+ evidence,
31
+ fix: say(lang, {
32
+ en: `Check that ${vendor} is assigned to a consent category in your consent manager and that the category is blocked when the visitor rejects.`,
33
+ de: `Prüfen, ob ${vendor} im Consent-Tool einer Kategorie zugeordnet ist und diese Kategorie beim Ablehnen blockiert wird.`,
34
+ }),
35
+ };
36
+ });
25
37
  },
26
38
  };
27
39
  //# sourceMappingURL=trackers-after-reject.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"trackers-after-reject.js","sourceRoot":"","sources":["../../src/rules/trackers-after-reject.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,EAAE,GAAG,uBAAuB,CAAC;AAEnC,MAAM,CAAC,MAAM,mBAAmB,GAAS;IACvC,EAAE,EAAE,EAAE;IACN,KAAK,EAAE,8CAA8C;IACrD,WAAW,EACT,0FAA0F;QAC1F,yFAAyF;QACzF,4DAA4D;IAC9D,UAAU,EAAE,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,CAAC;IACxE,KAAK,CAAC,EAAE,IAAI,EAAE;QACZ,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;QACxC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,eAAe,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,CACvD,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAW,EAAE,CAAC,CAAC;YACnC,MAAM,EAAE,EAAE;YACV,2FAA2F;YAC3F,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;YAClE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,KAAK,OAAO,CAAC,QAAQ,gCAAgC,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,QAAQ,IAAI;YACxH,OAAO,EAAE,IAAI,CAAC,QAAQ;YACtB,OAAO,EAAE,OAAO,CAAC,MAAM;YACvB,QAAQ;YACR,GAAG,EAAE,cAAc,OAAO,CAAC,MAAM,uHAAuH;SACzJ,CAAC,CACH,CAAC;IACJ,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"trackers-after-reject.js","sourceRoot":"","sources":["../../src/rules/trackers-after-reject.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEnE,MAAM,EAAE,GAAG,uBAAuB,CAAC;AAEnC,MAAM,CAAC,MAAM,mBAAmB,GAAS;IACvC,EAAE,EAAE,EAAE;IACN,KAAK,EAAE,8CAA8C;IACrD,WAAW,EACT,0FAA0F;QAC1F,yFAAyF;QACzF,4DAA4D;IAC9D,UAAU,EAAE,CAAC,mBAAmB,EAAE,mBAAmB,EAAE,gBAAgB,CAAC;IACxE,KAAK,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE;QAClB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC;QACxC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,GAAG,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACxF,OAAO,eAAe,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,GAAG,CAC7D,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAW,EAAE;YACjC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;YAC3B,MAAM,QAAQ,GAAG,aAAa,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACvD,OAAO;gBACL,MAAM,EAAE,EAAE;gBACV,2FAA2F;gBAC3F,QAAQ,EAAE,OAAO,CAAC,QAAQ,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;gBAClE,OAAO,EAAE,GAAG,CAAC,IAAI,EAAE;oBACjB,EAAE,EAAE,GAAG,MAAM,KAAK,QAAQ,gCAAgC,MAAM,IAAI;oBACpE,EAAE,EAAE,GAAG,MAAM,KAAK,QAAQ,2BAA2B,MAAM,mBAAmB;iBAC/E,CAAC;gBACF,OAAO,EAAE,IAAI,CAAC,QAAQ;gBACtB,OAAO,EAAE,MAAM;gBACf,QAAQ;gBACR,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE;oBACb,EAAE,EAAE,cAAc,MAAM,uHAAuH;oBAC/I,EAAE,EAAE,cAAc,MAAM,mGAAmG;iBAC5H,CAAC;aACH,CAAC;QACJ,CAAC,CACF,CAAC;IACJ,CAAC;CACF,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { Lang } from './i18n.js';
2
+ import type { Region } from './legal.js';
3
+ import type { NetworkAdapter, NetworkLimits } from './network.js';
4
+ import type { PageSnapshot, Rule } from './types.js';
5
+ export interface ScanOptions {
6
+ maxPages?: number | undefined;
7
+ settleMs?: number | undefined;
8
+ /** Navigation/network-idle timeout, distinct from transport and capture deadlines. */
9
+ timeoutMs?: number | undefined;
10
+ locale?: string | undefined;
11
+ lang?: Lang | undefined;
12
+ /** Tool classification profile; does not determine which law applies. */
13
+ region?: Region | undefined;
14
+ rules?: readonly Rule[];
15
+ /** Legacy test helper: third-party requests receive synthetic empty responses. */
16
+ offline?: boolean;
17
+ consentCheck?: boolean | undefined;
18
+ headed?: boolean | undefined;
19
+ channel?: string | undefined;
20
+ /** Trusted hook receiving RAW observations. Do not log/expose without redaction. */
21
+ onPage?: (snapshot: PageSnapshot) => void;
22
+ /** Caller-owned transport; scan owns browser, fresh per-page contexts and request signals. */
23
+ networkAdapter?: NetworkAdapter;
24
+ /** Per-page budgets across initial loading and rejection. Requires networkAdapter. */
25
+ networkLimits?: NetworkLimits;
26
+ signal?: AbortSignal;
27
+ /** Entire capture including queue wait and consent; default 60s with an adapter. */
28
+ captureTimeoutMs?: number;
29
+ /** Stored issue details per page; counters are never truncated. Default 100. */
30
+ maxDiagnostics?: number;
31
+ /** Legacy default: throw on start-page navigation failure. Adapter default: report it. */
32
+ failureMode?: 'throw' | 'report';
33
+ }
34
+ export declare const DEFAULTS: {
35
+ maxPages: number;
36
+ settleMs: number;
37
+ timeoutMs: number;
38
+ locale: string;
39
+ };
@@ -0,0 +1,2 @@
1
+ export const DEFAULTS = { maxPages: 1, settleMs: 2_000, timeoutMs: 30_000, locale: 'de-DE' };
2
+ //# sourceMappingURL=scan-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scan-options.js","sourceRoot":"","sources":["../src/scan-options.ts"],"names":[],"mappings":"AAkCA,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC"}
package/dist/scanner.d.ts CHANGED
@@ -1,33 +1,5 @@
1
- import type { PageSnapshot, Rule, ScanResult } from './types.js';
2
- export interface ScanOptions {
3
- /** Maximum number of pages on the same site to visit, starting with the given URL. */
4
- maxPages?: number | undefined;
5
- /** Extra time to wait after the network is idle, to catch delayed tags. */
6
- settleMs?: number | undefined;
7
- /** Navigation timeout per page. */
8
- timeoutMs?: number | undefined;
9
- locale?: string | undefined;
10
- rules?: readonly Rule[];
11
- /**
12
- * Answer every third-party request with an empty response instead of fetching it.
13
- * Requests are still recorded. Used by the test suite to stay off the network.
14
- */
15
- offline?: boolean;
16
- /**
17
- * Click "reject" on the start page's consent banner and record what still loads.
18
- * On by default.
19
- */
20
- consentCheck?: boolean | undefined;
21
- /**
22
- * Open a visible browser window instead of running headless. Some sites only show their
23
- * consent banner to regular browsers. Needs a display, so it doesn't work on CI runners.
24
- */
25
- headed?: boolean | undefined;
26
- /**
27
- * Use an installed browser instead of Playwright's bundled Chromium,
28
- * e.g. `chrome` or `msedge`.
29
- */
30
- channel?: string | undefined;
31
- onPage?: (snapshot: PageSnapshot) => void;
32
- }
1
+ import { type ScanOptions } from './scan-options.js';
2
+ import type { ScanResult } from './types.js';
3
+ export type { ScanOptions } from './scan-options.js';
4
+ export declare function throwIfAborted(signal?: AbortSignal): void;
33
5
  export declare function scan(target: string, options?: ScanOptions): Promise<ScanResult>;
package/dist/scanner.js CHANGED
@@ -1,39 +1,67 @@
1
1
  import { chromium } from 'playwright';
2
- import { observeConsent } from './consent.js';
2
+ import { assessPage } from './assessment.js';
3
+ import { capturePage } from './capture.js';
4
+ import { diagnosticUrl } from './diagnostics.js';
5
+ import { applyRegion } from './legal.js';
6
+ import { networkLimits } from './network.js';
3
7
  import { defaultRules } from './rules/index.js';
4
- import { crawlTarget, isFirstParty, withoutHash } from './url.js';
5
- const DEFAULTS = {
6
- maxPages: 1,
7
- settleMs: 2_000,
8
- timeoutMs: 30_000,
9
- locale: 'de-DE',
10
- };
8
+ import { DEFAULTS } from './scan-options.js';
9
+ import { crawlTarget, withoutHash } from './url.js';
10
+ function integer(value, name, minimum) {
11
+ if (!Number.isSafeInteger(value) || value < minimum) {
12
+ throw new RangeError(`${name} must be a safe integer of at least ${minimum}`);
13
+ }
14
+ }
15
+ export function throwIfAborted(signal) {
16
+ if (signal?.aborted)
17
+ throw new DOMException('Scan aborted', 'AbortError');
18
+ }
11
19
  export async function scan(target, options = {}) {
20
+ throwIfAborted(options.signal);
12
21
  const startedAt = Date.now();
13
22
  const rules = options.rules ?? defaultRules;
14
- const maxPages = Math.max(1, options.maxPages ?? DEFAULTS.maxPages);
23
+ const maxPages = options.maxPages ?? DEFAULTS.maxPages;
24
+ integer(maxPages, 'maxPages', 1);
25
+ integer(options.settleMs ?? DEFAULTS.settleMs, 'settleMs', 0);
26
+ integer(options.timeoutMs ?? DEFAULTS.timeoutMs, 'timeoutMs', 1);
27
+ integer(options.maxDiagnostics ?? 100, 'maxDiagnostics', 0);
28
+ if (options.captureTimeoutMs !== undefined)
29
+ integer(options.captureTimeoutMs, 'captureTimeoutMs', 1);
30
+ if (options.networkLimits && !options.networkAdapter) {
31
+ throw new TypeError('networkLimits requires networkAdapter');
32
+ }
33
+ if (options.networkAdapter && options.offline) {
34
+ throw new TypeError('offline cannot be combined with networkAdapter');
35
+ }
36
+ if (options.networkAdapter)
37
+ networkLimits(options.networkLimits);
38
+ const lang = options.lang ?? 'en';
39
+ const region = options.region ?? 'de';
15
40
  let siteUrl = new URL(target);
41
+ if (!/^https?:$/.test(siteUrl.protocol))
42
+ throw new TypeError('Expected an HTTP(S) target');
16
43
  const browser = await chromium.launch({
17
44
  headless: options.headed !== true,
18
45
  ...(options.channel === undefined ? {} : { channel: options.channel }),
19
46
  });
20
47
  try {
48
+ throwIfAborted(options.signal);
21
49
  const snapshots = [];
22
50
  const queue = [siteUrl.href];
23
51
  const seen = new Set(queue);
24
52
  const captured = new Set();
25
53
  let url = queue.shift();
26
54
  while (url !== undefined && snapshots.length < maxPages) {
55
+ throwIfAborted(options.signal);
27
56
  const isStart = snapshots.length === 0;
28
57
  const snapshot = await capturePage(browser, url, siteUrl, options, isStart);
29
58
  if (isStart) {
30
- if (snapshot.error !== undefined) {
31
- throw new Error(`Could not load ${url}: ${snapshot.error}`);
59
+ const failureMode = options.failureMode ?? (options.networkAdapter ? 'report' : 'throw');
60
+ if (snapshot.error !== undefined && failureMode === 'throw') {
61
+ throw new Error(`Could not load ${diagnosticUrl(url)}: ${snapshot.error}`);
32
62
  }
33
- // Follow redirects like http -> https or apex -> www for the rest of the crawl.
34
63
  siteUrl = new URL(snapshot.finalUrl);
35
64
  }
36
- // Several links can redirect to the same page; only lint it once.
37
65
  const finalUrl = withoutHash(snapshot.finalUrl);
38
66
  if (!captured.has(finalUrl)) {
39
67
  captured.add(finalUrl);
@@ -50,118 +78,73 @@ export async function scan(target, options = {}) {
50
78
  }
51
79
  url = queue.shift();
52
80
  }
53
- // Pages that failed, returned an error status or redirected off-site are listed but not linted.
54
- const findings = snapshots
55
- .filter((page) => page.error === undefined &&
56
- (page.status === null || page.status < 400) &&
57
- isFirstParty(page.finalUrl, siteUrl))
58
- .flatMap((page) => rules.flatMap((rule) => rule.check({ siteUrl, page })));
59
- return {
81
+ throwIfAborted(options.signal);
82
+ const assessments = snapshots.map((page, index) => assessPage(page, rules, siteUrl, lang, index === 0, options.consentCheck !== false));
83
+ const partial = assessments.some((assessment) => assessment.evaluations.some((evaluation) => evaluation.partial || evaluation.reason === 'consent-disabled')) || snapshots.some((page) => !page.capture?.complete);
84
+ const result = {
60
85
  siteUrl: siteUrl.href,
86
+ lang,
87
+ region,
61
88
  scannedAt: new Date(startedAt).toISOString(),
62
89
  durationMs: Date.now() - startedAt,
63
- pages: snapshots.map((page) => ({
90
+ pages: snapshots.map((page, index) => ({
64
91
  url: page.url,
65
92
  finalUrl: page.finalUrl,
66
93
  status: page.status,
67
- requestCount: page.requests.length,
94
+ requestCount: page.requestCount ?? page.requests.length,
68
95
  cookieCount: page.cookies.length,
69
96
  ...(page.error === undefined ? {} : { error: page.error }),
97
+ ...(page.capture === undefined ? {} : { capture: page.capture }),
98
+ ruleEvaluations: assessments[index]?.evaluations ?? [],
70
99
  })),
71
100
  ...consentSummary(snapshots[0]),
72
- findings,
101
+ findings: applyRegion(assessments.flatMap((assessment) => assessment.findings), region),
73
102
  rules: rules.map(({ check: _check, ...meta }) => meta),
103
+ coverage: partial ? 'partial' : 'complete',
74
104
  };
105
+ return options.networkAdapter ? redactReport(result) : result;
75
106
  }
76
107
  finally {
77
108
  await browser.close();
78
109
  }
79
110
  }
80
111
  function consentSummary(start) {
81
- if (start?.consent === undefined) {
112
+ if (start?.consent === undefined)
82
113
  return {};
83
- }
84
114
  const { afterReject: _afterReject, ...summary } = start.consent;
85
115
  return { consent: summary };
86
116
  }
87
- async function capturePage(browser, url, siteUrl, options, checkConsent) {
88
- // A fresh context per page means no cookies or storage carry over:
89
- // every page is seen the way a first-time visitor sees it.
90
- const context = await browser.newContext({ locale: options.locale ?? DEFAULTS.locale });
91
- const requests = [];
92
- try {
93
- if (options.offline) {
94
- await context.route('**/*', (route) => isFirstParty(route.request().url(), siteUrl)
95
- ? route.continue()
96
- : route.fulfill({ status: 200, body: '' }));
97
- }
98
- const page = await context.newPage();
99
- page.on('request', (request) => {
100
- requests.push({ url: request.url(), resourceType: request.resourceType() });
101
- });
102
- const response = await page.goto(url, {
103
- waitUntil: 'load',
104
- timeout: options.timeoutMs ?? DEFAULTS.timeoutMs,
105
- });
106
- await page
107
- .waitForLoadState('networkidle', { timeout: options.timeoutMs ?? DEFAULTS.timeoutMs })
108
- .catch(() => {
109
- // Long-polling or streaming pages never go idle; carry on with what we have.
110
- });
111
- await page.waitForTimeout(options.settleMs ?? DEFAULTS.settleMs);
112
- const links = await page.$$eval('a[href]', (anchors) => anchors.map((a) => ({
113
- href: a.href,
114
- text: [a.textContent, a.getAttribute('aria-label'), a.getAttribute('title')]
115
- .filter(Boolean)
116
- .join(' ')
117
- .replace(/\s+/g, ' ')
118
- .trim()
119
- .slice(0, 200),
120
- })));
121
- const resourceHints = await page.$$eval('link[rel][href]', (elements) => elements.map((link) => ({
122
- rel: (link.getAttribute('rel') ?? '').toLowerCase(),
123
- href: link.href,
124
- })));
125
- const cookies = (await context.cookies()).map(({ name, domain, path, expires }) => ({
126
- name,
127
- domain,
128
- path,
129
- expires,
130
- }));
131
- const snapshot = {
132
- url,
133
- finalUrl: page.url(),
134
- status: response?.status() ?? null,
135
- // Copy: the listener keeps writing to `requests` during the consent check.
136
- requests: [...requests],
137
- cookies,
138
- links,
139
- resourceHints,
140
- };
141
- if (checkConsent && options.consentCheck !== false) {
142
- snapshot.consent = await observeConsent(page, context, requests, {
143
- settleMs: options.settleMs ?? DEFAULTS.settleMs,
144
- timeoutMs: options.timeoutMs ?? DEFAULTS.timeoutMs,
145
- });
146
- }
147
- return snapshot;
148
- }
149
- catch (error) {
150
- // Downloads, timeouts and crashed pages should not end the whole crawl.
151
- const message = error instanceof Error ? error.message : String(error);
152
- return {
153
- url,
154
- finalUrl: url,
155
- status: null,
156
- requests,
157
- cookies: [],
158
- links: [],
159
- resourceHints: [],
160
- error: message.split('\n')[0] ?? message,
161
- };
162
- }
163
- finally {
164
- await context.close();
165
- }
117
+ /** Adapter reports omit URL credentials, paths, queries and fragments by default. */
118
+ function redactReport(result) {
119
+ const redactText = (text) => text.replace(/https?:\/\/[^\s<>"']+/gi, diagnosticUrl);
120
+ return {
121
+ ...result,
122
+ siteUrl: diagnosticUrl(result.siteUrl),
123
+ ...(result.consent === undefined
124
+ ? {}
125
+ : {
126
+ consent: {
127
+ ...result.consent,
128
+ ...(result.consent.acceptLabel === undefined
129
+ ? {}
130
+ : { acceptLabel: redactText(result.consent.acceptLabel) }),
131
+ ...(result.consent.rejectLabel === undefined
132
+ ? {}
133
+ : { rejectLabel: redactText(result.consent.rejectLabel) }),
134
+ },
135
+ }),
136
+ pages: result.pages.map((page) => ({
137
+ ...page,
138
+ url: diagnosticUrl(page.url),
139
+ finalUrl: diagnosticUrl(page.finalUrl),
140
+ })),
141
+ findings: result.findings.map((finding) => ({
142
+ ...finding,
143
+ pageUrl: diagnosticUrl(finding.pageUrl),
144
+ message: redactText(finding.message),
145
+ evidence: finding.evidence.map(redactText),
146
+ ...(finding.fix === undefined ? {} : { fix: redactText(finding.fix) }),
147
+ })),
148
+ };
166
149
  }
167
150
  //# sourceMappingURL=scanner.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -1,7 +1,59 @@
1
+ import type { Lang } from './i18n.js';
2
+ import type { Region } from './legal.js';
1
3
  export type Severity = 'error' | 'warning' | 'info';
2
4
  export interface RecordedRequest {
3
5
  url: string;
4
6
  resourceType: string;
7
+ /** Absent on legacy snapshots. Only received responses count as network evidence. */
8
+ outcome?: 'pending' | 'response' | 'failed';
9
+ status?: number;
10
+ }
11
+ export type LoadIssueKind = 'security-blocked' | 'unsupported-method' | 'unsupported-protocol' | 'dns' | 'tls' | 'connection' | 'timeout' | 'resource-limit' | 'http-error' | 'browser-failure' | 'aborted';
12
+ export type CapturePhase = 'before-consent' | 'after-reject';
13
+ export interface LoadDiagnostic {
14
+ kind: LoadIssueKind;
15
+ phase: CapturePhase;
16
+ /** Origin only: user info, path, query and fragment are never retained. */
17
+ url: string;
18
+ status?: number;
19
+ limit?: 'requests' | 'response-bytes' | 'total-bytes' | 'redirects';
20
+ }
21
+ export interface LoadDiagnostics {
22
+ /** HTTP(S) browser requests, including documents and every redirect hop, across both phases. */
23
+ requests: number;
24
+ /** Requests counted but omitted from bounded in-memory rule evidence. */
25
+ omittedRequests: number;
26
+ /** Received HTTP responses, including redirects and HTTP errors; may overlap failed. */
27
+ responses: number;
28
+ /** Browser requestfinished events (including HTTP errors). */
29
+ finished: number;
30
+ /** Failed or unfinished requests at capture end; disjoint from finished. */
31
+ failed: number;
32
+ /** Subset of failed: explicitly denied by the adapter's security policy. */
33
+ blocked: number;
34
+ /** Subset of responses: status >= 400. */
35
+ httpErrors: number;
36
+ /** Adapter response-body bytes consumed, including the chunk exceeding a limit. */
37
+ bytes: number;
38
+ /** All issues, even when their details exceed the storage cap. */
39
+ issueCount: number;
40
+ dropped: number;
41
+ issues: LoadDiagnostic[];
42
+ }
43
+ export type ObservationKind = 'document' | 'network' | 'consent' | 'after-reject';
44
+ export interface RuleEvaluation {
45
+ ruleId: string;
46
+ status: 'executed' | 'skipped' | 'not-evaluable';
47
+ reason?: 'disabled' | 'consent-disabled' | 'not-start-page' | 'missing-observation';
48
+ /** Executed against available positive evidence, without ruling out additional findings. */
49
+ partial: boolean;
50
+ }
51
+ export interface CaptureObservation {
52
+ complete: boolean;
53
+ /** Loading coverage before the consent interaction, for pre-consent rule evaluation. */
54
+ beforeConsentComplete?: boolean;
55
+ document: boolean;
56
+ diagnostics: LoadDiagnostics;
5
57
  }
6
58
  export interface RecordedCookie {
7
59
  name: string;
@@ -21,6 +73,7 @@ export interface ResourceHint {
21
73
  }
22
74
  /** What happened when privalint looked for a consent banner and clicked "reject". */
23
75
  export interface ConsentObservation {
76
+ state?: 'not-detected' | 'reject-unavailable' | 'interaction-failed' | 'reject-tested';
24
77
  bannerFound: boolean;
25
78
  /** Name of the consent manager, if it was recognised. */
26
79
  cmp?: string;
@@ -35,6 +88,8 @@ export interface ConsentObservation {
35
88
  requests: RecordedRequest[];
36
89
  cookies: RecordedCookie[];
37
90
  };
91
+ /** True only if the reject observation window completed without load issues. */
92
+ afterRejectComplete?: boolean;
38
93
  error?: string;
39
94
  }
40
95
  /**
@@ -46,6 +101,8 @@ export interface PageSnapshot {
46
101
  finalUrl: string;
47
102
  status: number | null;
48
103
  requests: RecordedRequest[];
104
+ /** Initial-phase request count, including records beyond the retained evidence cap. */
105
+ requestCount?: number;
49
106
  cookies: RecordedCookie[];
50
107
  links: PageLink[];
51
108
  resourceHints: ResourceHint[];
@@ -53,6 +110,7 @@ export interface PageSnapshot {
53
110
  consent?: ConsentObservation;
54
111
  /** Set when the page could not be loaded, e.g. a download or a timeout. */
55
112
  error?: string;
113
+ capture?: CaptureObservation;
56
114
  }
57
115
  export interface Finding {
58
116
  ruleId: string;
@@ -63,10 +121,14 @@ export interface Finding {
63
121
  fix?: string;
64
122
  /** What the finding is about, e.g. a vendor. Keeps fingerprints stable across runs. */
65
123
  subject?: string;
124
+ /** Legal basis in the country the scan was run for. */
125
+ references?: string[];
66
126
  }
67
127
  export interface RuleContext {
68
128
  siteUrl: URL;
69
129
  page: PageSnapshot;
130
+ /** Language for messages and fixes. */
131
+ lang: Lang;
70
132
  }
71
133
  export interface Rule {
72
134
  id: string;
@@ -74,6 +136,8 @@ export interface Rule {
74
136
  description: string;
75
137
  /** Laws, rulings or guidelines the rule is based on. */
76
138
  references: string[];
139
+ /** Observations needed by custom rules. Missing means a complete document is required. */
140
+ requires?: readonly ObservationKind[];
77
141
  check(context: RuleContext): Finding[];
78
142
  }
79
143
  export interface PageSummary {
@@ -83,10 +147,14 @@ export interface PageSummary {
83
147
  requestCount: number;
84
148
  cookieCount: number;
85
149
  error?: string;
150
+ capture?: CaptureObservation;
151
+ ruleEvaluations?: RuleEvaluation[];
86
152
  }
87
153
  export type ConsentSummary = Omit<ConsentObservation, 'afterReject'>;
88
154
  export interface ScanResult {
89
155
  siteUrl: string;
156
+ lang: Lang;
157
+ region: Region;
90
158
  scannedAt: string;
91
159
  durationMs: number;
92
160
  pages: PageSummary[];
@@ -94,4 +162,6 @@ export interface ScanResult {
94
162
  consent?: ConsentSummary;
95
163
  findings: Finding[];
96
164
  rules: Array<Omit<Rule, 'check'>>;
165
+ /** Observation coverage, not a legal compliance judgement. */
166
+ coverage?: 'complete' | 'partial';
97
167
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "privalint",
3
- "version": "0.1.1",
3
+ "version": "0.3.0",
4
4
  "description": "Lint your website for GDPR problems: trackers before consent and after reject, Google Fonts, embeds, missing Impressum. Real browser, CLI and GitHub Action.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -15,7 +15,8 @@
15
15
  }
16
16
  },
17
17
  "files": [
18
- "dist"
18
+ "dist",
19
+ "README.de.md"
19
20
  ],
20
21
  "engines": {
21
22
  "node": ">=20.12"
@@ -41,7 +42,11 @@
41
42
  "impressum",
42
43
  "lint",
43
44
  "playwright",
44
- "github-action"
45
+ "github-action",
46
+ "tdddg",
47
+ "dsg",
48
+ "oesterreich",
49
+ "schweiz"
45
50
  ],
46
51
  "license": "MIT",
47
52
  "dependencies": {
@@ -55,8 +60,8 @@
55
60
  },
56
61
  "repository": {
57
62
  "type": "git",
58
- "url": "git+https://github.com/jexcellence/privalint.git"
63
+ "url": "git+https://github.com/JExcellence/privalint.git"
59
64
  },
60
- "homepage": "https://github.com/jexcellence/privalint#readme",
61
- "bugs": "https://github.com/jexcellence/privalint/issues"
65
+ "homepage": "https://github.com/JExcellence/privalint#readme",
66
+ "bugs": "https://github.com/JExcellence/privalint/issues"
62
67
  }