@link-foundation/sensitive-data-sanitizer 0.17.1 → 0.19.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/CHANGELOG.md +15 -0
- package/README.md +25 -0
- package/bin/sensitive-data-sanitizer.js +8 -0
- package/docs/API.md +266 -34
- package/docs/COVERAGE.md +135 -13
- package/docs/RULES.md +403 -0
- package/examples/custom-log-policy.mjs +23 -0
- package/examples/prose-logs.mjs +16 -0
- package/package.json +18 -2
- package/src/compiled-known.js +55 -0
- package/src/context.js +9 -1
- package/src/detection.js +11 -1
- package/src/document-catalog.js +246 -0
- package/src/document-dates.js +15 -0
- package/src/document-entities.js +27 -14
- package/src/document-experience.js +115 -0
- package/src/document-extras.js +96 -64
- package/src/document-fields.js +3 -1
- package/src/document-vocabulary.js +80 -68
- package/src/engines.js +93 -36
- package/src/index.d.ts +57 -4
- package/src/index.js +5 -0
- package/src/jsonl-stream.js +2 -5
- package/src/ner.d.ts +5 -0
- package/src/ner.js +96 -0
- package/src/personal-variants.js +29 -8
- package/src/placeholder-types.js +1 -0
- package/src/policy.js +107 -0
- package/src/propagation.js +5 -1
- package/src/proper-names.js +222 -0
- package/src/record-worker.js +6 -0
- package/src/rule-catalog.js +669 -0
- package/src/sanitizer.js +114 -21
- package/src/stream-worker.js +5 -0
- package/src/stream.js +11 -6
- package/src/strict.js +66 -0
- package/src/structured.js +131 -59
- package/src/sync-detectors.js +38 -0
- package/src/sync-sanitizer.js +16 -0
- package/src/transforms.js +85 -7
- package/src/vocabulary.js +217 -0
- package/src/worker-options.js +12 -0
package/docs/COVERAGE.md
CHANGED
|
@@ -60,7 +60,7 @@ The `documents` profile also reads employers from CV experience lines anchored o
|
|
|
60
60
|
a date range: `Title, Employer (2019-05 – 2021-03)`, `Title — Employer, 2019–2021`
|
|
61
61
|
and `Title (Employer), 2019–2021`. Ranges use years, `YYYY-MM`, `MM.YYYY`, full
|
|
62
62
|
dates or `month YYYY`, with `по настоящее время`, `н.в.`, `по н.в.`, `present` and
|
|
63
|
-
similar open ends.
|
|
63
|
+
similar open ends. For the non-parenthesized layouts, titles have one to nine words. A label of one or two words
|
|
64
64
|
before the title (`Опыт: `, `Опыт работы - `, `Опыт — `) is read like a list marker,
|
|
65
65
|
and a segment after `|` is read like a line start, so
|
|
66
66
|
`Должность: Менеджер | Опыт: Аналитик, Ромашка (2018 – 2020)` also yields the
|
|
@@ -68,9 +68,9 @@ employer. The date range stays the anchor and the employer must still start with
|
|
|
68
68
|
capital letter, a digit or a quote. Confidence is 0.60, and the employer then
|
|
69
69
|
propagates through the document like other organizations. The default profile
|
|
70
70
|
ignores these lines. Lines
|
|
71
|
-
with
|
|
72
|
-
|
|
73
|
-
the same shape can be flagged.
|
|
71
|
+
with parenthesized ranges additionally support arbitrary title prefixes,
|
|
72
|
+
comma-containing employers and titles, as described below. Date-anchored prose
|
|
73
|
+
with the same shape can be flagged.
|
|
74
74
|
|
|
75
75
|
The gazetteer has only the cities/regions explicitly listed in that module,
|
|
76
76
|
including Springfield and Riverton. It is not a worldwide geocoder. Location
|
|
@@ -243,16 +243,17 @@ The `documents` profile additionally recognizes:
|
|
|
243
243
|
- Company/employer/workplace labels, capitalized company/holding/bank names,
|
|
244
244
|
employment prose and year-range employer lines; residence/metro/district
|
|
245
245
|
labels and contextual six-digit postal codes.
|
|
246
|
-
- Russian street-plus-house/flat structures
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
inspect and review standalone public addresses before publication.
|
|
246
|
+
- Russian street-plus-house/flat structures, including inline addresses added
|
|
247
|
+
by #117. `Музей находится на ул. Ленина, д. 5, кв. 12` now removes the full
|
|
248
|
+
street/house/apartment span. Public addresses have the same shape as personal
|
|
249
|
+
addresses and may be flagged; inspect them before publication.
|
|
251
250
|
|
|
252
251
|
Explicit limits accepted for the ambiguous examples in #82/#83:
|
|
253
|
-
`Спасибо, Анна!`, `Анна, добрый
|
|
252
|
+
`Спасибо, Анна!`, `Анна, добрый день!` and `Менеджер Анна`
|
|
254
253
|
remain unchanged without known values or additional context. Bare given names
|
|
255
254
|
and unmarked employer/title fragments cannot reliably establish identity.
|
|
255
|
+
The reversed employer cue `Лютик, менеджер` now redacts `Лютик`, as requested
|
|
256
|
+
by #117. English `Worked at Romashka` now redacts the employer under #115.
|
|
256
257
|
`зарплата 150 000 руб.` and `Salary: 150000` also remain unchanged: salary is
|
|
257
258
|
outside the native document rules. Supply `knownPersonal` or an additional
|
|
258
259
|
recognizer when these fields must be removed. Initial/name rules do not cover
|
|
@@ -376,7 +377,8 @@ Employer labels consume multi-word values to a comma or line/quote boundary.
|
|
|
376
377
|
Dated CV entries additionally support compound titles, title parentheses/slashes,
|
|
377
378
|
quoted group names, legal-form/location suffixes, empty/`null` range ends,
|
|
378
379
|
multiple entries, employer-first role entries and pipe-separated entries.
|
|
379
|
-
The
|
|
380
|
+
The parenthesized date-anchor scanner accepts arbitrary forward titles and
|
|
381
|
+
lead-ins. The reversed `Company, manager` layout retains finite role cues. Undated
|
|
380
382
|
`Менеджер, Ромашка Лютик`, `- Ромашка Лютик 5 лет` and
|
|
381
383
|
`Последнее место работы Ромашка Лютик` remain explicit ambiguous limits, as
|
|
382
384
|
permitted by #94. Use known values or schema declarations for them.
|
|
@@ -385,8 +387,7 @@ Company prose recognizes `компании «…»`, `опыт в`, `Роль в
|
|
|
385
387
|
capitalized lists after those cues, `опыт (Company Name)`, and names followed
|
|
386
388
|
by `(2 года)`/`(3 года)`-style durations. Arbitrary uncued company words remain
|
|
387
389
|
ambiguous; synchronous callers can supply `knownPersonal` lists or precomputed
|
|
388
|
-
`findings`.
|
|
389
|
-
only. No model or network service is required for these native rules.
|
|
390
|
+
`findings`. Array-returning custom `detectors` also run in synchronous methods; Promise-returning callbacks require `createSanitizer`. No model or network service is required for these native rules.
|
|
390
391
|
|
|
391
392
|
`контакт:` and `пишите,` introduce domestic mobile numbers. A complete line
|
|
392
393
|
consisting of an 11-digit `79…` or `89…` mobile shape is also sensitive. Arbitrary
|
|
@@ -402,3 +403,124 @@ redaction replaces the fragment instead of repeating an unchanged result until
|
|
|
402
403
|
|
|
403
404
|
The [requirement inventory and research](case-studies/issue-100/README.md)
|
|
404
405
|
records the complete scope and regression evidence.
|
|
406
|
+
|
|
407
|
+
## Dated entries and optional prose (issues 104–106)
|
|
408
|
+
|
|
409
|
+
Parenthesized date anchors now delimit successive entries on the same line,
|
|
410
|
+
including arbitrary title words and lead-ins (`Стаж = 5 лет:`, `= 3 года,`).
|
|
411
|
+
Employers can contain comma suffixes, nested parentheses, slash-separated names,
|
|
412
|
+
legal forms and multiple words. A pipe after the employer preserves the industry
|
|
413
|
+
suffix. `Founder, consultant, Romashka (2019–2021)` and equivalent Russian entries
|
|
414
|
+
share the parser. The employer must start with a capital letter, a quote, or a
|
|
415
|
+
company-style digit/letter combination. **Lowercase employers remain a limit**:
|
|
416
|
+
`Менеджер, romashka (2019–2021)` / `Менеджер, ромашка (2019–2021)` stay, preserving
|
|
417
|
+
ambiguous controls such as `Встреча, понедельник (2019–2021)`.
|
|
418
|
+
|
|
419
|
+
`ГК` and `группа компаний` introduce quoted or unquoted group names. Employment
|
|
420
|
+
lists continue through repeated `, потом в` and `, затем в` items. Domestic
|
|
421
|
+
`79…`/`89…` mobile shapes after a comma work in Russian and English text; plain
|
|
422
|
+
`order`/`id` numbers stay. School detection also covers `(МГУ, 2012)`,
|
|
423
|
+
`Образование (МГУ)`, `в МГТУ им. Баумана` and `Обучение: Skillbox, 2020`.
|
|
424
|
+
The acronym vocabulary remains finite.
|
|
425
|
+
|
|
426
|
+
Weak organization experience/duration cues preserve a small explicit technology
|
|
427
|
+
set: Python, Go, SQL, Excel, Power BI, Kubernetes, PostgreSQL, Telegram and Scrum.
|
|
428
|
+
This is an ambiguity policy, not general recognition of every product; a real
|
|
429
|
+
employer with one of those names needs `knownPersonal` or a declared sensitive
|
|
430
|
+
field. Explicit private values/fields and stronger legal-form cues still win.
|
|
431
|
+
|
|
432
|
+
Document share-link detection also recognizes HTTP(S) URLs containing `/d/<id>`
|
|
433
|
+
for any host and nonempty id length. Ordinary website roots remain a limit.
|
|
434
|
+
Relative operational paths such as `{"location":"apps/api"}` stay; explicit
|
|
435
|
+
personal paths and credential rules remain active. Caller-sensitive field names
|
|
436
|
+
now use the same case/underscore/hyphen normalization as built-in document names.
|
|
437
|
+
|
|
438
|
+
For no-cue prose, enable [`properNames: 'capitalized'`](API.md#structural-prose-redaction).
|
|
439
|
+
It activates per value, defaults to Cyrillic script letters or structural data
|
|
440
|
+
triggers, and supports configurable Unicode scripts. It intentionally loses
|
|
441
|
+
capitalized ordinary words. Labels, lowercase words, dates, paths, JSON keys and
|
|
442
|
+
untriggered values are outside this additional rule. English untriggered prose,
|
|
443
|
+
lowercase names and a name immediately before a colon remain documented limits.
|
|
444
|
+
The API describes the interaction with existing detectors and placeholder modes.
|
|
445
|
+
|
|
446
|
+
The date-anchor scan visits each preceding entry once; the optional prose scanner
|
|
447
|
+
classifies maximal tokens in fixed passes, avoiding repeated suffix searches on
|
|
448
|
+
long unspaced input. This is not a latency guarantee for the whole pipeline.
|
|
449
|
+
[The requirement inventory](case-studies/issue-106/README.md) records alternatives,
|
|
450
|
+
research, regression evidence and reproducible bounded performance measurements.
|
|
451
|
+
|
|
452
|
+
## Stored-log policy and expanded document catalog
|
|
453
|
+
|
|
454
|
+
The documents profile now covers the exact positive/control rows of issues
|
|
455
|
+
115 and 117: spaced/obfuscated contacts, Discord/MAX handles, additional
|
|
456
|
+
personal-link routes, birthday/age cues, military/VIN identifiers, employer and
|
|
457
|
+
school layouts, patronymics/initials/mixed scripts, complete street/house spans,
|
|
458
|
+
regions, citizenship and plates. Regression tests compare complete text output
|
|
459
|
+
and JSON/JSONL output in both engines. `ОГРНИП` without a value, operational logs,
|
|
460
|
+
work durations, weekdays and technologies remain unchanged. Bare `Сергей`,
|
|
461
|
+
`Смирнова`, `Смирновой`, `СПб`, `Питер` and `Вчера Анна написала` deliberately remain
|
|
462
|
+
ambiguous without explicit known values, the optional recognizer or aggressive
|
|
463
|
+
proper-name mode. Romashka Inc keeps its existing legal-form treatment.
|
|
464
|
+
|
|
465
|
+
`strict: true` independently opts into personal attributes (gender, family,
|
|
466
|
+
ethnicity, religion, health, criminal/political status) and bare dates, ages,
|
|
467
|
+
passport/mobile/postal shapes. It is intended for stored personal prose and can
|
|
468
|
+
remove ordinary uses of these words. It preserves ISO timestamps, work durations,
|
|
469
|
+
unit-bearing request timings and `Дети: нет`. In JSON/JSONL, numeric primitives
|
|
470
|
+
stay numeric, and `structuralFields` suppresses strict findings in declared
|
|
471
|
+
string metadata. Credentials/known values still take priority. Strict is off by
|
|
472
|
+
default and does not claim to identify every sensitive attribute or validate
|
|
473
|
+
that every date/number belongs to a real person.
|
|
474
|
+
|
|
475
|
+
## Optional offline recognition measurements
|
|
476
|
+
|
|
477
|
+
The `/ner` entry uses the separately installed optional
|
|
478
|
+
[NLP.js](https://github.com/axa-group/nlp.js) regex extractor, finite multilingual
|
|
479
|
+
name/place dictionaries and bounded contextual rules. It has synchronous
|
|
480
|
+
inference after `await createNerDetector(...)`. There is no analysis-time network,
|
|
481
|
+
external service, Python process or model download. The core import graph never
|
|
482
|
+
imports `/ner` or NLP.js, so the optional component has no runtime or loading cost
|
|
483
|
+
when disabled. This is symbolic recognition, not general statistical NER; unknown
|
|
484
|
+
cue-free surnames, schools, employers and locations can be missed.
|
|
485
|
+
|
|
486
|
+
[Test set](../experiments/issue-118/ner-corpus.json): 32 synthetic sentences,
|
|
487
|
+
including all six requested positives, eight benign controls and eight challenge
|
|
488
|
+
misses. Exact type and UTF-16 start/end must all match. **All** rows, including
|
|
489
|
+
challenge rows, contribute to these scores. Each language/type has two true
|
|
490
|
+
positives, zero false positives and one false negative:
|
|
491
|
+
|
|
492
|
+
| Language | Type | Precision | Recall | TP / FP / FN |
|
|
493
|
+
| -------- | ------------ | --------- | ------ | ------------ |
|
|
494
|
+
| ru | PERSON | 100% | 66.7% | 2 / 0 / 1 |
|
|
495
|
+
| ru | ORGANIZATION | 100% | 66.7% | 2 / 0 / 1 |
|
|
496
|
+
| ru | EDUCATION | 100% | 66.7% | 2 / 0 / 1 |
|
|
497
|
+
| ru | LOCATION | 100% | 66.7% | 2 / 0 / 1 |
|
|
498
|
+
| en | PERSON | 100% | 66.7% | 2 / 0 / 1 |
|
|
499
|
+
| en | ORGANIZATION | 100% | 66.7% | 2 / 0 / 1 |
|
|
500
|
+
| en | EDUCATION | 100% | 66.7% | 2 / 0 / 1 |
|
|
501
|
+
| en | LOCATION | 100% | 66.7% | 2 / 0 / 1 |
|
|
502
|
+
|
|
503
|
+
These small authored regression scores do not estimate accuracy on arbitrary
|
|
504
|
+
user documents. The test includes explicit misses such as `Zephyr Quill`,
|
|
505
|
+
`Asterion expanded rapidly`, `ETHZ` and `Reykjavik` so dictionary limits remain
|
|
506
|
+
visible. Acceptance rows also test both sanitizers and unchanged controls.
|
|
507
|
+
|
|
508
|
+
On Node v26.13.0, Linux x64, five bounded groups, the median warm detector cost
|
|
509
|
+
was **0.0434 ms per 1 KiB** of ASCII employer prose; module loading plus factory
|
|
510
|
+
initialization took **48.6 ms** once. Cost excludes the sanitizer's other
|
|
511
|
+
recognizers and varies with text and hardware. No latency guarantee is implied.
|
|
512
|
+
|
|
513
|
+
## Compiled known-value measurements
|
|
514
|
+
|
|
515
|
+
On the same host and 1 KiB operational record, median complete `sanitize` cost
|
|
516
|
+
was **2.80 ms** without a list, **3.30 ms** with 500 compiled values and **8.56 ms**
|
|
517
|
+
with a plain array. A direct 500-value matching pass cost **0.73 ms**. Validation
|
|
518
|
+
cost was **0.00416 ms** without a list and **0.00356 ms** compiled (the difference
|
|
519
|
+
is measurement noise), below the allowed 10% of the no-list call, excluding
|
|
520
|
+
matching. Compilation skips per-record entry validation, normalization and regex
|
|
521
|
+
construction; matching itself still scales with list length and pipeline passes.
|
|
522
|
+
|
|
523
|
+
Reproduce with `node --max-old-space-size=384 experiments/issue-118/measure.mjs`.
|
|
524
|
+
The [raw results](case-studies/issue-118/measurements.json) include environment,
|
|
525
|
+
exact-span counts and measurement method. This fixed experiment uses no large
|
|
526
|
+
corpus, unbounded input, parallel builds or external analysis service.
|
package/docs/RULES.md
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# Stable finding rule identifiers
|
|
2
|
+
|
|
3
|
+
Use keys from the exported frozen `builtInRules` list in `rules: {id: false}`.
|
|
4
|
+
Unknown ids fail with ERR_CONFIG. Credential and mixed encoded/scanner rules
|
|
5
|
+
require `allowCredentialDisable: true`. A disabled rule does not suppress
|
|
6
|
+
another rule's overlapping finding. Rules gate automatic built-in findings;
|
|
7
|
+
explicit findings and custom callbacks retain caller policy. See [API](API.md#rule-type-and-field-policy).
|
|
8
|
+
|
|
9
|
+
Regenerate with `node experiments/issue-118/generate-rule-catalog.mjs`.
|
|
10
|
+
|
|
11
|
+
- `age-context`
|
|
12
|
+
- `anthropic`
|
|
13
|
+
- `authorization`
|
|
14
|
+
- `aws`
|
|
15
|
+
- `birth-context`
|
|
16
|
+
- `birth-year`
|
|
17
|
+
- `booking-context`
|
|
18
|
+
- `booking-filename`
|
|
19
|
+
- `catalog/aws`
|
|
20
|
+
- `catalog/azure`
|
|
21
|
+
- `catalog/google`
|
|
22
|
+
- `cli-password`
|
|
23
|
+
- `context`
|
|
24
|
+
- `cookie`
|
|
25
|
+
- `databricks`
|
|
26
|
+
- `detect-secrets`
|
|
27
|
+
- `digitalocean`
|
|
28
|
+
- `discord`
|
|
29
|
+
- `document-age-unit`
|
|
30
|
+
- `document-at-employer`
|
|
31
|
+
- `document-bank-suffix`
|
|
32
|
+
- `document-birthday`
|
|
33
|
+
- `document-citizenship`
|
|
34
|
+
- `document-email-obfuscation`
|
|
35
|
+
- `document-email-spaces`
|
|
36
|
+
- `document-employer-dash`
|
|
37
|
+
- `document-employer-prefix`
|
|
38
|
+
- `document-industry`
|
|
39
|
+
- `document-introduction`
|
|
40
|
+
- `document-legal-lowercase`
|
|
41
|
+
- `document-local-phone`
|
|
42
|
+
- `document-messenger`
|
|
43
|
+
- `document-military-id`
|
|
44
|
+
- `document-mixed-name`
|
|
45
|
+
- `document-mobile-bare`
|
|
46
|
+
- `document-mobile-comma`
|
|
47
|
+
- `document-mobile-context`
|
|
48
|
+
- `document-msu`
|
|
49
|
+
- `document-patronymic`
|
|
50
|
+
- `document-personal-path`
|
|
51
|
+
- `document-phone`
|
|
52
|
+
- `document-phone-label`
|
|
53
|
+
- `document-propagation`
|
|
54
|
+
- `document-region`
|
|
55
|
+
- `document-resume-link`
|
|
56
|
+
- `document-reversed-role`
|
|
57
|
+
- `document-role`
|
|
58
|
+
- `document-role-employer`
|
|
59
|
+
- `document-school-context`
|
|
60
|
+
- `document-school-year`
|
|
61
|
+
- `document-share-link`
|
|
62
|
+
- `document-share-path`
|
|
63
|
+
- `document-social-link`
|
|
64
|
+
- `document-spaced-mobile`
|
|
65
|
+
- `document-street`
|
|
66
|
+
- `document-street-complete`
|
|
67
|
+
- `document-unfamiliar-name`
|
|
68
|
+
- `document-uppercase-initials`
|
|
69
|
+
- `document-vehicle-plate`
|
|
70
|
+
- `document-vin`
|
|
71
|
+
- `education-acronym`
|
|
72
|
+
- `education-bracket`
|
|
73
|
+
- `education-label`
|
|
74
|
+
- `education-named-acronym`
|
|
75
|
+
- `education-russian`
|
|
76
|
+
- `education-training`
|
|
77
|
+
- `education-type`
|
|
78
|
+
- `email`
|
|
79
|
+
- `employer-label`
|
|
80
|
+
- `employer-prose`
|
|
81
|
+
- `employer-years`
|
|
82
|
+
- `encoded`
|
|
83
|
+
- `encoded-engine`
|
|
84
|
+
- `encoded-limit`
|
|
85
|
+
- `entity-format`
|
|
86
|
+
- `entropy`
|
|
87
|
+
- `firebase`
|
|
88
|
+
- `gender-context`
|
|
89
|
+
- `github`
|
|
90
|
+
- `gitlab`
|
|
91
|
+
- `gitlab-runner`
|
|
92
|
+
- `gitleaks/1password-secret-key`
|
|
93
|
+
- `gitleaks/1password-service-account-token`
|
|
94
|
+
- `gitleaks/adafruit-api-key`
|
|
95
|
+
- `gitleaks/adobe-client-id`
|
|
96
|
+
- `gitleaks/adobe-client-secret`
|
|
97
|
+
- `gitleaks/age-secret-key`
|
|
98
|
+
- `gitleaks/airtable-api-key`
|
|
99
|
+
- `gitleaks/airtable-personnal-access-token`
|
|
100
|
+
- `gitleaks/algolia-api-key`
|
|
101
|
+
- `gitleaks/alibaba-access-key-id`
|
|
102
|
+
- `gitleaks/alibaba-secret-key`
|
|
103
|
+
- `gitleaks/anthropic-admin-api-key`
|
|
104
|
+
- `gitleaks/anthropic-api-key`
|
|
105
|
+
- `gitleaks/artifactory-api-key`
|
|
106
|
+
- `gitleaks/artifactory-reference-token`
|
|
107
|
+
- `gitleaks/asana-client-id`
|
|
108
|
+
- `gitleaks/asana-client-secret`
|
|
109
|
+
- `gitleaks/atlassian-api-token`
|
|
110
|
+
- `gitleaks/authress-service-client-access-key`
|
|
111
|
+
- `gitleaks/aws-access-token`
|
|
112
|
+
- `gitleaks/aws-amazon-bedrock-api-key-long-lived`
|
|
113
|
+
- `gitleaks/aws-amazon-bedrock-api-key-short-lived`
|
|
114
|
+
- `gitleaks/azure-ad-client-secret`
|
|
115
|
+
- `gitleaks/beamer-api-token`
|
|
116
|
+
- `gitleaks/bitbucket-client-id`
|
|
117
|
+
- `gitleaks/bitbucket-client-secret`
|
|
118
|
+
- `gitleaks/bittrex-access-key`
|
|
119
|
+
- `gitleaks/bittrex-secret-key`
|
|
120
|
+
- `gitleaks/cisco-meraki-api-key`
|
|
121
|
+
- `gitleaks/clickhouse-cloud-api-secret-key`
|
|
122
|
+
- `gitleaks/clojars-api-token`
|
|
123
|
+
- `gitleaks/cloudflare-api-key`
|
|
124
|
+
- `gitleaks/cloudflare-global-api-key`
|
|
125
|
+
- `gitleaks/cloudflare-origin-ca-key`
|
|
126
|
+
- `gitleaks/codecov-access-token`
|
|
127
|
+
- `gitleaks/cohere-api-token`
|
|
128
|
+
- `gitleaks/coinbase-access-token`
|
|
129
|
+
- `gitleaks/confluent-access-token`
|
|
130
|
+
- `gitleaks/confluent-secret-key`
|
|
131
|
+
- `gitleaks/contentful-delivery-api-token`
|
|
132
|
+
- `gitleaks/curl-auth-header`
|
|
133
|
+
- `gitleaks/curl-auth-user`
|
|
134
|
+
- `gitleaks/databricks-api-token`
|
|
135
|
+
- `gitleaks/datadog-access-token`
|
|
136
|
+
- `gitleaks/defined-networking-api-token`
|
|
137
|
+
- `gitleaks/digitalocean-access-token`
|
|
138
|
+
- `gitleaks/digitalocean-pat`
|
|
139
|
+
- `gitleaks/digitalocean-refresh-token`
|
|
140
|
+
- `gitleaks/discord-api-token`
|
|
141
|
+
- `gitleaks/discord-client-id`
|
|
142
|
+
- `gitleaks/discord-client-secret`
|
|
143
|
+
- `gitleaks/doppler-api-token`
|
|
144
|
+
- `gitleaks/droneci-access-token`
|
|
145
|
+
- `gitleaks/dropbox-api-token`
|
|
146
|
+
- `gitleaks/dropbox-long-lived-api-token`
|
|
147
|
+
- `gitleaks/dropbox-short-lived-api-token`
|
|
148
|
+
- `gitleaks/duffel-api-token`
|
|
149
|
+
- `gitleaks/dynatrace-api-token`
|
|
150
|
+
- `gitleaks/easypost-api-token`
|
|
151
|
+
- `gitleaks/easypost-test-api-token`
|
|
152
|
+
- `gitleaks/etsy-access-token`
|
|
153
|
+
- `gitleaks/facebook-access-token`
|
|
154
|
+
- `gitleaks/facebook-page-access-token`
|
|
155
|
+
- `gitleaks/facebook-secret`
|
|
156
|
+
- `gitleaks/fastly-api-token`
|
|
157
|
+
- `gitleaks/finicity-api-token`
|
|
158
|
+
- `gitleaks/finicity-client-secret`
|
|
159
|
+
- `gitleaks/finnhub-access-token`
|
|
160
|
+
- `gitleaks/flickr-access-token`
|
|
161
|
+
- `gitleaks/flutterwave-encryption-key`
|
|
162
|
+
- `gitleaks/flutterwave-public-key`
|
|
163
|
+
- `gitleaks/flutterwave-secret-key`
|
|
164
|
+
- `gitleaks/flyio-access-token`
|
|
165
|
+
- `gitleaks/frameio-api-token`
|
|
166
|
+
- `gitleaks/freemius-secret-key`
|
|
167
|
+
- `gitleaks/freshbooks-access-token`
|
|
168
|
+
- `gitleaks/gcp-api-key`
|
|
169
|
+
- `gitleaks/generic-api-key`
|
|
170
|
+
- `gitleaks/github-app-token`
|
|
171
|
+
- `gitleaks/github-fine-grained-pat`
|
|
172
|
+
- `gitleaks/github-oauth`
|
|
173
|
+
- `gitleaks/github-pat`
|
|
174
|
+
- `gitleaks/github-refresh-token`
|
|
175
|
+
- `gitleaks/gitlab-cicd-job-token`
|
|
176
|
+
- `gitleaks/gitlab-deploy-token`
|
|
177
|
+
- `gitleaks/gitlab-feature-flag-client-token`
|
|
178
|
+
- `gitleaks/gitlab-feed-token`
|
|
179
|
+
- `gitleaks/gitlab-incoming-mail-token`
|
|
180
|
+
- `gitleaks/gitlab-kubernetes-agent-token`
|
|
181
|
+
- `gitleaks/gitlab-oauth-app-secret`
|
|
182
|
+
- `gitleaks/gitlab-pat`
|
|
183
|
+
- `gitleaks/gitlab-pat-routable`
|
|
184
|
+
- `gitleaks/gitlab-ptt`
|
|
185
|
+
- `gitleaks/gitlab-rrt`
|
|
186
|
+
- `gitleaks/gitlab-runner-authentication-token`
|
|
187
|
+
- `gitleaks/gitlab-runner-authentication-token-routable`
|
|
188
|
+
- `gitleaks/gitlab-scim-token`
|
|
189
|
+
- `gitleaks/gitlab-session-cookie`
|
|
190
|
+
- `gitleaks/gitter-access-token`
|
|
191
|
+
- `gitleaks/gocardless-api-token`
|
|
192
|
+
- `gitleaks/grafana-api-key`
|
|
193
|
+
- `gitleaks/grafana-cloud-api-token`
|
|
194
|
+
- `gitleaks/grafana-service-account-token`
|
|
195
|
+
- `gitleaks/harness-api-key`
|
|
196
|
+
- `gitleaks/hashicorp-tf-api-token`
|
|
197
|
+
- `gitleaks/hashicorp-tf-password`
|
|
198
|
+
- `gitleaks/heroku-api-key`
|
|
199
|
+
- `gitleaks/heroku-api-key-v2`
|
|
200
|
+
- `gitleaks/hubspot-api-key`
|
|
201
|
+
- `gitleaks/huggingface-access-token`
|
|
202
|
+
- `gitleaks/huggingface-organization-api-token`
|
|
203
|
+
- `gitleaks/infracost-api-token`
|
|
204
|
+
- `gitleaks/intercom-api-key`
|
|
205
|
+
- `gitleaks/intra42-client-secret`
|
|
206
|
+
- `gitleaks/jfrog-api-key`
|
|
207
|
+
- `gitleaks/jfrog-identity-token`
|
|
208
|
+
- `gitleaks/jwt`
|
|
209
|
+
- `gitleaks/jwt-base64`
|
|
210
|
+
- `gitleaks/kraken-access-token`
|
|
211
|
+
- `gitleaks/kubernetes-secret-yaml`
|
|
212
|
+
- `gitleaks/kucoin-access-token`
|
|
213
|
+
- `gitleaks/kucoin-secret-key`
|
|
214
|
+
- `gitleaks/launchdarkly-access-token`
|
|
215
|
+
- `gitleaks/linear-api-key`
|
|
216
|
+
- `gitleaks/linear-client-secret`
|
|
217
|
+
- `gitleaks/linkedin-client-id`
|
|
218
|
+
- `gitleaks/linkedin-client-secret`
|
|
219
|
+
- `gitleaks/lob-api-key`
|
|
220
|
+
- `gitleaks/lob-pub-api-key`
|
|
221
|
+
- `gitleaks/looker-client-id`
|
|
222
|
+
- `gitleaks/looker-client-secret`
|
|
223
|
+
- `gitleaks/mailchimp-api-key`
|
|
224
|
+
- `gitleaks/mailgun-private-api-token`
|
|
225
|
+
- `gitleaks/mailgun-pub-key`
|
|
226
|
+
- `gitleaks/mailgun-signing-key`
|
|
227
|
+
- `gitleaks/mapbox-api-token`
|
|
228
|
+
- `gitleaks/mattermost-access-token`
|
|
229
|
+
- `gitleaks/maxmind-license-key`
|
|
230
|
+
- `gitleaks/messagebird-api-token`
|
|
231
|
+
- `gitleaks/messagebird-client-id`
|
|
232
|
+
- `gitleaks/microsoft-teams-webhook`
|
|
233
|
+
- `gitleaks/netlify-access-token`
|
|
234
|
+
- `gitleaks/new-relic-browser-api-token`
|
|
235
|
+
- `gitleaks/new-relic-insert-key`
|
|
236
|
+
- `gitleaks/new-relic-user-api-id`
|
|
237
|
+
- `gitleaks/new-relic-user-api-key`
|
|
238
|
+
- `gitleaks/notion-api-token`
|
|
239
|
+
- `gitleaks/npm-access-token`
|
|
240
|
+
- `gitleaks/nuget-config-password`
|
|
241
|
+
- `gitleaks/nytimes-access-token`
|
|
242
|
+
- `gitleaks/octopus-deploy-api-key`
|
|
243
|
+
- `gitleaks/okta-access-token`
|
|
244
|
+
- `gitleaks/openai-api-key`
|
|
245
|
+
- `gitleaks/openshift-user-token`
|
|
246
|
+
- `gitleaks/perplexity-api-key`
|
|
247
|
+
- `gitleaks/pkcs12-file`
|
|
248
|
+
- `gitleaks/plaid-api-token`
|
|
249
|
+
- `gitleaks/plaid-client-id`
|
|
250
|
+
- `gitleaks/plaid-secret-key`
|
|
251
|
+
- `gitleaks/planetscale-api-token`
|
|
252
|
+
- `gitleaks/planetscale-oauth-token`
|
|
253
|
+
- `gitleaks/planetscale-password`
|
|
254
|
+
- `gitleaks/postman-api-token`
|
|
255
|
+
- `gitleaks/prefect-api-token`
|
|
256
|
+
- `gitleaks/private-key`
|
|
257
|
+
- `gitleaks/privateai-api-token`
|
|
258
|
+
- `gitleaks/pulumi-api-token`
|
|
259
|
+
- `gitleaks/pypi-upload-token`
|
|
260
|
+
- `gitleaks/rapidapi-access-token`
|
|
261
|
+
- `gitleaks/readme-api-token`
|
|
262
|
+
- `gitleaks/rubygems-api-token`
|
|
263
|
+
- `gitleaks/scalingo-api-token`
|
|
264
|
+
- `gitleaks/sendbird-access-id`
|
|
265
|
+
- `gitleaks/sendbird-access-token`
|
|
266
|
+
- `gitleaks/sendgrid-api-token`
|
|
267
|
+
- `gitleaks/sendinblue-api-token`
|
|
268
|
+
- `gitleaks/sentry-access-token`
|
|
269
|
+
- `gitleaks/sentry-org-token`
|
|
270
|
+
- `gitleaks/sentry-user-token`
|
|
271
|
+
- `gitleaks/settlemint-application-access-token`
|
|
272
|
+
- `gitleaks/settlemint-personal-access-token`
|
|
273
|
+
- `gitleaks/settlemint-service-access-token`
|
|
274
|
+
- `gitleaks/shippo-api-token`
|
|
275
|
+
- `gitleaks/shopify-access-token`
|
|
276
|
+
- `gitleaks/shopify-custom-access-token`
|
|
277
|
+
- `gitleaks/shopify-private-app-access-token`
|
|
278
|
+
- `gitleaks/shopify-shared-secret`
|
|
279
|
+
- `gitleaks/sidekiq-secret`
|
|
280
|
+
- `gitleaks/sidekiq-sensitive-url`
|
|
281
|
+
- `gitleaks/slack-app-token`
|
|
282
|
+
- `gitleaks/slack-bot-token`
|
|
283
|
+
- `gitleaks/slack-config-access-token`
|
|
284
|
+
- `gitleaks/slack-config-refresh-token`
|
|
285
|
+
- `gitleaks/slack-legacy-bot-token`
|
|
286
|
+
- `gitleaks/slack-legacy-token`
|
|
287
|
+
- `gitleaks/slack-legacy-workspace-token`
|
|
288
|
+
- `gitleaks/slack-user-token`
|
|
289
|
+
- `gitleaks/slack-webhook-url`
|
|
290
|
+
- `gitleaks/snyk-api-token`
|
|
291
|
+
- `gitleaks/sonar-api-token`
|
|
292
|
+
- `gitleaks/sourcegraph-access-token`
|
|
293
|
+
- `gitleaks/square-access-token`
|
|
294
|
+
- `gitleaks/squarespace-access-token`
|
|
295
|
+
- `gitleaks/stripe-access-token`
|
|
296
|
+
- `gitleaks/sumologic-access-id`
|
|
297
|
+
- `gitleaks/sumologic-access-token`
|
|
298
|
+
- `gitleaks/telegram-bot-api-token`
|
|
299
|
+
- `gitleaks/travisci-access-token`
|
|
300
|
+
- `gitleaks/twilio-api-key`
|
|
301
|
+
- `gitleaks/twitch-api-token`
|
|
302
|
+
- `gitleaks/twitter-access-secret`
|
|
303
|
+
- `gitleaks/twitter-access-token`
|
|
304
|
+
- `gitleaks/twitter-api-key`
|
|
305
|
+
- `gitleaks/twitter-api-secret`
|
|
306
|
+
- `gitleaks/twitter-bearer-token`
|
|
307
|
+
- `gitleaks/typeform-api-token`
|
|
308
|
+
- `gitleaks/vault-batch-token`
|
|
309
|
+
- `gitleaks/vault-service-token`
|
|
310
|
+
- `gitleaks/yandex-access-token`
|
|
311
|
+
- `gitleaks/yandex-api-key`
|
|
312
|
+
- `gitleaks/yandex-aws-access-token`
|
|
313
|
+
- `gitleaks/zendesk-secret-key`
|
|
314
|
+
- `google`
|
|
315
|
+
- `grafana`
|
|
316
|
+
- `home-path`
|
|
317
|
+
- `huggingface`
|
|
318
|
+
- `iban-checksum`
|
|
319
|
+
- `identity-number`
|
|
320
|
+
- `international-phone`
|
|
321
|
+
- `ipv4`
|
|
322
|
+
- `ipv6`
|
|
323
|
+
- `jwt`
|
|
324
|
+
- `known-personal`
|
|
325
|
+
- `known-secret`
|
|
326
|
+
- `label`
|
|
327
|
+
- `linear`
|
|
328
|
+
- `location-gazetteer`
|
|
329
|
+
- `location-label`
|
|
330
|
+
- `location-residence`
|
|
331
|
+
- `luhn`
|
|
332
|
+
- `mac`
|
|
333
|
+
- `mailchimp`
|
|
334
|
+
- `messenger-handle`
|
|
335
|
+
- `messenger-label`
|
|
336
|
+
- `mrz`
|
|
337
|
+
- `name-case`
|
|
338
|
+
- `name-document`
|
|
339
|
+
- `name-gazetteer`
|
|
340
|
+
- `name-identity`
|
|
341
|
+
- `name-initials`
|
|
342
|
+
- `name-title`
|
|
343
|
+
- `name-variant`
|
|
344
|
+
- `national-format`
|
|
345
|
+
- `national-phone`
|
|
346
|
+
- `ner/offline`
|
|
347
|
+
- `netrc`
|
|
348
|
+
- `notion`
|
|
349
|
+
- `npm`
|
|
350
|
+
- `nuget`
|
|
351
|
+
- `numbered-school`
|
|
352
|
+
- `obfuscated-email`
|
|
353
|
+
- `openai`
|
|
354
|
+
- `organization-context`
|
|
355
|
+
- `organization-continuation`
|
|
356
|
+
- `organization-core`
|
|
357
|
+
- `organization-cue`
|
|
358
|
+
- `organization-duration`
|
|
359
|
+
- `organization-experience`
|
|
360
|
+
- `organization-experience-prose`
|
|
361
|
+
- `organization-group`
|
|
362
|
+
- `organization-group-cue`
|
|
363
|
+
- `organization-legal-form`
|
|
364
|
+
- `passport-format`
|
|
365
|
+
- `passport-issuer`
|
|
366
|
+
- `passport-series`
|
|
367
|
+
- `personal-profile-url`
|
|
368
|
+
- `phone-context`
|
|
369
|
+
- `postal-address`
|
|
370
|
+
- `postal-code`
|
|
371
|
+
- `presidio`
|
|
372
|
+
- `private-key`
|
|
373
|
+
- `profile-attribute`
|
|
374
|
+
- `profile-prose`
|
|
375
|
+
- `proper-name-prose`
|
|
376
|
+
- `putty-private-key`
|
|
377
|
+
- `pypi`
|
|
378
|
+
- `quoted-entropy`
|
|
379
|
+
- `residence-prose`
|
|
380
|
+
- `school-acronym-context`
|
|
381
|
+
- `secretlint`
|
|
382
|
+
- `sendgrid`
|
|
383
|
+
- `shopify`
|
|
384
|
+
- `slack`
|
|
385
|
+
- `square`
|
|
386
|
+
- `strict-age`
|
|
387
|
+
- `strict-attribute`
|
|
388
|
+
- `strict-date`
|
|
389
|
+
- `strict-number`
|
|
390
|
+
- `stripe`
|
|
391
|
+
- `tel-uri`
|
|
392
|
+
- `telegram`
|
|
393
|
+
- `ticket-context`
|
|
394
|
+
- `twilio`
|
|
395
|
+
- `url-userinfo`
|
|
396
|
+
- `us-ssn`
|
|
397
|
+
- `vault`
|
|
398
|
+
- `vercel`
|
|
399
|
+
- `visa-context`
|
|
400
|
+
- `vocabulary-cue`
|
|
401
|
+
- `vocabulary-role`
|
|
402
|
+
- `webhook`
|
|
403
|
+
- `wordpress`
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { compileKnown, createSyncSanitizer } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
// Reuse caller-owned compilation in a synchronous logging hook.
|
|
4
|
+
const known = compileKnown([{ type: 'ORGANIZATION', value: 'Ромашка' }]);
|
|
5
|
+
const loggerPolicy = createSyncSanitizer({
|
|
6
|
+
profile: 'documents',
|
|
7
|
+
knownPersonal: known,
|
|
8
|
+
vocabulary: { roles: ['маркетолог'] },
|
|
9
|
+
structured: 'json',
|
|
10
|
+
allowedFields: ['level', 'msg'],
|
|
11
|
+
fields: { location: 'scan' },
|
|
12
|
+
transformations: { ORGANIZATION: () => 'PrivateCompany' },
|
|
13
|
+
});
|
|
14
|
+
console.log(
|
|
15
|
+
loggerPolicy.sanitize(
|
|
16
|
+
JSON.stringify({
|
|
17
|
+
level: 'info',
|
|
18
|
+
msg: 'работал в Ромашке',
|
|
19
|
+
privateNote: 'caller-private text',
|
|
20
|
+
count: 1,
|
|
21
|
+
})
|
|
22
|
+
).text
|
|
23
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { sanitize } from '../src/index.js';
|
|
2
|
+
|
|
3
|
+
// Synthetic mixed records: only the prose string activates the extra rule.
|
|
4
|
+
const source = JSON.stringify({
|
|
5
|
+
note: 'Работал в Ромашке менеджером по продажам.',
|
|
6
|
+
operational: 'Connection to Postgres lost, retrying',
|
|
7
|
+
count: 42,
|
|
8
|
+
});
|
|
9
|
+
console.log(
|
|
10
|
+
sanitize(source, {
|
|
11
|
+
profile: 'documents',
|
|
12
|
+
properNames: 'capitalized',
|
|
13
|
+
scripts: ['Cyrillic'],
|
|
14
|
+
structured: 'json',
|
|
15
|
+
}).text
|
|
16
|
+
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@link-foundation/sensitive-data-sanitizer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Offline sensitive-text sanitization with credential, multilingual personal-data and scanner union detection",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -24,7 +24,10 @@
|
|
|
24
24
|
"LICENSE",
|
|
25
25
|
"README.md",
|
|
26
26
|
"examples/shared-tool-logs.mjs",
|
|
27
|
-
"examples/reversible-placeholders.mjs"
|
|
27
|
+
"examples/reversible-placeholders.mjs",
|
|
28
|
+
"examples/prose-logs.mjs",
|
|
29
|
+
"docs/RULES.md",
|
|
30
|
+
"examples/custom-log-policy.mjs"
|
|
28
31
|
],
|
|
29
32
|
"exports": {
|
|
30
33
|
".": {
|
|
@@ -39,6 +42,10 @@
|
|
|
39
42
|
"types": "./src/legacy.d.ts",
|
|
40
43
|
"browser": "./src/arithmetic.js",
|
|
41
44
|
"import": "./src/legacy.js"
|
|
45
|
+
},
|
|
46
|
+
"./ner": {
|
|
47
|
+
"types": "./src/ner.d.ts",
|
|
48
|
+
"import": "./src/ner.js"
|
|
42
49
|
}
|
|
43
50
|
},
|
|
44
51
|
"scripts": {
|
|
@@ -95,6 +102,7 @@
|
|
|
95
102
|
"devDependencies": {
|
|
96
103
|
"@changesets/cli": "^3.0.3",
|
|
97
104
|
"@eslint/js": "^10.0.1",
|
|
105
|
+
"@nlpjs/ner": "4.27.0",
|
|
98
106
|
"browser-commander": "0.34.1",
|
|
99
107
|
"cmd-shim": "^9.0.2",
|
|
100
108
|
"command-stream": "2.6.0",
|
|
@@ -150,5 +158,13 @@
|
|
|
150
158
|
},
|
|
151
159
|
"overrides": {
|
|
152
160
|
"minimatch@>=7 <11": "^11.0.5"
|
|
161
|
+
},
|
|
162
|
+
"peerDependencies": {
|
|
163
|
+
"@nlpjs/ner": "^4.27.0"
|
|
164
|
+
},
|
|
165
|
+
"peerDependenciesMeta": {
|
|
166
|
+
"@nlpjs/ner": {
|
|
167
|
+
"optional": true
|
|
168
|
+
}
|
|
153
169
|
}
|
|
154
170
|
}
|