namespace-guard 0.22.0 → 0.23.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/LICENSE +1 -1
- package/README.md +50 -40
- package/THIRD-PARTY-NOTICES.md +120 -0
- package/dist/adapters/drizzle.js +6 -5
- package/dist/adapters/drizzle.mjs +6 -5
- package/dist/adapters/knex.js +1 -1
- package/dist/adapters/knex.mjs +1 -1
- package/dist/adapters/kysely.js +5 -2
- package/dist/adapters/kysely.mjs +5 -2
- package/dist/adapters/mikro-orm.js +5 -2
- package/dist/adapters/mikro-orm.mjs +5 -2
- package/dist/adapters/mongoose.js +4 -6
- package/dist/adapters/mongoose.mjs +4 -6
- package/dist/adapters/prisma.js +2 -4
- package/dist/adapters/prisma.mjs +2 -4
- package/dist/adapters/raw.js +1 -2
- package/dist/adapters/raw.mjs +1 -2
- package/dist/adapters/sequelize.js +1 -1
- package/dist/adapters/sequelize.mjs +1 -1
- package/dist/adapters/typeorm.js +5 -5
- package/dist/adapters/typeorm.mjs +5 -5
- package/dist/cli.js +8721 -5620
- package/dist/cli.mjs +8721 -5620
- package/dist/composability-vectors.js +7660 -5106
- package/dist/composability-vectors.mjs +7660 -5106
- package/dist/confusable-weights.d.mts +7 -1
- package/dist/confusable-weights.d.ts +7 -1
- package/dist/confusable-weights.js +4563 -378
- package/dist/confusable-weights.mjs +4563 -378
- package/dist/font-specific-weights.d.mts +8 -2
- package/dist/font-specific-weights.d.ts +8 -2
- package/dist/font-specific-weights.js +34890 -2492
- package/dist/font-specific-weights.mjs +34890 -2492
- package/dist/index.d.mts +162 -81
- package/dist/index.d.ts +162 -81
- package/dist/index.js +10927 -5776
- package/dist/index.mjs +10924 -5776
- package/dist/profanity-en.d.mts +5 -3
- package/dist/profanity-en.d.ts +5 -3
- package/dist/profanity-en.js +10306 -5304
- package/dist/profanity-en.mjs +10305 -5304
- package/package.json +10 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,47 @@
|
|
|
1
|
+
/*! namespace-guard, MIT License, copyright (c) 2026 Paul Wood FRSA (@paultendo).
|
|
2
|
+
* Includes data from Unicode confusables.txt (2026-08-06, 01:05:35 GMT), copyright 1991-Present Unicode, Inc., Unicode License v3
|
|
3
|
+
* (https://www.unicode.org/terms_of_use.html), and from confusable-vision measurements by Paul Wood FRSA (@paultendo),
|
|
4
|
+
* CC BY 4.0 (https://github.com/paultendo/confusable-vision). */
|
|
5
|
+
/** Unicode confusables.txt version these maps were built from. */
|
|
6
|
+
declare const CONFUSABLES_DATE = "2026-08-06, 01:05:35 GMT";
|
|
7
|
+
/**
|
|
8
|
+
* Non-ASCII characters that look like Latin letters or digits, to their lowercase prototype, for pipelines that run
|
|
9
|
+
* NFKC first and for rejecting names that contain one: Unicode's mappings, Latin small capitals, and confusable-vision's
|
|
10
|
+
* strict-tier lookalikes Unicode does not map. 1018 entries.
|
|
11
|
+
*/
|
|
12
|
+
declare const CONFUSABLE_MAP: Record<string, string>;
|
|
13
|
+
/**
|
|
14
|
+
* Every character that looks like Latin letters or digits, to its lowercase prototype: all of Unicode's mappings to
|
|
15
|
+
* Latin (including multi-character targets such as m -> rn and the ASCII entries 1 -> l, 0 -> o, | -> l), Latin small
|
|
16
|
+
* capitals, and confusable-vision's measured lookalikes. Case matters: look a character up as typed. 2216 entries.
|
|
17
|
+
*/
|
|
18
|
+
declare const CONFUSABLE_MAP_FULL: Record<string, string>;
|
|
19
|
+
/** ASCII capitals Unicode maps to another letter, used where case is preserved (capital I looks like l). */
|
|
20
|
+
declare const CONFUSABLE_MAP_CASED: Record<string, string>;
|
|
21
|
+
/** One measured lookalike: the letter or digit it passes for, its prototype, and where it holds. */
|
|
22
|
+
type MeasuredConfusable = {
|
|
23
|
+
/** The ASCII letter(s) or digit(s) it was measured against. */
|
|
24
|
+
letter: string;
|
|
25
|
+
/** namespace-guard's lowercase prototype of that letter. */
|
|
26
|
+
target: string;
|
|
27
|
+
/** strict: as alike as the median accepted confusable in that context; broad: as alike as the least alike one;
|
|
28
|
+
* design: alike to the letter as another common font draws it. */
|
|
29
|
+
tier: "strict" | "broad" | "design";
|
|
30
|
+
/** Where it holds: font and size (px), set between neighbours with the platform's fallback. */
|
|
31
|
+
contexts: string[];
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* confusable-vision's in-place measurements: each character found alike to an ASCII letter or digit, with the best
|
|
35
|
+
* letter it passes for and where. 974 characters.
|
|
36
|
+
*/
|
|
37
|
+
declare const MEASURED_CONFUSABLES: Record<string, MeasuredConfusable>;
|
|
38
|
+
|
|
39
|
+
/*! Data licensing:
|
|
40
|
+
* - TR39-derived mappings: Unicode confusables.txt, copyright 1991-Present Unicode, Inc.,
|
|
41
|
+
* Unicode License v3 (https://www.unicode.org/license.txt)
|
|
42
|
+
* - confusable-vision-derived weights: CC-BY-4.0 (https://creativecommons.org/licenses/by/4.0/).
|
|
43
|
+
* Attribution: Paul Wood FRSA (@paultendo), confusable-vision
|
|
44
|
+
* (https://github.com/paultendo/confusable-vision). */
|
|
1
45
|
type LlmConfusableSource = "tr39" | "novel";
|
|
2
46
|
type LlmConfusableMapEntry = {
|
|
3
47
|
latin: string;
|
|
@@ -12,13 +56,18 @@ type LlmConfusableMapEntry = {
|
|
|
12
56
|
};
|
|
13
57
|
type LlmConfusableMap = Readonly<Record<string, readonly LlmConfusableMapEntry[]>>;
|
|
14
58
|
declare const LLM_CONFUSABLE_MAP: LlmConfusableMap;
|
|
15
|
-
declare const LLM_CONFUSABLE_MAP_PAIR_COUNT =
|
|
16
|
-
declare const LLM_CONFUSABLE_MAP_CHAR_COUNT =
|
|
59
|
+
declare const LLM_CONFUSABLE_MAP_PAIR_COUNT = 2326;
|
|
60
|
+
declare const LLM_CONFUSABLE_MAP_CHAR_COUNT = 2219;
|
|
17
61
|
declare const LLM_CONFUSABLE_MAP_SOURCE_COUNTS: Readonly<{
|
|
18
|
-
tr39:
|
|
19
|
-
novel:
|
|
62
|
+
tr39: 2016;
|
|
63
|
+
novel: 310;
|
|
20
64
|
}>;
|
|
21
65
|
|
|
66
|
+
/*! namespace-guard, MIT License, copyright (c) 2026 Paul Wood FRSA (@paultendo).
|
|
67
|
+
* Includes data from Unicode confusables.txt, copyright 1991-Present Unicode, Inc.,
|
|
68
|
+
* under the Unicode License v3 (https://www.unicode.org/license.txt), and from confusable-vision measurements by
|
|
69
|
+
* Paul Wood FRSA (@paultendo), CC BY 4.0 (https://github.com/paultendo/confusable-vision). */
|
|
70
|
+
|
|
22
71
|
/** A database table or model to check for slug/handle collisions. */
|
|
23
72
|
type NamespaceSource = {
|
|
24
73
|
/** Table/model name (must match the adapter's lookup key) */
|
|
@@ -37,8 +86,11 @@ type NamespaceValidatorResult = {
|
|
|
37
86
|
available: false;
|
|
38
87
|
message: string;
|
|
39
88
|
} | null;
|
|
40
|
-
/** Async validator hook type used by `NamespaceConfig.validators`.
|
|
41
|
-
|
|
89
|
+
/** Async validator hook type used by `NamespaceConfig.validators`. Receives the normalized value, and the identifier
|
|
90
|
+
* as typed (before lowercasing), since whether a character looks like a letter can depend on its case. */
|
|
91
|
+
type NamespaceValidator = (value: string, context?: {
|
|
92
|
+
identifier: string;
|
|
93
|
+
}) => Promise<NamespaceValidatorResult>;
|
|
42
94
|
/** Configuration for a namespace guard instance. */
|
|
43
95
|
type NamespaceConfig = {
|
|
44
96
|
/** Reserved names - flat list, Set, or categorized record */
|
|
@@ -83,10 +135,13 @@ type NamespaceConfig = {
|
|
|
83
135
|
};
|
|
84
136
|
/** Default risk policy for `checkRisk()` / `enforceRisk()` */
|
|
85
137
|
risk?: {
|
|
86
|
-
/** Include reserved names as protected targets by default (default: true)
|
|
138
|
+
/** Include reserved names as protected targets by default (default: true). A reserved name that is not in
|
|
139
|
+
* `protect` is matched by lookalikes only. */
|
|
87
140
|
includeReserved?: boolean;
|
|
88
141
|
/** Default protected targets when none are passed to `checkRisk`/`enforceRisk` (default: none). */
|
|
89
142
|
protect?: string[];
|
|
143
|
+
/** Count leetspeak swaps (4 for a, 1 for i, 5 for s) as lookalikes (default: false) */
|
|
144
|
+
leetspeak?: boolean;
|
|
90
145
|
/** Number of top matches returned by default (default: 3) */
|
|
91
146
|
maxMatches?: number;
|
|
92
147
|
/** Default warn threshold for score/action mapping (default: 45) */
|
|
@@ -133,18 +188,27 @@ type ProfanityVariantProfile = "balanced" | "aggressive";
|
|
|
133
188
|
type ProfanityValidatorOptions = {
|
|
134
189
|
/** Custom rejection message (default: "That name is not allowed."). */
|
|
135
190
|
message?: string;
|
|
136
|
-
/**
|
|
191
|
+
/** Also refuse names that contain a listed word, not only names that are one (default: `true`). Short entries,
|
|
192
|
+
* entries of several words and entries common inside English words are matched there only as whole words. */
|
|
137
193
|
checkSubstrings?: boolean;
|
|
138
194
|
/** `basic`: lowercase matching only. `evasion`: Unicode+substitute folding (default: `evasion`). */
|
|
139
195
|
mode?: ProfanityValidationMode;
|
|
140
196
|
/** Variant strictness for evasion mode (default: `balanced`). */
|
|
141
197
|
variantProfile?: ProfanityVariantProfile;
|
|
142
|
-
/**
|
|
198
|
+
/** Fewest letters (digits count, spaces and symbols don't) an entry needs to be looked for inside longer words
|
|
199
|
+
* (default: `4`). Shorter entries are matched as whole words: the whole name, or a part between separators or digits. */
|
|
143
200
|
minSubstringLength?: number;
|
|
144
201
|
/** Confusable map used for evasion folding (default: `CONFUSABLE_MAP_FULL`). */
|
|
145
202
|
map?: Record<string, string>;
|
|
146
203
|
/** Max folded candidates generated per value in evasion mode (default: `64`). */
|
|
147
204
|
maxFoldVariants?: number;
|
|
205
|
+
/** Words that contain a listed word but are fine, such as names and places: `["scunthorpe", "dickson"]`. A listed
|
|
206
|
+
* word found only inside one of these doesn't count, wherever the allowed word appears in the name
|
|
207
|
+
* ("scunthorpe-fc"), while a listed word outside it still does. Case and lookalike letters are folded as for the
|
|
208
|
+
* list, but a digit in the name is not read as an allowed word's letter ("scunth0rpe" is refused). An allowed word
|
|
209
|
+
* that is itself a listed word lets that word through. An entry written with separators ("chorlton-cum-hardy")
|
|
210
|
+
* is exact: it lets through only a name that is exactly it, separators included. (default: none) */
|
|
211
|
+
allowlist?: readonly string[];
|
|
148
212
|
};
|
|
149
213
|
/** Options for `createInvisibleCharacterValidator()`. */
|
|
150
214
|
type InvisibleCharacterValidatorOptions = {
|
|
@@ -157,9 +221,20 @@ type InvisibleCharacterValidatorOptions = {
|
|
|
157
221
|
/** Reject combining marks (Unicode category `M*`) often used for visual obfuscation (default: `false`). */
|
|
158
222
|
rejectCombiningMarks?: boolean;
|
|
159
223
|
};
|
|
160
|
-
/**
|
|
224
|
+
/**
|
|
225
|
+
* Options for `canonicalise()` LLM preprocessing.
|
|
226
|
+
*
|
|
227
|
+
* A word is rewritten when it shows a sign of tampering: it mixes Latin with another script, it has a letter not in
|
|
228
|
+
* modern use that is built on a Latin letter (ɦ, ꞡ, ɏ; a fullwidth or mathematical letter), or it has Latin letters
|
|
229
|
+
* and a listed lookalike scoring at least `threshold`. In such a word every lookalike is replaced: listed lookalikes
|
|
230
|
+
* whatever their score, and any letter built on a Latin letter (ŧ, İ) by its base letter, in the case of the word
|
|
231
|
+
* around it. A word with no such sign is left as it is, so Turkish İstanbul or Russian Москва pass through.
|
|
232
|
+
*/
|
|
161
233
|
type CanonicaliseOptions = {
|
|
162
|
-
/**
|
|
234
|
+
/**
|
|
235
|
+
* The visual score at which a listed lookalike on its own marks a word as tampered (default: `0.7`). When you set it,
|
|
236
|
+
* it is also a floor: lookalikes scoring below it are never replaced.
|
|
237
|
+
*/
|
|
163
238
|
threshold?: number;
|
|
164
239
|
/** Include confusable-vision novel discoveries in addition to TR39 mappings (default: `true`). */
|
|
165
240
|
includeNovel?: boolean;
|
|
@@ -168,14 +243,15 @@ type CanonicaliseOptions = {
|
|
|
168
243
|
/**
|
|
169
244
|
* Canonicalisation strategy (default: `"mixed"`).
|
|
170
245
|
*
|
|
171
|
-
* - `"mixed"` -- only
|
|
172
|
-
*
|
|
173
|
-
* preserved. Safe for multilingual text.
|
|
246
|
+
* - `"mixed"` -- only rewrite words that show a sign of tampering (see above).
|
|
247
|
+
* Standalone non-Latin words (e.g. "Москва") and ordinary accented words
|
|
248
|
+
* (e.g. "İstanbul") are preserved. Safe for multilingual text.
|
|
174
249
|
*
|
|
175
250
|
* - `"all"` -- replace every confusable character regardless of surrounding
|
|
176
|
-
* context
|
|
177
|
-
*
|
|
178
|
-
*
|
|
251
|
+
* context, and fold every letter built on a Latin letter (ŧ, İ, ö) to it.
|
|
252
|
+
* Use this when the document is known to be Latin-script (e.g. an English
|
|
253
|
+
* contract) and you want to catch attackers who substitute every character
|
|
254
|
+
* in a word.
|
|
179
255
|
*/
|
|
180
256
|
strategy?: "mixed" | "all";
|
|
181
257
|
/**
|
|
@@ -206,10 +282,13 @@ type ScanFinding = {
|
|
|
206
282
|
script: string;
|
|
207
283
|
/** Canonical Latin equivalent selected by the lookup table. */
|
|
208
284
|
latinEquivalent: string;
|
|
209
|
-
/** Visual similarity score for this mapping (0–1, from RaySpace measurement). */
|
|
285
|
+
/** Visual similarity score for this mapping (0–1, from RaySpace measurement; 0 for a `fold`). */
|
|
210
286
|
visualScore: number;
|
|
211
|
-
/**
|
|
212
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Mapping source: `tr39` baseline, `novel` discovery, or `fold`, a letter built on a Latin letter (ŧ, ɦ, İ) folded
|
|
289
|
+
* to it in a word that shows tampering.
|
|
290
|
+
*/
|
|
291
|
+
source: "tr39" | "novel" | "fold";
|
|
213
292
|
/** UTF-16 code-unit offset in the input string. */
|
|
214
293
|
index: number;
|
|
215
294
|
/** Token/word containing this character. */
|
|
@@ -244,6 +323,10 @@ type SkeletonOptions = {
|
|
|
244
323
|
* Default: `CONFUSABLE_MAP_FULL` (complete TR39 map, no NFKC filtering).
|
|
245
324
|
* Pass `CONFUSABLE_MAP` if your pipeline runs NFKC before calling skeleton(). */
|
|
246
325
|
map?: Record<string, string>;
|
|
326
|
+
/** Treat the input as shown with its case, so ASCII capitals Unicode maps to another letter count as that letter
|
|
327
|
+
* (capital I as l: "paypaI" has the skeleton of "paypal"). For names displayed as typed. Default `false`: names
|
|
328
|
+
* compare without case ("ADMIN" is "admin"). `areConfusable()` checks both. */
|
|
329
|
+
preserveCase?: boolean;
|
|
247
330
|
/** Also remove diacritics (the combining marks of the Combining Diacritical Marks blocks, such as a dot below,
|
|
248
331
|
* an accent or a hook), so `ạ` matches `a` and `ẹ` matches `e`. TR39 keeps them, but a small mark is easy to
|
|
249
332
|
* miss, and Chromium removes Latin diacritics when it checks a domain against top domains. Marks that scripts
|
|
@@ -253,11 +336,11 @@ type SkeletonOptions = {
|
|
|
253
336
|
/** Options for `areConfusable()` with optional weight-based matching. */
|
|
254
337
|
type AreConfusableOptions = SkeletonOptions & {
|
|
255
338
|
/** Optional measured visual weights for weight-based confusable matching.
|
|
256
|
-
* When provided,
|
|
339
|
+
* When provided, a measured pair at the same position also counts as alike. */
|
|
257
340
|
weights?: ConfusableWeights;
|
|
258
341
|
/** Filter weights by deployment context.
|
|
259
|
-
* - `'identifier'`: only apply
|
|
260
|
-
* - `'domain'`: only apply
|
|
342
|
+
* - `'identifier'`: only apply pairs whose two characters are both XID_Continue
|
|
343
|
+
* - `'domain'`: only apply pairs whose two characters are both IDNA PVALID
|
|
261
344
|
* - `'all'` (default): apply all weights regardless of properties */
|
|
262
345
|
context?: "identifier" | "domain" | "all";
|
|
263
346
|
};
|
|
@@ -290,8 +373,8 @@ type ConfusableDistanceOptions = {
|
|
|
290
373
|
* and novel pairs (not in TR39 map) use their visual-weight cost. */
|
|
291
374
|
weights?: ConfusableWeights;
|
|
292
375
|
/** Filter weights by deployment context.
|
|
293
|
-
* - `'identifier'`: only apply
|
|
294
|
-
* - `'domain'`: only apply
|
|
376
|
+
* - `'identifier'`: only apply pairs whose two characters are both XID_Continue
|
|
377
|
+
* - `'domain'`: only apply pairs whose two characters are both IDNA PVALID
|
|
295
378
|
* - `'all'` (default): apply all weights regardless of properties */
|
|
296
379
|
context?: "identifier" | "domain" | "all";
|
|
297
380
|
};
|
|
@@ -366,6 +449,14 @@ type RiskReason = {
|
|
|
366
449
|
type RiskLevel = "low" | "medium" | "high";
|
|
367
450
|
/** Policy action derived from the configured thresholds. */
|
|
368
451
|
type RiskAction = "allow" | "warn" | "block";
|
|
452
|
+
/**
|
|
453
|
+
* How a name matched a protected target:
|
|
454
|
+
* - `exact`: it is the target.
|
|
455
|
+
* - `lookalike`: it differs only by lookalikes and invisible characters, or its skeleton collides. Can block.
|
|
456
|
+
* - `lookalike-extension`: it spells the whole target with a lookalike and adds letters. Warns at most.
|
|
457
|
+
* - `typo`: one letter changed, added, dropped or swapped, not a lookalike, from a name in `protect`. Warns at most.
|
|
458
|
+
*/
|
|
459
|
+
type RiskMatchEvidence = "exact" | "lookalike" | "lookalike-extension" | "typo";
|
|
369
460
|
/** A nearest protected target returned by risk scoring. */
|
|
370
461
|
type RiskMatch = {
|
|
371
462
|
target: string;
|
|
@@ -373,14 +464,20 @@ type RiskMatch = {
|
|
|
373
464
|
distance: number;
|
|
374
465
|
chainDepth: number;
|
|
375
466
|
skeletonEqual: boolean;
|
|
467
|
+
/** How the name matched the target. */
|
|
468
|
+
evidence: RiskMatchEvidence;
|
|
376
469
|
reasons: string[];
|
|
377
470
|
};
|
|
378
471
|
/** Options for `guard.checkRisk()`. */
|
|
379
472
|
type CheckRiskOptions = {
|
|
380
473
|
/** Additional high-value identifiers to protect against confusable variants. */
|
|
381
474
|
protect?: string[];
|
|
382
|
-
/** Include configured reserved names in the protected target set (default: true).
|
|
475
|
+
/** Include configured reserved names in the protected target set (default: true). A reserved name that is not in
|
|
476
|
+
* `protect` is matched by lookalikes only, not by a one-letter typo. */
|
|
383
477
|
includeReserved?: boolean;
|
|
478
|
+
/** Count leetspeak swaps (4 for a, 1 for i, 5 for s, $ for s) as lookalikes, so p4ypal or adm1n can block
|
|
479
|
+
* (default: false). The table is the profanity matcher's aggressive one. */
|
|
480
|
+
leetspeak?: boolean;
|
|
384
481
|
/** Confusable map used for skeletoning and distance scoring (default: `CONFUSABLE_MAP_FULL`). */
|
|
385
482
|
map?: Record<string, string>;
|
|
386
483
|
/** Number of highest-risk matches to return (default: 3). */
|
|
@@ -397,6 +494,9 @@ type RiskCheckResult = {
|
|
|
397
494
|
score: number;
|
|
398
495
|
level: RiskLevel;
|
|
399
496
|
action: RiskAction;
|
|
497
|
+
/** False when the name shows nothing visual: no lookalike or exact match of a protected target, and no lookalike,
|
|
498
|
+
* invisible or mixed-script characters. Its score is then held below the block threshold, whatever it is. */
|
|
499
|
+
canBlock: boolean;
|
|
400
500
|
reasons: RiskReason[];
|
|
401
501
|
matches: RiskMatch[];
|
|
402
502
|
};
|
|
@@ -452,6 +552,8 @@ type NamespaceProfileName = "consumer-handle" | "org-slug" | "developer-id";
|
|
|
452
552
|
type NamespaceProfilePreset = {
|
|
453
553
|
description: string;
|
|
454
554
|
pattern: RegExp;
|
|
555
|
+
/** The message for a name that doesn't match `pattern`, used unless you set `messages.invalid` or your own pattern */
|
|
556
|
+
invalidMessage?: string;
|
|
455
557
|
normalizeUnicode: boolean;
|
|
456
558
|
allowPurelyNumeric: boolean;
|
|
457
559
|
risk: Required<NonNullable<NamespaceConfig["risk"]>>;
|
|
@@ -504,16 +606,24 @@ declare function createPredicateValidator(predicate: (value: string) => boolean
|
|
|
504
606
|
/**
|
|
505
607
|
* Create a validator that rejects identifiers containing profanity or offensive words.
|
|
506
608
|
*
|
|
507
|
-
* Supply your own word list
|
|
609
|
+
* Supply your own word list, or the English list from `namespace-guard/profanity-en`.
|
|
508
610
|
* The returned function is compatible with `config.validators`.
|
|
509
611
|
*
|
|
612
|
+
* An entry of one word with at least `minSubstringLength` letters is looked for anywhere in a name ("shitposter").
|
|
613
|
+
* Other entries are matched as whole words: the whole name, or a part of it between separators, digits or its ends,
|
|
614
|
+
* with or without separators between its letters. These are shorter entries ("ass" refuses "my-ass", not "class"),
|
|
615
|
+
* entries written as several words ("h e l l", "blow job"), and entries whose letters are common inside ordinary
|
|
616
|
+
* English words (122 sequences measured from the English list, such as anal, rape and cock). Words on `allowlist`
|
|
617
|
+
* shield the listed words inside them: with `allowlist: ["scunthorpe"]`, "scunthorpe-fc" and "scun-thorpe" pass, and
|
|
618
|
+
* "xcuntx" and "s-cunt-horpe" don't, since a part of a name that is a listed word on its own still counts.
|
|
619
|
+
*
|
|
510
620
|
* @param words - Array of words to block
|
|
511
621
|
* @param options - Optional settings
|
|
512
622
|
* @param options.message - Custom rejection message (default: "That name is not allowed.")
|
|
513
|
-
* @param options.checkSubstrings -
|
|
623
|
+
* @param options.checkSubstrings - Also refuse names that contain a listed word, not only names that are one (default: true)
|
|
514
624
|
* @param options.mode - `basic` (lowercase only) or `evasion` (Unicode/substitute folding) (default: `evasion`)
|
|
515
625
|
* @param options.variantProfile - `balanced` (precision-first) or `aggressive` (broader substitutes) (default: `balanced`)
|
|
516
|
-
* @param options.minSubstringLength -
|
|
626
|
+
* @param options.minSubstringLength - Fewest letters an entry needs to be looked for inside longer words (default: `4`)
|
|
517
627
|
* @param options.map - Confusable map used by `mode: "evasion"` (default: `CONFUSABLE_MAP_FULL`)
|
|
518
628
|
* @param options.maxFoldVariants - Max folded candidates considered in evasion mode (default: 64)
|
|
519
629
|
* @returns An async validator function for use in `config.validators`
|
|
@@ -532,39 +642,6 @@ declare function createPredicateValidator(predicate: (value: string) => boolean
|
|
|
532
642
|
* ```
|
|
533
643
|
*/
|
|
534
644
|
declare function createProfanityValidator(words: readonly string[], options?: ProfanityValidatorOptions): NamespaceValidator;
|
|
535
|
-
/**
|
|
536
|
-
* Mapping of visually confusable Unicode characters to their Latin/digit equivalents.
|
|
537
|
-
* Generated from Unicode TR39 confusables.txt + supplemental Latin small capitals.
|
|
538
|
-
* Covers every single-character mapping to a lowercase Latin letter or digit,
|
|
539
|
-
* excluding characters already handled by NFKC normalization (either collapsed
|
|
540
|
-
* to the same target, or mapped to a different valid Latin char/digit).
|
|
541
|
-
* Regenerate: `npx tsx scripts/generate-confusables.ts`
|
|
542
|
-
*
|
|
543
|
-
* Data source: Unicode confusables.txt (https://unicode.org/Public/security/latest/confusables.txt)
|
|
544
|
-
* Copyright 1991-Present Unicode, Inc. Licensed under the Unicode License v3.
|
|
545
|
-
* See https://www.unicode.org/terms_of_use.html
|
|
546
|
-
*/
|
|
547
|
-
declare const CONFUSABLE_MAP: Record<string, string>;
|
|
548
|
-
/**
|
|
549
|
-
* Complete TR39 confusable mapping: every single-character mapping to a
|
|
550
|
-
* lowercase Latin letter or digit from confusables.txt, with no NFKC filtering.
|
|
551
|
-
*
|
|
552
|
-
* Use this when your pipeline does NOT run NFKC normalization before confusable
|
|
553
|
-
* detection (which is most real-world systems: TR39 skeleton uses NFD, Chromium
|
|
554
|
-
* uses NFD, Rust uses NFC, django-registration uses no normalization at all).
|
|
555
|
-
*
|
|
556
|
-
* Includes ~1,400 entries vs CONFUSABLE_MAP's ~613 NFKC-deduped entries.
|
|
557
|
-
* The additional entries cover characters that NFKC normalization would handle
|
|
558
|
-
* (mathematical alphanumerics, fullwidth forms, etc.) plus the 31 entries where
|
|
559
|
-
* TR39 and NFKC disagree on the target letter.
|
|
560
|
-
*
|
|
561
|
-
* Regenerate: `npx tsx scripts/generate-confusables.ts`
|
|
562
|
-
*
|
|
563
|
-
* Data source: Unicode confusables.txt (https://unicode.org/Public/security/latest/confusables.txt)
|
|
564
|
-
* Copyright 1991-Present Unicode, Inc. Licensed under the Unicode License v3.
|
|
565
|
-
* See https://www.unicode.org/terms_of_use.html
|
|
566
|
-
*/
|
|
567
|
-
declare const CONFUSABLE_MAP_FULL: Record<string, string>;
|
|
568
645
|
/**
|
|
569
646
|
* Derive the set of characters where TR39 prototype mapping and NFKC lowercase
|
|
570
647
|
* mapping disagree on single ASCII letter/digit outcomes.
|
|
@@ -576,7 +653,7 @@ declare function deriveNfkcTr39DivergenceVectors(map?: Record<string, string>):
|
|
|
576
653
|
*/
|
|
577
654
|
declare const NFKC_TR39_DIVERGENCE_VECTORS: NfkcTr39DivergenceVector[];
|
|
578
655
|
/** Named composability regression suite (TR39-full vs NFKC lowercase). */
|
|
579
|
-
declare const COMPOSABILITY_VECTOR_SUITE = "nfkc-tr39-divergence-
|
|
656
|
+
declare const COMPOSABILITY_VECTOR_SUITE = "nfkc-tr39-divergence-v2";
|
|
580
657
|
/** Named alias for `NFKC_TR39_DIVERGENCE_VECTORS` for cross-library regression tests. */
|
|
581
658
|
declare const COMPOSABILITY_VECTORS: readonly ComposabilityVector[];
|
|
582
659
|
/** Number of vectors in the composability regression suite. */
|
|
@@ -586,9 +663,10 @@ declare const COMPOSABILITY_VECTORS_COUNT: number;
|
|
|
586
663
|
*
|
|
587
664
|
* Catches spoofing attacks where characters from other scripts are substituted for
|
|
588
665
|
* visually identical Latin characters (e.g., Cyrillic "а" for Latin "a" in "admin").
|
|
589
|
-
* Uses
|
|
590
|
-
*
|
|
591
|
-
*
|
|
666
|
+
* Uses `CONFUSABLE_MAP` (1,018 characters): Unicode's confusables.txt with NFKC's own
|
|
667
|
+
* mappings left out, plus the lookalikes confusable-vision found alike in place. It
|
|
668
|
+
* refuses any name containing one of these characters, including names written wholly
|
|
669
|
+
* in another script (москва) and Latin letters listed as lookalikes (í, ı).
|
|
592
670
|
*
|
|
593
671
|
* @param options - Optional settings
|
|
594
672
|
* @param options.message - Custom rejection message (default: "That name contains characters that could be confused with other letters.")
|
|
@@ -639,14 +717,16 @@ declare function canonicalise(text: string, options?: CanonicaliseOptions): stri
|
|
|
639
717
|
*/
|
|
640
718
|
declare function scan(text: string, options?: ScanOptions): ScanResult;
|
|
641
719
|
/**
|
|
642
|
-
* Fast gate for LLM pipelines
|
|
720
|
+
* Fast gate for LLM pipelines: `true` exactly when `canonicalise()` with the
|
|
721
|
+
* same options would leave the text unchanged.
|
|
643
722
|
*
|
|
644
|
-
* With the default `strategy: "mixed"`, returns `false` as soon as a
|
|
645
|
-
*
|
|
723
|
+
* With the default `strategy: "mixed"`, returns `false` as soon as a word
|
|
724
|
+
* that shows a sign of tampering has a character to rewrite (see
|
|
725
|
+
* `CanonicaliseOptions`). Standalone non-Latin words and ordinary accented
|
|
646
726
|
* words do not fail this gate.
|
|
647
727
|
*
|
|
648
|
-
* With `strategy: "all"`, returns `false` if any confusable character
|
|
649
|
-
* found, regardless of surrounding context.
|
|
728
|
+
* With `strategy: "all"`, returns `false` if any confusable character or
|
|
729
|
+
* letter built on a Latin letter is found, regardless of surrounding context.
|
|
650
730
|
*/
|
|
651
731
|
declare function isClean(text: string, options?: ScanOptions): boolean;
|
|
652
732
|
/**
|
|
@@ -689,8 +769,9 @@ declare function confusableDistance(a: string, b: string, options?: ConfusableDi
|
|
|
689
769
|
* Check whether two strings are visually confusable.
|
|
690
770
|
*
|
|
691
771
|
* Without `weights`, compares TR39 skeletons only (backward compatible).
|
|
692
|
-
* With `weights`, also
|
|
693
|
-
*
|
|
772
|
+
* With `weights`, also lines the two strings up character by character: they are confusable when each position's
|
|
773
|
+
* characters share a skeleton or are a measured pair (in `context`), in order, with one character allowed to stand
|
|
774
|
+
* for several (m for rn). This finds pairs no standard lists, such as two non-Latin scripts.
|
|
694
775
|
*
|
|
695
776
|
* @param a - First string
|
|
696
777
|
* @param b - Second string
|
|
@@ -701,8 +782,8 @@ declare function confusableDistance(a: string, b: string, options?: ConfusableDi
|
|
|
701
782
|
* ```ts
|
|
702
783
|
* areConfusable("paypal", "\u0440\u0430ypal") // true (skeleton match)
|
|
703
784
|
* areConfusable("hello", "world") // false
|
|
704
|
-
* areConfusable("\
|
|
705
|
-
* areConfusable("\
|
|
785
|
+
* areConfusable("\u3163", "\u4E28", { weights }) // true: Hangul \u3163 and Han \u4E28, a measured pair
|
|
786
|
+
* areConfusable("\u3163", "\u4E28") // false (no weights = skeleton only)
|
|
706
787
|
* ```
|
|
707
788
|
*/
|
|
708
789
|
declare function areConfusable(a: string, b: string, options?: AreConfusableOptions): boolean;
|
|
@@ -739,7 +820,7 @@ type CrossScriptRiskResult = {
|
|
|
739
820
|
* @example
|
|
740
821
|
* ```ts
|
|
741
822
|
* detectCrossScriptRisk("hello") // riskLevel: "none"
|
|
742
|
-
* detectCrossScriptRisk("\
|
|
823
|
+
* detectCrossScriptRisk("\u3163\u4E28", { weights }) // riskLevel: "high", scripts: ["han", "hangul"]
|
|
743
824
|
* ```
|
|
744
825
|
*/
|
|
745
826
|
declare function detectCrossScriptRisk(identifier: string, options?: {
|
|
@@ -801,8 +882,8 @@ type DomainSpoofOptions = {
|
|
|
801
882
|
* character in the target, verified against the confusable map and optional
|
|
802
883
|
* measured weights.
|
|
803
884
|
*
|
|
804
|
-
* Mixed-script substitutions are
|
|
805
|
-
*
|
|
885
|
+
* Mixed-script substitutions are left out because most registries refuse them
|
|
886
|
+
* (some registered ones exist; d0ma1n finds those).
|
|
806
887
|
*
|
|
807
888
|
* @param label - The suspicious domain label to check (e.g. Cyrillic "раураl")
|
|
808
889
|
* @param target - The legitimate domain label (e.g. "paypal")
|
|
@@ -817,9 +898,9 @@ type DomainSpoofOptions = {
|
|
|
817
898
|
* // Full-Cyrillic lookalike — realistic, registrable spoof
|
|
818
899
|
* isDomainSpoof("\u0440\u0430\u0443\u0440\u0430\u04cf", "paypal",
|
|
819
900
|
* { weights: CONFUSABLE_WEIGHTS });
|
|
820
|
-
* // { spoof: true, script: "cyrillic", danger: 0.
|
|
901
|
+
* // { spoof: true, script: "cyrillic", danger: 0.781, substitutions: [...] }
|
|
821
902
|
*
|
|
822
|
-
* // Mixed-script —
|
|
903
|
+
* // Mixed-script — most registries refuse it, so it isn't checked
|
|
823
904
|
* isDomainSpoof("\u0440aypal", "paypal",
|
|
824
905
|
* { weights: CONFUSABLE_WEIGHTS });
|
|
825
906
|
* // { spoof: false }
|
|
@@ -904,4 +985,4 @@ declare function createNamespaceGuard(config: NamespaceConfig, adapter: Namespac
|
|
|
904
985
|
/** The guard instance returned by `createNamespaceGuard`. */
|
|
905
986
|
type NamespaceGuard = ReturnType<typeof createNamespaceGuard>;
|
|
906
987
|
|
|
907
|
-
export { type AreConfusableOptions, type AssertClaimableOptions, COMPOSABILITY_VECTORS, COMPOSABILITY_VECTORS_COUNT, COMPOSABILITY_VECTOR_SUITE, CONFUSABLE_MAP, CONFUSABLE_MAP_FULL, type CanonicaliseOptions, type CheckManyOptions, type CheckResult, type CheckRiskOptions, type ClaimOptions, type ClaimResult, type ComposabilityVector, type ConfusableDistanceOptions, type ConfusableDistanceResult, type ConfusableDistanceStep, type ConfusableWeight, type ConfusableWeights, type CrossScriptRiskResult, DEFAULT_PROTECTED_TOKENS, type DomainSpoofOptions, type DomainSpoofResult, type DomainSpoofSubstitution, type EnforceRiskOptions, type EnforceRiskResult, type FindOneOptions, type InvisibleCharacterValidatorOptions, LLM_CONFUSABLE_MAP, LLM_CONFUSABLE_MAP_CHAR_COUNT, LLM_CONFUSABLE_MAP_PAIR_COUNT, LLM_CONFUSABLE_MAP_SOURCE_COUNTS, NAMESPACE_PROFILES, NFKC_TR39_DIVERGENCE_VECTORS, type NamespaceAdapter, type NamespaceConfig, type NamespaceGuard, type NamespaceProfileName, type NamespaceProfilePreset, type NamespaceSource, type NamespaceValidator, type NamespaceValidatorResult, type NfkcTr39DivergenceVector, type OwnershipScope, type PredicateValidatorOptions, type ProfanityValidationMode, type ProfanityValidatorOptions, type ProfanityVariantProfile, type RiskAction, type RiskCheckResult, type RiskLevel, type RiskMatch, type RiskReason, type RiskReasonCode, type ScanFinding, type ScanOptions, type ScanResult, type SkeletonOptions, type SuggestStrategyName, type UniqueViolationDetector, areConfusable, canonicalise, confusableDistance, createHomoglyphValidator, createInvisibleCharacterValidator, createNamespaceGuard, createNamespaceGuardWithProfile, createPredicateValidator, createProfanityValidator, deriveNfkcTr39DivergenceVectors, detectCrossScriptRisk, isClean, isDomainSpoof, isLikelyUniqueViolationError, normalize, scan, skeleton };
|
|
988
|
+
export { type AreConfusableOptions, type AssertClaimableOptions, COMPOSABILITY_VECTORS, COMPOSABILITY_VECTORS_COUNT, COMPOSABILITY_VECTOR_SUITE, CONFUSABLES_DATE, CONFUSABLE_MAP, CONFUSABLE_MAP_CASED, CONFUSABLE_MAP_FULL, type CanonicaliseOptions, type CheckManyOptions, type CheckResult, type CheckRiskOptions, type ClaimOptions, type ClaimResult, type ComposabilityVector, type ConfusableDistanceOptions, type ConfusableDistanceResult, type ConfusableDistanceStep, type ConfusableWeight, type ConfusableWeights, type CrossScriptRiskResult, DEFAULT_PROTECTED_TOKENS, type DomainSpoofOptions, type DomainSpoofResult, type DomainSpoofSubstitution, type EnforceRiskOptions, type EnforceRiskResult, type FindOneOptions, type InvisibleCharacterValidatorOptions, LLM_CONFUSABLE_MAP, LLM_CONFUSABLE_MAP_CHAR_COUNT, LLM_CONFUSABLE_MAP_PAIR_COUNT, LLM_CONFUSABLE_MAP_SOURCE_COUNTS, type LlmConfusableMapEntry, MEASURED_CONFUSABLES, type MeasuredConfusable, NAMESPACE_PROFILES, NFKC_TR39_DIVERGENCE_VECTORS, type NamespaceAdapter, type NamespaceConfig, type NamespaceGuard, type NamespaceProfileName, type NamespaceProfilePreset, type NamespaceSource, type NamespaceValidator, type NamespaceValidatorResult, type NfkcTr39DivergenceVector, type OwnershipScope, type PredicateValidatorOptions, type ProfanityValidationMode, type ProfanityValidatorOptions, type ProfanityVariantProfile, type RiskAction, type RiskCheckResult, type RiskLevel, type RiskMatch, type RiskMatchEvidence, type RiskReason, type RiskReasonCode, type ScanFinding, type ScanOptions, type ScanResult, type SkeletonOptions, type SuggestStrategyName, type UniqueViolationDetector, areConfusable, canonicalise, confusableDistance, createHomoglyphValidator, createInvisibleCharacterValidator, createNamespaceGuard, createNamespaceGuardWithProfile, createPredicateValidator, createProfanityValidator, deriveNfkcTr39DivergenceVectors, detectCrossScriptRisk, isClean, isDomainSpoof, isLikelyUniqueViolationError, normalize, scan, skeleton };
|