@socprime/logtotal-sanitizer 0.0.1-beta.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 +23 -0
- package/LICENSE +201 -0
- package/README.md +199 -0
- package/chunk-TAMK67JP.js +2205 -0
- package/chunk-TAMK67JP.js.map +1 -0
- package/chunk-U6SJR7S7.js +933 -0
- package/chunk-U6SJR7S7.js.map +1 -0
- package/chunk-VW3VVW6L.cjs +2218 -0
- package/chunk-VW3VVW6L.cjs.map +1 -0
- package/chunk-WISLDJT3.cjs +944 -0
- package/chunk-WISLDJT3.cjs.map +1 -0
- package/cli.js +3431 -0
- package/cli.js.map +1 -0
- package/index-CutSH_48.d.cts +409 -0
- package/index-CutSH_48.d.ts +409 -0
- package/index.cjs +93 -0
- package/index.cjs.map +1 -0
- package/index.d.cts +157 -0
- package/index.d.ts +157 -0
- package/index.js +4 -0
- package/index.js.map +1 -0
- package/node.cjs +147 -0
- package/node.cjs.map +1 -0
- package/node.d.cts +63 -0
- package/node.d.ts +63 -0
- package/node.js +58 -0
- package/node.js.map +1 -0
- package/package.json +75 -0
- package/rules.cjs +24 -0
- package/rules.cjs.map +1 -0
- package/rules.d.cts +1 -0
- package/rules.d.ts +1 -0
- package/rules.js +3 -0
- package/rules.js.map +1 -0
|
@@ -0,0 +1,2205 @@
|
|
|
1
|
+
// src/core/errors.ts
|
|
2
|
+
var ERROR_BRAND = /* @__PURE__ */ Symbol.for("@socprime/logtotal-sanitizer/error");
|
|
3
|
+
var SanitizerError = class extends Error {
|
|
4
|
+
/** Machine-readable cause, stable across releases. */
|
|
5
|
+
code;
|
|
6
|
+
constructor(code, message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "SanitizerError";
|
|
9
|
+
this.code = code;
|
|
10
|
+
Object.defineProperty(this, ERROR_BRAND, { value: true, enumerable: false });
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
var InvalidRuleError = class extends SanitizerError {
|
|
14
|
+
constructor(message) {
|
|
15
|
+
super("INVALID_RULE", message);
|
|
16
|
+
this.name = "InvalidRuleError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
var UnknownRuleError = class extends SanitizerError {
|
|
20
|
+
constructor(message) {
|
|
21
|
+
super("UNKNOWN_RULE", message);
|
|
22
|
+
this.name = "UnknownRuleError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
var InvalidKeyError = class extends SanitizerError {
|
|
26
|
+
constructor(message) {
|
|
27
|
+
super("INVALID_KEY", message);
|
|
28
|
+
this.name = "InvalidKeyError";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
var InvalidOptionError = class extends SanitizerError {
|
|
32
|
+
constructor(message) {
|
|
33
|
+
super("INVALID_OPTION", message);
|
|
34
|
+
this.name = "InvalidOptionError";
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
var SanitizationAbortedError = class extends SanitizerError {
|
|
38
|
+
constructor(message = "Sanitization was aborted.") {
|
|
39
|
+
super("ABORTED", message);
|
|
40
|
+
this.name = "SanitizationAbortedError";
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
function isSanitizerError(value) {
|
|
44
|
+
return typeof value === "object" && value !== null && ERROR_BRAND in value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/rules/definitions/geoLocation.ts
|
|
48
|
+
function parseLatLonPair(match) {
|
|
49
|
+
const stripped = match.replace(/[()\s]/g, "");
|
|
50
|
+
if (!/^-?\d+\.\d+[,;]-?\d+\.\d+$/.test(stripped)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const [latStr, lonStr] = stripped.split(/[,;]/);
|
|
54
|
+
const lat = Number(latStr);
|
|
55
|
+
const lon = Number(lonStr);
|
|
56
|
+
if (!Number.isFinite(lat) || !Number.isFinite(lon)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
return { lat, lon };
|
|
60
|
+
}
|
|
61
|
+
function isPlausibleLatLon(lat, lon) {
|
|
62
|
+
if (lat === 0 && lon === 0) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return lat >= -90 && lat <= 90 && lon >= -180 && lon <= 180;
|
|
66
|
+
}
|
|
67
|
+
function validateGeo(match) {
|
|
68
|
+
const pair = parseLatLonPair(match);
|
|
69
|
+
if (pair) {
|
|
70
|
+
return isPlausibleLatLon(pair.lat, pair.lon);
|
|
71
|
+
}
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
var LAT_KEY = "(?:latitude|lat)";
|
|
75
|
+
var LON_KEY = "(?:longitude|long|lon|lng)";
|
|
76
|
+
var ZIP_KEY = "(?:zipCode|postalCode|postcode|zip)";
|
|
77
|
+
var ADDR_KEY = "(?:addressLine1|address|street)";
|
|
78
|
+
var OLC = "[23456789CFGHJMPQRVWXcfghjmpqrvwx]";
|
|
79
|
+
var GEOHASH = "[0-9b-hjkmnp-zB-HJKMNP-Z]";
|
|
80
|
+
var geoLocationRule = {
|
|
81
|
+
id: "geoLocation",
|
|
82
|
+
label: "Geo location",
|
|
83
|
+
description: "Coordinates, geohashes, plus codes, postcodes and context-anchored addresses become a stable <GEO:\u2026> token \u2014 the same value maps to the same token everywhere within a session.",
|
|
84
|
+
mode: "pseudo",
|
|
85
|
+
token: "GEO",
|
|
86
|
+
patterns: [
|
|
87
|
+
"(?:\\(\\s*-?\\d{1,3}\\.\\d{3,}\\s*[,;]\\s*-?\\d{1,3}\\.\\d{3,}\\s*\\))",
|
|
88
|
+
"(?:(?<![\\d.])-?\\d{1,3}\\.\\d{3,}\\s*[,;]\\s*-?\\d{1,3}\\.\\d{3,}(?!\\d))",
|
|
89
|
+
`(?:(?<=\\b${LAT_KEY}\\s*[=:]\\s*)-?\\d{1,3}\\.\\d+)`,
|
|
90
|
+
`(?:(?<=\\b${LON_KEY}\\s*[=:]\\s*)-?\\d{1,3}\\.\\d+)`,
|
|
91
|
+
`(?:(?<=\\bgeohash\\s*[=:]\\s*)${GEOHASH}{5,12}\\b)`,
|
|
92
|
+
`(?:(?<![\\w+])${OLC}{4,8}\\+${OLC}{2,3}(?![\\w+]))`,
|
|
93
|
+
`(?:\\b\\d{1,3}\xB0\\d{1,2}'\\d{1,2}(?:\\.\\d+)?"[NSEW]\\b)`,
|
|
94
|
+
"(?:\\b[A-Za-z]{1,2}\\d{1,2}[A-Za-z]?\\s\\d[A-Za-z]{2}\\b)",
|
|
95
|
+
`(?:(?<=\\b${ZIP_KEY}\\s*[=:]\\s*)\\d{5}(?:-\\d{4})?\\b)`
|
|
96
|
+
],
|
|
97
|
+
aggressivePatterns: [
|
|
98
|
+
"(?:\\(\\s*-?\\d{1,3}\\.\\d{2,}\\s*[,;]\\s*-?\\d{1,3}\\.\\d{2,}\\s*\\))",
|
|
99
|
+
"(?:(?<![\\d.])-?\\d{1,3}\\.\\d{2,}\\s*[,;]\\s*-?\\d{1,3}\\.\\d{2,}(?!\\d))",
|
|
100
|
+
`(?:(?<=\\b${ADDR_KEY}\\s*[=:]\\s*)[^\\n"'<>]{5,80})`
|
|
101
|
+
],
|
|
102
|
+
validate: validateGeo,
|
|
103
|
+
jsonKeys: [
|
|
104
|
+
"latitude",
|
|
105
|
+
"longitude",
|
|
106
|
+
"lat",
|
|
107
|
+
"lon",
|
|
108
|
+
"lng",
|
|
109
|
+
"long",
|
|
110
|
+
"coordinates",
|
|
111
|
+
"coords",
|
|
112
|
+
"geo",
|
|
113
|
+
"streetAddress",
|
|
114
|
+
"addressLine1",
|
|
115
|
+
"addressLine2",
|
|
116
|
+
"postalCode",
|
|
117
|
+
"postcode",
|
|
118
|
+
"zip",
|
|
119
|
+
"zipCode",
|
|
120
|
+
"geohash"
|
|
121
|
+
]
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// src/rules/definitions/govIdentifiers.ts
|
|
125
|
+
function luhnCheck(digits) {
|
|
126
|
+
if (!/^\d+$/.test(digits) || /^0+$/.test(digits)) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
let sum = 0;
|
|
130
|
+
let doubleIt = false;
|
|
131
|
+
for (let i = digits.length - 1; i >= 0; i -= 1) {
|
|
132
|
+
let digit = Number(digits[i]);
|
|
133
|
+
if (doubleIt) {
|
|
134
|
+
digit *= 2;
|
|
135
|
+
if (digit > 9) {
|
|
136
|
+
digit -= 9;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
sum += digit;
|
|
140
|
+
doubleIt = !doubleIt;
|
|
141
|
+
}
|
|
142
|
+
return sum % 10 === 0;
|
|
143
|
+
}
|
|
144
|
+
function isValidSsn(area, group, serial) {
|
|
145
|
+
if (area === "000" || area === "666" || area.startsWith("9")) {
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return group !== "00" && serial !== "0000";
|
|
149
|
+
}
|
|
150
|
+
function isValidItin(area, group, serial) {
|
|
151
|
+
if (!area.startsWith("9") || serial === "0000") {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
const groupNum = Number(group);
|
|
155
|
+
return groupNum >= 70 && groupNum <= 88 || groupNum >= 90 && groupNum <= 92 || groupNum >= 94 && groupNum <= 99;
|
|
156
|
+
}
|
|
157
|
+
function isValidNhs(digits) {
|
|
158
|
+
if (!/^\d{10}$/.test(digits) || /^0+$/.test(digits)) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
let sum = 0;
|
|
162
|
+
for (let i = 0; i < 9; i += 1) {
|
|
163
|
+
sum += Number(digits[i]) * (10 - i);
|
|
164
|
+
}
|
|
165
|
+
const remainder = sum % 11;
|
|
166
|
+
let check = 11 - remainder;
|
|
167
|
+
if (check === 11) {
|
|
168
|
+
check = 0;
|
|
169
|
+
}
|
|
170
|
+
if (check === 10) {
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
return check === Number(digits[9]);
|
|
174
|
+
}
|
|
175
|
+
function validateGovId(match) {
|
|
176
|
+
const dashed = /^(\d{3})-(\d{2})-(\d{4})$/.exec(match);
|
|
177
|
+
if (dashed?.[1] && dashed[2] && dashed[3]) {
|
|
178
|
+
return isValidSsn(dashed[1], dashed[2], dashed[3]) || isValidItin(dashed[1], dashed[2], dashed[3]);
|
|
179
|
+
}
|
|
180
|
+
if (/^\d{3}-\d{3}-\d{3}$/.test(match)) {
|
|
181
|
+
return luhnCheck(match.replace(/-/g, ""));
|
|
182
|
+
}
|
|
183
|
+
if (/^\d{3} \d{3} \d{4}$/.test(match)) {
|
|
184
|
+
return isValidNhs(match.replace(/ /g, ""));
|
|
185
|
+
}
|
|
186
|
+
const nhsPrefixed = /(?:NHS|nhs)(?:[_-]?number)?\s*[=:]\s*(\d{10})$/.exec(match);
|
|
187
|
+
const nhsDigits = nhsPrefixed?.[1];
|
|
188
|
+
if (nhsDigits) {
|
|
189
|
+
return isValidNhs(nhsDigits);
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
var SSN_CTX = "(?:[Ss][Ss][Nn]|[Ee][Ii][Nn]|[Ss][Ii][Nn]|tax[_-]?id|social[_ -]?security(?:[_ -]?number)?)";
|
|
194
|
+
var ID_CTX = "(?:passport(?:_no|_number)?|driver[_-]?license|national[_-]?id|tax[_-]?id)";
|
|
195
|
+
var govIdentifiersRule = {
|
|
196
|
+
id: "govIds",
|
|
197
|
+
label: "Government identifiers",
|
|
198
|
+
description: "Passport numbers, tax numbers, SSNs and similar IDs are redacted outright.",
|
|
199
|
+
mode: "mask",
|
|
200
|
+
token: "GOV_ID",
|
|
201
|
+
patterns: [
|
|
202
|
+
"(?:(?<![A-Z0-9<])P<[A-Z0-9<]{42}(?![A-Z0-9<]))",
|
|
203
|
+
"(?:\\b\\d{3}-\\d{2}-\\d{4}\\b)",
|
|
204
|
+
"(?:\\b\\d{3}-\\d{3}-\\d{3}\\b)",
|
|
205
|
+
"(?:\\b\\d{3} \\d{3} \\d{4}\\b)",
|
|
206
|
+
"(?:\\b(?:NHS|nhs)(?:[_-]?number)?\\s*[=:]\\s*\\d{10}\\b)",
|
|
207
|
+
"(?:\\b(?!BG|GB|NK|KN|TN|NT|ZZ)[A-CEGHJ-PR-TW-Z][A-CEGHJ-NPR-TW-Z]\\d{6}\\s?[A-D]\\b)",
|
|
208
|
+
"(?:(?<![A-Za-z0-9\u0410-\u042F\u0406\u0407\u0404\u0490\u0430-\u044F\u0456\u0457\u0454\u0491])[\u0410-\u042F\u0406\u0407\u0404\u0490]{2}\\d{6}\\b)",
|
|
209
|
+
`(?:(?<=\\b${SSN_CTX}\\s*[=:]?\\s*)\\d{9}\\b)`,
|
|
210
|
+
"(?:(?<=(?:\u0406\u041F\u041D|\u0420\u041D\u041E\u041A\u041F\u041F|tax[_-]?id)\\s*[=:]?\\s*)\\d{10}\\b)",
|
|
211
|
+
`(?:(?<=\\b${ID_CTX}\\s*[=:]\\s*)[A-Za-z0-9<]{5,32})`
|
|
212
|
+
],
|
|
213
|
+
aggressivePatterns: [
|
|
214
|
+
"(?:\\b[A-Z]{1,2}\\d{6,9}\\b)",
|
|
215
|
+
"(?:\\b\\d{9,11}\\b)"
|
|
216
|
+
],
|
|
217
|
+
validate: validateGovId,
|
|
218
|
+
jsonKeys: [
|
|
219
|
+
"ssn",
|
|
220
|
+
"passportNumber",
|
|
221
|
+
"taxId",
|
|
222
|
+
"nationalId",
|
|
223
|
+
"socialSecurityNumber",
|
|
224
|
+
"passport",
|
|
225
|
+
"nino",
|
|
226
|
+
"nhsNumber",
|
|
227
|
+
"driverLicense",
|
|
228
|
+
"licenseNumber",
|
|
229
|
+
"personalId",
|
|
230
|
+
"dob",
|
|
231
|
+
"dateOfBirth"
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
// src/rules/definitions/healthInfo.ts
|
|
236
|
+
function isValidDea(match) {
|
|
237
|
+
if (!/^[ABFGMPRX][A-Z9]\d{7}$/.test(match)) {
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
const digits = match.slice(2);
|
|
241
|
+
const sum = Number(digits[0]) + Number(digits[2]) + Number(digits[4]) + 2 * (Number(digits[1]) + Number(digits[3]) + Number(digits[5]));
|
|
242
|
+
return sum % 10 === Number(digits[6]);
|
|
243
|
+
}
|
|
244
|
+
function validateHealth(match) {
|
|
245
|
+
if (/^[ABFGMPRX][A-Z9]\d{7}$/.test(match)) {
|
|
246
|
+
return isValidDea(match);
|
|
247
|
+
}
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
var MRN_KEY = "(?:[Mm][Rr][Nn]|medical[_-]?record(?:[_-]?number)?)";
|
|
251
|
+
var healthInfoRule = {
|
|
252
|
+
id: "healthInfo",
|
|
253
|
+
label: "Protected health information",
|
|
254
|
+
description: "PHI in SOC logs is almost always structured: JSON field names (diagnosis, medication, MRN, \u2026) carry most of the coverage. Freeform patterns only match context-anchored medical codes (ICD, SNOMED, LOINC, NDC, DEA, MRN).",
|
|
255
|
+
mode: "mask",
|
|
256
|
+
token: "PHI",
|
|
257
|
+
patterns: [
|
|
258
|
+
"(?:\\b(?:[Ii][Cc][Dd]-10|[Ii][Cc][Dd]10):?\\s*[A-Z]\\d{2}(?:\\.\\d{1,4})?\\b)",
|
|
259
|
+
"(?:\\b(?:[Ii][Cc][Dd]-9|[Ii][Cc][Dd]9):?\\s*(?:[VE]\\d{2,3}|\\d{3})(?:\\.\\d{1,2})?\\b)",
|
|
260
|
+
"(?:\\b(?:SNOMED(?:\\s*CT)?|[Ss]nomed(?:\\s*CT)?)[:\\s]+\\d{6,18}\\b)",
|
|
261
|
+
"(?:\\b(?:LOINC|[Ll]oinc)[:\\s]+\\d{1,5}-\\d\\b)",
|
|
262
|
+
"(?:\\b(?:NDC|[Nn]dc)[:\\s]+\\d{4,5}-\\d{3,4}-\\d{1,2}\\b)",
|
|
263
|
+
"(?:\\b[ABFGMPRX][A-Z9]\\d{7}\\b)",
|
|
264
|
+
"(?:\\b(?:[Nn][Pp][Ii])\\s*[=:]\\s*\\d{10}\\b)",
|
|
265
|
+
`(?:(?<=\\b${MRN_KEY}\\s*[=:]\\s*)[A-Za-z0-9-]{4,}\\b)`
|
|
266
|
+
],
|
|
267
|
+
aggressivePatterns: [
|
|
268
|
+
"(?:(?<![A-Za-z0-9])[A-TV-Z]\\d{2}(?:\\.\\w{1,4})?(?![A-Za-z0-9:]))"
|
|
269
|
+
],
|
|
270
|
+
validate: validateHealth,
|
|
271
|
+
jsonKeys: [
|
|
272
|
+
"diagnosis",
|
|
273
|
+
"prescription",
|
|
274
|
+
"medication",
|
|
275
|
+
"icd10",
|
|
276
|
+
"icd9",
|
|
277
|
+
"snomed",
|
|
278
|
+
"condition",
|
|
279
|
+
"allergy",
|
|
280
|
+
"allergies",
|
|
281
|
+
"bloodType",
|
|
282
|
+
"patientId",
|
|
283
|
+
"mrn",
|
|
284
|
+
"medicalRecordNumber",
|
|
285
|
+
"insuranceNumber",
|
|
286
|
+
"labResult",
|
|
287
|
+
"observation",
|
|
288
|
+
"vitalSigns"
|
|
289
|
+
]
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// src/rules/definitions/homePaths.ts
|
|
293
|
+
var USER_SEG = `[^/\\\\\\s"'<>]+`;
|
|
294
|
+
function validateHomeUser(match) {
|
|
295
|
+
return match !== "." && match !== "..";
|
|
296
|
+
}
|
|
297
|
+
var homePathsRule = {
|
|
298
|
+
id: "paths",
|
|
299
|
+
label: "Home-directory usernames",
|
|
300
|
+
description: "Only the username segment of a home-directory path is redacted (e.g. /home/jdoe/app \u2192 /home/<R:\u2026>/app); the rest of the path stays visible.",
|
|
301
|
+
mode: "mask",
|
|
302
|
+
token: "PATH",
|
|
303
|
+
patterns: [
|
|
304
|
+
`(?:(?<=/export/home/)${USER_SEG})`,
|
|
305
|
+
`(?:(?<=/usr/home/)${USER_SEG})`,
|
|
306
|
+
`(?:(?<=/var/home/)${USER_SEG})`,
|
|
307
|
+
`(?:(?<=/home/)${USER_SEG})`,
|
|
308
|
+
`(?:(?<=/Users/)${USER_SEG})`,
|
|
309
|
+
`(?:(?<=[A-Za-z]:\\\\Users\\\\)${USER_SEG})`,
|
|
310
|
+
`(?:(?<=[A-Za-z]:/Users/)${USER_SEG})`,
|
|
311
|
+
`(?:(?<=\\\\\\\\\\?\\\\[A-Za-z]:\\\\Users\\\\)${USER_SEG})`,
|
|
312
|
+
`(?:(?<=[A-Za-z]:\\\\Documents and Settings\\\\)${USER_SEG})`,
|
|
313
|
+
`(?:(?<=[A-Za-z]:\\\\\\\\Users\\\\\\\\)${USER_SEG})`,
|
|
314
|
+
`(?:(?<=/mnt/[A-Za-z]/Users/)${USER_SEG})`,
|
|
315
|
+
`(?:(?<=//wsl\\$/(?:[A-Za-z0-9_-]+)/home/)${USER_SEG})`
|
|
316
|
+
],
|
|
317
|
+
validate: validateHomeUser
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// src/rules/definitions/hostnames.ts
|
|
321
|
+
var GTLD = [
|
|
322
|
+
"abbott",
|
|
323
|
+
"abogado",
|
|
324
|
+
"ac",
|
|
325
|
+
"academy",
|
|
326
|
+
"accountant",
|
|
327
|
+
"accountants",
|
|
328
|
+
"active",
|
|
329
|
+
"actor",
|
|
330
|
+
"ad",
|
|
331
|
+
"ads",
|
|
332
|
+
"adult",
|
|
333
|
+
"ae",
|
|
334
|
+
"aero",
|
|
335
|
+
"af",
|
|
336
|
+
"afl",
|
|
337
|
+
"ag",
|
|
338
|
+
"agency",
|
|
339
|
+
"ai",
|
|
340
|
+
"airforce",
|
|
341
|
+
"al",
|
|
342
|
+
"allfinanz",
|
|
343
|
+
"alsace",
|
|
344
|
+
"am",
|
|
345
|
+
"amsterdam",
|
|
346
|
+
"an",
|
|
347
|
+
"android",
|
|
348
|
+
"ao",
|
|
349
|
+
"apartments",
|
|
350
|
+
"aq",
|
|
351
|
+
"aquarelle",
|
|
352
|
+
"ar",
|
|
353
|
+
"archi",
|
|
354
|
+
"army",
|
|
355
|
+
"arpa",
|
|
356
|
+
"as",
|
|
357
|
+
"asia",
|
|
358
|
+
"associates",
|
|
359
|
+
"at",
|
|
360
|
+
"attorney",
|
|
361
|
+
"au",
|
|
362
|
+
"auction",
|
|
363
|
+
"audio",
|
|
364
|
+
"autos",
|
|
365
|
+
"aw",
|
|
366
|
+
"ax",
|
|
367
|
+
"axa",
|
|
368
|
+
"az",
|
|
369
|
+
"ba",
|
|
370
|
+
"band",
|
|
371
|
+
"bank",
|
|
372
|
+
"bar",
|
|
373
|
+
"barclaycard",
|
|
374
|
+
"barclays",
|
|
375
|
+
"bargains",
|
|
376
|
+
"bauhaus",
|
|
377
|
+
"bayern",
|
|
378
|
+
"bb",
|
|
379
|
+
"bbc",
|
|
380
|
+
"bd",
|
|
381
|
+
"be",
|
|
382
|
+
"beer",
|
|
383
|
+
"berlin",
|
|
384
|
+
"best",
|
|
385
|
+
"bf",
|
|
386
|
+
"bg",
|
|
387
|
+
"bh",
|
|
388
|
+
"bi",
|
|
389
|
+
"bid",
|
|
390
|
+
"bike",
|
|
391
|
+
"bingo",
|
|
392
|
+
"bio",
|
|
393
|
+
"biz",
|
|
394
|
+
"bj",
|
|
395
|
+
"bl",
|
|
396
|
+
"black",
|
|
397
|
+
"blackfriday",
|
|
398
|
+
"bloomberg",
|
|
399
|
+
"blue",
|
|
400
|
+
"bm",
|
|
401
|
+
"bmw",
|
|
402
|
+
"bn",
|
|
403
|
+
"bnpparibas",
|
|
404
|
+
"bo",
|
|
405
|
+
"boats",
|
|
406
|
+
"bond",
|
|
407
|
+
"boo",
|
|
408
|
+
"boutique",
|
|
409
|
+
"bq",
|
|
410
|
+
"br",
|
|
411
|
+
"brussels",
|
|
412
|
+
"bs",
|
|
413
|
+
"bt",
|
|
414
|
+
"budapest",
|
|
415
|
+
"build",
|
|
416
|
+
"builders",
|
|
417
|
+
"business",
|
|
418
|
+
"buzz",
|
|
419
|
+
"bv",
|
|
420
|
+
"bw",
|
|
421
|
+
"by",
|
|
422
|
+
"bz",
|
|
423
|
+
"bzh",
|
|
424
|
+
"ca",
|
|
425
|
+
"cab",
|
|
426
|
+
"cafe",
|
|
427
|
+
"cal",
|
|
428
|
+
"camera",
|
|
429
|
+
"camp",
|
|
430
|
+
"cancerresearch",
|
|
431
|
+
"canon",
|
|
432
|
+
"capetown",
|
|
433
|
+
"capital",
|
|
434
|
+
"caravan",
|
|
435
|
+
"cards",
|
|
436
|
+
"care",
|
|
437
|
+
"career",
|
|
438
|
+
"careers",
|
|
439
|
+
"cartier",
|
|
440
|
+
"casa",
|
|
441
|
+
"cash",
|
|
442
|
+
"casino",
|
|
443
|
+
"cat",
|
|
444
|
+
"catering",
|
|
445
|
+
"cbn",
|
|
446
|
+
"cc",
|
|
447
|
+
"cd",
|
|
448
|
+
"center",
|
|
449
|
+
"ceo",
|
|
450
|
+
"cern",
|
|
451
|
+
"cf",
|
|
452
|
+
"cfd",
|
|
453
|
+
"cg",
|
|
454
|
+
"ch",
|
|
455
|
+
"channel",
|
|
456
|
+
"chat",
|
|
457
|
+
"cheap",
|
|
458
|
+
"chloe",
|
|
459
|
+
"christmas",
|
|
460
|
+
"chrome",
|
|
461
|
+
"church",
|
|
462
|
+
"ci",
|
|
463
|
+
"citic",
|
|
464
|
+
"city",
|
|
465
|
+
"ck",
|
|
466
|
+
"cl",
|
|
467
|
+
"claims",
|
|
468
|
+
"cleaning",
|
|
469
|
+
"click",
|
|
470
|
+
"clinic",
|
|
471
|
+
"clothing",
|
|
472
|
+
"club",
|
|
473
|
+
"cm",
|
|
474
|
+
"cn",
|
|
475
|
+
"co",
|
|
476
|
+
"coach",
|
|
477
|
+
"codes",
|
|
478
|
+
"coffee",
|
|
479
|
+
"college",
|
|
480
|
+
"cologne",
|
|
481
|
+
"com",
|
|
482
|
+
"community",
|
|
483
|
+
"company",
|
|
484
|
+
"computer",
|
|
485
|
+
"condos",
|
|
486
|
+
"construction",
|
|
487
|
+
"consulting",
|
|
488
|
+
"contractors",
|
|
489
|
+
"cooking",
|
|
490
|
+
"cool",
|
|
491
|
+
"coop",
|
|
492
|
+
"country",
|
|
493
|
+
"courses",
|
|
494
|
+
"cr",
|
|
495
|
+
"credit",
|
|
496
|
+
"creditcard",
|
|
497
|
+
"cricket",
|
|
498
|
+
"crs",
|
|
499
|
+
"cruises",
|
|
500
|
+
"cu",
|
|
501
|
+
"cuisinella",
|
|
502
|
+
"cv",
|
|
503
|
+
"cw",
|
|
504
|
+
"cx",
|
|
505
|
+
"cy",
|
|
506
|
+
"cymru",
|
|
507
|
+
"cyou",
|
|
508
|
+
"cz",
|
|
509
|
+
"dabur",
|
|
510
|
+
"dad",
|
|
511
|
+
"dance",
|
|
512
|
+
"date",
|
|
513
|
+
"dating",
|
|
514
|
+
"datsun",
|
|
515
|
+
"day",
|
|
516
|
+
"dclk",
|
|
517
|
+
"de",
|
|
518
|
+
"deals",
|
|
519
|
+
"degree",
|
|
520
|
+
"delivery",
|
|
521
|
+
"democrat",
|
|
522
|
+
"dental",
|
|
523
|
+
"dentist",
|
|
524
|
+
"desi",
|
|
525
|
+
"design",
|
|
526
|
+
"dev",
|
|
527
|
+
"diamonds",
|
|
528
|
+
"diet",
|
|
529
|
+
"digital",
|
|
530
|
+
"direct",
|
|
531
|
+
"directory",
|
|
532
|
+
"discount",
|
|
533
|
+
"dj",
|
|
534
|
+
"dk",
|
|
535
|
+
"dm",
|
|
536
|
+
"dnp",
|
|
537
|
+
"do",
|
|
538
|
+
"docs",
|
|
539
|
+
"doha",
|
|
540
|
+
"domains",
|
|
541
|
+
"doosan",
|
|
542
|
+
"download",
|
|
543
|
+
"durban",
|
|
544
|
+
"dvag",
|
|
545
|
+
"dz",
|
|
546
|
+
"eat",
|
|
547
|
+
"ec",
|
|
548
|
+
"edu",
|
|
549
|
+
"education",
|
|
550
|
+
"ee",
|
|
551
|
+
"eg",
|
|
552
|
+
"eh",
|
|
553
|
+
"email",
|
|
554
|
+
"emerck",
|
|
555
|
+
"energy",
|
|
556
|
+
"engineer",
|
|
557
|
+
"engineering",
|
|
558
|
+
"enterprises",
|
|
559
|
+
"epson",
|
|
560
|
+
"equipment",
|
|
561
|
+
"er",
|
|
562
|
+
"erni",
|
|
563
|
+
"es",
|
|
564
|
+
"esq",
|
|
565
|
+
"estate",
|
|
566
|
+
"et",
|
|
567
|
+
"eu",
|
|
568
|
+
"eurovision",
|
|
569
|
+
"eus",
|
|
570
|
+
"events",
|
|
571
|
+
"everbank",
|
|
572
|
+
"exchange",
|
|
573
|
+
"expert",
|
|
574
|
+
"exposed",
|
|
575
|
+
"express",
|
|
576
|
+
"fail",
|
|
577
|
+
"faith",
|
|
578
|
+
"fan",
|
|
579
|
+
"fans",
|
|
580
|
+
"farm",
|
|
581
|
+
"fashion",
|
|
582
|
+
"feedback",
|
|
583
|
+
"fi",
|
|
584
|
+
"film",
|
|
585
|
+
"finance",
|
|
586
|
+
"financial",
|
|
587
|
+
"firmdale",
|
|
588
|
+
"fish",
|
|
589
|
+
"fishing",
|
|
590
|
+
"fit",
|
|
591
|
+
"fitness",
|
|
592
|
+
"fj",
|
|
593
|
+
"fk",
|
|
594
|
+
"flights",
|
|
595
|
+
"florist",
|
|
596
|
+
"flowers",
|
|
597
|
+
"flsmidth",
|
|
598
|
+
"fly",
|
|
599
|
+
"fm",
|
|
600
|
+
"fo",
|
|
601
|
+
"foo",
|
|
602
|
+
"football",
|
|
603
|
+
"forex",
|
|
604
|
+
"forsale",
|
|
605
|
+
"foundation",
|
|
606
|
+
"fr",
|
|
607
|
+
"frl",
|
|
608
|
+
"frogans",
|
|
609
|
+
"fund",
|
|
610
|
+
"furniture",
|
|
611
|
+
"futbol",
|
|
612
|
+
"ga",
|
|
613
|
+
"gal",
|
|
614
|
+
"gallery",
|
|
615
|
+
"garden",
|
|
616
|
+
"gb",
|
|
617
|
+
"gbiz",
|
|
618
|
+
"gd",
|
|
619
|
+
"gdn",
|
|
620
|
+
"ge",
|
|
621
|
+
"gent",
|
|
622
|
+
"gf",
|
|
623
|
+
"gg",
|
|
624
|
+
"ggee",
|
|
625
|
+
"gh",
|
|
626
|
+
"gi",
|
|
627
|
+
"gift",
|
|
628
|
+
"gifts",
|
|
629
|
+
"gives",
|
|
630
|
+
"gl",
|
|
631
|
+
"glass",
|
|
632
|
+
"gle",
|
|
633
|
+
"global",
|
|
634
|
+
"globo",
|
|
635
|
+
"gm",
|
|
636
|
+
"gmail",
|
|
637
|
+
"gmo",
|
|
638
|
+
"gmx",
|
|
639
|
+
"gn",
|
|
640
|
+
"gold",
|
|
641
|
+
"goldpoint",
|
|
642
|
+
"golf",
|
|
643
|
+
"goo",
|
|
644
|
+
"goog",
|
|
645
|
+
"google",
|
|
646
|
+
"gop",
|
|
647
|
+
"gov",
|
|
648
|
+
"gp",
|
|
649
|
+
"gq",
|
|
650
|
+
"gr",
|
|
651
|
+
"graphics",
|
|
652
|
+
"gratis",
|
|
653
|
+
"green",
|
|
654
|
+
"gripe",
|
|
655
|
+
"gs",
|
|
656
|
+
"gt",
|
|
657
|
+
"gu",
|
|
658
|
+
"guge",
|
|
659
|
+
"guide",
|
|
660
|
+
"guitars",
|
|
661
|
+
"guru",
|
|
662
|
+
"gw",
|
|
663
|
+
"gy",
|
|
664
|
+
"hamburg",
|
|
665
|
+
"hangout",
|
|
666
|
+
"haus",
|
|
667
|
+
"healthcare",
|
|
668
|
+
"help",
|
|
669
|
+
"here",
|
|
670
|
+
"hermes",
|
|
671
|
+
"hiphop",
|
|
672
|
+
"hiv",
|
|
673
|
+
"hk",
|
|
674
|
+
"hm",
|
|
675
|
+
"hn",
|
|
676
|
+
"holdings",
|
|
677
|
+
"holiday",
|
|
678
|
+
"homes",
|
|
679
|
+
"horse",
|
|
680
|
+
"host",
|
|
681
|
+
"hosting",
|
|
682
|
+
"house",
|
|
683
|
+
"how",
|
|
684
|
+
"hr",
|
|
685
|
+
"ht",
|
|
686
|
+
"hu",
|
|
687
|
+
"ibm",
|
|
688
|
+
"id",
|
|
689
|
+
"ie",
|
|
690
|
+
"ifm",
|
|
691
|
+
"il",
|
|
692
|
+
"im",
|
|
693
|
+
"immo",
|
|
694
|
+
"immobilien",
|
|
695
|
+
"in",
|
|
696
|
+
"industries",
|
|
697
|
+
"infiniti",
|
|
698
|
+
"info",
|
|
699
|
+
"ing",
|
|
700
|
+
"ink",
|
|
701
|
+
"institute",
|
|
702
|
+
"insure",
|
|
703
|
+
"int",
|
|
704
|
+
"international",
|
|
705
|
+
"investments",
|
|
706
|
+
"io",
|
|
707
|
+
"iq",
|
|
708
|
+
"ir",
|
|
709
|
+
"irish",
|
|
710
|
+
"is",
|
|
711
|
+
"it",
|
|
712
|
+
"iwc",
|
|
713
|
+
"java",
|
|
714
|
+
"jcb",
|
|
715
|
+
"je",
|
|
716
|
+
"jetzt",
|
|
717
|
+
"jm",
|
|
718
|
+
"jo",
|
|
719
|
+
"jobs",
|
|
720
|
+
"joburg",
|
|
721
|
+
"jp",
|
|
722
|
+
"juegos",
|
|
723
|
+
"kaufen",
|
|
724
|
+
"kddi",
|
|
725
|
+
"ke",
|
|
726
|
+
"kg",
|
|
727
|
+
"kh",
|
|
728
|
+
"ki",
|
|
729
|
+
"kim",
|
|
730
|
+
"kitchen",
|
|
731
|
+
"kiwi",
|
|
732
|
+
"km",
|
|
733
|
+
"kn",
|
|
734
|
+
"koeln",
|
|
735
|
+
"komatsu",
|
|
736
|
+
"kp",
|
|
737
|
+
"kr",
|
|
738
|
+
"krd",
|
|
739
|
+
"kred",
|
|
740
|
+
"kw",
|
|
741
|
+
"ky",
|
|
742
|
+
"kyoto",
|
|
743
|
+
"kz",
|
|
744
|
+
"la",
|
|
745
|
+
"lacaixa",
|
|
746
|
+
"land",
|
|
747
|
+
"lat",
|
|
748
|
+
"latrobe",
|
|
749
|
+
"lawyer",
|
|
750
|
+
"lb",
|
|
751
|
+
"lc",
|
|
752
|
+
"lds",
|
|
753
|
+
"lease",
|
|
754
|
+
"leclerc",
|
|
755
|
+
"legal",
|
|
756
|
+
"lgbt",
|
|
757
|
+
"li",
|
|
758
|
+
"lidl",
|
|
759
|
+
"life",
|
|
760
|
+
"lighting",
|
|
761
|
+
"limited",
|
|
762
|
+
"limo",
|
|
763
|
+
"link",
|
|
764
|
+
"lk",
|
|
765
|
+
"loan",
|
|
766
|
+
"loans",
|
|
767
|
+
"london",
|
|
768
|
+
"lotte",
|
|
769
|
+
"lotto",
|
|
770
|
+
"love",
|
|
771
|
+
"lr",
|
|
772
|
+
"ls",
|
|
773
|
+
"lt",
|
|
774
|
+
"ltda",
|
|
775
|
+
"lu",
|
|
776
|
+
"luxe",
|
|
777
|
+
"luxury",
|
|
778
|
+
"lv",
|
|
779
|
+
"ly",
|
|
780
|
+
"ma",
|
|
781
|
+
"madrid",
|
|
782
|
+
"maif",
|
|
783
|
+
"maison",
|
|
784
|
+
"management",
|
|
785
|
+
"mango",
|
|
786
|
+
"market",
|
|
787
|
+
"marketing",
|
|
788
|
+
"markets",
|
|
789
|
+
"marriott",
|
|
790
|
+
"mc",
|
|
791
|
+
"md",
|
|
792
|
+
"me",
|
|
793
|
+
"media",
|
|
794
|
+
"meet",
|
|
795
|
+
"melbourne",
|
|
796
|
+
"meme",
|
|
797
|
+
"memorial",
|
|
798
|
+
"menu",
|
|
799
|
+
"mf",
|
|
800
|
+
"mg",
|
|
801
|
+
"mh",
|
|
802
|
+
"miami",
|
|
803
|
+
"mil",
|
|
804
|
+
"mini",
|
|
805
|
+
"mk",
|
|
806
|
+
"ml",
|
|
807
|
+
"mm",
|
|
808
|
+
"mma",
|
|
809
|
+
"mn",
|
|
810
|
+
"mo",
|
|
811
|
+
"mobi",
|
|
812
|
+
"moda",
|
|
813
|
+
"moe",
|
|
814
|
+
"monash",
|
|
815
|
+
"money",
|
|
816
|
+
"mormon",
|
|
817
|
+
"mortgage",
|
|
818
|
+
"moscow",
|
|
819
|
+
"motorcycles",
|
|
820
|
+
"mov",
|
|
821
|
+
"movie",
|
|
822
|
+
"mp",
|
|
823
|
+
"mq",
|
|
824
|
+
"mr",
|
|
825
|
+
"ms",
|
|
826
|
+
"mt",
|
|
827
|
+
"mtn",
|
|
828
|
+
"mtpc",
|
|
829
|
+
"mu",
|
|
830
|
+
"museum",
|
|
831
|
+
"mv",
|
|
832
|
+
"mw",
|
|
833
|
+
"mx",
|
|
834
|
+
"my",
|
|
835
|
+
"mz",
|
|
836
|
+
"na",
|
|
837
|
+
"nagoya",
|
|
838
|
+
"name",
|
|
839
|
+
"navy",
|
|
840
|
+
"nc",
|
|
841
|
+
"ne",
|
|
842
|
+
"net",
|
|
843
|
+
"network",
|
|
844
|
+
"neustar",
|
|
845
|
+
"new",
|
|
846
|
+
"news",
|
|
847
|
+
"nexus",
|
|
848
|
+
"nf",
|
|
849
|
+
"ng",
|
|
850
|
+
"ngo",
|
|
851
|
+
"nhk",
|
|
852
|
+
"ni",
|
|
853
|
+
"nico",
|
|
854
|
+
"ninja",
|
|
855
|
+
"nissan",
|
|
856
|
+
"nl",
|
|
857
|
+
"no",
|
|
858
|
+
"np",
|
|
859
|
+
"nr",
|
|
860
|
+
"nra",
|
|
861
|
+
"nrw",
|
|
862
|
+
"ntt",
|
|
863
|
+
"nu",
|
|
864
|
+
"nyc",
|
|
865
|
+
"nz",
|
|
866
|
+
"okinawa",
|
|
867
|
+
"om",
|
|
868
|
+
"one",
|
|
869
|
+
"ong",
|
|
870
|
+
"onl",
|
|
871
|
+
"online",
|
|
872
|
+
"ooo",
|
|
873
|
+
"org",
|
|
874
|
+
"organic",
|
|
875
|
+
"osaka",
|
|
876
|
+
"otsuka",
|
|
877
|
+
"ovh",
|
|
878
|
+
"pa",
|
|
879
|
+
"page",
|
|
880
|
+
"panerai",
|
|
881
|
+
"paris",
|
|
882
|
+
"partners",
|
|
883
|
+
"parts",
|
|
884
|
+
"party",
|
|
885
|
+
"pe",
|
|
886
|
+
"pf",
|
|
887
|
+
"pg",
|
|
888
|
+
"ph",
|
|
889
|
+
"pharmacy",
|
|
890
|
+
"photo",
|
|
891
|
+
"photography",
|
|
892
|
+
"photos",
|
|
893
|
+
"physio",
|
|
894
|
+
"piaget",
|
|
895
|
+
"pics",
|
|
896
|
+
"pictet",
|
|
897
|
+
"pictures",
|
|
898
|
+
"pink",
|
|
899
|
+
"pizza",
|
|
900
|
+
"pk",
|
|
901
|
+
"pl",
|
|
902
|
+
"place",
|
|
903
|
+
"plumbing",
|
|
904
|
+
"plus",
|
|
905
|
+
"pm",
|
|
906
|
+
"pn",
|
|
907
|
+
"pohl",
|
|
908
|
+
"poker",
|
|
909
|
+
"porn",
|
|
910
|
+
"post",
|
|
911
|
+
"pr",
|
|
912
|
+
"praxi",
|
|
913
|
+
"press",
|
|
914
|
+
"pro",
|
|
915
|
+
"prod",
|
|
916
|
+
"productions",
|
|
917
|
+
"prof",
|
|
918
|
+
"properties",
|
|
919
|
+
"property",
|
|
920
|
+
"ps",
|
|
921
|
+
"pt",
|
|
922
|
+
"pub",
|
|
923
|
+
"pw",
|
|
924
|
+
"py",
|
|
925
|
+
"qa",
|
|
926
|
+
"qpon",
|
|
927
|
+
"quebec",
|
|
928
|
+
"racing",
|
|
929
|
+
"re",
|
|
930
|
+
"realtor",
|
|
931
|
+
"recipes",
|
|
932
|
+
"red",
|
|
933
|
+
"redstone",
|
|
934
|
+
"rehab",
|
|
935
|
+
"reise",
|
|
936
|
+
"reisen",
|
|
937
|
+
"reit",
|
|
938
|
+
"ren",
|
|
939
|
+
"rentals",
|
|
940
|
+
"repair",
|
|
941
|
+
"report",
|
|
942
|
+
"republican",
|
|
943
|
+
"rest",
|
|
944
|
+
"restaurant",
|
|
945
|
+
"review",
|
|
946
|
+
"reviews",
|
|
947
|
+
"rich",
|
|
948
|
+
"rio",
|
|
949
|
+
"rip",
|
|
950
|
+
"ro",
|
|
951
|
+
"rocks",
|
|
952
|
+
"rodeo",
|
|
953
|
+
"rs",
|
|
954
|
+
"rsvp",
|
|
955
|
+
"ru",
|
|
956
|
+
"ruhr",
|
|
957
|
+
"rw",
|
|
958
|
+
"ryukyu",
|
|
959
|
+
"sa",
|
|
960
|
+
"saarland",
|
|
961
|
+
"sale",
|
|
962
|
+
"samsung",
|
|
963
|
+
"sap",
|
|
964
|
+
"sarl",
|
|
965
|
+
"saxo",
|
|
966
|
+
"sb",
|
|
967
|
+
"sc",
|
|
968
|
+
"sca",
|
|
969
|
+
"scb",
|
|
970
|
+
"schmidt",
|
|
971
|
+
"scholarships",
|
|
972
|
+
"school",
|
|
973
|
+
"schule",
|
|
974
|
+
"schwarz",
|
|
975
|
+
"science",
|
|
976
|
+
"scot",
|
|
977
|
+
"sd",
|
|
978
|
+
"se",
|
|
979
|
+
"services",
|
|
980
|
+
"sew",
|
|
981
|
+
"sexy",
|
|
982
|
+
"sg",
|
|
983
|
+
"sh",
|
|
984
|
+
"shiksha",
|
|
985
|
+
"shoes",
|
|
986
|
+
"shriram",
|
|
987
|
+
"si",
|
|
988
|
+
"singles",
|
|
989
|
+
"site",
|
|
990
|
+
"sj",
|
|
991
|
+
"sk",
|
|
992
|
+
"sky",
|
|
993
|
+
"sl",
|
|
994
|
+
"sm",
|
|
995
|
+
"sn",
|
|
996
|
+
"so",
|
|
997
|
+
"social",
|
|
998
|
+
"software",
|
|
999
|
+
"sohu",
|
|
1000
|
+
"solar",
|
|
1001
|
+
"solutions",
|
|
1002
|
+
"soy",
|
|
1003
|
+
"space",
|
|
1004
|
+
"spiegel",
|
|
1005
|
+
"spreadbetting",
|
|
1006
|
+
"sr",
|
|
1007
|
+
"ss",
|
|
1008
|
+
"st",
|
|
1009
|
+
"study",
|
|
1010
|
+
"style",
|
|
1011
|
+
"su",
|
|
1012
|
+
"sucks",
|
|
1013
|
+
"supplies",
|
|
1014
|
+
"supply",
|
|
1015
|
+
"support",
|
|
1016
|
+
"surf",
|
|
1017
|
+
"surgery",
|
|
1018
|
+
"suzuki",
|
|
1019
|
+
"sv",
|
|
1020
|
+
"sx",
|
|
1021
|
+
"sy",
|
|
1022
|
+
"sydney",
|
|
1023
|
+
"systems",
|
|
1024
|
+
"sz",
|
|
1025
|
+
"taipei",
|
|
1026
|
+
"tatar",
|
|
1027
|
+
"tattoo",
|
|
1028
|
+
"tax",
|
|
1029
|
+
"tc",
|
|
1030
|
+
"td",
|
|
1031
|
+
"tech",
|
|
1032
|
+
"technology",
|
|
1033
|
+
"tel",
|
|
1034
|
+
"temasek",
|
|
1035
|
+
"tennis",
|
|
1036
|
+
"tf",
|
|
1037
|
+
"tg",
|
|
1038
|
+
"th",
|
|
1039
|
+
"tickets",
|
|
1040
|
+
"tienda",
|
|
1041
|
+
"tips",
|
|
1042
|
+
"tires",
|
|
1043
|
+
"tirol",
|
|
1044
|
+
"tj",
|
|
1045
|
+
"tk",
|
|
1046
|
+
"tl",
|
|
1047
|
+
"tm",
|
|
1048
|
+
"tn",
|
|
1049
|
+
"to",
|
|
1050
|
+
"today",
|
|
1051
|
+
"tokyo",
|
|
1052
|
+
"tools",
|
|
1053
|
+
"top",
|
|
1054
|
+
"toshiba",
|
|
1055
|
+
"tours",
|
|
1056
|
+
"town",
|
|
1057
|
+
"toys",
|
|
1058
|
+
"tp",
|
|
1059
|
+
"tr",
|
|
1060
|
+
"trade",
|
|
1061
|
+
"trading",
|
|
1062
|
+
"training",
|
|
1063
|
+
"travel",
|
|
1064
|
+
"trust",
|
|
1065
|
+
"tt",
|
|
1066
|
+
"tui",
|
|
1067
|
+
"tv",
|
|
1068
|
+
"tw",
|
|
1069
|
+
"tz",
|
|
1070
|
+
"ua",
|
|
1071
|
+
"ug",
|
|
1072
|
+
"uk",
|
|
1073
|
+
"um",
|
|
1074
|
+
"university",
|
|
1075
|
+
"uno",
|
|
1076
|
+
"uol",
|
|
1077
|
+
"us",
|
|
1078
|
+
"uy",
|
|
1079
|
+
"uz",
|
|
1080
|
+
"va",
|
|
1081
|
+
"vacations",
|
|
1082
|
+
"vc",
|
|
1083
|
+
"ve",
|
|
1084
|
+
"vegas",
|
|
1085
|
+
"ventures",
|
|
1086
|
+
"versicherung",
|
|
1087
|
+
"vet",
|
|
1088
|
+
"vg",
|
|
1089
|
+
"vi",
|
|
1090
|
+
"viajes",
|
|
1091
|
+
"video",
|
|
1092
|
+
"villas",
|
|
1093
|
+
"vision",
|
|
1094
|
+
"vlaanderen",
|
|
1095
|
+
"vn",
|
|
1096
|
+
"vodka",
|
|
1097
|
+
"vote",
|
|
1098
|
+
"voting",
|
|
1099
|
+
"voto",
|
|
1100
|
+
"voyage",
|
|
1101
|
+
"vu",
|
|
1102
|
+
"wales",
|
|
1103
|
+
"wang",
|
|
1104
|
+
"watch",
|
|
1105
|
+
"webcam",
|
|
1106
|
+
"website",
|
|
1107
|
+
"wed",
|
|
1108
|
+
"wedding",
|
|
1109
|
+
"wf",
|
|
1110
|
+
"whoswho",
|
|
1111
|
+
"wien",
|
|
1112
|
+
"wiki",
|
|
1113
|
+
"williamhill",
|
|
1114
|
+
"win",
|
|
1115
|
+
"wme",
|
|
1116
|
+
"work",
|
|
1117
|
+
"works",
|
|
1118
|
+
"world",
|
|
1119
|
+
"ws",
|
|
1120
|
+
"wtc",
|
|
1121
|
+
"wtf",
|
|
1122
|
+
"xin",
|
|
1123
|
+
"\u6D4B\u8BD5",
|
|
1124
|
+
"\u092A\u0930\u0940\u0915\u094D\u0937\u093E",
|
|
1125
|
+
"\u4F5B\u5C71",
|
|
1126
|
+
"\u6148\u5584",
|
|
1127
|
+
"\u96C6\u56E2",
|
|
1128
|
+
"\u5728\u7EBF",
|
|
1129
|
+
"\uD55C\uAD6D",
|
|
1130
|
+
"\u09AD\u09BE\u09B0\u09A4",
|
|
1131
|
+
"\u516B\u5366",
|
|
1132
|
+
"\u0645\u0648\u0642\u0639",
|
|
1133
|
+
"\u09AC\u09BE\u0982\u09B2\u09BE",
|
|
1134
|
+
"\u516C\u76CA",
|
|
1135
|
+
"\u516C\u53F8",
|
|
1136
|
+
"\u79FB\u52A8",
|
|
1137
|
+
"\u6211\u7231\u4F60",
|
|
1138
|
+
"\u043C\u043E\u0441\u043A\u0432\u0430",
|
|
1139
|
+
"\u0438\u0441\u043F\u044B\u0442\u0430\u043D\u0438\u0435",
|
|
1140
|
+
"\u049B\u0430\u0437",
|
|
1141
|
+
"\u043E\u043D\u043B\u0430\u0439\u043D",
|
|
1142
|
+
"\u0441\u0430\u0439\u0442",
|
|
1143
|
+
"\u0441\u0440\u0431",
|
|
1144
|
+
"\u0431\u0435\u043B",
|
|
1145
|
+
"\u65F6\u5C1A",
|
|
1146
|
+
"\uD14C\uC2A4\uD2B8",
|
|
1147
|
+
"\u6DE1\u9A6C\u9521",
|
|
1148
|
+
"\u043E\u0440\u0433",
|
|
1149
|
+
"\uC0BC\uC131",
|
|
1150
|
+
"\u0B9A\u0BBF\u0B99\u0BCD\u0B95\u0BAA\u0BCD\u0BAA\u0BC2\u0BB0\u0BCD",
|
|
1151
|
+
"\u5546\u6807",
|
|
1152
|
+
"\u5546\u5E97",
|
|
1153
|
+
"\u5546\u57CE",
|
|
1154
|
+
"\u0434\u0435\u0442\u0438",
|
|
1155
|
+
"\u043C\u043A\u0434",
|
|
1156
|
+
"\u05D8\u05E2\u05E1\u05D8",
|
|
1157
|
+
"\u4E2D\u6587\u7F51",
|
|
1158
|
+
"\u4E2D\u4FE1",
|
|
1159
|
+
"\u4E2D\u56FD",
|
|
1160
|
+
"\u4E2D\u570B",
|
|
1161
|
+
"\u8C37\u6B4C",
|
|
1162
|
+
"\u0C2D\u0C3E\u0C30\u0C24\u0C4D",
|
|
1163
|
+
"\u0DBD\u0D82\u0D9A\u0DCF",
|
|
1164
|
+
"\u6E2C\u8A66",
|
|
1165
|
+
"\u0AAD\u0ABE\u0AB0\u0AA4",
|
|
1166
|
+
"\u092D\u093E\u0930\u0924",
|
|
1167
|
+
"\u0622\u0632\u0645\u0627\u06CC\u0634\u06CC",
|
|
1168
|
+
"\u0BAA\u0BB0\u0BBF\u0B9F\u0BCD\u0B9A\u0BC8",
|
|
1169
|
+
"\u7F51\u5E97",
|
|
1170
|
+
"\u0938\u0902\u0917\u0920\u0928",
|
|
1171
|
+
"\u7F51\u7EDC",
|
|
1172
|
+
"\u0443\u043A\u0440",
|
|
1173
|
+
"\u9999\u6E2F",
|
|
1174
|
+
"\u03B4\u03BF\u03BA\u03B9\u03BC\u03AE",
|
|
1175
|
+
"\u98DE\u5229\u6D66",
|
|
1176
|
+
"\u0625\u062E\u062A\u0628\u0627\u0631",
|
|
1177
|
+
"\u53F0\u6E7E",
|
|
1178
|
+
"\u53F0\u7063",
|
|
1179
|
+
"\u624B\u673A",
|
|
1180
|
+
"\u043C\u043E\u043D",
|
|
1181
|
+
"\u0627\u0644\u062C\u0632\u0627\u0626\u0631",
|
|
1182
|
+
"\u0639\u0645\u0627\u0646",
|
|
1183
|
+
"\u0627\u06CC\u0631\u0627\u0646",
|
|
1184
|
+
"\u0627\u0645\u0627\u0631\u0627\u062A",
|
|
1185
|
+
"\u0628\u0627\u0632\u0627\u0631",
|
|
1186
|
+
"\u067E\u0627\u06A9\u0633\u062A\u0627\u0646",
|
|
1187
|
+
"\u0627\u0644\u0627\u0631\u062F\u0646",
|
|
1188
|
+
"\u0628\u06BE\u0627\u0631\u062A",
|
|
1189
|
+
"\u0627\u0644\u0645\u063A\u0631\u0628",
|
|
1190
|
+
"\u0627\u0644\u0633\u0639\u0648\u062F\u064A\u0629",
|
|
1191
|
+
"\u0633\u0648\u062F\u0627\u0646",
|
|
1192
|
+
"\u0639\u0631\u0627\u0642",
|
|
1193
|
+
"\u0645\u0644\u064A\u0633\u064A\u0627",
|
|
1194
|
+
"\u653F\u5E9C",
|
|
1195
|
+
"\u0634\u0628\u0643\u0629",
|
|
1196
|
+
"\u10D2\u10D4",
|
|
1197
|
+
"\u673A\u6784",
|
|
1198
|
+
"\u7EC4\u7EC7\u673A\u6784",
|
|
1199
|
+
"\u5065\u5EB7",
|
|
1200
|
+
"\u0E44\u0E17\u0E22",
|
|
1201
|
+
"\u0633\u0648\u0631\u064A\u0629",
|
|
1202
|
+
"\u0440\u0443\u0441",
|
|
1203
|
+
"\u0440\u0444",
|
|
1204
|
+
"\u062A\u0648\u0646\u0633",
|
|
1205
|
+
"\u307F\u3093\u306A",
|
|
1206
|
+
"\u30B0\u30FC\u30B0\u30EB",
|
|
1207
|
+
"\u4E16\u754C",
|
|
1208
|
+
"\u0A2D\u0A3E\u0A30\u0A24",
|
|
1209
|
+
"\u7F51\u5740",
|
|
1210
|
+
"\u6E38\u620F",
|
|
1211
|
+
"verm\xF6gensberater",
|
|
1212
|
+
"verm\xF6gensberatung",
|
|
1213
|
+
"\u4F01\u4E1A",
|
|
1214
|
+
"\u4FE1\u606F",
|
|
1215
|
+
"\u0645\u0635\u0631",
|
|
1216
|
+
"\u0642\u0637\u0631",
|
|
1217
|
+
"\u5E7F\u4E1C",
|
|
1218
|
+
"\u0B87\u0BB2\u0B99\u0BCD\u0B95\u0BC8",
|
|
1219
|
+
"\u0B87\u0BA8\u0BCD\u0BA4\u0BBF\u0BAF\u0BBE",
|
|
1220
|
+
"\u0570\u0561\u0575",
|
|
1221
|
+
"\u65B0\u52A0\u5761",
|
|
1222
|
+
"\u0641\u0644\u0633\u0637\u064A\u0646",
|
|
1223
|
+
"\u30C6\u30B9\u30C8",
|
|
1224
|
+
"\u653F\u52A1",
|
|
1225
|
+
"xxx",
|
|
1226
|
+
"xyz",
|
|
1227
|
+
"yachts",
|
|
1228
|
+
"yandex",
|
|
1229
|
+
"ye",
|
|
1230
|
+
"yodobashi",
|
|
1231
|
+
"yoga",
|
|
1232
|
+
"yokohama",
|
|
1233
|
+
"youtube",
|
|
1234
|
+
"yt",
|
|
1235
|
+
"za",
|
|
1236
|
+
"zm",
|
|
1237
|
+
"zone",
|
|
1238
|
+
"zuerich",
|
|
1239
|
+
"zw"
|
|
1240
|
+
];
|
|
1241
|
+
var INTERNAL_SUFFIXES = [
|
|
1242
|
+
"local",
|
|
1243
|
+
"internal",
|
|
1244
|
+
"intranet",
|
|
1245
|
+
"corp",
|
|
1246
|
+
"lan",
|
|
1247
|
+
"home",
|
|
1248
|
+
"localdomain",
|
|
1249
|
+
"arpa"
|
|
1250
|
+
];
|
|
1251
|
+
var INTERNAL_COMPOUND_SUFFIXES = [
|
|
1252
|
+
"svc\\.cluster\\.local",
|
|
1253
|
+
"ec2\\.internal",
|
|
1254
|
+
"compute\\.internal"
|
|
1255
|
+
];
|
|
1256
|
+
var FILE_EXT_COLLISION_TLDS = [
|
|
1257
|
+
"ac",
|
|
1258
|
+
"am",
|
|
1259
|
+
"cc",
|
|
1260
|
+
"gs",
|
|
1261
|
+
"hiv",
|
|
1262
|
+
"in",
|
|
1263
|
+
"is",
|
|
1264
|
+
"it",
|
|
1265
|
+
"java",
|
|
1266
|
+
"la",
|
|
1267
|
+
"md",
|
|
1268
|
+
"ml",
|
|
1269
|
+
"pl",
|
|
1270
|
+
"py",
|
|
1271
|
+
"rs",
|
|
1272
|
+
"sh",
|
|
1273
|
+
"so",
|
|
1274
|
+
"tf"
|
|
1275
|
+
];
|
|
1276
|
+
var COLLISION_TLD_SET = new Set(FILE_EXT_COLLISION_TLDS);
|
|
1277
|
+
var GTLD_UNRESTRICTED = GTLD.filter((tld) => !COLLISION_TLD_SET.has(tld));
|
|
1278
|
+
var TLD_EXACT = [...INTERNAL_COMPOUND_SUFFIXES, ...GTLD_UNRESTRICTED, ...INTERNAL_SUFFIXES].join(
|
|
1279
|
+
"|"
|
|
1280
|
+
);
|
|
1281
|
+
var TLD_COLLISION = FILE_EXT_COLLISION_TLDS.join("|");
|
|
1282
|
+
var LABEL = "[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?";
|
|
1283
|
+
var HOST_EXACT = `(?:(?<![\\w.$-])(?<![^:/\\\\][/\\\\])(?:${LABEL}\\.)+(?:${TLD_EXACT})(?!\\.?\\w))`;
|
|
1284
|
+
var HOST_COLLISION = `(?:(?<=(?:https?|sftp|ftps?|wss?)://(?:[^/@\\s]+@)?)(?:${LABEL}\\.)+(?:${TLD_COLLISION})(?!\\.?\\w))`;
|
|
1285
|
+
var UNC_COMPUTER = "(?:(?<=\\\\\\\\)[A-Za-z][A-Za-z0-9-]{0,14}(?=\\\\[A-Za-z0-9$]))";
|
|
1286
|
+
var SPN_HOST = "(?:(?<=\\b[A-Za-z][A-Za-z0-9]*/)[A-Za-z0-9][A-Za-z0-9.-]*(?=@[A-Z0-9.-]+\\b))";
|
|
1287
|
+
var HOST_CTX_KEY = "(?:host|hostname|computer)";
|
|
1288
|
+
var HOST_CTX = `(?:(?<=\\b${HOST_CTX_KEY}\\s*[=:]\\s*)[A-Za-z0-9](?:[A-Za-z0-9.-]*[A-Za-z0-9])?)`;
|
|
1289
|
+
var NETBIOS_AGGRESSIVE = "(?:\\b(?:WIN|DESKTOP)-[A-Z0-9]{7,10}\\b)";
|
|
1290
|
+
var hostnamesRule = {
|
|
1291
|
+
id: "hosts",
|
|
1292
|
+
label: "Hostnames & domains",
|
|
1293
|
+
description: "FQDNs, UNC computer names and Kerberos SPN hosts become the same token everywhere within a session.",
|
|
1294
|
+
mode: "pseudo",
|
|
1295
|
+
token: "HOST",
|
|
1296
|
+
patterns: [HOST_EXACT, HOST_COLLISION, UNC_COMPUTER, SPN_HOST, HOST_CTX],
|
|
1297
|
+
aggressivePatterns: [NETBIOS_AGGRESSIVE],
|
|
1298
|
+
jsonKeys: [
|
|
1299
|
+
"hostname",
|
|
1300
|
+
"host",
|
|
1301
|
+
"fqdn",
|
|
1302
|
+
"domain",
|
|
1303
|
+
"computerName",
|
|
1304
|
+
"serverName",
|
|
1305
|
+
"nodeName",
|
|
1306
|
+
"dnsName",
|
|
1307
|
+
"machineName",
|
|
1308
|
+
"targetHost"
|
|
1309
|
+
]
|
|
1310
|
+
};
|
|
1311
|
+
|
|
1312
|
+
// src/rules/definitions/ipAddresses.ts
|
|
1313
|
+
var IPV4_OCTET = "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)";
|
|
1314
|
+
var IPV4 = `(?:(?<![\\d.])\\b${IPV4_OCTET}(?:\\.${IPV4_OCTET}){3}\\b(?!\\.\\d))`;
|
|
1315
|
+
var IPV6_SEG = "[0-9a-fA-F]{1,4}";
|
|
1316
|
+
var IPV6_FULL = `(?:${IPV6_SEG}:){7}${IPV6_SEG}`;
|
|
1317
|
+
var IPV6_COMPRESSED = `(?:(?:${IPV6_SEG}:){1,7}:|(?:${IPV6_SEG}:){1,6}:${IPV6_SEG}|(?:${IPV6_SEG}:){1,5}(?::${IPV6_SEG}){1,2}|(?:${IPV6_SEG}:){1,4}(?::${IPV6_SEG}){1,3}|(?:${IPV6_SEG}:){1,3}(?::${IPV6_SEG}){1,4}|(?:${IPV6_SEG}:){1,2}(?::${IPV6_SEG}){1,5}|${IPV6_SEG}:(?::${IPV6_SEG}){1,6}|:(?:(?::${IPV6_SEG}){1,7}|:))`;
|
|
1318
|
+
var IPV4_MAPPED = `(?:::ffff:${IPV4})`;
|
|
1319
|
+
var IPV6 = `(?:(?<![A-Za-z0-9])(?:${IPV4_MAPPED}|${IPV6_FULL}|${IPV6_COMPRESSED})(?![A-Za-z0-9:])(?!\\.\\d)(?:%[A-Za-z0-9_.-]+)?)`;
|
|
1320
|
+
var PTR_V4 = "(?:\\b(?:\\d{1,3}\\.){4}in-addr\\.arpa\\b)";
|
|
1321
|
+
var PTR_V6 = "(?:\\b(?:[0-9a-fA-F]\\.){4,32}ip6\\.arpa\\b)";
|
|
1322
|
+
var MAC_COLON = "(?:(?<!&)\\b[0-9A-Fa-f]{2}(?::[0-9A-Fa-f]{2}){5}\\b)";
|
|
1323
|
+
var MAC_DASH = "(?:\\b[0-9A-Fa-f]{2}(?:-[0-9A-Fa-f]{2}){5}\\b)";
|
|
1324
|
+
var MAC_CISCO = "(?:\\b[0-9A-Fa-f]{4}(?:\\.[0-9A-Fa-f]{4}){2}\\b)";
|
|
1325
|
+
function ipv4ToOctets(ip) {
|
|
1326
|
+
return ip.split(".").map(Number);
|
|
1327
|
+
}
|
|
1328
|
+
function isNetmaskShape(octets) {
|
|
1329
|
+
const bin = octets.map((octet) => octet.toString(2).padStart(8, "0")).join("");
|
|
1330
|
+
return /^1*0*$/.test(bin);
|
|
1331
|
+
}
|
|
1332
|
+
function isReservedIpv4(ip) {
|
|
1333
|
+
const octets = ipv4ToOctets(ip);
|
|
1334
|
+
if (octets.length !== 4 || octets.some((octet) => Number.isNaN(octet))) {
|
|
1335
|
+
return false;
|
|
1336
|
+
}
|
|
1337
|
+
if (octets[0] === 127) {
|
|
1338
|
+
return true;
|
|
1339
|
+
}
|
|
1340
|
+
if (octets[0] === 0) {
|
|
1341
|
+
return true;
|
|
1342
|
+
}
|
|
1343
|
+
if (octets[0] === 169 && octets[1] === 254) {
|
|
1344
|
+
return true;
|
|
1345
|
+
}
|
|
1346
|
+
if (octets[0] >= 224 && octets[0] <= 239) {
|
|
1347
|
+
return true;
|
|
1348
|
+
}
|
|
1349
|
+
return isNetmaskShape(octets);
|
|
1350
|
+
}
|
|
1351
|
+
function validateIp(match) {
|
|
1352
|
+
if (/^\d{1,3}(?:\.\d{1,3}){3}$/.test(match)) {
|
|
1353
|
+
return !isReservedIpv4(match);
|
|
1354
|
+
}
|
|
1355
|
+
const withoutZone = match.split("%")[0] ?? match;
|
|
1356
|
+
if (withoutZone === "::" || withoutZone === "::1") {
|
|
1357
|
+
return false;
|
|
1358
|
+
}
|
|
1359
|
+
const mapped = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/.exec(withoutZone);
|
|
1360
|
+
if (mapped?.[1]) {
|
|
1361
|
+
return !isReservedIpv4(mapped[1]);
|
|
1362
|
+
}
|
|
1363
|
+
return true;
|
|
1364
|
+
}
|
|
1365
|
+
var ipAddressesRule = {
|
|
1366
|
+
id: "ips",
|
|
1367
|
+
label: "Network addresses",
|
|
1368
|
+
description: "IPv4/IPv6 addresses, MAC addresses and reverse-DNS (PTR) names become the same token everywhere within a session.",
|
|
1369
|
+
mode: "pseudo",
|
|
1370
|
+
token: "IP",
|
|
1371
|
+
patterns: [PTR_V4, PTR_V6, IPV6, IPV4, MAC_COLON, MAC_DASH, MAC_CISCO],
|
|
1372
|
+
validate: validateIp,
|
|
1373
|
+
jsonKeys: [
|
|
1374
|
+
"ip",
|
|
1375
|
+
"ipAddress",
|
|
1376
|
+
"ip_address",
|
|
1377
|
+
"clientIp",
|
|
1378
|
+
"remoteAddr",
|
|
1379
|
+
"srcIp",
|
|
1380
|
+
"dstIp",
|
|
1381
|
+
"sourceIp",
|
|
1382
|
+
"destinationIp",
|
|
1383
|
+
"xForwardedFor",
|
|
1384
|
+
"mac",
|
|
1385
|
+
"macAddress"
|
|
1386
|
+
]
|
|
1387
|
+
};
|
|
1388
|
+
|
|
1389
|
+
// src/rules/shared/hmacSha256.ts
|
|
1390
|
+
var BLOCK_BYTES = 64;
|
|
1391
|
+
var DIGEST_BYTES = 32;
|
|
1392
|
+
var K = new Uint32Array([
|
|
1393
|
+
1116352408,
|
|
1394
|
+
1899447441,
|
|
1395
|
+
3049323471,
|
|
1396
|
+
3921009573,
|
|
1397
|
+
961987163,
|
|
1398
|
+
1508970993,
|
|
1399
|
+
2453635748,
|
|
1400
|
+
2870763221,
|
|
1401
|
+
3624381080,
|
|
1402
|
+
310598401,
|
|
1403
|
+
607225278,
|
|
1404
|
+
1426881987,
|
|
1405
|
+
1925078388,
|
|
1406
|
+
2162078206,
|
|
1407
|
+
2614888103,
|
|
1408
|
+
3248222580,
|
|
1409
|
+
3835390401,
|
|
1410
|
+
4022224774,
|
|
1411
|
+
264347078,
|
|
1412
|
+
604807628,
|
|
1413
|
+
770255983,
|
|
1414
|
+
1249150122,
|
|
1415
|
+
1555081692,
|
|
1416
|
+
1996064986,
|
|
1417
|
+
2554220882,
|
|
1418
|
+
2821834349,
|
|
1419
|
+
2952996808,
|
|
1420
|
+
3210313671,
|
|
1421
|
+
3336571891,
|
|
1422
|
+
3584528711,
|
|
1423
|
+
113926993,
|
|
1424
|
+
338241895,
|
|
1425
|
+
666307205,
|
|
1426
|
+
773529912,
|
|
1427
|
+
1294757372,
|
|
1428
|
+
1396182291,
|
|
1429
|
+
1695183700,
|
|
1430
|
+
1986661051,
|
|
1431
|
+
2177026350,
|
|
1432
|
+
2456956037,
|
|
1433
|
+
2730485921,
|
|
1434
|
+
2820302411,
|
|
1435
|
+
3259730800,
|
|
1436
|
+
3345764771,
|
|
1437
|
+
3516065817,
|
|
1438
|
+
3600352804,
|
|
1439
|
+
4094571909,
|
|
1440
|
+
275423344,
|
|
1441
|
+
430227734,
|
|
1442
|
+
506948616,
|
|
1443
|
+
659060556,
|
|
1444
|
+
883997877,
|
|
1445
|
+
958139571,
|
|
1446
|
+
1322822218,
|
|
1447
|
+
1537002063,
|
|
1448
|
+
1747873779,
|
|
1449
|
+
1955562222,
|
|
1450
|
+
2024104815,
|
|
1451
|
+
2227730452,
|
|
1452
|
+
2361852424,
|
|
1453
|
+
2428436474,
|
|
1454
|
+
2756734187,
|
|
1455
|
+
3204031479,
|
|
1456
|
+
3329325298
|
|
1457
|
+
]);
|
|
1458
|
+
var INITIAL_STATE = new Uint32Array([
|
|
1459
|
+
1779033703,
|
|
1460
|
+
3144134277,
|
|
1461
|
+
1013904242,
|
|
1462
|
+
2773480762,
|
|
1463
|
+
1359893119,
|
|
1464
|
+
2600822924,
|
|
1465
|
+
528734635,
|
|
1466
|
+
1541459225
|
|
1467
|
+
]);
|
|
1468
|
+
function rotr(value, bits) {
|
|
1469
|
+
return (value >>> bits | value << 32 - bits) >>> 0;
|
|
1470
|
+
}
|
|
1471
|
+
function padMessage(message) {
|
|
1472
|
+
const bitLength = message.length * 8;
|
|
1473
|
+
const paddedLength = Math.ceil((message.length + 9) / BLOCK_BYTES) * BLOCK_BYTES;
|
|
1474
|
+
const padded = new Uint8Array(paddedLength);
|
|
1475
|
+
padded.set(message);
|
|
1476
|
+
padded[message.length] = 128;
|
|
1477
|
+
const view = new DataView(padded.buffer);
|
|
1478
|
+
view.setUint32(paddedLength - 8, Math.floor(bitLength / 2 ** 32), false);
|
|
1479
|
+
view.setUint32(paddedLength - 4, bitLength >>> 0, false);
|
|
1480
|
+
return padded;
|
|
1481
|
+
}
|
|
1482
|
+
function sha256(message) {
|
|
1483
|
+
const padded = padMessage(message);
|
|
1484
|
+
const view = new DataView(padded.buffer);
|
|
1485
|
+
const state = Uint32Array.from(INITIAL_STATE);
|
|
1486
|
+
const w = new Uint32Array(64);
|
|
1487
|
+
for (let offset = 0; offset < padded.length; offset += BLOCK_BYTES) {
|
|
1488
|
+
for (let i = 0; i < 16; i += 1) {
|
|
1489
|
+
w[i] = view.getUint32(offset + i * 4, false);
|
|
1490
|
+
}
|
|
1491
|
+
for (let i = 16; i < 64; i += 1) {
|
|
1492
|
+
const s0 = rotr(w[i - 15], 7) ^ rotr(w[i - 15], 18) ^ w[i - 15] >>> 3;
|
|
1493
|
+
const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ w[i - 2] >>> 10;
|
|
1494
|
+
w[i] = w[i - 16] + s0 + w[i - 7] + s1 >>> 0;
|
|
1495
|
+
}
|
|
1496
|
+
let [a, b, c, d, e, f, g, h] = state;
|
|
1497
|
+
for (let i = 0; i < 64; i += 1) {
|
|
1498
|
+
const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
|
|
1499
|
+
const ch = e & f ^ ~e & g;
|
|
1500
|
+
const temp1 = h + s1 + ch + K[i] + w[i] >>> 0;
|
|
1501
|
+
const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
|
|
1502
|
+
const maj = a & b ^ a & c ^ b & c;
|
|
1503
|
+
const temp2 = s0 + maj >>> 0;
|
|
1504
|
+
h = g;
|
|
1505
|
+
g = f;
|
|
1506
|
+
f = e;
|
|
1507
|
+
e = d + temp1 >>> 0;
|
|
1508
|
+
d = c;
|
|
1509
|
+
c = b;
|
|
1510
|
+
b = a;
|
|
1511
|
+
a = temp1 + temp2 >>> 0;
|
|
1512
|
+
}
|
|
1513
|
+
state[0] = state[0] + a >>> 0;
|
|
1514
|
+
state[1] = state[1] + b >>> 0;
|
|
1515
|
+
state[2] = state[2] + c >>> 0;
|
|
1516
|
+
state[3] = state[3] + d >>> 0;
|
|
1517
|
+
state[4] = state[4] + e >>> 0;
|
|
1518
|
+
state[5] = state[5] + f >>> 0;
|
|
1519
|
+
state[6] = state[6] + g >>> 0;
|
|
1520
|
+
state[7] = state[7] + h >>> 0;
|
|
1521
|
+
}
|
|
1522
|
+
const digest = new Uint8Array(DIGEST_BYTES);
|
|
1523
|
+
const digestView = new DataView(digest.buffer);
|
|
1524
|
+
for (let i = 0; i < 8; i += 1) {
|
|
1525
|
+
digestView.setUint32(i * 4, state[i], false);
|
|
1526
|
+
}
|
|
1527
|
+
return digest;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
// src/rules/definitions/paymentInfo.ts
|
|
1531
|
+
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
1532
|
+
function luhnCheck2(digits) {
|
|
1533
|
+
if (!/^\d{13,19}$/.test(digits) || /^0+$/.test(digits)) {
|
|
1534
|
+
return false;
|
|
1535
|
+
}
|
|
1536
|
+
let sum = 0;
|
|
1537
|
+
let doubleIt = false;
|
|
1538
|
+
for (let i = digits.length - 1; i >= 0; i -= 1) {
|
|
1539
|
+
let digit = Number(digits[i]);
|
|
1540
|
+
if (doubleIt) {
|
|
1541
|
+
digit *= 2;
|
|
1542
|
+
if (digit > 9) {
|
|
1543
|
+
digit -= 9;
|
|
1544
|
+
}
|
|
1545
|
+
}
|
|
1546
|
+
sum += digit;
|
|
1547
|
+
doubleIt = !doubleIt;
|
|
1548
|
+
}
|
|
1549
|
+
return sum % 10 === 0;
|
|
1550
|
+
}
|
|
1551
|
+
function ibanMod97(iban) {
|
|
1552
|
+
const compact = iban.replace(/\s/g, "").toUpperCase();
|
|
1553
|
+
if (!/^[A-Z]{2}\d{2}[A-Z0-9]{10,30}$/.test(compact)) {
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
const rearranged = compact.slice(4) + compact.slice(0, 4);
|
|
1557
|
+
const numeric = rearranged.replace(/[A-Z]/g, (char) => String(char.charCodeAt(0) - 55));
|
|
1558
|
+
let remainder = 0;
|
|
1559
|
+
for (let i = 0; i < numeric.length; i += 1) {
|
|
1560
|
+
remainder = (remainder * 10 + Number(numeric[i])) % 97;
|
|
1561
|
+
}
|
|
1562
|
+
return remainder === 1;
|
|
1563
|
+
}
|
|
1564
|
+
var IBAN_COUNTRY_CODES = /* @__PURE__ */ new Set([
|
|
1565
|
+
"AD",
|
|
1566
|
+
"AE",
|
|
1567
|
+
"AL",
|
|
1568
|
+
"AO",
|
|
1569
|
+
"AT",
|
|
1570
|
+
"AZ",
|
|
1571
|
+
"BA",
|
|
1572
|
+
"BE",
|
|
1573
|
+
"BF",
|
|
1574
|
+
"BG",
|
|
1575
|
+
"BH",
|
|
1576
|
+
"BI",
|
|
1577
|
+
"BJ",
|
|
1578
|
+
"BR",
|
|
1579
|
+
"BY",
|
|
1580
|
+
"CF",
|
|
1581
|
+
"CG",
|
|
1582
|
+
"CH",
|
|
1583
|
+
"CI",
|
|
1584
|
+
"CM",
|
|
1585
|
+
"CR",
|
|
1586
|
+
"CV",
|
|
1587
|
+
"CY",
|
|
1588
|
+
"CZ",
|
|
1589
|
+
"DE",
|
|
1590
|
+
"DJ",
|
|
1591
|
+
"DK",
|
|
1592
|
+
"DO",
|
|
1593
|
+
"DZ",
|
|
1594
|
+
"EE",
|
|
1595
|
+
"EG",
|
|
1596
|
+
"ES",
|
|
1597
|
+
"FI",
|
|
1598
|
+
"FO",
|
|
1599
|
+
"FR",
|
|
1600
|
+
"GA",
|
|
1601
|
+
"GB",
|
|
1602
|
+
"GE",
|
|
1603
|
+
"GI",
|
|
1604
|
+
"GL",
|
|
1605
|
+
"GQ",
|
|
1606
|
+
"GR",
|
|
1607
|
+
"GT",
|
|
1608
|
+
"GW",
|
|
1609
|
+
"HN",
|
|
1610
|
+
"HR",
|
|
1611
|
+
"HU",
|
|
1612
|
+
"IE",
|
|
1613
|
+
"IL",
|
|
1614
|
+
"IQ",
|
|
1615
|
+
"IR",
|
|
1616
|
+
"IS",
|
|
1617
|
+
"IT",
|
|
1618
|
+
"JO",
|
|
1619
|
+
"KM",
|
|
1620
|
+
"KW",
|
|
1621
|
+
"KZ",
|
|
1622
|
+
"LB",
|
|
1623
|
+
"LC",
|
|
1624
|
+
"LI",
|
|
1625
|
+
"LT",
|
|
1626
|
+
"LU",
|
|
1627
|
+
"LV",
|
|
1628
|
+
"LY",
|
|
1629
|
+
"MA",
|
|
1630
|
+
"MC",
|
|
1631
|
+
"MD",
|
|
1632
|
+
"ME",
|
|
1633
|
+
"MG",
|
|
1634
|
+
"MK",
|
|
1635
|
+
"ML",
|
|
1636
|
+
"MR",
|
|
1637
|
+
"MT",
|
|
1638
|
+
"MU",
|
|
1639
|
+
"MZ",
|
|
1640
|
+
"NE",
|
|
1641
|
+
"NI",
|
|
1642
|
+
"NL",
|
|
1643
|
+
"NO",
|
|
1644
|
+
"PK",
|
|
1645
|
+
"PL",
|
|
1646
|
+
"PS",
|
|
1647
|
+
"PT",
|
|
1648
|
+
"QA",
|
|
1649
|
+
"RO",
|
|
1650
|
+
"RS",
|
|
1651
|
+
"RU",
|
|
1652
|
+
"SA",
|
|
1653
|
+
"SC",
|
|
1654
|
+
"SD",
|
|
1655
|
+
"SE",
|
|
1656
|
+
"SI",
|
|
1657
|
+
"SK",
|
|
1658
|
+
"SM",
|
|
1659
|
+
"SN",
|
|
1660
|
+
"ST",
|
|
1661
|
+
"SV",
|
|
1662
|
+
"TD",
|
|
1663
|
+
"TG",
|
|
1664
|
+
"TL",
|
|
1665
|
+
"TN",
|
|
1666
|
+
"TR",
|
|
1667
|
+
"UA",
|
|
1668
|
+
"VA",
|
|
1669
|
+
"VG",
|
|
1670
|
+
"XK"
|
|
1671
|
+
]);
|
|
1672
|
+
function isIbanCountry(compact) {
|
|
1673
|
+
return IBAN_COUNTRY_CODES.has(compact.slice(0, 2).toUpperCase());
|
|
1674
|
+
}
|
|
1675
|
+
function isHexHashLength(value) {
|
|
1676
|
+
return /^(?:[A-Fa-f0-9]{32}|[A-Fa-f0-9]{40}|[A-Fa-f0-9]{64})$/.test(value);
|
|
1677
|
+
}
|
|
1678
|
+
function isCardSeparatorLayout(match) {
|
|
1679
|
+
if (!/[ -]/.test(match)) {
|
|
1680
|
+
return true;
|
|
1681
|
+
}
|
|
1682
|
+
const groups = match.split(/[ -]/).map((group) => group.length);
|
|
1683
|
+
if (groups.some((length) => length === 0)) {
|
|
1684
|
+
return false;
|
|
1685
|
+
}
|
|
1686
|
+
if (groups.length === 3 && groups[0] === 4 && groups[1] === 6 && (groups[2] === 4 || groups[2] === 5)) {
|
|
1687
|
+
return true;
|
|
1688
|
+
}
|
|
1689
|
+
const last = groups[groups.length - 1];
|
|
1690
|
+
return groups.length >= 2 && groups.slice(0, -1).every((length) => length === 4) && last >= 1 && last <= 7;
|
|
1691
|
+
}
|
|
1692
|
+
function bitcoinBase58Check(address) {
|
|
1693
|
+
const decoded = decodeBase58(address);
|
|
1694
|
+
if (!decoded || decoded.length < 5) {
|
|
1695
|
+
return false;
|
|
1696
|
+
}
|
|
1697
|
+
const payload = decoded.subarray(0, decoded.length - 4);
|
|
1698
|
+
const checksum = decoded.subarray(decoded.length - 4);
|
|
1699
|
+
const digest = sha256(sha256(payload));
|
|
1700
|
+
return digest[0] === checksum[0] && digest[1] === checksum[1] && digest[2] === checksum[2] && digest[3] === checksum[3];
|
|
1701
|
+
}
|
|
1702
|
+
function decodeBase58(input) {
|
|
1703
|
+
const bytes = [0];
|
|
1704
|
+
for (let i = 0; i < input.length; i += 1) {
|
|
1705
|
+
let carry = BASE58_ALPHABET.indexOf(input[i]);
|
|
1706
|
+
if (carry === -1) {
|
|
1707
|
+
return null;
|
|
1708
|
+
}
|
|
1709
|
+
for (let j = 0; j < bytes.length; j += 1) {
|
|
1710
|
+
carry += bytes[j] * 58;
|
|
1711
|
+
bytes[j] = carry % 256;
|
|
1712
|
+
carry = Math.floor(carry / 256);
|
|
1713
|
+
}
|
|
1714
|
+
while (carry > 0) {
|
|
1715
|
+
bytes.push(carry % 256);
|
|
1716
|
+
carry = Math.floor(carry / 256);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
let leadingOnes = 0;
|
|
1720
|
+
while (leadingOnes < input.length && input[leadingOnes] === "1") {
|
|
1721
|
+
leadingOnes += 1;
|
|
1722
|
+
}
|
|
1723
|
+
let end = bytes.length;
|
|
1724
|
+
while (end > 0 && bytes[end - 1] === 0) {
|
|
1725
|
+
end -= 1;
|
|
1726
|
+
}
|
|
1727
|
+
const out = new Uint8Array(leadingOnes + end);
|
|
1728
|
+
for (let i = 0; i < end; i += 1) {
|
|
1729
|
+
out[leadingOnes + end - 1 - i] = bytes[i];
|
|
1730
|
+
}
|
|
1731
|
+
return out;
|
|
1732
|
+
}
|
|
1733
|
+
function isKnownCardIin(digits) {
|
|
1734
|
+
const { length } = digits;
|
|
1735
|
+
const d2 = Number(digits.slice(0, 2));
|
|
1736
|
+
const d4 = Number(digits.slice(0, 4));
|
|
1737
|
+
switch (digits[0]) {
|
|
1738
|
+
case "4":
|
|
1739
|
+
return length === 13 || length === 16 || length === 19;
|
|
1740
|
+
case "5":
|
|
1741
|
+
return length === 16 && d2 >= 51 && d2 <= 55;
|
|
1742
|
+
case "2":
|
|
1743
|
+
return length === 16 && d4 >= 2221 && d4 <= 2720;
|
|
1744
|
+
case "3":
|
|
1745
|
+
if (length === 15) {
|
|
1746
|
+
return d2 === 34 || d2 === 37;
|
|
1747
|
+
}
|
|
1748
|
+
if (length === 14) {
|
|
1749
|
+
return d2 === 36 || d2 === 38 || d2 === 39 || d4 >= 3e3 && d4 <= 3059 || d4 === 3095;
|
|
1750
|
+
}
|
|
1751
|
+
return length >= 16 && length <= 19 && d4 >= 3528 && d4 <= 3589;
|
|
1752
|
+
case "6":
|
|
1753
|
+
return length >= 16 && length <= 19 && (digits.startsWith("6011") || digits.startsWith("62") || digits.startsWith("64") || digits.startsWith("65"));
|
|
1754
|
+
default:
|
|
1755
|
+
return false;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
function validatePayment(match) {
|
|
1759
|
+
const compactIban = match.replace(/\s/g, "");
|
|
1760
|
+
if (/^[A-Za-z]{2}\d{2}[A-Za-z0-9]+$/.test(compactIban)) {
|
|
1761
|
+
return !isHexHashLength(compactIban) && isIbanCountry(compactIban) && ibanMod97(match);
|
|
1762
|
+
}
|
|
1763
|
+
const compactPan = match.replace(/[ -]/g, "");
|
|
1764
|
+
if (/^[\d -]+$/.test(match) && /^\d{13,19}$/.test(compactPan)) {
|
|
1765
|
+
const grouped = /[ -]/.test(match);
|
|
1766
|
+
if (!grouped && compactPan.length > 16) {
|
|
1767
|
+
return false;
|
|
1768
|
+
}
|
|
1769
|
+
return isCardSeparatorLayout(match) && isKnownCardIin(compactPan) && luhnCheck2(compactPan);
|
|
1770
|
+
}
|
|
1771
|
+
if (/^[13][A-HJ-NP-Za-km-z1-9]{25,34}$/.test(match)) {
|
|
1772
|
+
return bitcoinBase58Check(match);
|
|
1773
|
+
}
|
|
1774
|
+
return true;
|
|
1775
|
+
}
|
|
1776
|
+
var CVV_KEY = "(?:[Cc][Vv][Vv]2?|[Cc][Vv][Cc]2?|[Cc][Ii][Dd])";
|
|
1777
|
+
var EXP_KEY = "(?:[Ee]xp(?:iry|iration)?|[Ee]xp[_-]?[Dd]ate)";
|
|
1778
|
+
var ROUTING_KEY = "(?:[Rr]outing(?:[_-]?[Nn]umber)?|[Aa][Bb][Aa])";
|
|
1779
|
+
var ACCOUNT_KEY = "(?:[Aa]ccount[_-]?[Nn]umber|[Aa]cct)";
|
|
1780
|
+
var paymentInfoRule = {
|
|
1781
|
+
id: "paymentInfo",
|
|
1782
|
+
label: "Payment info",
|
|
1783
|
+
description: "IBANs, card numbers (PAN, Luhn-checked), CVV/CVC, expiry dates and crypto addresses are redacted outright.",
|
|
1784
|
+
mode: "mask",
|
|
1785
|
+
token: "PAYMENT",
|
|
1786
|
+
patterns: [
|
|
1787
|
+
"(?:(?<![.\\d])\\b\\d(?:[ -]?\\d){12,18}\\b)",
|
|
1788
|
+
"(?:\\b[A-Z]{2}\\d{2}[A-Z0-9]{10,30}\\b)",
|
|
1789
|
+
"(?:\\b[A-Z]{2}\\d{2}(?: [A-Z0-9]{4}){2,7}(?: [A-Z0-9]{1,4})?\\b)",
|
|
1790
|
+
`(?:(?<=\\b${CVV_KEY}\\s*[=:]\\s*)\\d{3,4}\\b)`,
|
|
1791
|
+
`(?:(?<=\\b${EXP_KEY}\\s*[=:]\\s*)(?:0[1-9]|1[0-2])[/-](?:\\d{2}|\\d{4})\\b)`,
|
|
1792
|
+
"(?:\\bbc1[a-z0-9]{25,39}\\b)",
|
|
1793
|
+
"(?:(?<![A-Za-z0-9])[13][A-HJ-NP-Za-km-z1-9]{25,34}(?![A-Za-z0-9]))",
|
|
1794
|
+
"(?:\\b0x[A-Fa-f0-9]{40}\\b)"
|
|
1795
|
+
],
|
|
1796
|
+
aggressivePatterns: [
|
|
1797
|
+
"(?:(?<![\\\\/])\\b[A-Z]{6}[A-Z0-9]{2}(?:[A-Z0-9]{3})?\\b(?![\\\\/]))",
|
|
1798
|
+
`(?:(?<=\\b${ROUTING_KEY}\\s*[=:]\\s*)\\d{9}\\b)`,
|
|
1799
|
+
`(?:(?<=\\b${ACCOUNT_KEY}\\s*[=:]\\s*)\\d{8,12}\\b)`
|
|
1800
|
+
],
|
|
1801
|
+
validate: validatePayment,
|
|
1802
|
+
jsonKeys: [
|
|
1803
|
+
"iban",
|
|
1804
|
+
"cardNumber",
|
|
1805
|
+
"pan",
|
|
1806
|
+
"cvv",
|
|
1807
|
+
"cvc",
|
|
1808
|
+
"card_number",
|
|
1809
|
+
"creditCard",
|
|
1810
|
+
"cvv2",
|
|
1811
|
+
"accountNumber",
|
|
1812
|
+
"routingNumber",
|
|
1813
|
+
"bic",
|
|
1814
|
+
"swift",
|
|
1815
|
+
"expiryDate"
|
|
1816
|
+
]
|
|
1817
|
+
};
|
|
1818
|
+
|
|
1819
|
+
// src/rules/definitions/phoneNumbers.ts
|
|
1820
|
+
function validatePhone(match) {
|
|
1821
|
+
const digits = match.replace(/\D/g, "");
|
|
1822
|
+
const national = digits.startsWith("00") ? digits.slice(2) : digits;
|
|
1823
|
+
return national.length >= 7 && national.length <= 15;
|
|
1824
|
+
}
|
|
1825
|
+
var PHONE_KEY = "(?:telephone|phone|mobile|msisdn|cell|fax|tel)";
|
|
1826
|
+
var phoneNumbersRule = {
|
|
1827
|
+
id: "phoneNumbers",
|
|
1828
|
+
label: "Phone numbers",
|
|
1829
|
+
description: "Phone numbers become a stable <PHONE:\u2026> token \u2014 the same number maps to the same token everywhere within a session.",
|
|
1830
|
+
mode: "pseudo",
|
|
1831
|
+
token: "PHONE",
|
|
1832
|
+
patterns: [
|
|
1833
|
+
"(?:(?<![A-Za-z0-9])\\+[1-9](?:[\\s().-]*\\d){6,14}(?!\\d))",
|
|
1834
|
+
"(?:\\(\\d{3}\\)\\s?\\d{3}-\\d{4}(?!\\d))",
|
|
1835
|
+
"(?:\\b\\d{3}-\\d{3}-\\d{4}\\b)",
|
|
1836
|
+
"(?:\\b1-\\d{3}-\\d{3}-\\d{4}\\b)",
|
|
1837
|
+
"(?:\\b0\\d{2}-\\d{3}-\\d{2}-\\d{2}\\b)",
|
|
1838
|
+
"(?:\\b0\\d{2}\\s\\d{3}\\s\\d{2}\\s\\d{2}\\b)",
|
|
1839
|
+
"(?:(?<![.\\d])\\b00[1-9](?:[\\s().-]*\\d){7,13}(?!\\d))",
|
|
1840
|
+
"(?:(?<=\\b(?:tel|sms):)\\+?[1-9](?:[\\s().-]*\\d){6,14}(?!\\d))",
|
|
1841
|
+
`(?:(?<=\\b${PHONE_KEY}\\s*[=:]\\s*)\\+?[0-9](?:[\\s().-]*\\d){6,14}(?!\\d))`
|
|
1842
|
+
],
|
|
1843
|
+
validate: validatePhone,
|
|
1844
|
+
jsonKeys: [
|
|
1845
|
+
"phone",
|
|
1846
|
+
"phoneNumber",
|
|
1847
|
+
"phone_number",
|
|
1848
|
+
"mobile",
|
|
1849
|
+
"mobileNumber",
|
|
1850
|
+
"cell",
|
|
1851
|
+
"telephone",
|
|
1852
|
+
"tel",
|
|
1853
|
+
"fax",
|
|
1854
|
+
"msisdn",
|
|
1855
|
+
"contactNumber"
|
|
1856
|
+
]
|
|
1857
|
+
};
|
|
1858
|
+
|
|
1859
|
+
// src/rules/definitions/secrets.ts
|
|
1860
|
+
var ENTROPY_THRESHOLD = 3.5;
|
|
1861
|
+
function shannonEntropy(value) {
|
|
1862
|
+
const { length } = value;
|
|
1863
|
+
if (length === 0) {
|
|
1864
|
+
return 0;
|
|
1865
|
+
}
|
|
1866
|
+
const freq = {};
|
|
1867
|
+
for (let i = 0; i < length; i += 1) {
|
|
1868
|
+
const char = value[i];
|
|
1869
|
+
freq[char] = (freq[char] ?? 0) + 1;
|
|
1870
|
+
}
|
|
1871
|
+
return Object.values(freq).reduce((entropy, count) => {
|
|
1872
|
+
const p = count / length;
|
|
1873
|
+
return entropy - p * Math.log2(p);
|
|
1874
|
+
}, 0);
|
|
1875
|
+
}
|
|
1876
|
+
function isHexBlob(match) {
|
|
1877
|
+
return match.length >= 32 && /^[A-Fa-f0-9]+$/.test(match);
|
|
1878
|
+
}
|
|
1879
|
+
function isBase64Blob(match) {
|
|
1880
|
+
return match.length >= 40 && /^[A-Za-z0-9+/]+=*$/.test(match);
|
|
1881
|
+
}
|
|
1882
|
+
function validateSecret(match) {
|
|
1883
|
+
if (isHexBlob(match)) {
|
|
1884
|
+
return shannonEntropy(match) > ENTROPY_THRESHOLD;
|
|
1885
|
+
}
|
|
1886
|
+
if (isBase64Blob(match)) {
|
|
1887
|
+
return shannonEntropy(match) > ENTROPY_THRESHOLD && /[A-Za-z]/.test(match) && /[0-9+/]/.test(match);
|
|
1888
|
+
}
|
|
1889
|
+
return true;
|
|
1890
|
+
}
|
|
1891
|
+
var CONTEXT_KEYS = "(?:api[_-]?key|apikey|access[_-]?token|refresh[_-]?token|id[_-]?token|client[_-]?secret|secret[_-]?key|private[_-]?key|password|passwd|pwd|pass|auth[_-]?token|signing[_-]?key)";
|
|
1892
|
+
var CONTEXT_VALUE = `[^\\s,;}"']{4,}`;
|
|
1893
|
+
var CLI_FLAGS = "(?:password|secret|token|api-key|apikey|access-token|client-secret)";
|
|
1894
|
+
var secretsRule = {
|
|
1895
|
+
id: "secrets",
|
|
1896
|
+
label: "Tokens & credentials",
|
|
1897
|
+
description: "Bearer tokens, JWTs and API keys are redacted outright \u2014 they are not meant to be traced.",
|
|
1898
|
+
mode: "mask",
|
|
1899
|
+
token: "SECRET",
|
|
1900
|
+
patterns: [
|
|
1901
|
+
"(?:(?<=(?:Bearer|bearer|BEARER)\\s+)[A-Za-z0-9._\\-+/=]{8,})",
|
|
1902
|
+
"(?:(?<=(?:Basic|basic|BASIC)\\s+)[A-Za-z0-9+/]{8,}={0,2})",
|
|
1903
|
+
"(?:(?<![A-Za-z0-9_-])eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}(?:\\.[A-Za-z0-9_-]{8,})?)",
|
|
1904
|
+
"(?:-----BEGIN (?:(?:RSA |EC |DSA |OPENSSH |ENCRYPTED )?PRIVATE KEY|CERTIFICATE)-----[A-Za-z0-9+/=\\s\\\\]+?-----END (?:(?:RSA |EC |DSA |OPENSSH |ENCRYPTED )?PRIVATE KEY|CERTIFICATE)-----)",
|
|
1905
|
+
"(?:\\b(?:AKIA|ASIA|ABIA|ACCA)[A-Z0-9]{16}\\b)",
|
|
1906
|
+
"(?:(?<=(?:aws_secret_access_key|AWS_SECRET_ACCESS_KEY)\\s*[=:]\\s*)[A-Za-z0-9/+=]{30,})",
|
|
1907
|
+
"(?:github_pat_[A-Za-z0-9_]{22,})",
|
|
1908
|
+
"(?:gh[pousr]_[A-Za-z0-9]{36,})",
|
|
1909
|
+
"(?:xox[abprs]-[A-Za-z0-9-]{10,})",
|
|
1910
|
+
"(?:hooks\\.slack\\.com/services/[A-Za-z0-9/_-]{16,})",
|
|
1911
|
+
"(?:AIza[A-Za-z0-9_-]{35})",
|
|
1912
|
+
"(?:(?:sk_live_|sk_test_|rk_live_|rk_test_)[A-Za-z0-9]{16,})",
|
|
1913
|
+
"(?:sk-proj-[A-Za-z0-9_-]{16,})",
|
|
1914
|
+
"(?:sk-[A-Za-z0-9]{20,})",
|
|
1915
|
+
"(?:(?<=(?:[Tt]wilio.{0,60}|\\b(?:[Aa]ccount[Ss]id|[Aa]uth[Tt]oken|[Ss]id)\\s*[=:]\\s*))(?:AC|SK)[0-9A-Fa-f]{32})",
|
|
1916
|
+
"(?:SG\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,})",
|
|
1917
|
+
"(?:npm_[A-Za-z0-9]{36,})",
|
|
1918
|
+
`(?:(?<=[?&][Ss]ig=)[^&\\s"']{16,})`,
|
|
1919
|
+
"(?:(?<=[a-zA-Z][a-zA-Z0-9+.-]*://)[^/@\\s:]+:[^/@\\s]+@)",
|
|
1920
|
+
`(?:(?<=--${CLI_FLAGS}(?:\\s+|=)"?)[^\\s"']{4,})`,
|
|
1921
|
+
`(?:(?<=\\b${CONTEXT_KEYS}"?\\s*[=:]\\s*"?)${CONTEXT_VALUE})`
|
|
1922
|
+
],
|
|
1923
|
+
aggressivePatterns: [
|
|
1924
|
+
"(?:(?<![A-Za-z0-9+/])[A-Za-z0-9+/]{40,}={0,2}(?![A-Za-z0-9+/=]))",
|
|
1925
|
+
"(?:\\b[A-Fa-f0-9]{32,}\\b)"
|
|
1926
|
+
],
|
|
1927
|
+
validate: validateSecret,
|
|
1928
|
+
jsonKeys: [
|
|
1929
|
+
"password",
|
|
1930
|
+
"token",
|
|
1931
|
+
"authorization",
|
|
1932
|
+
"apiKey",
|
|
1933
|
+
"secret",
|
|
1934
|
+
"accessToken",
|
|
1935
|
+
"refreshToken",
|
|
1936
|
+
"passwd",
|
|
1937
|
+
"pwd",
|
|
1938
|
+
"pass",
|
|
1939
|
+
"clientSecret",
|
|
1940
|
+
"privateKey",
|
|
1941
|
+
"secretKey",
|
|
1942
|
+
"apiSecret",
|
|
1943
|
+
"credentials",
|
|
1944
|
+
"auth",
|
|
1945
|
+
"xApiKey",
|
|
1946
|
+
"idToken",
|
|
1947
|
+
"awsSecretAccessKey",
|
|
1948
|
+
"signature"
|
|
1949
|
+
]
|
|
1950
|
+
};
|
|
1951
|
+
|
|
1952
|
+
// src/rules/definitions/sessionCookies.ts
|
|
1953
|
+
var COOKIE_VALUE = "[^;\\s]{8,}";
|
|
1954
|
+
var sessionCookiesRule = {
|
|
1955
|
+
id: "sessionCookies",
|
|
1956
|
+
label: "Session cookies",
|
|
1957
|
+
description: "Session identifiers (sessionid, sid, JSESSIONID, PHPSESSID) are redacted outright.",
|
|
1958
|
+
mode: "mask",
|
|
1959
|
+
token: "SESSION",
|
|
1960
|
+
patterns: [
|
|
1961
|
+
`(?:(?<![\\w-])(?:[Ss]essionid|[Ss]ession_id|sessionId|SESSIONID)=${COOKIE_VALUE})`,
|
|
1962
|
+
`(?:(?<![\\w-])sid=${COOKIE_VALUE})`,
|
|
1963
|
+
`(?:PHPSESSID=${COOKIE_VALUE})`,
|
|
1964
|
+
`(?:JSESSIONID=${COOKIE_VALUE})`,
|
|
1965
|
+
`(?:ASP\\.NET_SessionId=${COOKIE_VALUE})`,
|
|
1966
|
+
`(?:ASPSESSIONID[A-Za-z0-9]*=${COOKIE_VALUE})`,
|
|
1967
|
+
`(?:connect\\.sid=${COOKIE_VALUE})`,
|
|
1968
|
+
`(?:laravel_session=${COOKIE_VALUE})`,
|
|
1969
|
+
`(?:_session_id=${COOKIE_VALUE})`,
|
|
1970
|
+
`(?:(?:csrftoken|csrf_token|XSRF-TOKEN|xsrf-token)=${COOKIE_VALUE})`,
|
|
1971
|
+
`(?:remember_token=${COOKIE_VALUE})`,
|
|
1972
|
+
`(?:(?:SSESS|SESS)[A-Fa-f0-9]{32}=${COOKIE_VALUE})`,
|
|
1973
|
+
`(?:(?<=[Ss]et-[Cc]ookie:\\s*)[^;=\\s]+=${COOKIE_VALUE})`
|
|
1974
|
+
],
|
|
1975
|
+
aggressivePatterns: [
|
|
1976
|
+
`(?:(?<![\\w-])[\\w-]*session[\\w-]*=${COOKIE_VALUE})`,
|
|
1977
|
+
`(?:(?<![\\w-])[\\w-]*token=${COOKIE_VALUE})`
|
|
1978
|
+
],
|
|
1979
|
+
jsonKeys: ["sessionId", "sid", "cookie", "cookies", "setCookie", "jsessionid", "csrfToken"]
|
|
1980
|
+
};
|
|
1981
|
+
|
|
1982
|
+
// src/rules/definitions/users.ts
|
|
1983
|
+
var EMAIL_LOCAL = "[A-Za-z0-9._%+-]+";
|
|
1984
|
+
var EMAIL_DOMAIN = "[A-Za-z0-9.-]+\\.[A-Za-z]{2,}";
|
|
1985
|
+
var EMAIL = `(?:\\b${EMAIL_LOCAL}@${EMAIL_DOMAIN})`;
|
|
1986
|
+
var EMAIL_IP_LITERAL = `(?:\\b${EMAIL_LOCAL}@\\[[0-9a-fA-F.:]+\\])`;
|
|
1987
|
+
var WIN_DOMAIN_EXCLUDE = "(?:BUILTIN|NT SERVICE|NT AUTHORITY|Users|Settings|Files)\\\\";
|
|
1988
|
+
var USER_SEG_SIMPLE = "[A-Za-z0-9_](?:[A-Za-z0-9_.$-]*[A-Za-z0-9$])";
|
|
1989
|
+
var FILE_EXT = "exe|dll|sys|bat|cmd|ps1|vbs|msi|tmp|hiv|log|dat|txt|ini|xml|json|lnk|cpl|ocx|drv";
|
|
1990
|
+
var NOT_FILENAME = `(?![A-Za-z0-9_$.-]*\\.(?:${FILE_EXT})(?![A-Za-z0-9]))`;
|
|
1991
|
+
var NOT_GUID = "(?![0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-)";
|
|
1992
|
+
var ACCOUNT_END = "(?![A-Za-z0-9_$\\\\/-])";
|
|
1993
|
+
var PATH_ROOT = "(?:\\b(?:HKLM|HKCU|HKU|HKCR|HKEY_[A-Z_]+)|\\b[A-Za-z]:)\\\\";
|
|
1994
|
+
var NOT_UNDER_PATH_ROOT = `(?<!${PATH_ROOT}(?:(?! [-/])[^<>'"\\n])*)`;
|
|
1995
|
+
var DOMAIN_USER = `(?:(?<!${WIN_DOMAIN_EXCLUDE})(?<=(?:^|[^\\\\.-])\\b[A-Za-z0-9](?:[A-Za-z0-9.-]{0,13}[A-Za-z0-9])?\\\\)${NOT_FILENAME}${NOT_GUID}${USER_SEG_SIMPLE}${ACCOUNT_END}${NOT_UNDER_PATH_ROOT})`;
|
|
1996
|
+
var SID = "(?:\\bS-1-(?:\\d+-){1,14}\\d+\\b)";
|
|
1997
|
+
var VALUE_WITH_LETTER = "[\\w.@+-]*[A-Za-z][\\w.@+-]*";
|
|
1998
|
+
var LDAP_DN_SINGLE = `(?:(?<=\\b(?:uid|sAMAccountName)=)${VALUE_WITH_LETTER})`;
|
|
1999
|
+
var CN_VALUE = "(?:[A-Za-z0-9.'-]+(?: [A-Za-z0-9.'-]+){0,3})";
|
|
2000
|
+
var LDAP_DN_CN = `(?:(?<=\\bcn=)${CN_VALUE})`;
|
|
2001
|
+
var ARN_USER = "(?:(?<=:user/)[\\w.@+=,-]+)";
|
|
2002
|
+
var ARN_ASSUMED_ROLE_SESSION = "(?:(?<=:assumed-role/[\\w+=,.@-]+/)[\\w.@+=,-]+)";
|
|
2003
|
+
var K8S_SERVICE_ACCOUNT = "(?:(?<=\\bsystem:serviceaccount:[A-Za-z0-9-]+:)[A-Za-z0-9-]+)";
|
|
2004
|
+
var SSHD_FOR_USER = "(?:(?<=(?:\\bfor (?:invalid )?user|\\b[Ii]nvalid user)\\s+)[A-Za-z0-9._-]+)";
|
|
2005
|
+
var SSHD_RUSER = "(?:(?<=\\bruser=)[A-Za-z0-9._-]+)";
|
|
2006
|
+
var SSHD_LOGNAME = "(?:(?<=\\blogname=)[A-Za-z0-9._-]+)";
|
|
2007
|
+
var SUDO_USER = "(?:(?<=\\bsudo:\\s+)[A-Za-z0-9._-]+(?=\\s+:))";
|
|
2008
|
+
var GENERIC_KEY = "(?:user|username|login|account|owner|actor|principal|requester|createdBy|modifiedBy|assignee|operator)";
|
|
2009
|
+
var GENERIC_CTX = `(?:(?<=\\b${GENERIC_KEY}\\s*[=:]\\s*)${VALUE_WITH_LETTER})`;
|
|
2010
|
+
var SLACK_USER_ID_AGGRESSIVE = "(?:\\bU[A-Z0-9]{8,10}\\b)";
|
|
2011
|
+
var SYSTEM_ACCOUNTS = /* @__PURE__ */ new Set([
|
|
2012
|
+
"root",
|
|
2013
|
+
"system",
|
|
2014
|
+
"daemon",
|
|
2015
|
+
"nobody",
|
|
2016
|
+
"www-data",
|
|
2017
|
+
"nginx",
|
|
2018
|
+
"sshd",
|
|
2019
|
+
"syslog"
|
|
2020
|
+
]);
|
|
2021
|
+
function isWellKnownWindowsAccount(match) {
|
|
2022
|
+
const upper = match.toUpperCase();
|
|
2023
|
+
if (upper === "SYSTEM" || upper === "LOCAL" || upper === "NETWORK" || upper === "ANONYMOUS") {
|
|
2024
|
+
return true;
|
|
2025
|
+
}
|
|
2026
|
+
return match.endsWith("$");
|
|
2027
|
+
}
|
|
2028
|
+
function isWellKnownSid(match) {
|
|
2029
|
+
if (match === "S-1-1-0") {
|
|
2030
|
+
return true;
|
|
2031
|
+
}
|
|
2032
|
+
if (/^S-1-5-(?:18|19|20)$/.test(match)) {
|
|
2033
|
+
return true;
|
|
2034
|
+
}
|
|
2035
|
+
return /^S-1-5-32-\d+$/.test(match);
|
|
2036
|
+
}
|
|
2037
|
+
function validateUser(match) {
|
|
2038
|
+
if (isWellKnownWindowsAccount(match)) {
|
|
2039
|
+
return false;
|
|
2040
|
+
}
|
|
2041
|
+
if (isWellKnownSid(match)) {
|
|
2042
|
+
return false;
|
|
2043
|
+
}
|
|
2044
|
+
return !SYSTEM_ACCOUNTS.has(match.toLowerCase());
|
|
2045
|
+
}
|
|
2046
|
+
var usersRule = {
|
|
2047
|
+
id: "users",
|
|
2048
|
+
label: "Usernames & emails",
|
|
2049
|
+
description: "Email addresses and account identifiers (DOMAIN\\user, SIDs, ARNs, LDAP DNs, sshd/PAM context) become the same token everywhere within a session.",
|
|
2050
|
+
mode: "pseudo",
|
|
2051
|
+
token: "USER",
|
|
2052
|
+
patterns: [
|
|
2053
|
+
EMAIL_IP_LITERAL,
|
|
2054
|
+
EMAIL,
|
|
2055
|
+
DOMAIN_USER,
|
|
2056
|
+
SID,
|
|
2057
|
+
LDAP_DN_SINGLE,
|
|
2058
|
+
LDAP_DN_CN,
|
|
2059
|
+
ARN_USER,
|
|
2060
|
+
ARN_ASSUMED_ROLE_SESSION,
|
|
2061
|
+
K8S_SERVICE_ACCOUNT,
|
|
2062
|
+
SSHD_FOR_USER,
|
|
2063
|
+
SSHD_RUSER,
|
|
2064
|
+
SSHD_LOGNAME,
|
|
2065
|
+
SUDO_USER,
|
|
2066
|
+
GENERIC_CTX
|
|
2067
|
+
],
|
|
2068
|
+
aggressivePatterns: [SLACK_USER_ID_AGGRESSIVE],
|
|
2069
|
+
validate: validateUser,
|
|
2070
|
+
jsonKeys: [
|
|
2071
|
+
"email",
|
|
2072
|
+
"username",
|
|
2073
|
+
"user",
|
|
2074
|
+
"emailAddress",
|
|
2075
|
+
"mail",
|
|
2076
|
+
"userEmail",
|
|
2077
|
+
"user_name",
|
|
2078
|
+
"login",
|
|
2079
|
+
"account",
|
|
2080
|
+
"samAccountName",
|
|
2081
|
+
"userPrincipalName",
|
|
2082
|
+
"upn",
|
|
2083
|
+
"actor",
|
|
2084
|
+
"principal",
|
|
2085
|
+
"owner",
|
|
2086
|
+
"createdBy",
|
|
2087
|
+
"modifiedBy",
|
|
2088
|
+
"firstName",
|
|
2089
|
+
"lastName",
|
|
2090
|
+
"fullName",
|
|
2091
|
+
"displayName",
|
|
2092
|
+
"givenName",
|
|
2093
|
+
"surname",
|
|
2094
|
+
"userId",
|
|
2095
|
+
"uid"
|
|
2096
|
+
]
|
|
2097
|
+
};
|
|
2098
|
+
|
|
2099
|
+
// src/rules/registry.ts
|
|
2100
|
+
var builtinRules = [
|
|
2101
|
+
secretsRule,
|
|
2102
|
+
sessionCookiesRule,
|
|
2103
|
+
paymentInfoRule,
|
|
2104
|
+
govIdentifiersRule,
|
|
2105
|
+
healthInfoRule,
|
|
2106
|
+
phoneNumbersRule,
|
|
2107
|
+
ipAddressesRule,
|
|
2108
|
+
hostnamesRule,
|
|
2109
|
+
usersRule,
|
|
2110
|
+
geoLocationRule,
|
|
2111
|
+
homePathsRule
|
|
2112
|
+
];
|
|
2113
|
+
var builtinRuleIds = builtinRules.map(
|
|
2114
|
+
(rule) => rule.id
|
|
2115
|
+
);
|
|
2116
|
+
var builtinsById = new Map(builtinRules.map((rule) => [rule.id, rule]));
|
|
2117
|
+
function getBuiltinRule(id) {
|
|
2118
|
+
return builtinsById.get(id);
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
// src/rules/validateRule.ts
|
|
2122
|
+
var RULE_ID_PATTERN = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
2123
|
+
var TOKEN_PATTERN = /^[A-Z][A-Z0-9]*$/;
|
|
2124
|
+
var NAMED_GROUP_PATTERN = /\(\?<[^=!]/;
|
|
2125
|
+
function requireText(value, rule, field) {
|
|
2126
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
2127
|
+
throw new InvalidRuleError(`Rule "${rule}": \`${field}\` must be a non-empty string.`);
|
|
2128
|
+
}
|
|
2129
|
+
return value;
|
|
2130
|
+
}
|
|
2131
|
+
function assertPatterns(patterns, ruleId, field) {
|
|
2132
|
+
if (patterns === void 0) {
|
|
2133
|
+
return [];
|
|
2134
|
+
}
|
|
2135
|
+
if (!Array.isArray(patterns)) {
|
|
2136
|
+
throw new InvalidRuleError(`Rule "${ruleId}": \`${field}\` must be an array of strings.`);
|
|
2137
|
+
}
|
|
2138
|
+
return patterns.map((pattern, index) => {
|
|
2139
|
+
if (typeof pattern !== "string" || pattern.length === 0) {
|
|
2140
|
+
throw new InvalidRuleError(
|
|
2141
|
+
`Rule "${ruleId}": \`${field}[${index}]\` must be a non-empty regular expression source.`
|
|
2142
|
+
);
|
|
2143
|
+
}
|
|
2144
|
+
if (NAMED_GROUP_PATTERN.test(pattern)) {
|
|
2145
|
+
throw new InvalidRuleError(
|
|
2146
|
+
`Rule "${ruleId}": \`${field}[${index}]\` must not declare a named capture group \u2014 the rule identifier is used as the group name.`
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
try {
|
|
2150
|
+
new RegExp(pattern, "u");
|
|
2151
|
+
} catch (cause) {
|
|
2152
|
+
throw new InvalidRuleError(
|
|
2153
|
+
`Rule "${ruleId}": \`${field}[${index}]\` is not a valid Unicode regular expression: ${String(cause)}`
|
|
2154
|
+
);
|
|
2155
|
+
}
|
|
2156
|
+
return pattern;
|
|
2157
|
+
});
|
|
2158
|
+
}
|
|
2159
|
+
function validateRule(rule) {
|
|
2160
|
+
if (typeof rule !== "object" || rule === null) {
|
|
2161
|
+
throw new InvalidRuleError("A rule must be an object.");
|
|
2162
|
+
}
|
|
2163
|
+
const id = requireText(rule.id, String(rule.id), "id");
|
|
2164
|
+
if (!RULE_ID_PATTERN.test(id)) {
|
|
2165
|
+
throw new InvalidRuleError(
|
|
2166
|
+
`Rule "${id}": \`id\` must be a valid ASCII identifier matching ${RULE_ID_PATTERN.source}, because it is used as a named capture group.`
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2169
|
+
requireText(rule.label, id, "label");
|
|
2170
|
+
requireText(rule.description, id, "description");
|
|
2171
|
+
if (rule.mode !== "pseudo" && rule.mode !== "mask") {
|
|
2172
|
+
throw new InvalidRuleError(`Rule "${id}": \`mode\` must be either "pseudo" or "mask".`);
|
|
2173
|
+
}
|
|
2174
|
+
if (rule.token !== void 0 && !TOKEN_PATTERN.test(rule.token)) {
|
|
2175
|
+
throw new InvalidRuleError(
|
|
2176
|
+
`Rule "${id}": \`token\` must match ${TOKEN_PATTERN.source}, so replacement tokens stay recognizable.`
|
|
2177
|
+
);
|
|
2178
|
+
}
|
|
2179
|
+
const patterns = assertPatterns(rule.patterns, id, "patterns");
|
|
2180
|
+
const aggressivePatterns = assertPatterns(rule.aggressivePatterns, id, "aggressivePatterns");
|
|
2181
|
+
if (patterns.length === 0 && aggressivePatterns.length === 0) {
|
|
2182
|
+
throw new InvalidRuleError(`Rule "${id}": at least one pattern is required.`);
|
|
2183
|
+
}
|
|
2184
|
+
if (rule.jsonKeys !== void 0) {
|
|
2185
|
+
if (!Array.isArray(rule.jsonKeys)) {
|
|
2186
|
+
throw new InvalidRuleError(`Rule "${id}": \`jsonKeys\` must be an array of strings.`);
|
|
2187
|
+
}
|
|
2188
|
+
for (const [index, key] of rule.jsonKeys.entries()) {
|
|
2189
|
+
requireText(key, id, `jsonKeys[${index}]`);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
if (rule.validate !== void 0 && typeof rule.validate !== "function") {
|
|
2193
|
+
throw new InvalidRuleError(`Rule "${id}": \`validate\` must be a function.`);
|
|
2194
|
+
}
|
|
2195
|
+
return rule;
|
|
2196
|
+
}
|
|
2197
|
+
|
|
2198
|
+
// src/rules/defineRule.ts
|
|
2199
|
+
function defineRule(rule) {
|
|
2200
|
+
return validateRule(rule);
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
export { InvalidKeyError, InvalidOptionError, InvalidRuleError, SanitizationAbortedError, SanitizerError, UnknownRuleError, builtinRuleIds, builtinRules, defineRule, getBuiltinRule, isSanitizerError, validateRule };
|
|
2204
|
+
//# sourceMappingURL=chunk-TAMK67JP.js.map
|
|
2205
|
+
//# sourceMappingURL=chunk-TAMK67JP.js.map
|