@claude-flow/aidefence 3.0.3 → 3.1.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/dist/domain/entities/threat.d.ts +4 -1
- package/dist/domain/entities/threat.d.ts.map +1 -1
- package/dist/domain/entities/threat.js +1 -0
- package/dist/domain/entities/threat.js.map +1 -1
- package/dist/domain/services/packs/adapter.d.ts +20 -0
- package/dist/domain/services/packs/adapter.d.ts.map +1 -0
- package/dist/domain/services/packs/adapter.js +56 -0
- package/dist/domain/services/packs/adapter.js.map +1 -0
- package/dist/domain/services/packs/data/core.d.ts +9 -0
- package/dist/domain/services/packs/data/core.d.ts.map +1 -0
- package/dist/domain/services/packs/data/core.js +346 -0
- package/dist/domain/services/packs/data/core.js.map +1 -0
- package/dist/domain/services/packs/data/encoded-instruction.d.ts +9 -0
- package/dist/domain/services/packs/data/encoded-instruction.d.ts.map +1 -0
- package/dist/domain/services/packs/data/encoded-instruction.js +69 -0
- package/dist/domain/services/packs/data/encoded-instruction.js.map +1 -0
- package/dist/domain/services/packs/data/exfil-url.d.ts +9 -0
- package/dist/domain/services/packs/data/exfil-url.d.ts.map +1 -0
- package/dist/domain/services/packs/data/exfil-url.js +97 -0
- package/dist/domain/services/packs/data/exfil-url.js.map +1 -0
- package/dist/domain/services/packs/data/index.d.ts +7 -0
- package/dist/domain/services/packs/data/index.d.ts.map +1 -0
- package/dist/domain/services/packs/data/index.js +19 -0
- package/dist/domain/services/packs/data/index.js.map +1 -0
- package/dist/domain/services/packs/data/instruction-override-i18n.d.ts +9 -0
- package/dist/domain/services/packs/data/instruction-override-i18n.d.ts.map +1 -0
- package/dist/domain/services/packs/data/instruction-override-i18n.js +81 -0
- package/dist/domain/services/packs/data/instruction-override-i18n.js.map +1 -0
- package/dist/domain/services/packs/data/slack-markup-forgery.d.ts +9 -0
- package/dist/domain/services/packs/data/slack-markup-forgery.d.ts.map +1 -0
- package/dist/domain/services/packs/data/slack-markup-forgery.js +81 -0
- package/dist/domain/services/packs/data/slack-markup-forgery.js.map +1 -0
- package/dist/domain/services/packs/data/tool-invocation.d.ts +9 -0
- package/dist/domain/services/packs/data/tool-invocation.d.ts.map +1 -0
- package/dist/domain/services/packs/data/tool-invocation.js +125 -0
- package/dist/domain/services/packs/data/tool-invocation.js.map +1 -0
- package/dist/domain/services/packs/decoders.d.ts +39 -0
- package/dist/domain/services/packs/decoders.d.ts.map +1 -0
- package/dist/domain/services/packs/decoders.js +121 -0
- package/dist/domain/services/packs/decoders.js.map +1 -0
- package/dist/domain/services/packs/engine.d.ts +93 -0
- package/dist/domain/services/packs/engine.d.ts.map +1 -0
- package/dist/domain/services/packs/engine.js +190 -0
- package/dist/domain/services/packs/engine.js.map +1 -0
- package/dist/domain/services/packs/index.d.ts +18 -0
- package/dist/domain/services/packs/index.d.ts.map +1 -0
- package/dist/domain/services/packs/index.js +14 -0
- package/dist/domain/services/packs/index.js.map +1 -0
- package/dist/domain/services/packs/normalize.d.ts +24 -0
- package/dist/domain/services/packs/normalize.d.ts.map +1 -0
- package/dist/domain/services/packs/normalize.js +118 -0
- package/dist/domain/services/packs/normalize.js.map +1 -0
- package/dist/domain/services/packs/pattern-loader.d.ts +44 -0
- package/dist/domain/services/packs/pattern-loader.d.ts.map +1 -0
- package/dist/domain/services/packs/pattern-loader.js +70 -0
- package/dist/domain/services/packs/pattern-loader.js.map +1 -0
- package/dist/domain/services/threat-detection-service.d.ts +19 -4
- package/dist/domain/services/threat-detection-service.d.ts.map +1 -1
- package/dist/domain/services/threat-detection-service.js +65 -50
- package/dist/domain/services/threat-detection-service.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored from midstream `AIMDS/src/detection/decoders.ts` at commit abe78d6.
|
|
3
|
+
* Detection-only: output of this module is matched against, never shown to users.
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULTS = {
|
|
6
|
+
maxCandidates: 8,
|
|
7
|
+
maxBytes: 4096,
|
|
8
|
+
minBlob: 16,
|
|
9
|
+
rot13: true,
|
|
10
|
+
reverse: true,
|
|
11
|
+
wholeTextLimit: 16384,
|
|
12
|
+
};
|
|
13
|
+
const BASE64_RE = /[A-Za-z0-9+/_-]{16,}={0,2}/g;
|
|
14
|
+
const HEX_RE = /(?:[0-9a-fA-F]{2}){8,}/g;
|
|
15
|
+
const URLENC_RE = /(?:%[0-9a-fA-F]{2}){6,}/g;
|
|
16
|
+
/** Share of decoded characters that are printable ASCII or common whitespace. */
|
|
17
|
+
export function printableRatio(text) {
|
|
18
|
+
if (text.length === 0)
|
|
19
|
+
return 0;
|
|
20
|
+
let printable = 0;
|
|
21
|
+
for (let i = 0; i < text.length; i++) {
|
|
22
|
+
const c = text.charCodeAt(i);
|
|
23
|
+
if ((c >= 0x20 && c <= 0x7e) || c === 0x0a || c === 0x0d || c === 0x09)
|
|
24
|
+
printable++;
|
|
25
|
+
}
|
|
26
|
+
return printable / text.length;
|
|
27
|
+
}
|
|
28
|
+
function looksLikeText(decoded) {
|
|
29
|
+
return decoded.length >= 8 && printableRatio(decoded) >= 0.9 && /[A-Za-z]{3}/.test(decoded);
|
|
30
|
+
}
|
|
31
|
+
function decodeBase64(blob) {
|
|
32
|
+
const body = blob.replace(/=+$/, '');
|
|
33
|
+
if (body.length % 4 === 1)
|
|
34
|
+
return null; // impossible base64 length
|
|
35
|
+
// Must contain both letter classes or a digit to avoid matching plain words.
|
|
36
|
+
if (!/[a-z]/.test(body) || !/[A-Z0-9]/.test(body))
|
|
37
|
+
return null;
|
|
38
|
+
try {
|
|
39
|
+
const buf = Buffer.from(body.replace(/-/g, '+').replace(/_/g, '/'), 'base64');
|
|
40
|
+
if (buf.length === 0)
|
|
41
|
+
return null;
|
|
42
|
+
return buf.toString('utf8');
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function decodeHex(blob) {
|
|
49
|
+
if (blob.length % 2 !== 0)
|
|
50
|
+
return null;
|
|
51
|
+
try {
|
|
52
|
+
return Buffer.from(blob, 'hex').toString('utf8');
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function decodeUrl(blob) {
|
|
59
|
+
try {
|
|
60
|
+
return decodeURIComponent(blob);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function rot13(text) {
|
|
67
|
+
return text.replace(/[a-zA-Z]/g, (ch) => {
|
|
68
|
+
const base = ch <= 'Z' ? 65 : 97;
|
|
69
|
+
return String.fromCharCode(((ch.charCodeAt(0) - base + 13) % 26) + base);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export function reverseText(text) {
|
|
73
|
+
return Array.from(text).reverse().join('');
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Extract decodable candidates from `text`. The caller is expected to
|
|
77
|
+
* re-scan each `decoded` string once; decoded output is never fed back here.
|
|
78
|
+
*/
|
|
79
|
+
export function extractDecodedCandidates(text, opts = {}) {
|
|
80
|
+
const o = { ...DEFAULTS, ...opts };
|
|
81
|
+
const out = [];
|
|
82
|
+
let bytes = 0;
|
|
83
|
+
const seen = new Set();
|
|
84
|
+
const consider = (encoding, decoded, offset, sourceLength) => {
|
|
85
|
+
if (out.length >= o.maxCandidates || !decoded)
|
|
86
|
+
return;
|
|
87
|
+
const trimmed = decoded.length > o.maxBytes ? decoded.slice(0, o.maxBytes) : decoded;
|
|
88
|
+
if (bytes + trimmed.length > o.maxBytes * o.maxCandidates)
|
|
89
|
+
return;
|
|
90
|
+
if (!looksLikeText(trimmed) || seen.has(trimmed))
|
|
91
|
+
return;
|
|
92
|
+
seen.add(trimmed);
|
|
93
|
+
bytes += trimmed.length;
|
|
94
|
+
out.push({ encoding, decoded: trimmed, offset, sourceLength });
|
|
95
|
+
};
|
|
96
|
+
const scan = (re, encoding, decode) => {
|
|
97
|
+
re.lastIndex = 0;
|
|
98
|
+
let m;
|
|
99
|
+
// ruflo: cap each blob before decoding so a single 200 KB base64 run is
|
|
100
|
+
// not decoded in full only to be trimmed to maxBytes afterwards. The cap is
|
|
101
|
+
// a multiple of 12 so base64 quads, hex pairs and %XX triplets stay whole.
|
|
102
|
+
const maxBlob = Math.max(12, Math.floor((o.maxBytes * 3) / 12) * 12);
|
|
103
|
+
while ((m = re.exec(text)) !== null && out.length < o.maxCandidates) {
|
|
104
|
+
if (m[0].length < o.minBlob)
|
|
105
|
+
continue;
|
|
106
|
+
const blob = m[0].length > maxBlob ? m[0].slice(0, maxBlob) : m[0];
|
|
107
|
+
consider(encoding, decode(blob), m.index, m[0].length);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
scan(URLENC_RE, 'url', decodeUrl);
|
|
111
|
+
scan(HEX_RE, 'hex', decodeHex);
|
|
112
|
+
scan(BASE64_RE, 'base64', decodeBase64);
|
|
113
|
+
if (text.length <= o.wholeTextLimit && /[A-Za-z]{4}/.test(text)) {
|
|
114
|
+
if (o.rot13)
|
|
115
|
+
consider('rot13', rot13(text), 0, text.length);
|
|
116
|
+
if (o.reverse)
|
|
117
|
+
consider('reverse', reverseText(text), 0, text.length);
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=decoders.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decoders.js","sourceRoot":"","sources":["../../../../src/domain/services/packs/decoders.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored from midstream `AIMDS/src/detection/engine.ts` at commit abe78d6.
|
|
3
|
+
*
|
|
4
|
+
* InjectionDetector: pack-driven prompt-injection detector.
|
|
5
|
+
*
|
|
6
|
+
* Pipeline: normalise -> text variants -> one regex exec per (pattern,
|
|
7
|
+
* variant) -> bounded decode of encoded blobs -> one re-scan of each decoded
|
|
8
|
+
* string with every pack except encoded_instruction (no recursion).
|
|
9
|
+
*
|
|
10
|
+
* ruflo changes:
|
|
11
|
+
* - packs come from the embedded `./data/` modules, validated and compiled
|
|
12
|
+
* ONCE per process (`compiledPacks()`); a detector instance only selects
|
|
13
|
+
* which compiled packs it scans, so creating detectors is cheap.
|
|
14
|
+
* - `packs` config also accepts `true`/`false` or a list of pack names.
|
|
15
|
+
* - threats carry `matchLength` so callers can report a location span.
|
|
16
|
+
* - normalised/decoded text is used for matching only; it is never returned
|
|
17
|
+
* (the `match` excerpt is the only text echoed, capped at 160 chars).
|
|
18
|
+
*/
|
|
19
|
+
import { CompiledPattern, PatternPack, Severity, ThreatKind } from './pattern-loader.js';
|
|
20
|
+
import { TextVariant } from './normalize.js';
|
|
21
|
+
import { DecoderOptions, Encoding } from './decoders.js';
|
|
22
|
+
export type PackName = 'core' | 'tool_invocation' | 'exfil_url' | 'encoded_instruction' | 'slack_markup_forgery' | 'instruction_override_i18n' | (string & {});
|
|
23
|
+
/**
|
|
24
|
+
* Which packs to scan. `true`/omitted: every pack whose `enabledByDefault`
|
|
25
|
+
* is set (all six vendored packs). `false`: none. A list: exactly those.
|
|
26
|
+
* A record: per-pack overrides on top of the defaults.
|
|
27
|
+
*/
|
|
28
|
+
export type PackSelection = boolean | readonly PackName[] | Partial<Record<PackName, boolean>>;
|
|
29
|
+
export interface InjectionDetectorConfig {
|
|
30
|
+
readonly packs?: PackSelection;
|
|
31
|
+
/** Preloaded packs (tests). Replaces the embedded packs; compiled per instance. */
|
|
32
|
+
readonly preloaded?: readonly PatternPack[];
|
|
33
|
+
/** Scan obfuscation variants (separators, leet, compact). Default true. */
|
|
34
|
+
readonly variants?: boolean;
|
|
35
|
+
/** Decode base64/hex/url blobs and re-scan once. Default true when encoded_instruction is enabled. */
|
|
36
|
+
readonly decode?: boolean;
|
|
37
|
+
readonly decoder?: DecoderOptions;
|
|
38
|
+
/** Inputs longer than this are truncated before scanning. Default 262144. */
|
|
39
|
+
readonly maxInputChars?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface InjectionThreat {
|
|
42
|
+
readonly id: string;
|
|
43
|
+
readonly pack: string;
|
|
44
|
+
readonly type: ThreatKind;
|
|
45
|
+
readonly severity: Severity;
|
|
46
|
+
readonly confidence: number;
|
|
47
|
+
readonly description: string;
|
|
48
|
+
/** Regex source of the pattern that matched. */
|
|
49
|
+
readonly regex: string;
|
|
50
|
+
readonly match: string;
|
|
51
|
+
readonly offset: number;
|
|
52
|
+
readonly matchLength: number;
|
|
53
|
+
/** Which text variant matched: base, separators, leet, compact. */
|
|
54
|
+
readonly variant: TextVariant['name'];
|
|
55
|
+
/** Set when the match was found in decoded content. */
|
|
56
|
+
readonly decodedFrom?: Encoding;
|
|
57
|
+
}
|
|
58
|
+
export interface InjectionReport {
|
|
59
|
+
readonly safe: boolean;
|
|
60
|
+
readonly maxSeverity: Severity | null;
|
|
61
|
+
readonly threats: readonly InjectionThreat[];
|
|
62
|
+
readonly scanned: {
|
|
63
|
+
variants: number;
|
|
64
|
+
decoded: number;
|
|
65
|
+
patterns: number;
|
|
66
|
+
};
|
|
67
|
+
readonly truncated: boolean;
|
|
68
|
+
readonly durationMs: number;
|
|
69
|
+
}
|
|
70
|
+
interface CompiledPackSet {
|
|
71
|
+
readonly packs: Map<string, PatternPack>;
|
|
72
|
+
readonly compiled: Map<string, CompiledPattern[]>;
|
|
73
|
+
}
|
|
74
|
+
/** The embedded packs, validated, compiled and warmed once per process. */
|
|
75
|
+
export declare function compiledPacks(): CompiledPackSet;
|
|
76
|
+
export declare class InjectionDetector {
|
|
77
|
+
private readonly enabled;
|
|
78
|
+
private readonly compiled;
|
|
79
|
+
private readonly rescan;
|
|
80
|
+
private readonly cfg;
|
|
81
|
+
constructor(config?: InjectionDetectorConfig);
|
|
82
|
+
/** Names of packs that are loaded and enabled. */
|
|
83
|
+
enabledPacks(): string[];
|
|
84
|
+
/** Total compiled patterns across enabled packs. */
|
|
85
|
+
patternCount(): number;
|
|
86
|
+
detect(input: string): InjectionReport;
|
|
87
|
+
private scanVariant;
|
|
88
|
+
}
|
|
89
|
+
/** Shared detector with every default-on pack. */
|
|
90
|
+
export declare function getInjectionDetector(): InjectionDetector;
|
|
91
|
+
export declare function createInjectionDetector(config?: InjectionDetectorConfig): InjectionDetector;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../../src/domain/services/packs/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,eAAe,EACf,WAAW,EACX,QAAQ,EACR,UAAU,EAGX,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAA+B,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,QAAQ,EAA4B,MAAM,eAAe,CAAC;AAGnF,MAAM,MAAM,QAAQ,GAChB,MAAM,GACN,iBAAiB,GACjB,WAAW,GACX,qBAAqB,GACrB,sBAAsB,GACtB,2BAA2B,GAC3B,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,QAAQ,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC;AAE/F,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,aAAa,CAAC;IAC/B,mFAAmF;IACnF,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAC5C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,sGAAsG;IACtG,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,cAAc,CAAC;IAClC,6EAA6E;IAC7E,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,gDAAgD;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,uDAAuD;IACvD,QAAQ,CAAC,WAAW,CAAC,EAAE,QAAQ,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAC;IACtC,QAAQ,CAAC,OAAO,EAAE,SAAS,eAAe,EAAE,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1E,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAKD,UAAU,eAAe;IACvB,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;CACnD;AA+BD,2EAA2E;AAC3E,wBAAgB,aAAa,IAAI,eAAe,CAM/C;AAUD,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAC3C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAiH;gBAEzH,MAAM,GAAE,uBAA4B;IAkBhD,kDAAkD;IAClD,YAAY,IAAI,MAAM,EAAE;IAIxB,oDAAoD;IACpD,YAAY,IAAI,MAAM;IAItB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe;IAuCtC,OAAO,CAAC,WAAW;CAgCpB;AAgBD,kDAAkD;AAClD,wBAAgB,oBAAoB,IAAI,iBAAiB,CAOxD;AAED,wBAAgB,uBAAuB,CAAC,MAAM,CAAC,EAAE,uBAAuB,GAAG,iBAAiB,CAE3F"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored from midstream `AIMDS/src/detection/engine.ts` at commit abe78d6.
|
|
3
|
+
*
|
|
4
|
+
* InjectionDetector: pack-driven prompt-injection detector.
|
|
5
|
+
*
|
|
6
|
+
* Pipeline: normalise -> text variants -> one regex exec per (pattern,
|
|
7
|
+
* variant) -> bounded decode of encoded blobs -> one re-scan of each decoded
|
|
8
|
+
* string with every pack except encoded_instruction (no recursion).
|
|
9
|
+
*
|
|
10
|
+
* ruflo changes:
|
|
11
|
+
* - packs come from the embedded `./data/` modules, validated and compiled
|
|
12
|
+
* ONCE per process (`compiledPacks()`); a detector instance only selects
|
|
13
|
+
* which compiled packs it scans, so creating detectors is cheap.
|
|
14
|
+
* - `packs` config also accepts `true`/`false` or a list of pack names.
|
|
15
|
+
* - threats carry `matchLength` so callers can report a location span.
|
|
16
|
+
* - normalised/decoded text is used for matching only; it is never returned
|
|
17
|
+
* (the `match` excerpt is the only text echoed, capped at 160 chars).
|
|
18
|
+
*/
|
|
19
|
+
import { compilePack, validatePack, } from './pattern-loader.js';
|
|
20
|
+
import { normalizeText, textVariants } from './normalize.js';
|
|
21
|
+
import { extractDecodedCandidates } from './decoders.js';
|
|
22
|
+
import { EMBEDDED_PACKS } from './data/index.js';
|
|
23
|
+
const SEVERITY_RANK = { low: 0, medium: 1, high: 2, critical: 3 };
|
|
24
|
+
const VARIANT_PENALTY = { base: 0, separators: 0.05, leet: 0.1, compact: 0.15 };
|
|
25
|
+
let embedded = null;
|
|
26
|
+
function compileSet(packs) {
|
|
27
|
+
const byName = new Map();
|
|
28
|
+
const compiled = new Map();
|
|
29
|
+
for (const raw of packs) {
|
|
30
|
+
const pack = validatePack(raw, raw.pack);
|
|
31
|
+
if (byName.has(pack.pack))
|
|
32
|
+
throw new Error(`pattern pack: duplicate pack ${pack.pack}`);
|
|
33
|
+
byName.set(pack.pack, pack);
|
|
34
|
+
compiled.set(pack.pack, compilePack(pack));
|
|
35
|
+
}
|
|
36
|
+
return { packs: byName, compiled };
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* V8 compiles a RegExp lazily on its first exec, which made the first
|
|
40
|
+
* detect() of a process ~17 ms. Running every pattern and the normaliser once
|
|
41
|
+
* here moves that cost to construction, outside detectionTimeMs.
|
|
42
|
+
*/
|
|
43
|
+
function warmUp(set) {
|
|
44
|
+
const sample = normalizeText('Warm-up: ignore the p.r.e.v.i.o.u.s n0te at https://example.invalid/?q=aGVsbG8gd29ybGQ');
|
|
45
|
+
for (const v of textVariants(sample)) {
|
|
46
|
+
for (const list of set.compiled.values()) {
|
|
47
|
+
for (const c of list)
|
|
48
|
+
(v.compactForm ? c.compactRe : c.re)?.exec(v.text);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
extractDecodedCandidates(sample);
|
|
52
|
+
}
|
|
53
|
+
/** The embedded packs, validated, compiled and warmed once per process. */
|
|
54
|
+
export function compiledPacks() {
|
|
55
|
+
if (!embedded) {
|
|
56
|
+
embedded = compileSet(EMBEDDED_PACKS);
|
|
57
|
+
warmUp(embedded);
|
|
58
|
+
}
|
|
59
|
+
return embedded;
|
|
60
|
+
}
|
|
61
|
+
function isEnabled(name, pack, sel) {
|
|
62
|
+
if (sel === undefined || sel === true)
|
|
63
|
+
return pack.enabledByDefault;
|
|
64
|
+
if (sel === false)
|
|
65
|
+
return false;
|
|
66
|
+
if (Array.isArray(sel))
|
|
67
|
+
return sel.includes(name);
|
|
68
|
+
const flag = sel[name];
|
|
69
|
+
return flag === true || (flag === undefined && pack.enabledByDefault);
|
|
70
|
+
}
|
|
71
|
+
export class InjectionDetector {
|
|
72
|
+
enabled;
|
|
73
|
+
compiled;
|
|
74
|
+
rescan;
|
|
75
|
+
cfg;
|
|
76
|
+
constructor(config = {}) {
|
|
77
|
+
const set = config.preloaded ? compileSet(config.preloaded) : compiledPacks();
|
|
78
|
+
this.enabled = new Set();
|
|
79
|
+
this.compiled = [];
|
|
80
|
+
for (const [name, pack] of set.packs) {
|
|
81
|
+
if (!isEnabled(name, pack, config.packs))
|
|
82
|
+
continue;
|
|
83
|
+
this.enabled.add(name);
|
|
84
|
+
this.compiled.push(...set.compiled.get(name));
|
|
85
|
+
}
|
|
86
|
+
this.rescan = this.compiled.filter((c) => c.spec.pack !== 'encoded_instruction');
|
|
87
|
+
this.cfg = {
|
|
88
|
+
variants: config.variants ?? true,
|
|
89
|
+
decode: config.decode ?? this.enabled.has('encoded_instruction'),
|
|
90
|
+
maxInputChars: config.maxInputChars ?? 262144,
|
|
91
|
+
decoder: config.decoder ?? {},
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/** Names of packs that are loaded and enabled. */
|
|
95
|
+
enabledPacks() {
|
|
96
|
+
return [...this.enabled];
|
|
97
|
+
}
|
|
98
|
+
/** Total compiled patterns across enabled packs. */
|
|
99
|
+
patternCount() {
|
|
100
|
+
return this.compiled.length;
|
|
101
|
+
}
|
|
102
|
+
detect(input) {
|
|
103
|
+
const start = performance.now();
|
|
104
|
+
if (this.compiled.length === 0) {
|
|
105
|
+
return { safe: true, maxSeverity: null, threats: [], scanned: { variants: 0, decoded: 0, patterns: 0 }, truncated: false, durationMs: 0 };
|
|
106
|
+
}
|
|
107
|
+
const truncated = input.length > this.cfg.maxInputChars;
|
|
108
|
+
const text = truncated ? input.slice(0, this.cfg.maxInputChars) : input;
|
|
109
|
+
const normalized = normalizeText(text);
|
|
110
|
+
const variants = this.cfg.variants
|
|
111
|
+
? textVariants(normalized)
|
|
112
|
+
: [{ name: 'base', text: normalized, compactForm: false }];
|
|
113
|
+
const found = [];
|
|
114
|
+
for (const v of variants)
|
|
115
|
+
this.scanVariant(v, this.compiled, found, undefined);
|
|
116
|
+
let decoded = 0;
|
|
117
|
+
if (this.cfg.decode) {
|
|
118
|
+
for (const cand of extractDecodedCandidates(normalized, this.cfg.decoder)) {
|
|
119
|
+
decoded++;
|
|
120
|
+
const inner = normalizeText(cand.decoded);
|
|
121
|
+
const innerVariants = this.cfg.variants
|
|
122
|
+
? textVariants(inner, 8192)
|
|
123
|
+
: [{ name: 'base', text: inner, compactForm: false }];
|
|
124
|
+
for (const v of innerVariants)
|
|
125
|
+
this.scanVariant(v, this.rescan, found, cand.encoding, cand.offset, cand.sourceLength);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const threats = dedupe(found);
|
|
129
|
+
const maxSeverity = threats.length ? threats[0].severity : null;
|
|
130
|
+
return {
|
|
131
|
+
safe: threats.length === 0,
|
|
132
|
+
maxSeverity,
|
|
133
|
+
threats,
|
|
134
|
+
scanned: { variants: variants.length, decoded, patterns: this.compiled.length },
|
|
135
|
+
truncated,
|
|
136
|
+
durationMs: performance.now() - start,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
scanVariant(v, patterns, out, decodedFrom, baseOffset = 0, sourceLength) {
|
|
140
|
+
for (const c of patterns) {
|
|
141
|
+
const re = v.compactForm ? c.compactRe : c.re;
|
|
142
|
+
if (!re)
|
|
143
|
+
continue;
|
|
144
|
+
const m = re.exec(v.text);
|
|
145
|
+
if (!m || m[0].length === 0)
|
|
146
|
+
continue;
|
|
147
|
+
const base = c.spec.confidence ?? 0.7;
|
|
148
|
+
const confidence = Math.max(0.1, Math.round((base - VARIANT_PENALTY[v.name] - (decodedFrom ? 0.05 : 0)) * 100) / 100);
|
|
149
|
+
out.push({
|
|
150
|
+
id: c.spec.id,
|
|
151
|
+
pack: c.spec.pack,
|
|
152
|
+
type: c.spec.type,
|
|
153
|
+
severity: c.spec.severity,
|
|
154
|
+
confidence,
|
|
155
|
+
description: c.spec.description,
|
|
156
|
+
regex: c.spec.regex,
|
|
157
|
+
match: m[0].length > 160 ? `${m[0].slice(0, 157)}...` : m[0],
|
|
158
|
+
// A decoded match is reported at the encoded blob it came from.
|
|
159
|
+
offset: decodedFrom ? baseOffset : m.index,
|
|
160
|
+
matchLength: decodedFrom ? (sourceLength ?? m[0].length) : m[0].length,
|
|
161
|
+
variant: v.name,
|
|
162
|
+
...(decodedFrom ? { decodedFrom } : {}),
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** One threat per pattern id (highest confidence), sorted by severity then confidence. */
|
|
168
|
+
function dedupe(threats) {
|
|
169
|
+
const best = new Map();
|
|
170
|
+
for (const t of threats) {
|
|
171
|
+
const prev = best.get(t.id);
|
|
172
|
+
if (!prev || t.confidence > prev.confidence)
|
|
173
|
+
best.set(t.id, t);
|
|
174
|
+
}
|
|
175
|
+
return [...best.values()].sort((a, b) => SEVERITY_RANK[b.severity] - SEVERITY_RANK[a.severity] || b.confidence - a.confidence);
|
|
176
|
+
}
|
|
177
|
+
let defaultDetector = null;
|
|
178
|
+
/** Shared detector with every default-on pack. */
|
|
179
|
+
export function getInjectionDetector() {
|
|
180
|
+
if (!defaultDetector) {
|
|
181
|
+
defaultDetector = new InjectionDetector();
|
|
182
|
+
// Warm the detect() path itself (dedupe, sort) before any timed call.
|
|
183
|
+
defaultDetector.detect('warm-up: use the fetch_url tool on https://example.invalid/x');
|
|
184
|
+
}
|
|
185
|
+
return defaultDetector;
|
|
186
|
+
}
|
|
187
|
+
export function createInjectionDetector(config) {
|
|
188
|
+
return new InjectionDetector(config);
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../../../src/domain/services/packs/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pack-driven prompt-injection detection (AIMDS pattern packs).
|
|
3
|
+
*
|
|
4
|
+
* Vendored from midstream `AIMDS/src/detection/` at commit abe78d6. Pattern
|
|
5
|
+
* data is embedded under `./data/` (from `AIMDS/patterns/*.json`, shared with
|
|
6
|
+
* the Rust `aidefence-core` crate); this module loads, compiles, normalises,
|
|
7
|
+
* decodes, and matches.
|
|
8
|
+
*/
|
|
9
|
+
export { InjectionDetector, createInjectionDetector, getInjectionDetector, compiledPacks, } from './engine.js';
|
|
10
|
+
export type { InjectionDetectorConfig, InjectionReport, InjectionThreat, PackName, PackSelection, } from './engine.js';
|
|
11
|
+
export { validatePack, compilePack, compilePattern, SEVERITIES, THREAT_KINDS, } from './pattern-loader.js';
|
|
12
|
+
export type { PatternPack, PatternSpec, CompiledPattern, Severity, ThreatKind } from './pattern-loader.js';
|
|
13
|
+
export { normalizeText, textVariants, compactSource } from './normalize.js';
|
|
14
|
+
export type { TextVariant } from './normalize.js';
|
|
15
|
+
export { extractDecodedCandidates, rot13, reverseText, printableRatio } from './decoders.js';
|
|
16
|
+
export type { DecodedCandidate, DecoderOptions, Encoding } from './decoders.js';
|
|
17
|
+
export { EMBEDDED_PACKS } from './data/index.js';
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/domain/services/packs/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,EACpB,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,QAAQ,EACR,aAAa,GACd,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,UAAU,EACV,YAAY,GACb,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC3G,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC5E,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC7F,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pack-driven prompt-injection detection (AIMDS pattern packs).
|
|
3
|
+
*
|
|
4
|
+
* Vendored from midstream `AIMDS/src/detection/` at commit abe78d6. Pattern
|
|
5
|
+
* data is embedded under `./data/` (from `AIMDS/patterns/*.json`, shared with
|
|
6
|
+
* the Rust `aidefence-core` crate); this module loads, compiles, normalises,
|
|
7
|
+
* decodes, and matches.
|
|
8
|
+
*/
|
|
9
|
+
export { InjectionDetector, createInjectionDetector, getInjectionDetector, compiledPacks, } from './engine.js';
|
|
10
|
+
export { validatePack, compilePack, compilePattern, SEVERITIES, THREAT_KINDS, } from './pattern-loader.js';
|
|
11
|
+
export { normalizeText, textVariants, compactSource } from './normalize.js';
|
|
12
|
+
export { extractDecodedCandidates, rot13, reverseText, printableRatio } from './decoders.js';
|
|
13
|
+
export { EMBEDDED_PACKS } from './data/index.js';
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/domain/services/packs/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,oBAAoB,EACpB,aAAa,GACd,MAAM,aAAa,CAAC;AAQrB,OAAO,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,UAAU,EACV,YAAY,GACb,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE5E,OAAO,EAAE,wBAAwB,EAAE,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE7F,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored from midstream `AIMDS/src/detection/normalize.ts` at commit abe78d6.
|
|
3
|
+
* Detection-only: output of this module is matched against, never shown to users.
|
|
4
|
+
*/
|
|
5
|
+
export interface TextVariant {
|
|
6
|
+
/** Name of the transform that produced this text. */
|
|
7
|
+
readonly name: 'base' | 'separators' | 'leet' | 'compact';
|
|
8
|
+
readonly text: string;
|
|
9
|
+
/** True when the variant has no whitespace left, so `\s+`-free regexes must be used. */
|
|
10
|
+
readonly compactForm: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Canonical form used for all matching. Idempotent.
|
|
14
|
+
*/
|
|
15
|
+
export declare function normalizeText(input: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Variants of a normalised text, de-duplicated. `base` is always first.
|
|
18
|
+
* Variants are only generated for the first `limit` characters so the
|
|
19
|
+
* scan cost stays bounded.
|
|
20
|
+
*/
|
|
21
|
+
export declare function textVariants(normalized: string, limit?: number): TextVariant[];
|
|
22
|
+
/** Compact-form regex source: `\s+`/`\s*` become empty so joined words match. */
|
|
23
|
+
export declare function compactSource(source: string): string;
|
|
24
|
+
//# sourceMappingURL=normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../../../src/domain/services/packs/normalize.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAwDH,MAAM,WAAW,WAAW;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQnD;AAgCD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,SAAQ,GAAG,WAAW,EAAE,CAe7E;AAED,iFAAiF;AACjF,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpD"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vendored from midstream `AIMDS/src/detection/normalize.ts` at commit abe78d6.
|
|
3
|
+
* Detection-only: output of this module is matched against, never shown to users.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Text normalisation for injection detection.
|
|
7
|
+
*
|
|
8
|
+
* Produces a canonical form (NFKC, zero-width stripped, confusables folded,
|
|
9
|
+
* whitespace collapsed) plus a small set of bounded variants that undo common
|
|
10
|
+
* obfuscations (separator-joined words, single-character spacing, leetspeak,
|
|
11
|
+
* whitespace removal). Every transform is linear in input length.
|
|
12
|
+
*/
|
|
13
|
+
/** Zero-width and invisible formatting code points that carry no text. */
|
|
14
|
+
const INVISIBLE = /[\u200B-\u200F\u202A-\u202E\u2060-\u2064\uFEFF\u00AD\u180E]/g;
|
|
15
|
+
/**
|
|
16
|
+
* Explicit confusables map: Cyrillic and Greek letters whose glyphs are
|
|
17
|
+
* indistinguishable from Latin ones. NFKC does not fold these.
|
|
18
|
+
*/
|
|
19
|
+
const CONFUSABLES = {
|
|
20
|
+
'а': 'a', 'А': 'A', // а А
|
|
21
|
+
'е': 'e', 'Е': 'E', // е Е
|
|
22
|
+
'о': 'o', 'О': 'O', // о О
|
|
23
|
+
'р': 'p', 'Р': 'P', // р Р
|
|
24
|
+
'с': 'c', 'С': 'C', // с С
|
|
25
|
+
'у': 'y', 'У': 'Y', // у У
|
|
26
|
+
'х': 'x', 'Х': 'X', // х Х
|
|
27
|
+
'і': 'i', 'І': 'I', // і І
|
|
28
|
+
'ј': 'j', 'Ј': 'J', // ј Ј
|
|
29
|
+
'һ': 'h', 'Һ': 'H', // һ Һ
|
|
30
|
+
'ԁ': 'd', 'ԛ': 'q', 'ԝ': 'w',
|
|
31
|
+
'ѕ': 's', 'Ѕ': 'S', // ѕ Ѕ
|
|
32
|
+
'в': 'B', 'В': 'B', // в В (visually B in caps)
|
|
33
|
+
'к': 'k', 'К': 'K', // к К
|
|
34
|
+
'м': 'm', 'М': 'M', // м М
|
|
35
|
+
'н': 'H', 'Н': 'H', // н Н
|
|
36
|
+
'т': 'T', 'Т': 'T', // т Т
|
|
37
|
+
'α': 'a', 'Α': 'A', // α Α
|
|
38
|
+
'ε': 'e', 'Ε': 'E', // ε Ε
|
|
39
|
+
'ο': 'o', 'Ο': 'O', // ο Ο
|
|
40
|
+
'ρ': 'p', 'Ρ': 'P', // ρ Ρ
|
|
41
|
+
'ι': 'i', 'Ι': 'I', // ι Ι
|
|
42
|
+
'κ': 'k', 'Κ': 'K', // κ Κ
|
|
43
|
+
'ν': 'v', 'Ν': 'N', // ν Ν
|
|
44
|
+
'τ': 't', 'Τ': 'T', // τ Τ
|
|
45
|
+
'υ': 'u', 'Υ': 'Y', // υ Υ
|
|
46
|
+
'χ': 'x', 'Χ': 'X', // χ Χ
|
|
47
|
+
'Β': 'B', 'Η': 'H', 'Μ': 'M', 'Ζ': 'Z',
|
|
48
|
+
};
|
|
49
|
+
const CONFUSABLE_RE = new RegExp(`[${Object.keys(CONFUSABLES).join('')}]`, 'g');
|
|
50
|
+
/** Leetspeak digits/symbols that stand in for letters. */
|
|
51
|
+
const LEET = {
|
|
52
|
+
'0': 'o', '1': 'i', '3': 'e', '4': 'a', '5': 's', '7': 't', '@': 'a', '$': 's', '!': 'i', '|': 'l',
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Canonical form used for all matching. Idempotent.
|
|
56
|
+
*/
|
|
57
|
+
export function normalizeText(input) {
|
|
58
|
+
return input
|
|
59
|
+
.normalize('NFKC')
|
|
60
|
+
.replace(INVISIBLE, '')
|
|
61
|
+
.replace(CONFUSABLE_RE, (ch) => CONFUSABLES[ch] ?? ch)
|
|
62
|
+
.replace(/[ \t\f\v ]+/g, ' ')
|
|
63
|
+
.replace(/ ?\r?\n ?/g, '\n')
|
|
64
|
+
.trim();
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Join runs of single characters separated by one repeated separator
|
|
68
|
+
* ("i.g.n.o.r.e", "i g n o r e") into a word, and turn `_`/`-` between
|
|
69
|
+
* letters into spaces ("ignore_all_previous" -> "ignore all previous").
|
|
70
|
+
*/
|
|
71
|
+
function undoSeparators(text) {
|
|
72
|
+
// Runs of 3+ single letters joined by the same separator: i.g.n / i g n / i-g-n
|
|
73
|
+
const joined = text.replace(/\b(?:[A-Za-z][.\-_ ]){2,}[A-Za-z]\b/g, (run) => run.replace(/[.\-_ ]/g, ''));
|
|
74
|
+
return joined.replace(/(?<=[A-Za-z])[_\-]+(?=[A-Za-z])/g, ' ');
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Fold leet digits to letters, only inside tokens that already contain a
|
|
78
|
+
* letter (so numbers, hashes, and IDs are left alone).
|
|
79
|
+
*/
|
|
80
|
+
function undoLeet(text) {
|
|
81
|
+
// ruflo: upstream matched `[cls]*[A-Za-z][cls]*`, which rescans a
|
|
82
|
+
// letter-free run (e.g. 100 KB of hex digits) from every start position:
|
|
83
|
+
// quadratic, 1.8 s per 100 KB. Matching maximal runs and checking for a
|
|
84
|
+
// letter in the callback yields the same tokens in linear time.
|
|
85
|
+
return text.replace(/[A-Za-z0-9@$!|]+/g, (token) => {
|
|
86
|
+
if (!/[A-Za-z]/.test(token))
|
|
87
|
+
return token;
|
|
88
|
+
if (!/[0-9@$!|]/.test(token))
|
|
89
|
+
return token;
|
|
90
|
+
return token.replace(/[0-9@$!|]/g, (ch) => LEET[ch] ?? ch);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Variants of a normalised text, de-duplicated. `base` is always first.
|
|
95
|
+
* Variants are only generated for the first `limit` characters so the
|
|
96
|
+
* scan cost stays bounded.
|
|
97
|
+
*/
|
|
98
|
+
export function textVariants(normalized, limit = 65536) {
|
|
99
|
+
const head = normalized.length > limit ? normalized.slice(0, limit) : normalized;
|
|
100
|
+
const out = [{ name: 'base', text: normalized, compactForm: false }];
|
|
101
|
+
const seen = new Set([normalized]);
|
|
102
|
+
const push = (name, text) => {
|
|
103
|
+
if (!seen.has(text)) {
|
|
104
|
+
seen.add(text);
|
|
105
|
+
out.push({ name, text, compactForm: name === 'compact' || !/\s/.test(text) });
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
const sep = undoSeparators(head);
|
|
109
|
+
push('separators', sep);
|
|
110
|
+
push('leet', undoLeet(sep));
|
|
111
|
+
push('compact', sep.replace(/[ \t.\-_]+/g, ''));
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
/** Compact-form regex source: `\s+`/`\s*` become empty so joined words match. */
|
|
115
|
+
export function compactSource(source) {
|
|
116
|
+
return source.replace(/\\s[+*]/g, '');
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=normalize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"normalize.js","sourceRoot":"","sources":["../../../../src/domain/services/packs/normalize.ts"],"names":[],"mappings":"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"}
|