@link-foundation/sensitive-data-sanitizer 0.18.0 → 0.19.1

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.
@@ -7,6 +7,19 @@ const commaMobile = /,[ \t]*[78]9\d{9}(?![\p{L}\p{N}_])/u;
7
7
  const mobile = /^[78]9\d{9}$/u;
8
8
  const tokenPattern = /[\p{L}\p{M}\p{N}_./:\\?&#%=@+'’~+-]+/gu;
9
9
  const upper = /[\p{Lu}\p{Lt}]/u;
10
+ // Only known labels are exempt before colons in activated Cyrillic prose.
11
+ const proseLabels = new Set(
12
+ 'опыт стаж навыки образование компания работодатель должность обязанности достижения проекты контакты телефон почта адрес возраст пол гражданство дети национальность вероисповедание инвалидность судимость резюме имя фамилия отчество город статус дата время уровень языки интересы о себе место работы семейное положение'.split(
13
+ ' '
14
+ )
15
+ );
16
+ const completeTypes = new Set([
17
+ 'EMAIL',
18
+ 'PHONE',
19
+ 'ID',
20
+ 'VIN',
21
+ 'PASSPORT_NUMBER',
22
+ ]);
10
23
  const quotePairs = new Map([
11
24
  ['«', '»'],
12
25
  ['“', '”'],
@@ -109,6 +122,8 @@ export function detectProseAndDocuments(text, emit, options, detectDocuments) {
109
122
  return;
110
123
  }
111
124
  const runs = [[]];
125
+ let complete = 0,
126
+ suppressed = 0;
112
127
  detectProperNames(
113
128
  text,
114
129
  (finding) => {
@@ -123,12 +138,20 @@ export function detectProseAndDocuments(text, emit, options, detectDocuments) {
123
138
  detectDocuments(
124
139
  text,
125
140
  (finding) => {
126
- if (!runs.some((run) => overlapsRun(run, finding))) {
141
+ if (completeTypes.has(finding.type)) {
142
+ // Contacts and identifiers are indivisible: a domain or series alone
143
+ // cannot safely replace the complete value detected by document rules.
144
+ emit(finding);
145
+ complete++;
146
+ } else if (!runs.some((run) => overlapsRun(run, finding))) {
127
147
  emit(finding);
148
+ } else {
149
+ suppressed++;
128
150
  }
129
151
  },
130
152
  options
131
153
  );
154
+ options.debug?.({ event: 'document-prose-overlap', complete, suppressed });
132
155
  }
133
156
  function overlapsRun(run, finding) {
134
157
  let low = 0,
@@ -197,8 +220,8 @@ function preserveToken(text, raw, value, start, end, range) {
197
220
  !value ||
198
221
  (range && start >= range.index && end <= range.index + range[0].length) ||
199
222
  (value === 'REDACTED' && text[start - 1] === '[' && text[end] === ']') ||
200
- raw.endsWith(':') ||
201
- text[next] === ':'
223
+ ((raw.endsWith(':') || text[next] === ':') &&
224
+ (!/^[А-ЯЁа-яё-]+$/u.test(value) || proseLabels.has(value.toLowerCase())))
202
225
  );
203
226
  }
204
227
  function tokenType(value) {
@@ -1,5 +1,11 @@
1
1
  import { parentPort, workerData } from 'node:worker_threads';
2
2
  import { createSanitizer, prepareStreamEngine } from './engines.js';
3
+ import { compileKnown } from './compiled-known.js';
4
+
5
+ // Structured cloning discards the caller's weak compilation metadata.
6
+ if (workerData.knownPersonal) {
7
+ workerData.knownPersonal = compileKnown(workerData.knownPersonal);
8
+ }
3
9
 
4
10
  parentPort.on(
5
11
  'message',