@skillsmith/core 0.11.1 → 0.11.3
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 +20 -0
- package/README.md +6 -7
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/analysis/McpReferenceExtractor.d.ts +59 -5
- package/dist/src/analysis/McpReferenceExtractor.d.ts.map +1 -1
- package/dist/src/analysis/McpReferenceExtractor.hardening.test.d.ts +2 -0
- package/dist/src/analysis/McpReferenceExtractor.hardening.test.d.ts.map +1 -0
- package/dist/src/analysis/McpReferenceExtractor.hardening.test.js +278 -0
- package/dist/src/analysis/McpReferenceExtractor.hardening.test.js.map +1 -0
- package/dist/src/analysis/McpReferenceExtractor.js +276 -20
- package/dist/src/analysis/McpReferenceExtractor.js.map +1 -1
- package/dist/src/analysis/McpReferenceExtractor.test.js +4 -0
- package/dist/src/analysis/McpReferenceExtractor.test.js.map +1 -1
- package/dist/src/config/config-atomic-write.d.ts +58 -0
- package/dist/src/config/config-atomic-write.d.ts.map +1 -0
- package/dist/src/config/config-atomic-write.js +151 -0
- package/dist/src/config/config-atomic-write.js.map +1 -0
- package/dist/src/config/config-atomic-write.test.d.ts +9 -0
- package/dist/src/config/config-atomic-write.test.d.ts.map +1 -0
- package/dist/src/config/config-atomic-write.test.js +92 -0
- package/dist/src/config/config-atomic-write.test.js.map +1 -0
- package/dist/src/config/device-identity.d.ts +16 -0
- package/dist/src/config/device-identity.d.ts.map +1 -1
- package/dist/src/config/device-identity.js +82 -14
- package/dist/src/config/device-identity.js.map +1 -1
- package/dist/src/config/device-identity.test.js +158 -1
- package/dist/src/config/device-identity.test.js.map +1 -1
- package/dist/src/config/index.d.ts +14 -0
- package/dist/src/config/index.d.ts.map +1 -1
- package/dist/src/config/index.js +31 -15
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/exports/services.d.ts +2 -1
- package/dist/src/exports/services.d.ts.map +1 -1
- package/dist/src/exports/services.js +1 -0
- package/dist/src/exports/services.js.map +1 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/indexer/SwarmIndexer.d.ts +1 -1
- package/dist/src/indexer/SwarmIndexer.js +2 -2
- package/dist/src/indexer/SwarmIndexer.js.map +1 -1
- package/dist/src/install/paths.d.ts +1 -1
- package/dist/src/install/paths.d.ts.map +1 -1
- package/dist/src/install/paths.js +2 -0
- package/dist/src/install/paths.js.map +1 -1
- package/dist/src/install/paths.test.js +10 -1
- package/dist/src/install/paths.test.js.map +1 -1
- package/dist/src/journal/reader.d.ts.map +1 -1
- package/dist/src/journal/reader.js +4 -1
- package/dist/src/journal/reader.js.map +1 -1
- package/dist/src/journal/types.d.ts +13 -3
- package/dist/src/journal/types.d.ts.map +1 -1
- package/dist/src/journal/types.js +8 -2
- package/dist/src/journal/types.js.map +1 -1
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +36 -0
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
- package/dist/src/security/audit-types.d.ts +1 -1
- package/dist/src/security/audit-types.d.ts.map +1 -1
- package/dist/src/security/audit-types.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.exec.d.ts +7 -0
- package/dist/src/security/scanner/SecurityScanner.exec.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.exec.js +10 -74
- package/dist/src/security/scanner/SecurityScanner.exec.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.formatters.d.ts +14 -1
- package/dist/src/security/scanner/SecurityScanner.formatters.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.formatters.js +33 -0
- package/dist/src/security/scanner/SecurityScanner.formatters.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.formatters.test.d.ts +9 -0
- package/dist/src/security/scanner/SecurityScanner.formatters.test.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.formatters.test.js +65 -0
- package/dist/src/security/scanner/SecurityScanner.formatters.test.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.js +15 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
- package/dist/src/security/scanner/confusables.d.ts +28 -0
- package/dist/src/security/scanner/confusables.d.ts.map +1 -0
- package/dist/src/security/scanner/confusables.js +87 -0
- package/dist/src/security/scanner/confusables.js.map +1 -0
- package/dist/src/security/scanner/confusables.test.d.ts +10 -0
- package/dist/src/security/scanner/confusables.test.d.ts.map +1 -0
- package/dist/src/security/scanner/confusables.test.js +44 -0
- package/dist/src/security/scanner/confusables.test.js.map +1 -0
- package/dist/src/security/scanner/index.d.ts +7 -1
- package/dist/src/security/scanner/index.d.ts.map +1 -1
- package/dist/src/security/scanner/index.js +13 -0
- package/dist/src/security/scanner/index.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +14 -1
- package/dist/src/security/scanner/types.d.ts.map +1 -1
- package/dist/src/security/scanner/typosquat-reference-list.d.ts +54 -0
- package/dist/src/security/scanner/typosquat-reference-list.d.ts.map +1 -0
- package/dist/src/security/scanner/typosquat-reference-list.js +55 -0
- package/dist/src/security/scanner/typosquat-reference-list.js.map +1 -0
- package/dist/src/security/scanner/typosquat-reference-list.test.d.ts +5 -0
- package/dist/src/security/scanner/typosquat-reference-list.test.d.ts.map +1 -0
- package/dist/src/security/scanner/typosquat-reference-list.test.js +52 -0
- package/dist/src/security/scanner/typosquat-reference-list.test.js.map +1 -0
- package/dist/src/security/scanner/typosquat.d.ts +100 -0
- package/dist/src/security/scanner/typosquat.d.ts.map +1 -0
- package/dist/src/security/scanner/typosquat.js +256 -0
- package/dist/src/security/scanner/typosquat.js.map +1 -0
- package/dist/src/security/scanner/typosquat.test.d.ts +12 -0
- package/dist/src/security/scanner/typosquat.test.d.ts.map +1 -0
- package/dist/src/security/scanner/typosquat.test.js +140 -0
- package/dist/src/security/scanner/typosquat.test.js.map +1 -0
- package/dist/src/security/scanner/weights.d.ts.map +1 -1
- package/dist/src/security/scanner/weights.js +10 -0
- package/dist/src/security/scanner/weights.js.map +1 -1
- package/dist/src/services/skill-installation.helpers.d.ts +21 -0
- package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
- package/dist/src/services/skill-installation.helpers.js +46 -2
- package/dist/src/services/skill-installation.helpers.js.map +1 -1
- package/dist/src/services/skill-installation.helpers.test.d.ts +2 -0
- package/dist/src/services/skill-installation.helpers.test.d.ts.map +1 -0
- package/dist/src/services/skill-installation.helpers.test.js +80 -0
- package/dist/src/services/skill-installation.helpers.test.js.map +1 -0
- package/dist/src/sync/inventory-collector.d.ts +15 -4
- package/dist/src/sync/inventory-collector.d.ts.map +1 -1
- package/dist/src/sync/inventory-collector.js +73 -24
- package/dist/src/sync/inventory-collector.js.map +1 -1
- package/dist/src/sync/inventory-collector.test.d.ts +15 -1
- package/dist/src/sync/inventory-collector.test.d.ts.map +1 -1
- package/dist/src/sync/inventory-collector.test.js +75 -5
- package/dist/src/sync/inventory-collector.test.js.map +1 -1
- package/dist/src/telemetry/tracer-imports.d.ts +1 -1
- package/dist/src/telemetry/tracer-imports.js +1 -1
- package/dist/src/telemetry/tracer.js +1 -1
- package/dist/tests/SecurityScanner.scoring.test.js +60 -0
- package/dist/tests/SecurityScanner.scoring.test.js.map +1 -1
- package/dist/tests/SwarmIndexer.test.js +4 -5
- package/dist/tests/SwarmIndexer.test.js.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.js +1 -0
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/services/aidefence-feedback.test.js +1 -0
- package/dist/tests/services/aidefence-feedback.test.js.map +1 -1
- package/dist/tests/skill-scanner/allowlist.test.js +8 -1
- package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
- package/package.json +13 -8
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confusable / homoglyph normalization primitives
|
|
3
|
+
* @module @skillsmith/core/security/scanner/confusables
|
|
4
|
+
*
|
|
5
|
+
* SMI-595: extracted from `SecurityScanner.exec.ts` (originally built there
|
|
6
|
+
* under SMI-5359 Wave 4.2 for *content* obfuscation detection) so the
|
|
7
|
+
* name-agnostic folding primitives can be reused for *skill-name* typosquat
|
|
8
|
+
* comparison (`typosquat.ts`) without coupling it to the code-execution /
|
|
9
|
+
* obfuscated-directive detectors. Pure extraction — no behavior change:
|
|
10
|
+
* `SecurityScanner.exec.ts` re-imports these symbols and its own test suite
|
|
11
|
+
* (`SecurityScanner.exec.test.ts`) passes unchanged.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Conservative homoglyph map (a curated subset of UTS-#39 confusables): only the
|
|
15
|
+
* unambiguous Cyrillic / Greek look-alikes that real homoglyph attacks use to
|
|
16
|
+
* disguise Latin letters. Fullwidth Latin (offset 0xFEE0) and the Mathematical
|
|
17
|
+
* Alphanumeric Symbols block (U+1D400-1D7FF, e.g. bold/italic/script 𝐢𝐠𝐧𝐨𝐫𝐞)
|
|
18
|
+
* are handled programmatically below — never via a blanket NFKC pass, which would
|
|
19
|
+
* also fold fullwidth CJK to ASCII and false-positive. (NFKC is applied per-char
|
|
20
|
+
* ONLY to the math-alphanumeric range, which contains no CJK.)
|
|
21
|
+
*/
|
|
22
|
+
export const CONFUSABLES = {
|
|
23
|
+
// Cyrillic -> Latin
|
|
24
|
+
а: 'a',
|
|
25
|
+
е: 'e',
|
|
26
|
+
о: 'o',
|
|
27
|
+
р: 'p',
|
|
28
|
+
с: 'c',
|
|
29
|
+
у: 'y',
|
|
30
|
+
х: 'x',
|
|
31
|
+
і: 'i',
|
|
32
|
+
ј: 'j',
|
|
33
|
+
ѕ: 's',
|
|
34
|
+
ԁ: 'd',
|
|
35
|
+
һ: 'h',
|
|
36
|
+
к: 'k',
|
|
37
|
+
м: 'm',
|
|
38
|
+
т: 't',
|
|
39
|
+
в: 'b',
|
|
40
|
+
н: 'h',
|
|
41
|
+
// Greek -> Latin
|
|
42
|
+
ο: 'o',
|
|
43
|
+
α: 'a',
|
|
44
|
+
ρ: 'p',
|
|
45
|
+
ε: 'e',
|
|
46
|
+
τ: 't',
|
|
47
|
+
ι: 'i',
|
|
48
|
+
κ: 'k',
|
|
49
|
+
υ: 'u',
|
|
50
|
+
χ: 'x',
|
|
51
|
+
ν: 'v',
|
|
52
|
+
ϲ: 'c',
|
|
53
|
+
β: 'b',
|
|
54
|
+
};
|
|
55
|
+
export function isFullwidthLatin(cp) {
|
|
56
|
+
return (cp >= 0xff21 && cp <= 0xff3a) || (cp >= 0xff41 && cp <= 0xff5a);
|
|
57
|
+
}
|
|
58
|
+
/** Mathematical Alphanumeric Symbols (bold/italic/script/fraktur/double-struck/sans/mono). */
|
|
59
|
+
export function isMathAlphanumeric(cp) {
|
|
60
|
+
return cp >= 0x1d400 && cp <= 0x1d7ff;
|
|
61
|
+
}
|
|
62
|
+
/** Map homoglyphs + fullwidth Latin + math-alphanumeric to their ASCII skeleton. */
|
|
63
|
+
export function confusableSkeleton(s) {
|
|
64
|
+
let out = '';
|
|
65
|
+
for (const ch of s) {
|
|
66
|
+
const cp = ch.codePointAt(0) ?? 0;
|
|
67
|
+
if (isFullwidthLatin(cp)) {
|
|
68
|
+
out += String.fromCodePoint(cp - 0xfee0);
|
|
69
|
+
}
|
|
70
|
+
else if (isMathAlphanumeric(cp)) {
|
|
71
|
+
// NFKC folds a math-styled glyph to its base; chain through CONFUSABLES so a
|
|
72
|
+
// math-styled Greek/Cyrillic homoglyph (folds to Greek/Cyrillic) still maps
|
|
73
|
+
// to Latin (SMI-5359 retro NIT). Safe: the range contains no CJK; a reserved
|
|
74
|
+
// hole stays unchanged (won't match).
|
|
75
|
+
const folded = ch.normalize('NFKC');
|
|
76
|
+
out += CONFUSABLES[folded] ?? folded;
|
|
77
|
+
}
|
|
78
|
+
else if (CONFUSABLES[ch]) {
|
|
79
|
+
out += CONFUSABLES[ch];
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
out += ch;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=confusables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confusables.js","sourceRoot":"","sources":["../../../../src/security/scanner/confusables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,WAAW,GAA2B;IACjD,oBAAoB;IACpB,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,iBAAiB;IACjB,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;IACN,CAAC,EAAE,GAAG;CACP,CAAA;AAED,MAAM,UAAU,gBAAgB,CAAC,EAAU;IACzC,OAAO,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,MAAM,IAAI,EAAE,IAAI,MAAM,CAAC,CAAA;AACzE,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,kBAAkB,CAAC,EAAU;IAC3C,OAAO,EAAE,IAAI,OAAO,IAAI,EAAE,IAAI,OAAO,CAAA;AACvC,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,kBAAkB,CAAC,CAAS;IAC1C,IAAI,GAAG,GAAG,EAAE,CAAA;IACZ,KAAK,MAAM,EAAE,IAAI,CAAC,EAAE,CAAC;QACnB,MAAM,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACjC,IAAI,gBAAgB,CAAC,EAAE,CAAC,EAAE,CAAC;YACzB,GAAG,IAAI,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,MAAM,CAAC,CAAA;QAC1C,CAAC;aAAM,IAAI,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAAC;YAClC,6EAA6E;YAC7E,4EAA4E;YAC5E,6EAA6E;YAC7E,sCAAsC;YACtC,MAAM,MAAM,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;YACnC,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,IAAI,MAAM,CAAA;QACtC,CAAC;aAAM,IAAI,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;YAC3B,GAAG,IAAI,WAAW,CAAC,EAAE,CAAC,CAAA;QACxB,CAAC;aAAM,CAAC;YACN,GAAG,IAAI,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAA;AACZ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-595: confusables.ts — extraction sanity tests.
|
|
3
|
+
*
|
|
4
|
+
* The real regression guard for "this extraction changed no behavior" is
|
|
5
|
+
* `SecurityScanner.exec.test.ts` passing unchanged (it exercises these
|
|
6
|
+
* primitives indirectly through `scanObfuscatedDirective`). These tests cover
|
|
7
|
+
* the extracted module directly, in isolation from content-scanning.
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=confusables.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confusables.test.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/confusables.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-595: confusables.ts — extraction sanity tests.
|
|
3
|
+
*
|
|
4
|
+
* The real regression guard for "this extraction changed no behavior" is
|
|
5
|
+
* `SecurityScanner.exec.test.ts` passing unchanged (it exercises these
|
|
6
|
+
* primitives indirectly through `scanObfuscatedDirective`). These tests cover
|
|
7
|
+
* the extracted module directly, in isolation from content-scanning.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect } from 'vitest';
|
|
10
|
+
import { CONFUSABLES, isFullwidthLatin, isMathAlphanumeric, confusableSkeleton, } from './confusables.js';
|
|
11
|
+
describe('confusables — extraction sanity (SMI-595)', () => {
|
|
12
|
+
it('maps known Cyrillic homoglyphs to their Latin look-alike', () => {
|
|
13
|
+
expect(CONFUSABLES['а']).toBe('a'); // Cyrillic а (U+0430)
|
|
14
|
+
expect(CONFUSABLES['е']).toBe('e'); // Cyrillic е (U+0435)
|
|
15
|
+
expect(CONFUSABLES['о']).toBe('o'); // Cyrillic о (U+043E)
|
|
16
|
+
});
|
|
17
|
+
it('maps known Greek homoglyphs to their Latin look-alike', () => {
|
|
18
|
+
expect(CONFUSABLES['ο']).toBe('o');
|
|
19
|
+
expect(CONFUSABLES['α']).toBe('a');
|
|
20
|
+
});
|
|
21
|
+
it('identifies fullwidth Latin code points', () => {
|
|
22
|
+
expect(isFullwidthLatin('a'.codePointAt(0))).toBe(true); // fullwidth 'a'
|
|
23
|
+
expect(isFullwidthLatin('a'.codePointAt(0))).toBe(false); // ordinary ascii 'a'
|
|
24
|
+
});
|
|
25
|
+
it('identifies Mathematical Alphanumeric Symbols code points', () => {
|
|
26
|
+
expect(isMathAlphanumeric(0x1d400)).toBe(true); // start of the block
|
|
27
|
+
expect(isMathAlphanumeric(0x1d7ff)).toBe(true); // end of the block
|
|
28
|
+
expect(isMathAlphanumeric(0x0061)).toBe(false); // ascii 'a'
|
|
29
|
+
});
|
|
30
|
+
it('confusableSkeleton folds Cyrillic homoglyphs to ASCII', () => {
|
|
31
|
+
expect(confusableSkeleton('аnthropic')).toBe('anthropic'); // Cyrillic а
|
|
32
|
+
});
|
|
33
|
+
it('confusableSkeleton folds fullwidth Latin to ASCII by offset', () => {
|
|
34
|
+
expect(confusableSkeleton('hello')).toBe('hello');
|
|
35
|
+
});
|
|
36
|
+
it('confusableSkeleton leaves plain ASCII untouched', () => {
|
|
37
|
+
expect(confusableSkeleton('anthropic-community-tools')).toBe('anthropic-community-tools');
|
|
38
|
+
});
|
|
39
|
+
it('confusableSkeleton leaves benign CJK untouched (no blanket NFKC)', () => {
|
|
40
|
+
const cjk = '全角文字のテスト';
|
|
41
|
+
expect(confusableSkeleton(cjk)).toBe(cjk);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=confusables.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"confusables.test.js","sourceRoot":"","sources":["../../../../src/security/scanner/confusables.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,QAAQ,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACzD,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAC,sBAAsB;QACzD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAC,sBAAsB;QACzD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA,CAAC,sBAAsB;IAC3D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAClC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACpC,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAC,gBAAgB;QACzE,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA,CAAC,qBAAqB;IACjF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAC,qBAAqB;QACpE,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAC,mBAAmB;QAClE,MAAM,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA,CAAC,YAAY;IAC7D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA,CAAC,aAAa;IACzE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACnD,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,kBAAkB,CAAC,2BAA2B,CAAC,CAAC,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAA;IAC3F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,GAAG,GAAG,UAAU,CAAA;QACtB,MAAM,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC3C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
|
|
@@ -3,10 +3,16 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Re-exports for security scanning functionality.
|
|
5
5
|
*/
|
|
6
|
-
export type { SecurityFindingType, SecuritySeverity, SecurityFinding, RiskScoreBreakdown, ScanReport, ScannerOptions, HostileUpdateVerdict, } from './types.js';
|
|
6
|
+
export type { SecurityFindingType, SecuritySeverity, SecurityFinding, RiskScoreBreakdown, ScanReport, ScannerOptions, HostileUpdateVerdict, TyposquatEnforcementMode, } from './types.js';
|
|
7
7
|
export { DEFAULT_ALLOWED_DOMAINS, SENSITIVE_PATH_PATTERNS, JAILBREAK_PATTERNS, SUSPICIOUS_PATTERNS, SOCIAL_ENGINEERING_PATTERNS, PROMPT_LEAKING_PATTERNS, DATA_EXFILTRATION_PATTERNS, PRIVILEGE_ESCALATION_PATTERNS, AI_DEFENCE_PATTERNS, SSRF_INSTRUCTION_PATTERNS, PII_PATTERNS, CODE_EXECUTION_PATTERNS, } from './patterns.js';
|
|
8
8
|
export { SEVERITY_WEIGHTS, CATEGORY_WEIGHTS } from './weights.js';
|
|
9
9
|
export { MAX_LINE_LENGTH_FOR_REGEX, safeRegexTest, safeRegexCheck } from './regex-utils.js';
|
|
10
|
+
export { stripInvisible } from './SecurityScanner.exec.js';
|
|
11
|
+
export { CONFUSABLES, isFullwidthLatin, isMathAlphanumeric, confusableSkeleton, } from './confusables.js';
|
|
12
|
+
export { BRAND_ALIASES, levenshteinDistance, scanTyposquat, detectTyposquat, applyTyposquatEnforcementMode, resolveTyposquatEnforcementMode, DEFAULT_TYPOSQUAT_ENFORCEMENT_MODE, } from './typosquat.js';
|
|
13
|
+
export { buildTyposquatReferenceList, DEFAULT_TOP_INSTALLED_LIMIT, } from './typosquat-reference-list.js';
|
|
14
|
+
export type { ReferenceSkillEntry, InstalledSkillEntry, BuildTyposquatReferenceListOptions, } from './typosquat-reference-list.js';
|
|
15
|
+
export { describeSignals } from './SecurityScanner.formatters.js';
|
|
10
16
|
export { SecurityScanner, default } from './SecurityScanner.js';
|
|
11
17
|
export { compareScanReports, DEFAULT_RISK_THRESHOLD } from './SecurityScanner.hostile-update.js';
|
|
12
18
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,oBAAoB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,eAAe,EACf,kBAAkB,EAClB,UAAU,EACV,cAAc,EACd,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,YAAY,CAAA;AAGnB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,0BAA0B,EAC1B,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,uBAAuB,GACxB,MAAM,eAAe,CAAA;AAGtB,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAGjE,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAK3F,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAI1D,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAGzB,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,6BAA6B,EAC7B,+BAA+B,EAC/B,kCAAkC,GACnC,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,+BAA+B,CAAA;AACtC,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,kCAAkC,GACnC,MAAM,+BAA+B,CAAA;AAGtC,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAGjE,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAG/D,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA"}
|
|
@@ -9,6 +9,19 @@ export { DEFAULT_ALLOWED_DOMAINS, SENSITIVE_PATH_PATTERNS, JAILBREAK_PATTERNS, S
|
|
|
9
9
|
export { SEVERITY_WEIGHTS, CATEGORY_WEIGHTS } from './weights.js';
|
|
10
10
|
// Regex utilities (for testing/extending)
|
|
11
11
|
export { MAX_LINE_LENGTH_FOR_REGEX, safeRegexTest, safeRegexCheck } from './regex-utils.js';
|
|
12
|
+
// Obfuscation-defeat primitive (SMI-4703: reused by
|
|
13
|
+
// packages/doc-retrieval-mcp/src/security/memory-injection-scanner.ts's
|
|
14
|
+
// normalization pipeline rather than reimplemented there)
|
|
15
|
+
export { stripInvisible } from './SecurityScanner.exec.js';
|
|
16
|
+
// Confusable/homoglyph normalization primitives (SMI-595 extraction, for testing/extending;
|
|
17
|
+
// also reused by SMI-4703's memory-injection-scanner.ts confusable-fold step)
|
|
18
|
+
export { CONFUSABLES, isFullwidthLatin, isMathAlphanumeric, confusableSkeleton, } from './confusables.js';
|
|
19
|
+
// Typosquat detector (SMI-595)
|
|
20
|
+
export { BRAND_ALIASES, levenshteinDistance, scanTyposquat, detectTyposquat, applyTyposquatEnforcementMode, resolveTyposquatEnforcementMode, DEFAULT_TYPOSQUAT_ENFORCEMENT_MODE, } from './typosquat.js';
|
|
21
|
+
// Typosquat reference-name list builder (SMI-595)
|
|
22
|
+
export { buildTyposquatReferenceList, DEFAULT_TOP_INSTALLED_LIMIT, } from './typosquat-reference-list.js';
|
|
23
|
+
// describeSignals (SMI-595, for testing/extending)
|
|
24
|
+
export { describeSignals } from './SecurityScanner.formatters.js';
|
|
12
25
|
// Main class
|
|
13
26
|
export { SecurityScanner, default } from './SecurityScanner.js';
|
|
14
27
|
// Hostile-update comparator (SMI-5535, R0 Wave 2A)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/security/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/security/scanner/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,mCAAmC;AACnC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,EACnB,2BAA2B,EAC3B,uBAAuB,EACvB,0BAA0B,EAC1B,6BAA6B,EAC7B,mBAAmB,EACnB,yBAAyB,EACzB,YAAY,EACZ,uBAAuB,GACxB,MAAM,eAAe,CAAA;AAEtB,kCAAkC;AAClC,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAEjE,0CAA0C;AAC1C,OAAO,EAAE,yBAAyB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAE3F,oDAAoD;AACpD,wEAAwE;AACxE,0DAA0D;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAE1D,4FAA4F;AAC5F,8EAA8E;AAC9E,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA;AAEzB,+BAA+B;AAC/B,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,6BAA6B,EAC7B,+BAA+B,EAC/B,kCAAkC,GACnC,MAAM,gBAAgB,CAAA;AAEvB,kDAAkD;AAClD,OAAO,EACL,2BAA2B,EAC3B,2BAA2B,GAC5B,MAAM,+BAA+B,CAAA;AAOtC,mDAAmD;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAA;AAEjE,aAAa;AACb,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAE/D,mDAAmD;AACnD,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAA"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* Types of security findings that can be detected
|
|
8
8
|
*/
|
|
9
|
-
export type SecurityFindingType = 'url' | 'sensitive_path' | 'jailbreak' | 'suspicious_pattern' | 'social_engineering' | 'prompt_leaking' | 'data_exfiltration' | 'privilege_escalation' | 'ai_defence' | 'ssrf' | 'pii' | 'code_execution' | 'obfuscated_directive';
|
|
9
|
+
export type SecurityFindingType = 'url' | 'sensitive_path' | 'jailbreak' | 'suspicious_pattern' | 'social_engineering' | 'prompt_leaking' | 'data_exfiltration' | 'privilege_escalation' | 'ai_defence' | 'ssrf' | 'pii' | 'code_execution' | 'obfuscated_directive' | 'typosquat';
|
|
10
10
|
/**
|
|
11
11
|
* Severity levels for security findings
|
|
12
12
|
*/
|
|
@@ -53,6 +53,7 @@ export interface RiskScoreBreakdown {
|
|
|
53
53
|
pii: number;
|
|
54
54
|
codeExecution: number;
|
|
55
55
|
obfuscatedDirective: number;
|
|
56
|
+
typosquat: number;
|
|
56
57
|
}
|
|
57
58
|
/**
|
|
58
59
|
* Comprehensive scan report with risk scoring
|
|
@@ -89,6 +90,16 @@ export interface HostileUpdateVerdict {
|
|
|
89
90
|
/** One concrete human-readable sentence citing the deciding signal. */
|
|
90
91
|
reason: string;
|
|
91
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* SMI-595: Rollout mode for the typosquat detector.
|
|
95
|
+
* - `off` — detector output is discarded entirely (no findings surface).
|
|
96
|
+
* - `warn` — shadow mode (default): findings surface but are capped at
|
|
97
|
+
* `medium` severity regardless of the raw detector's confidence, so a
|
|
98
|
+
* false positive on this early-stage heuristic can't alone quarantine
|
|
99
|
+
* an install.
|
|
100
|
+
* - `block` — findings surface at their raw (uncapped) severity.
|
|
101
|
+
*/
|
|
102
|
+
export type TyposquatEnforcementMode = 'off' | 'warn' | 'block';
|
|
92
103
|
/**
|
|
93
104
|
* Configuration options for the security scanner
|
|
94
105
|
*/
|
|
@@ -98,5 +109,7 @@ export interface ScannerOptions {
|
|
|
98
109
|
maxContentLength?: number;
|
|
99
110
|
/** Risk score threshold for failing a scan (default: 40) */
|
|
100
111
|
riskThreshold?: number;
|
|
112
|
+
/** SMI-595: typosquat detector enforcement mode (default: 'warn'). */
|
|
113
|
+
typosquatEnforcementMode?: TyposquatEnforcementMode;
|
|
101
114
|
}
|
|
102
115
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B,KAAK,GACL,gBAAgB,GAChB,WAAW,GACX,oBAAoB,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,mBAAmB,GACnB,sBAAsB,GACtB,YAAY,GACZ,MAAM,GACN,KAAK,GACL,gBAAgB,GAChB,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B,KAAK,GACL,gBAAgB,GAChB,WAAW,GACX,oBAAoB,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,mBAAmB,GACnB,sBAAsB,GACtB,YAAY,GACZ,MAAM,GACN,KAAK,GACL,gBAAgB,GAChB,sBAAsB,GACtB,WAAW,CAAA;AAEf;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAA;AAErE;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAA;AAEzD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,mBAAmB,CAAA;IACzB,QAAQ,EAAE,gBAAgB,CAAA;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iFAAiF;IACjF,sBAAsB,CAAC,EAAE,OAAO,CAAA;IAChC,qEAAqE;IACrE,UAAU,CAAC,EAAE,iBAAiB,CAAA;IAC9B,0IAA0I;IAC1I,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,cAAc,EAAE,MAAM,CAAA;IACtB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,SAAS,EAAE,IAAI,CAAA;IACf,cAAc,EAAE,MAAM,CAAA;IACtB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAA;IACjB,0CAA0C;IAC1C,aAAa,EAAE,kBAAkB,CAAA;CAClC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,QAAQ,GAAG,YAAY,GAAG,SAAS,CAAA;IAC5C,8EAA8E;IAC9E,WAAW,EAAE,eAAe,EAAE,CAAA;IAC9B,qEAAqE;IACrE,SAAS,EAAE,MAAM,CAAA;IACjB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAA;CACf;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,wBAAwB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAA;AAE/D;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,sEAAsE;IACtE,wBAAwB,CAAC,EAAE,wBAAwB,CAAA;CACpD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typosquat reference-name list builder
|
|
3
|
+
* @module @skillsmith/core/security/scanner/typosquat-reference-list
|
|
4
|
+
*
|
|
5
|
+
* SMI-595 §2: a name-comparison detector is only as good as its reference
|
|
6
|
+
* list; a hand-maintained list goes stale the moment a new popular skill
|
|
7
|
+
* ships. The reference list is built from two sources:
|
|
8
|
+
*
|
|
9
|
+
* 1. Every skill published by an owner in `HIGH_TRUST_OWNERS`
|
|
10
|
+
* (`packages/core/src/scripts/github-import/signal-of-intent.ts`).
|
|
11
|
+
* 2. The top-N installed skills by install count (N configurable, default
|
|
12
|
+
* 200) — covers popular community skills that aren't from a
|
|
13
|
+
* `HIGH_TRUST_OWNERS` publisher but are popular enough to be worth
|
|
14
|
+
* impersonating.
|
|
15
|
+
*
|
|
16
|
+
* Plus `BRAND_ALIASES` (`typosquat.ts` §3) — brand names not derivable from
|
|
17
|
+
* either source above.
|
|
18
|
+
*
|
|
19
|
+
* This module is a pure builder: it accepts the two source arrays already
|
|
20
|
+
* fetched by the caller (rather than querying a database or the GitHub API
|
|
21
|
+
* itself), so it stays cheaply unit-testable. Wiring a *live* caller that
|
|
22
|
+
* sources real `HIGH_TRUST_OWNERS`-published skill names and real
|
|
23
|
+
* install-count data, refreshed on the existing metadata-refresh cadence
|
|
24
|
+
* (`.claude/development/deployment-guide.md`'s "Metadata Refresh (every 4h
|
|
25
|
+
* :30)" job — no new cron), is a follow-up integration point, not built out
|
|
26
|
+
* this wave.
|
|
27
|
+
*/
|
|
28
|
+
/** A minimal reference-skill shape: only the fields this builder needs. */
|
|
29
|
+
export interface ReferenceSkillEntry {
|
|
30
|
+
author: string;
|
|
31
|
+
name: string;
|
|
32
|
+
}
|
|
33
|
+
/** An installed skill together with its install count, for the top-N filter. */
|
|
34
|
+
export interface InstalledSkillEntry extends ReferenceSkillEntry {
|
|
35
|
+
installCount: number;
|
|
36
|
+
}
|
|
37
|
+
/** SMI-595 §2: default top-N cutoff for installed skills folded into the list. */
|
|
38
|
+
export declare const DEFAULT_TOP_INSTALLED_LIMIT = 200;
|
|
39
|
+
export interface BuildTyposquatReferenceListOptions {
|
|
40
|
+
/** Skills published by an owner in `HIGH_TRUST_OWNERS`. */
|
|
41
|
+
highTrustOwnerSkills?: ReferenceSkillEntry[];
|
|
42
|
+
/** All installed skills with a known install count; only the top N (by
|
|
43
|
+
* count, descending) are folded in. */
|
|
44
|
+
installedSkills?: InstalledSkillEntry[];
|
|
45
|
+
/** Max number of installed skills to fold in. Default: `DEFAULT_TOP_INSTALLED_LIMIT` (200). */
|
|
46
|
+
topInstalledLimit?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Build the lowercase reference-name set used by `scanTyposquat()`. Combines
|
|
50
|
+
* HIGH_TRUST_OWNERS-published skill names, the top-N installed skills by
|
|
51
|
+
* install count, and the bare brand tokens from `BRAND_ALIASES`.
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildTyposquatReferenceList(options?: BuildTyposquatReferenceListOptions): ReadonlySet<string>;
|
|
54
|
+
//# sourceMappingURL=typosquat-reference-list.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typosquat-reference-list.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat-reference-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,2EAA2E;AAC3E,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;CACb;AAED,gFAAgF;AAChF,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB;IAC9D,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,kFAAkF;AAClF,eAAO,MAAM,2BAA2B,MAAM,CAAA;AAE9C,MAAM,WAAW,kCAAkC;IACjD,2DAA2D;IAC3D,oBAAoB,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAC5C;4CACwC;IACxC,eAAe,CAAC,EAAE,mBAAmB,EAAE,CAAA;IACvC,+FAA+F;IAC/F,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,GAAE,kCAAuC,GAC/C,WAAW,CAAC,MAAM,CAAC,CAyBrB"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typosquat reference-name list builder
|
|
3
|
+
* @module @skillsmith/core/security/scanner/typosquat-reference-list
|
|
4
|
+
*
|
|
5
|
+
* SMI-595 §2: a name-comparison detector is only as good as its reference
|
|
6
|
+
* list; a hand-maintained list goes stale the moment a new popular skill
|
|
7
|
+
* ships. The reference list is built from two sources:
|
|
8
|
+
*
|
|
9
|
+
* 1. Every skill published by an owner in `HIGH_TRUST_OWNERS`
|
|
10
|
+
* (`packages/core/src/scripts/github-import/signal-of-intent.ts`).
|
|
11
|
+
* 2. The top-N installed skills by install count (N configurable, default
|
|
12
|
+
* 200) — covers popular community skills that aren't from a
|
|
13
|
+
* `HIGH_TRUST_OWNERS` publisher but are popular enough to be worth
|
|
14
|
+
* impersonating.
|
|
15
|
+
*
|
|
16
|
+
* Plus `BRAND_ALIASES` (`typosquat.ts` §3) — brand names not derivable from
|
|
17
|
+
* either source above.
|
|
18
|
+
*
|
|
19
|
+
* This module is a pure builder: it accepts the two source arrays already
|
|
20
|
+
* fetched by the caller (rather than querying a database or the GitHub API
|
|
21
|
+
* itself), so it stays cheaply unit-testable. Wiring a *live* caller that
|
|
22
|
+
* sources real `HIGH_TRUST_OWNERS`-published skill names and real
|
|
23
|
+
* install-count data, refreshed on the existing metadata-refresh cadence
|
|
24
|
+
* (`.claude/development/deployment-guide.md`'s "Metadata Refresh (every 4h
|
|
25
|
+
* :30)" job — no new cron), is a follow-up integration point, not built out
|
|
26
|
+
* this wave.
|
|
27
|
+
*/
|
|
28
|
+
import { BRAND_ALIASES } from './typosquat.js';
|
|
29
|
+
/** SMI-595 §2: default top-N cutoff for installed skills folded into the list. */
|
|
30
|
+
export const DEFAULT_TOP_INSTALLED_LIMIT = 200;
|
|
31
|
+
/**
|
|
32
|
+
* Build the lowercase reference-name set used by `scanTyposquat()`. Combines
|
|
33
|
+
* HIGH_TRUST_OWNERS-published skill names, the top-N installed skills by
|
|
34
|
+
* install count, and the bare brand tokens from `BRAND_ALIASES`.
|
|
35
|
+
*/
|
|
36
|
+
export function buildTyposquatReferenceList(options = {}) {
|
|
37
|
+
const { highTrustOwnerSkills = [], installedSkills = [], topInstalledLimit = DEFAULT_TOP_INSTALLED_LIMIT, } = options;
|
|
38
|
+
const names = new Set();
|
|
39
|
+
for (const entry of highTrustOwnerSkills) {
|
|
40
|
+
if (entry.name)
|
|
41
|
+
names.add(entry.name.toLowerCase());
|
|
42
|
+
}
|
|
43
|
+
const topInstalled = [...installedSkills]
|
|
44
|
+
.sort((a, b) => b.installCount - a.installCount)
|
|
45
|
+
.slice(0, Math.max(0, topInstalledLimit));
|
|
46
|
+
for (const entry of topInstalled) {
|
|
47
|
+
if (entry.name)
|
|
48
|
+
names.add(entry.name.toLowerCase());
|
|
49
|
+
}
|
|
50
|
+
for (const brand of Object.keys(BRAND_ALIASES)) {
|
|
51
|
+
names.add(brand.toLowerCase());
|
|
52
|
+
}
|
|
53
|
+
return names;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=typosquat-reference-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typosquat-reference-list.js","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat-reference-list.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAa9C,kFAAkF;AAClF,MAAM,CAAC,MAAM,2BAA2B,GAAG,GAAG,CAAA;AAY9C;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CACzC,UAA8C,EAAE;IAEhD,MAAM,EACJ,oBAAoB,GAAG,EAAE,EACzB,eAAe,GAAG,EAAE,EACpB,iBAAiB,GAAG,2BAA2B,GAChD,GAAG,OAAO,CAAA;IAEX,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAA;IAE/B,KAAK,MAAM,KAAK,IAAI,oBAAoB,EAAE,CAAC;QACzC,IAAI,KAAK,CAAC,IAAI;YAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;IACrD,CAAC;IAED,MAAM,YAAY,GAAG,CAAC,GAAG,eAAe,CAAC;SACtC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,YAAY,CAAC;SAC/C,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAA;IAC3C,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,IAAI;YAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;IACrD,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;QAC/C,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;IAChC,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typosquat-reference-list.test.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat-reference-list.test.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-595 §2: typosquat-reference-list.ts — reference-list builder tests.
|
|
3
|
+
*/
|
|
4
|
+
import { describe, it, expect } from 'vitest';
|
|
5
|
+
import { buildTyposquatReferenceList, DEFAULT_TOP_INSTALLED_LIMIT, } from './typosquat-reference-list.js';
|
|
6
|
+
import { BRAND_ALIASES } from './typosquat.js';
|
|
7
|
+
describe('buildTyposquatReferenceList (SMI-595 §2)', () => {
|
|
8
|
+
it('folds in HIGH_TRUST_OWNERS-published skill names, lowercased', () => {
|
|
9
|
+
const list = buildTyposquatReferenceList({
|
|
10
|
+
highTrustOwnerSkills: [{ author: 'anthropics', name: 'Claude-Helper' }],
|
|
11
|
+
});
|
|
12
|
+
expect(list.has('claude-helper')).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
it('folds in the top-N installed skills by install count', () => {
|
|
15
|
+
const list = buildTyposquatReferenceList({
|
|
16
|
+
installedSkills: [
|
|
17
|
+
{ author: 'a', name: 'popular-skill', installCount: 1000 },
|
|
18
|
+
{ author: 'b', name: 'niche-skill', installCount: 1 },
|
|
19
|
+
],
|
|
20
|
+
topInstalledLimit: 1,
|
|
21
|
+
});
|
|
22
|
+
expect(list.has('popular-skill')).toBe(true);
|
|
23
|
+
expect(list.has('niche-skill')).toBe(false); // cut off by the limit
|
|
24
|
+
});
|
|
25
|
+
it('respects the default top-N limit of 200', () => {
|
|
26
|
+
expect(DEFAULT_TOP_INSTALLED_LIMIT).toBe(200);
|
|
27
|
+
const installedSkills = Array.from({ length: 250 }, (_, i) => ({
|
|
28
|
+
author: 'a',
|
|
29
|
+
name: `skill-${i}`,
|
|
30
|
+
installCount: 250 - i, // skill-0 has the highest count
|
|
31
|
+
}));
|
|
32
|
+
const list = buildTyposquatReferenceList({ installedSkills });
|
|
33
|
+
expect(list.has('skill-0')).toBe(true); // rank 1, well within top 200
|
|
34
|
+
expect(list.has('skill-249')).toBe(false); // rank 250, cut off
|
|
35
|
+
});
|
|
36
|
+
it('always includes the BRAND_ALIASES keys, even with no other sources', () => {
|
|
37
|
+
const list = buildTyposquatReferenceList();
|
|
38
|
+
for (const brand of Object.keys(BRAND_ALIASES)) {
|
|
39
|
+
expect(list.has(brand)).toBe(true);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
it('deduplicates a name that appears in both sources', () => {
|
|
43
|
+
const list = buildTyposquatReferenceList({
|
|
44
|
+
highTrustOwnerSkills: [{ author: 'anthropics', name: 'shared-skill' }],
|
|
45
|
+
installedSkills: [{ author: 'anthropics', name: 'shared-skill', installCount: 5 }],
|
|
46
|
+
});
|
|
47
|
+
// A Set naturally dedupes; just confirm the entry is present exactly once
|
|
48
|
+
// (Set semantics already guarantee this, this is a readability check).
|
|
49
|
+
expect([...list].filter((n) => n === 'shared-skill')).toHaveLength(1);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
//# sourceMappingURL=typosquat-reference-list.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typosquat-reference-list.test.js","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat-reference-list.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typosquat / impersonation detector for skill names
|
|
3
|
+
* @module @skillsmith/core/security/scanner/typosquat
|
|
4
|
+
*
|
|
5
|
+
* SMI-595: no Levenshtein-distance / character-substitution / homoglyph
|
|
6
|
+
* name-comparison detector against a popular-skill-name list existed
|
|
7
|
+
* anywhere in this codebase prior to this module — content-based scanning
|
|
8
|
+
* (`SecurityScanner`) cannot catch a name-only impersonation attempt where
|
|
9
|
+
* the skill's actual content is benign (e.g. `anthopic/claude-helper` or
|
|
10
|
+
* `аnthropic/skill` with a Cyrillic `а`).
|
|
11
|
+
*
|
|
12
|
+
* Three independent checks, deliberately NOT conflated:
|
|
13
|
+
*
|
|
14
|
+
* 1. **Exact-skeleton impersonation** — fires ONLY on exact confusable-folded
|
|
15
|
+
* equality to a reference name, never on substring/affix match. This is a
|
|
16
|
+
* deliberately narrow rule: `anthropic-community-mcp`, `unofficial-claude-
|
|
17
|
+
* tools`, `awesome-gemini-skills` are normal, non-malicious community
|
|
18
|
+
* naming conventions that happen to contain a brand token and must NOT be
|
|
19
|
+
* blocked by this rule.
|
|
20
|
+
* 2. **Levenshtein edit-distance ≤2** — catches near-miss variants
|
|
21
|
+
* (`anthropc`, `anthropic1`, `anthropci`) that don't fold to an exact
|
|
22
|
+
* skeleton match but are still suspiciously close to a reference name.
|
|
23
|
+
* 3. **Authority-claiming affix** — INDEPENDENT of #1: a candidate containing
|
|
24
|
+
* a reference brand token PLUS an affix that claims official status
|
|
25
|
+
* (`-official`, `-verified`, `-authentic`, `-genuine`) is flagged
|
|
26
|
+
* regardless of whether it passes the exact-skeleton check, because the
|
|
27
|
+
* affix itself is the impersonation vector. Benign functional affixes
|
|
28
|
+
* (`-mcp`, `-tools`, `-community`) do NOT trigger this — they simply
|
|
29
|
+
* aren't in the curated affix list.
|
|
30
|
+
*
|
|
31
|
+
* Consumer-surface decision (Wave 1 Step 6): the install-time scanner path
|
|
32
|
+
* (risk-score wiring — see types.ts/weights.ts/SecurityScanner.helpers.ts) is
|
|
33
|
+
* the primary *enforcement* surface (blocks/warns before a skill lands on
|
|
34
|
+
* disk). `skill_audit` (existing MCP tool) is the recommended primary
|
|
35
|
+
* *consumer-facing* surface for on-demand querying of typosquat status on an
|
|
36
|
+
* already-installed skill — live-wiring that tool to this detector is
|
|
37
|
+
* deferred to a filed follow-up (SMI-5711), out of this wave's scope.
|
|
38
|
+
*
|
|
39
|
+
* Integration note: `SecurityScanner.scan()` takes `content` only, not a
|
|
40
|
+
* skill name/author — this wave does NOT change that signature or thread a
|
|
41
|
+
* live reference list through `scripts/skill-scanner/scanner.ts`'s scan
|
|
42
|
+
* pipeline (that would require sourcing real HIGH_TRUST_OWNERS-published
|
|
43
|
+
* skill data and real install-count data, which is a live-data/infra
|
|
44
|
+
* integration, not a detector-design concern). This module is a
|
|
45
|
+
* self-contained, fully-tested unit ready to be wired into that pipeline as a
|
|
46
|
+
* follow-up.
|
|
47
|
+
*/
|
|
48
|
+
import type { SecurityFinding, TyposquatEnforcementMode } from './types.js';
|
|
49
|
+
export type { TyposquatEnforcementMode };
|
|
50
|
+
/**
|
|
51
|
+
* Brand names that are NOT derivable from `HIGH_TRUST_OWNERS`' GitHub owner
|
|
52
|
+
* slugs (`signal-of-intent.ts`) or from the installed-skill corpus at all —
|
|
53
|
+
* the product/brand a typosquat would actually target (`anthropic`, `claude`,
|
|
54
|
+
* `gemini`) differs from the GitHub org slug that publishes it (`anthropics`,
|
|
55
|
+
* `google-gemini`). Folded into the reference list
|
|
56
|
+
* (`typosquat-reference-list.ts`) as bare-brand reference entries, and also
|
|
57
|
+
* used as the curated "brand token" corpus for the authority-claiming-affix
|
|
58
|
+
* check below (§1 rule 3) — a narrower, independent check from the
|
|
59
|
+
* exact-skeleton rule (§1 rule 1).
|
|
60
|
+
*
|
|
61
|
+
* Values cross-checked against `HIGH_TRUST_OWNERS`
|
|
62
|
+
* (`packages/core/src/scripts/github-import/signal-of-intent.ts`) — kept in
|
|
63
|
+
* sync manually, same convention as that file's own documented
|
|
64
|
+
* cross-boundary-import constraint.
|
|
65
|
+
*/
|
|
66
|
+
export declare const BRAND_ALIASES: Readonly<Record<string, string>>;
|
|
67
|
+
/**
|
|
68
|
+
* Standard iterative-DP Levenshtein edit distance (insertion/deletion/
|
|
69
|
+
* substitution). Inputs here are short skill-name strings, so an O(n*m) table
|
|
70
|
+
* is more than fast enough — no early-exit optimization needed.
|
|
71
|
+
*/
|
|
72
|
+
export declare function levenshteinDistance(a: string, b: string): number;
|
|
73
|
+
/**
|
|
74
|
+
* Run all three checks against a single candidate skill name/id. Returns
|
|
75
|
+
* RAW findings (uncapped severity) — callers apply
|
|
76
|
+
* `applyTyposquatEnforcementMode()` before surfacing them, per the
|
|
77
|
+
* `typosquatEnforcementMode` rollout config (§6).
|
|
78
|
+
*
|
|
79
|
+
* `referenceNames` should already be lowercase (as produced by
|
|
80
|
+
* `buildTyposquatReferenceList()` in `typosquat-reference-list.ts`).
|
|
81
|
+
*/
|
|
82
|
+
export declare function scanTyposquat(candidateName: string, referenceNames: ReadonlySet<string>): SecurityFinding[];
|
|
83
|
+
/** SMI-595: default rollout mode — shadow mode, matching the
|
|
84
|
+
* `concurrency-audit-pr.yml` precedent (shadow for a period, then promote). */
|
|
85
|
+
export declare const DEFAULT_TYPOSQUAT_ENFORCEMENT_MODE: TyposquatEnforcementMode;
|
|
86
|
+
/** Resolve the effective enforcement mode, applying the default when unset. */
|
|
87
|
+
export declare function resolveTyposquatEnforcementMode(mode?: TyposquatEnforcementMode): TyposquatEnforcementMode;
|
|
88
|
+
/**
|
|
89
|
+
* Apply the rollout mode to raw typosquat findings:
|
|
90
|
+
* - `off` — discard all findings.
|
|
91
|
+
* - `warn` — cap severity at `medium` regardless of the raw detector's
|
|
92
|
+
* confidence (confidence is left untouched — only severity is capped).
|
|
93
|
+
* - `block` — pass findings through at their raw severity.
|
|
94
|
+
*/
|
|
95
|
+
export declare function applyTyposquatEnforcementMode(findings: SecurityFinding[], mode?: TyposquatEnforcementMode): SecurityFinding[];
|
|
96
|
+
/**
|
|
97
|
+
* Convenience one-shot: detect + apply the enforcement mode in a single call.
|
|
98
|
+
*/
|
|
99
|
+
export declare function detectTyposquat(candidateName: string, referenceNames: ReadonlySet<string>, mode?: TyposquatEnforcementMode): SecurityFinding[];
|
|
100
|
+
//# sourceMappingURL=typosquat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typosquat.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAG3E,YAAY,EAAE,wBAAwB,EAAE,CAAA;AAMxC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO1D,CAAA;AAmDD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBhE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,eAAe,EAAE,CAgEnB;AAMD;gFACgF;AAChF,eAAO,MAAM,kCAAkC,EAAE,wBAAiC,CAAA;AAElF,+EAA+E;AAC/E,wBAAgB,+BAA+B,CAC7C,IAAI,CAAC,EAAE,wBAAwB,GAC9B,wBAAwB,CAE1B;AAID;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,eAAe,EAAE,EAC3B,IAAI,GAAE,wBAA6D,GAClE,eAAe,EAAE,CAQnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,EACnC,IAAI,GAAE,wBAA6D,GAClE,eAAe,EAAE,CAEnB"}
|