@skillsmith/core 0.11.2 → 0.11.4
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 +36 -9
- 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 +4 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +13 -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/logging/logger.test.js +57 -0
- package/dist/src/logging/logger.test.js.map +1 -1
- package/dist/src/logging/rotation.d.ts +27 -2
- package/dist/src/logging/rotation.d.ts.map +1 -1
- package/dist/src/logging/rotation.js +44 -3
- package/dist/src/logging/rotation.js.map +1 -1
- package/dist/src/logging/rotation.test.js +69 -0
- package/dist/src/logging/rotation.test.js.map +1 -1
- package/dist/src/logging/types.d.ts +8 -2
- package/dist/src/logging/types.d.ts.map +1 -1
- package/dist/src/scripts/__tests__/scan-imported-skills.test.js +5 -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/index.d.ts +2 -1
- package/dist/src/security/index.d.ts.map +1 -1
- package/dist/src/security/index.js +3 -0
- package/dist/src/security/index.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.d.ts +2 -2
- package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.evidence.d.ts +76 -0
- package/dist/src/security/scanner/SecurityScanner.evidence.d.ts.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.evidence.js +176 -0
- package/dist/src/security/scanner/SecurityScanner.evidence.js.map +1 -0
- package/dist/src/security/scanner/SecurityScanner.exec.d.ts +15 -1
- package/dist/src/security/scanner/SecurityScanner.exec.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.exec.js +68 -76
- 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 +35 -10
- package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.helpers.js +121 -60
- package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.js +41 -13
- package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.pii.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.pii.js +23 -1
- package/dist/src/security/scanner/SecurityScanner.pii.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.scanners.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.scanners.js +15 -3
- package/dist/src/security/scanner/SecurityScanner.scanners.js.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.ssrf.d.ts +11 -1
- package/dist/src/security/scanner/SecurityScanner.ssrf.d.ts.map +1 -1
- package/dist/src/security/scanner/SecurityScanner.ssrf.js +18 -7
- package/dist/src/security/scanner/SecurityScanner.ssrf.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 +9 -3
- package/dist/src/security/scanner/index.d.ts.map +1 -1
- package/dist/src/security/scanner/index.js +18 -2
- package/dist/src/security/scanner/index.js.map +1 -1
- package/dist/src/security/scanner/patterns.d.ts +41 -14
- package/dist/src/security/scanner/patterns.d.ts.map +1 -1
- package/dist/src/security/scanner/patterns.jailbreak.d.ts +41 -0
- package/dist/src/security/scanner/patterns.jailbreak.d.ts.map +1 -0
- package/dist/src/security/scanner/patterns.jailbreak.evidence.d.ts +35 -0
- package/dist/src/security/scanner/patterns.jailbreak.evidence.d.ts.map +1 -0
- package/dist/src/security/scanner/patterns.jailbreak.evidence.js +119 -0
- package/dist/src/security/scanner/patterns.jailbreak.evidence.js.map +1 -0
- package/dist/src/security/scanner/patterns.jailbreak.js +370 -0
- package/dist/src/security/scanner/patterns.jailbreak.js.map +1 -0
- package/dist/src/security/scanner/patterns.js +101 -82
- package/dist/src/security/scanner/patterns.js.map +1 -1
- package/dist/src/security/scanner/patterns.scope.d.ts +62 -0
- package/dist/src/security/scanner/patterns.scope.d.ts.map +1 -0
- package/dist/src/security/scanner/patterns.scope.js +207 -0
- package/dist/src/security/scanner/patterns.scope.js.map +1 -0
- package/dist/src/security/scanner/regex-utils.d.ts +20 -0
- package/dist/src/security/scanner/regex-utils.d.ts.map +1 -1
- package/dist/src/security/scanner/regex-utils.js +20 -0
- package/dist/src/security/scanner/regex-utils.js.map +1 -1
- package/dist/src/security/scanner/types.d.ts +49 -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/services/skill-installation.io.d.ts.map +1 -1
- package/dist/src/services/skill-installation.io.js +12 -1
- package/dist/src/services/skill-installation.io.js.map +1 -1
- 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 +75 -26
- 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.ai-defence.test.js +32 -7
- package/dist/tests/SecurityScanner.ai-defence.test.js.map +1 -1
- package/dist/tests/SecurityScanner.exec.test.js +79 -0
- package/dist/tests/SecurityScanner.exec.test.js.map +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/ContinuousSecurity.performance.test.js +6 -0
- package/dist/tests/security/ContinuousSecurity.performance.test.js.map +1 -1
- package/dist/tests/security/ContinuousSecurity.test.js +15 -0
- package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
- package/dist/tests/security/ad-crlf-injection.test.d.ts +18 -0
- package/dist/tests/security/ad-crlf-injection.test.d.ts.map +1 -0
- package/dist/tests/security/ad-crlf-injection.test.js +150 -0
- package/dist/tests/security/ad-crlf-injection.test.js.map +1 -0
- package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts +25 -0
- package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts.map +1 -0
- package/dist/tests/security/ai-defence-multiline-promotion.test.js +138 -0
- package/dist/tests/security/ai-defence-multiline-promotion.test.js.map +1 -0
- package/dist/tests/security/nested-instruction-long-attribute.test.d.ts +15 -0
- package/dist/tests/security/nested-instruction-long-attribute.test.d.ts.map +1 -0
- package/dist/tests/security/nested-instruction-long-attribute.test.js +60 -0
- package/dist/tests/security/nested-instruction-long-attribute.test.js.map +1 -0
- package/dist/tests/security/pattern-scope.test.d.ts +17 -0
- package/dist/tests/security/pattern-scope.test.d.ts.map +1 -0
- package/dist/tests/security/pattern-scope.test.js +128 -0
- package/dist/tests/security/pattern-scope.test.js.map +1 -0
- package/dist/tests/security/pii-detection.test.js +39 -0
- package/dist/tests/security/pii-detection.test.js.map +1 -1
- package/dist/tests/security/scanner-evidence-tiers.test.d.ts +30 -0
- package/dist/tests/security/scanner-evidence-tiers.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-evidence-tiers.test.js +258 -0
- package/dist/tests/security/scanner-evidence-tiers.test.js.map +1 -0
- package/dist/tests/security/scanner-privesc-severity.test.d.ts +23 -0
- package/dist/tests/security/scanner-privesc-severity.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-privesc-severity.test.js +43 -0
- package/dist/tests/security/scanner-privesc-severity.test.js.map +1 -0
- package/dist/tests/security/scanner-regression-guard.exec-locality.test.d.ts +20 -0
- package/dist/tests/security/scanner-regression-guard.exec-locality.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-regression-guard.exec-locality.test.js +71 -0
- package/dist/tests/security/scanner-regression-guard.exec-locality.test.js.map +1 -0
- package/dist/tests/security/scanner-regression-guard.test.d.ts +24 -0
- package/dist/tests/security/scanner-regression-guard.test.d.ts.map +1 -1
- package/dist/tests/security/scanner-regression-guard.test.js +60 -3
- package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
- package/dist/tests/security/scanner-ssrf-word-boundary.test.d.ts +19 -0
- package/dist/tests/security/scanner-ssrf-word-boundary.test.d.ts.map +1 -0
- package/dist/tests/security/scanner-ssrf-word-boundary.test.js +65 -0
- package/dist/tests/security/scanner-ssrf-word-boundary.test.js.map +1 -0
- package/dist/tests/security.test.js +11 -2
- package/dist/tests/security.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 +16 -7
- package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
- package/dist/tests/unit/services/skill-installation.io.test.js +54 -1
- package/dist/tests/unit/services/skill-installation.io.test.js.map +1 -1
- package/package.json +13 -8
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Scanner — evidence-tier severity resolution (SMI-5876)
|
|
3
|
+
* @module @skillsmith/core/security/scanner/SecurityScanner.evidence
|
|
4
|
+
*
|
|
5
|
+
* Turns a pattern's `EvidenceType` (classified in
|
|
6
|
+
* `patterns.jailbreak.evidence.ts`'s `EVIDENCE_TYPE_BY_PATTERN`, SMI-5881)
|
|
7
|
+
* plus documentation context into a severity/confidence pair, and provides
|
|
8
|
+
* the corroboration escalation that lifts a `mention`-tier finding when it
|
|
9
|
+
* co-occurs with a genuinely dangerous non-documentation signal elsewhere in
|
|
10
|
+
* the same scan.
|
|
11
|
+
*/
|
|
12
|
+
import { EVIDENCE_TYPE_BY_PATTERN } from './patterns.jailbreak.evidence.js';
|
|
13
|
+
/**
|
|
14
|
+
* Classify a pattern's evidence tier by object identity. Unmapped patterns
|
|
15
|
+
* (i.e. a pattern added to JAILBREAK_PATTERNS/AI_DEFENCE_PATTERNS without an
|
|
16
|
+
* `EVIDENCE_TYPE_BY_PATTERN` entry) default to `imperative_instruction` —
|
|
17
|
+
* fail-closed: forgetting to classify a new pattern makes it the STRONGEST
|
|
18
|
+
* tier, never the weakest, so a missed classification can never silently
|
|
19
|
+
* soften detection.
|
|
20
|
+
*/
|
|
21
|
+
export function classifyEvidence(p) {
|
|
22
|
+
return EVIDENCE_TYPE_BY_PATTERN.get(p) ?? 'imperative_instruction';
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Relative strength ordering used by the merge-loop in
|
|
26
|
+
* `SecurityScanner.helpers.ts` to pick the strongest evidence per line across
|
|
27
|
+
* both the multi-line and single-line scan passes. `imperative_instruction`,
|
|
28
|
+
* `instruction_override`, and `state_assertion` are equal rank — the
|
|
29
|
+
* severity table below treats them identically, and none needs to out-rank
|
|
30
|
+
* the others. `state_assertion` (SMI-5876 design-pass follow-up) is a
|
|
31
|
+
* distinct machine-readable REASON CODE for a declarative jailbroken-state
|
|
32
|
+
* assertion ("Jailbreak activated") rather than a directive aimed at the
|
|
33
|
+
* model — same severity tuple, different diagnostic label.
|
|
34
|
+
*/
|
|
35
|
+
export const EVIDENCE_RANK = {
|
|
36
|
+
mention: 0,
|
|
37
|
+
role_turn_with_body: 1,
|
|
38
|
+
imperative_instruction: 2,
|
|
39
|
+
instruction_override: 2,
|
|
40
|
+
state_assertion: 2,
|
|
41
|
+
};
|
|
42
|
+
/** The highest rank in `EVIDENCE_RANK` — once reached, no later pattern on the same line can beat it. */
|
|
43
|
+
export const MAX_EVIDENCE_RANK = 2;
|
|
44
|
+
/**
|
|
45
|
+
* Severity/confidence decision table (SMI-5876 design §4.1). Doc-context
|
|
46
|
+
* confidence for the directive tiers is `medium`, not `low` — `low` would be
|
|
47
|
+
* self-contradictory next to a still-failing severity (it would also cut the
|
|
48
|
+
* risk-score weight to 0.3x, which is not the intent for a directive payload
|
|
49
|
+
* that merely happens to sit inside a fenced example).
|
|
50
|
+
*/
|
|
51
|
+
export const EVIDENCE_SEVERITY_TABLE = {
|
|
52
|
+
mention: {
|
|
53
|
+
nonDoc: { severity: 'low', confidence: 'low' },
|
|
54
|
+
doc: { severity: 'low', confidence: 'low' },
|
|
55
|
+
},
|
|
56
|
+
role_turn_with_body: {
|
|
57
|
+
nonDoc: { severity: 'high', confidence: 'high' },
|
|
58
|
+
doc: { severity: 'medium', confidence: 'medium' },
|
|
59
|
+
},
|
|
60
|
+
imperative_instruction: {
|
|
61
|
+
nonDoc: { severity: 'critical', confidence: 'high' },
|
|
62
|
+
doc: { severity: 'high', confidence: 'medium' },
|
|
63
|
+
},
|
|
64
|
+
instruction_override: {
|
|
65
|
+
nonDoc: { severity: 'critical', confidence: 'high' },
|
|
66
|
+
doc: { severity: 'high', confidence: 'medium' },
|
|
67
|
+
},
|
|
68
|
+
// SMI-5876 design-pass follow-up: same tuple as imperative_instruction —
|
|
69
|
+
// state_assertion is a distinct reason code, not a distinct severity tier.
|
|
70
|
+
state_assertion: {
|
|
71
|
+
nonDoc: { severity: 'critical', confidence: 'high' },
|
|
72
|
+
doc: { severity: 'high', confidence: 'medium' },
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
/** Resolve the severity/confidence pair for an evidence tier + documentation context. */
|
|
76
|
+
export function resolveEvidenceSeverity(tier, inDocumentationContext) {
|
|
77
|
+
const entry = EVIDENCE_SEVERITY_TABLE[tier];
|
|
78
|
+
return inDocumentationContext ? entry.doc : entry.nonDoc;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* SMI-5876 (post-implementation correction): corroborator eligibility is an
|
|
82
|
+
* ALLOWLIST of INSTRUCTION-BEARING attack categories, not a denylist of
|
|
83
|
+
* everything-except-self.
|
|
84
|
+
*
|
|
85
|
+
* The original design specified `type ∉ {jailbreak, ai_defence}`, reasoning only
|
|
86
|
+
* about same-category circularity. That is the classic denylist failure — it
|
|
87
|
+
* silently admits every category nobody considered. It admitted `pii`, whose
|
|
88
|
+
* plain-email pattern fires on the author-contact bullet that nearly every
|
|
89
|
+
* SKILL.md carries, so an unrelated `- Email: support@…` 74 lines away lifted a
|
|
90
|
+
* benign "jailbreak" mention to high in this repo's OWN bundled SKILL.md — one of
|
|
91
|
+
* SMI-5876's three acceptance fixtures.
|
|
92
|
+
*
|
|
93
|
+
* Both existing precedents here are allowlists: CODE_EXECUTION_CO_OCCURRENCE
|
|
94
|
+
* (SecurityScanner.exec.ts) and SUPPLY_CHAIN_CO_SIGNALS
|
|
95
|
+
* (SecurityScanner.hostile-update.ts). This restores that discipline.
|
|
96
|
+
*
|
|
97
|
+
* Inclusion rule — the category must be INSTRUCTION-BEARING (its patterns match
|
|
98
|
+
* an instruction to do something adversarial). Deliberately excluded:
|
|
99
|
+
* • `pii` / `sensitive_path` — REFERENCE-bearing. A contact email or a `.env`
|
|
100
|
+
* path says something about data hygiene, nothing about whether a jailbreak
|
|
101
|
+
* ATTEMPT is present.
|
|
102
|
+
* • `suspicious_pattern` — `eval(` / `child_process` are ordinary vocabulary in
|
|
103
|
+
* dev-tooling skills.
|
|
104
|
+
* • `social_engineering` / `prompt_leaking` — semantically ADJACENT to jailbreak;
|
|
105
|
+
* admitting them recreates the very circularity the self-exclusion prevents
|
|
106
|
+
* (a persona/roleplay skill would lift its own jailbreak mentions).
|
|
107
|
+
* • `typosquat` — a property of the skill NAME, not its content.
|
|
108
|
+
* • `url` — capped at medium, so it could never qualify anyway.
|
|
109
|
+
* • `jailbreak` / `ai_defence` — same-category self-lift (original exclusion).
|
|
110
|
+
*/
|
|
111
|
+
const CORROBORATING_SIGNALS = new Set([
|
|
112
|
+
'code_execution',
|
|
113
|
+
'obfuscated_directive',
|
|
114
|
+
'data_exfiltration',
|
|
115
|
+
'privilege_escalation',
|
|
116
|
+
'ssrf',
|
|
117
|
+
]);
|
|
118
|
+
/**
|
|
119
|
+
* Maximum line distance between a mention and its corroborator.
|
|
120
|
+
*
|
|
121
|
+
* Why a bounded window is NOT a bypass vector: corroboration is a risk-SCORE and
|
|
122
|
+
* hostile-update amplifier only — it can never flip `passed`, because any
|
|
123
|
+
* qualifying corroborator is itself high/critical and therefore already fails the
|
|
124
|
+
* scan on its own (`passed = !hasCritical && !hasHigh && !exceedsThreshold`). An
|
|
125
|
+
* attacker who separates the two signals to duck the window still fails on the
|
|
126
|
+
* corroborator alone; all they achieve is a slightly lower numeric score on a
|
|
127
|
+
* scan that already blocks.
|
|
128
|
+
*
|
|
129
|
+
* 40 lines comfortably spans a real payload block (persona preamble plus the
|
|
130
|
+
* instructions it sets up) while excluding the "unrelated finding in a different
|
|
131
|
+
* section of a long document" case that motivated this constraint.
|
|
132
|
+
*/
|
|
133
|
+
const MAX_CORROBORATION_LINE_DISTANCE = 40;
|
|
134
|
+
/**
|
|
135
|
+
* Escalate a `mention`-tier finding when an ALLOWLISTED non-documentation
|
|
136
|
+
* high/critical finding is ALSO present within `MAX_CORROBORATION_LINE_DISTANCE`
|
|
137
|
+
* lines in the same scan (SMI-5876 design §4.3, corrected post-implementation —
|
|
138
|
+
* see `CORROBORATING_SIGNALS`' doc comment for the denylist->allowlist fix and
|
|
139
|
+
* the locality-window rationale). Mutates findings in place, mirroring
|
|
140
|
+
* `escalateCodeExecution`'s mutation style (SecurityScanner.exec.ts).
|
|
141
|
+
*
|
|
142
|
+
* MUST run after `escalateCodeExecution` (SecurityScanner.ts's `scan()`) so a
|
|
143
|
+
* freshly-critical `code_execution` finding can itself serve as a
|
|
144
|
+
* corroborator. No feedback loop the other way: `CODE_EXECUTION_CO_OCCURRENCE`
|
|
145
|
+
* (SecurityScanner.exec.ts) contains only `data_exfiltration`,
|
|
146
|
+
* `privilege_escalation`, `sensitive_path`, and `obfuscated_directive` — never
|
|
147
|
+
* `jailbreak` or `ai_defence` — so lifting a mention here can never
|
|
148
|
+
* retroactively change `escalateCodeExecution`'s own decision (verified by
|
|
149
|
+
* reading SecurityScanner.exec.ts directly, SMI-5876 implementation).
|
|
150
|
+
*/
|
|
151
|
+
export function escalateCorroboratedMentions(findings) {
|
|
152
|
+
const corroborators = findings.filter((f) => (f.severity === 'high' || f.severity === 'critical') &&
|
|
153
|
+
f.inDocumentationContext !== true &&
|
|
154
|
+
CORROBORATING_SIGNALS.has(f.type) &&
|
|
155
|
+
typeof f.lineNumber === 'number');
|
|
156
|
+
if (corroborators.length === 0)
|
|
157
|
+
return;
|
|
158
|
+
for (const finding of findings) {
|
|
159
|
+
if (finding.evidenceType !== 'mention')
|
|
160
|
+
continue;
|
|
161
|
+
if (typeof finding.lineNumber !== 'number')
|
|
162
|
+
continue;
|
|
163
|
+
const corroborator = corroborators.find((c) => Math.abs(c.lineNumber - finding.lineNumber) <=
|
|
164
|
+
MAX_CORROBORATION_LINE_DISTANCE);
|
|
165
|
+
if (!corroborator)
|
|
166
|
+
continue;
|
|
167
|
+
const inDoc = finding.inDocumentationContext === true;
|
|
168
|
+
finding.severity = inDoc ? 'medium' : 'high';
|
|
169
|
+
finding.confidence = inDoc ? 'low' : 'medium';
|
|
170
|
+
finding.corroborated = true;
|
|
171
|
+
finding.message =
|
|
172
|
+
`Corroborated by a co-occurring non-documentation ${corroborator.type} ` +
|
|
173
|
+
`finding at line ${corroborator.lineNumber} — ${finding.message}`;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=SecurityScanner.evidence.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecurityScanner.evidence.js","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.evidence.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,13 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import type { SecurityFinding } from './types.js';
|
|
34
34
|
import type { LineContext } from './SecurityScanner.helpers.js';
|
|
35
|
+
/**
|
|
36
|
+
* Remove invisible/format/bidi/tag/combining characters.
|
|
37
|
+
*
|
|
38
|
+
* Exported (SMI-4703): reused as-is by the memory-injection-scanner's
|
|
39
|
+
* normalization pipeline (invisible-strip step) — not reimplemented there.
|
|
40
|
+
*/
|
|
41
|
+
export declare function stripInvisible(s: string): string;
|
|
35
42
|
/**
|
|
36
43
|
* code_execution: remote fetch piped into an interpreter. Single-emission — at
|
|
37
44
|
* most one MEDIUM finding per skill (the first match). escalateCodeExecution()
|
|
@@ -48,9 +55,16 @@ export declare function scanObfuscatedDirective(content: string): SecurityFindin
|
|
|
48
55
|
/**
|
|
49
56
|
* Escalate the code_execution finding to CRITICAL when a NON-documentation
|
|
50
57
|
* high/critical exfiltration / privilege / credential-path / obfuscation signal
|
|
51
|
-
* is also present
|
|
58
|
+
* is also present WITHIN `MAX_CODE_EXECUTION_CO_SIGNAL_LINE_DISTANCE` lines
|
|
59
|
+
* (SMI-5880 — previously any distance qualified, so an unrelated finding in a
|
|
60
|
+
* different section of a long document escalated an unrelated code_execution
|
|
61
|
+
* finding). Mutates the finding in place. Requiring the co-signal to be
|
|
52
62
|
* non-documentation keeps legitimate security-research skills (whose examples
|
|
53
63
|
* live in fenced blocks) at MEDIUM.
|
|
64
|
+
*
|
|
65
|
+
* Runs BEFORE the sibling corroboration mechanism in `scan()` — see that
|
|
66
|
+
* function's doc comment (SecurityScanner.evidence.ts) for why the ordering is
|
|
67
|
+
* safe in both directions.
|
|
54
68
|
*/
|
|
55
69
|
export declare function escalateCodeExecution(findings: SecurityFinding[]): void;
|
|
56
70
|
//# sourceMappingURL=SecurityScanner.exec.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityScanner.exec.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAuB,MAAM,YAAY,CAAA;AAGtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"SecurityScanner.exec.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAuB,MAAM,YAAY,CAAA;AAGtE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AA4B/D;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AA0BD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,YAAY,CAAC,EAAE,WAAW,EAAE,GAC3B,eAAe,EAAE,CA6BnB;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAuC1E;AA8DD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI,CAiBvE"}
|
|
@@ -33,6 +33,11 @@
|
|
|
33
33
|
import { CODE_EXECUTION_PATTERNS } from './patterns.js';
|
|
34
34
|
import { safeRegexTest, safeRegexCheck } from './regex-utils.js';
|
|
35
35
|
import { analyzeMarkdownContext, isDocumentationContext } from './SecurityScanner.helpers.js';
|
|
36
|
+
// SMI-595: confusable/homoglyph primitives extracted to a standalone module
|
|
37
|
+
// (confusables.ts) so the typosquat detector can reuse them without coupling
|
|
38
|
+
// to this file's code-execution/obfuscated-directive detectors. Pure
|
|
39
|
+
// extraction — no behavior change.
|
|
40
|
+
import { CONFUSABLES, isFullwidthLatin, isMathAlphanumeric, confusableSkeleton, } from './confusables.js';
|
|
36
41
|
// ============================================================================
|
|
37
42
|
// Obfuscation primitives
|
|
38
43
|
// ============================================================================
|
|
@@ -46,83 +51,14 @@ const INVISIBLE_RANGE = '\\u0300-\\u036F\\u00AD\\u061C\\u180E\\u200B-\\u200F\\u2
|
|
|
46
51
|
const INVISIBLE_TEST = new RegExp('[' + INVISIBLE_RANGE + ']|[\\u{E0000}-\\u{E007F}]', 'u');
|
|
47
52
|
const INVISIBLE_STRIP = new RegExp('[' + INVISIBLE_RANGE + ']|[\\u{E0000}-\\u{E007F}]', 'gu');
|
|
48
53
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* are handled programmatically below — never via a blanket NFKC pass, which would
|
|
54
|
-
* also fold fullwidth CJK to ASCII and false-positive. (NFKC is applied per-char
|
|
55
|
-
* ONLY to the math-alphanumeric range, which contains no CJK.)
|
|
54
|
+
* Remove invisible/format/bidi/tag/combining characters.
|
|
55
|
+
*
|
|
56
|
+
* Exported (SMI-4703): reused as-is by the memory-injection-scanner's
|
|
57
|
+
* normalization pipeline (invisible-strip step) — not reimplemented there.
|
|
56
58
|
*/
|
|
57
|
-
|
|
58
|
-
// Cyrillic -> Latin
|
|
59
|
-
а: 'a',
|
|
60
|
-
е: 'e',
|
|
61
|
-
о: 'o',
|
|
62
|
-
р: 'p',
|
|
63
|
-
с: 'c',
|
|
64
|
-
у: 'y',
|
|
65
|
-
х: 'x',
|
|
66
|
-
і: 'i',
|
|
67
|
-
ј: 'j',
|
|
68
|
-
ѕ: 's',
|
|
69
|
-
ԁ: 'd',
|
|
70
|
-
һ: 'h',
|
|
71
|
-
к: 'k',
|
|
72
|
-
м: 'm',
|
|
73
|
-
т: 't',
|
|
74
|
-
в: 'b',
|
|
75
|
-
н: 'h',
|
|
76
|
-
// Greek -> Latin
|
|
77
|
-
ο: 'o',
|
|
78
|
-
α: 'a',
|
|
79
|
-
ρ: 'p',
|
|
80
|
-
ε: 'e',
|
|
81
|
-
τ: 't',
|
|
82
|
-
ι: 'i',
|
|
83
|
-
κ: 'k',
|
|
84
|
-
υ: 'u',
|
|
85
|
-
χ: 'x',
|
|
86
|
-
ν: 'v',
|
|
87
|
-
ϲ: 'c',
|
|
88
|
-
β: 'b',
|
|
89
|
-
};
|
|
90
|
-
function isFullwidthLatin(cp) {
|
|
91
|
-
return (cp >= 0xff21 && cp <= 0xff3a) || (cp >= 0xff41 && cp <= 0xff5a);
|
|
92
|
-
}
|
|
93
|
-
/** Mathematical Alphanumeric Symbols (bold/italic/script/fraktur/double-struck/sans/mono). */
|
|
94
|
-
function isMathAlphanumeric(cp) {
|
|
95
|
-
return cp >= 0x1d400 && cp <= 0x1d7ff;
|
|
96
|
-
}
|
|
97
|
-
/** Remove invisible/format/bidi/tag/combining characters. */
|
|
98
|
-
function stripInvisible(s) {
|
|
59
|
+
export function stripInvisible(s) {
|
|
99
60
|
return s.replace(INVISIBLE_STRIP, '');
|
|
100
61
|
}
|
|
101
|
-
/** Map homoglyphs + fullwidth Latin + math-alphanumeric to their ASCII skeleton. */
|
|
102
|
-
function confusableSkeleton(s) {
|
|
103
|
-
let out = '';
|
|
104
|
-
for (const ch of s) {
|
|
105
|
-
const cp = ch.codePointAt(0) ?? 0;
|
|
106
|
-
if (isFullwidthLatin(cp)) {
|
|
107
|
-
out += String.fromCodePoint(cp - 0xfee0);
|
|
108
|
-
}
|
|
109
|
-
else if (isMathAlphanumeric(cp)) {
|
|
110
|
-
// NFKC folds a math-styled glyph to its base; chain through CONFUSABLES so a
|
|
111
|
-
// math-styled Greek/Cyrillic homoglyph (folds to Greek/Cyrillic) still maps
|
|
112
|
-
// to Latin (SMI-5359 retro NIT). Safe: the range contains no CJK; a reserved
|
|
113
|
-
// hole stays unchanged (won't match).
|
|
114
|
-
const folded = ch.normalize('NFKC');
|
|
115
|
-
out += CONFUSABLES[folded] ?? folded;
|
|
116
|
-
}
|
|
117
|
-
else if (CONFUSABLES[ch]) {
|
|
118
|
-
out += CONFUSABLES[ch];
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
out += ch;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
return out;
|
|
125
|
-
}
|
|
126
62
|
/** True if the line contains a homoglyph / fullwidth-Latin / math-alphanumeric character. */
|
|
127
63
|
function hasConfusable(s) {
|
|
128
64
|
for (const ch of s) {
|
|
@@ -232,12 +168,67 @@ const CODE_EXECUTION_CO_OCCURRENCE = new Set([
|
|
|
232
168
|
'sensitive_path',
|
|
233
169
|
'obfuscated_directive',
|
|
234
170
|
]);
|
|
171
|
+
/**
|
|
172
|
+
* Maximum line distance between the `code_execution` finding and the co-signal
|
|
173
|
+
* that escalates it (SMI-5880). Before this bound, a co-signal ANYWHERE in the
|
|
174
|
+
* document — any distance, any unrelated section — escalated the single
|
|
175
|
+
* `code_execution` finding to critical.
|
|
176
|
+
*
|
|
177
|
+
* Why a bounded window is NOT a bypass vector: every eligible co-signal is
|
|
178
|
+
* already `high` or `critical`, and `scan()` (SecurityScanner.ts) computes
|
|
179
|
+
* `passed = !hasCritical && !hasHigh && !exceedsThreshold` across ALL findings
|
|
180
|
+
* with no locality filter and no documentation filter. A qualifying co-signal
|
|
181
|
+
* anywhere therefore forces `passed === false` on its own, BEFORE this function
|
|
182
|
+
* runs. Splitting the two signals to duck this window cannot turn a failing scan
|
|
183
|
+
* into a passing one; it changes only this finding's own label (medium vs
|
|
184
|
+
* critical) and the risk-score magnitude of a scan that already blocks.
|
|
185
|
+
* Hostile-update detection is likewise untouched: `detectSupplyChain()`
|
|
186
|
+
* (SecurityScanner.hostile-update.ts) pairs a NEW non-doc `code_execution` of ANY
|
|
187
|
+
* severity with a NEW non-doc high/critical co-signal at ANY distance, so a
|
|
188
|
+
* far-apart rug-pull still returns the `hostile` verdict and its supply-chain
|
|
189
|
+
* reason string.
|
|
190
|
+
*
|
|
191
|
+
* Deliberately the same 40 as the sibling corroboration mechanism's locality
|
|
192
|
+
* constant (SecurityScanner.evidence.ts) so the scanner has ONE locality model
|
|
193
|
+
* — but kept as its own constant, not imported: the two mechanisms are
|
|
194
|
+
* independently tunable, and the edge twins (SMI-5879) cannot import from core
|
|
195
|
+
* and must carry their own copy.
|
|
196
|
+
*/
|
|
197
|
+
const MAX_CODE_EXECUTION_CO_SIGNAL_LINE_DISTANCE = 40;
|
|
198
|
+
/**
|
|
199
|
+
* Locality gate for `escalateCodeExecution`. Fail-CLOSED on missing metadata:
|
|
200
|
+
* when either side has no `lineNumber` the distance is unknowable, and this
|
|
201
|
+
* subsystem's convention is that missing metadata must never SILENTLY weaken
|
|
202
|
+
* detection — cf. `classifyEvidence` (SecurityScanner.evidence.ts), whose
|
|
203
|
+
* unmapped-pattern default is the STRONGEST tier for exactly this reason. An
|
|
204
|
+
* unknowable distance therefore preserves the pre-SMI-5880 unbounded behavior for
|
|
205
|
+
* that pair rather than suppressing the co-signal.
|
|
206
|
+
*
|
|
207
|
+
* Unreachable through `scan()` today: `scanCodeExecution` above and every emitter
|
|
208
|
+
* of the four CODE_EXECUTION_CO_OCCURRENCE types set `lineNumber` unconditionally
|
|
209
|
+
* (the sensitive-path/data-exfiltration/privilege-escalation scanners in the
|
|
210
|
+
* scanners module, the chmod-compound detector, and `scanObfuscatedDirective`
|
|
211
|
+
* above). Kept as an explicit, directly-tested branch so a future detector that
|
|
212
|
+
* forgets `lineNumber` degrades safe instead of silently ceasing to escalate.
|
|
213
|
+
*/
|
|
214
|
+
function isWithinCoSignalWindow(codeExecLine, coSignalLine) {
|
|
215
|
+
if (typeof codeExecLine !== 'number' || typeof coSignalLine !== 'number')
|
|
216
|
+
return true;
|
|
217
|
+
return Math.abs(codeExecLine - coSignalLine) <= MAX_CODE_EXECUTION_CO_SIGNAL_LINE_DISTANCE;
|
|
218
|
+
}
|
|
235
219
|
/**
|
|
236
220
|
* Escalate the code_execution finding to CRITICAL when a NON-documentation
|
|
237
221
|
* high/critical exfiltration / privilege / credential-path / obfuscation signal
|
|
238
|
-
* is also present
|
|
222
|
+
* is also present WITHIN `MAX_CODE_EXECUTION_CO_SIGNAL_LINE_DISTANCE` lines
|
|
223
|
+
* (SMI-5880 — previously any distance qualified, so an unrelated finding in a
|
|
224
|
+
* different section of a long document escalated an unrelated code_execution
|
|
225
|
+
* finding). Mutates the finding in place. Requiring the co-signal to be
|
|
239
226
|
* non-documentation keeps legitimate security-research skills (whose examples
|
|
240
227
|
* live in fenced blocks) at MEDIUM.
|
|
228
|
+
*
|
|
229
|
+
* Runs BEFORE the sibling corroboration mechanism in `scan()` — see that
|
|
230
|
+
* function's doc comment (SecurityScanner.evidence.ts) for why the ordering is
|
|
231
|
+
* safe in both directions.
|
|
241
232
|
*/
|
|
242
233
|
export function escalateCodeExecution(findings) {
|
|
243
234
|
const codeExec = findings.find((f) => f.type === 'code_execution');
|
|
@@ -246,7 +237,8 @@ export function escalateCodeExecution(findings) {
|
|
|
246
237
|
const hasDangerousCoSignal = findings.some((f) => f !== codeExec &&
|
|
247
238
|
CODE_EXECUTION_CO_OCCURRENCE.has(f.type) &&
|
|
248
239
|
f.inDocumentationContext !== true &&
|
|
249
|
-
(f.severity === 'high' || f.severity === 'critical')
|
|
240
|
+
(f.severity === 'high' || f.severity === 'critical') &&
|
|
241
|
+
isWithinCoSignalWindow(codeExec.lineNumber, f.lineNumber));
|
|
250
242
|
if (hasDangerousCoSignal) {
|
|
251
243
|
codeExec.severity = 'critical';
|
|
252
244
|
codeExec.message = `Remote fetch piped to an interpreter, co-occurring with exfiltration/privilege/credential signals — likely supply-chain execution. ${codeExec.message}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityScanner.exec.js","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAA;AACvD,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAEhE,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"SecurityScanner.exec.js","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Security Scanner Output Formatters
|
|
3
3
|
* @module @skillsmith/core/security/scanner/SecurityScanner.formatters
|
|
4
4
|
*/
|
|
5
|
-
import type { ScanReport } from './types.js';
|
|
5
|
+
import type { ScanReport, SecurityFinding } from './types.js';
|
|
6
6
|
/**
|
|
7
7
|
* SMI-1454: Generate minimal refs output for CI/CD integration
|
|
8
8
|
* Outputs findings in a compact, machine-readable format suitable for:
|
|
@@ -46,4 +46,17 @@ export declare function toSummary(report: ScanReport): {
|
|
|
46
46
|
byType: Record<string, number>;
|
|
47
47
|
scanDurationMs: number;
|
|
48
48
|
};
|
|
49
|
+
/**
|
|
50
|
+
* SMI-595: translate typosquat findings into user-facing copy.
|
|
51
|
+
*
|
|
52
|
+
* Banned-vocabulary rule (enforced by a dedicated test, not just this
|
|
53
|
+
* function's implementation): the strings this function returns must never
|
|
54
|
+
* contain "homoglyph", "confusable", "skeleton", or internal ladder-step/rule
|
|
55
|
+
* numbers — those describe the *detection method*, which means nothing to a
|
|
56
|
+
* skill installer and could help an attacker tune around the detector.
|
|
57
|
+
*
|
|
58
|
+
* @param findings - Any findings array; non-typosquat findings are ignored.
|
|
59
|
+
* @returns One user-facing string per typosquat finding, in input order.
|
|
60
|
+
*/
|
|
61
|
+
export declare function describeSignals(findings: SecurityFinding[]): string[];
|
|
49
62
|
//# sourceMappingURL=SecurityScanner.formatters.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityScanner.formatters.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.formatters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"SecurityScanner.formatters.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.formatters.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAM7D;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAQ1D;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAmElD;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,EAAE,CAQhE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG;IAC7C,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9B,cAAc,EAAE,MAAM,CAAA;CACvB,CAkBA;AAwBD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,MAAM,EAAE,CAIrE"}
|
|
@@ -136,4 +136,37 @@ export function toSummary(report) {
|
|
|
136
136
|
scanDurationMs: report.scanDurationMs,
|
|
137
137
|
};
|
|
138
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* SMI-595: fixed, curated user-facing copy for each typosquat detection
|
|
141
|
+
* rule, keyed by the finding's `category` (set by `scanTyposquat()` in
|
|
142
|
+
* `typosquat.ts`). Deliberately NOT derived from the finding's raw
|
|
143
|
+
* `message` — that field is free to use internal/technical language for
|
|
144
|
+
* developer-facing surfaces (logs, SARIF, CI annotations); this table is the
|
|
145
|
+
* one place user-facing copy is generated, so it can enforce the
|
|
146
|
+
* banned-vocabulary rule below independent of what the raw message says.
|
|
147
|
+
*/
|
|
148
|
+
const TYPOSQUAT_SIGNAL_COPY = {
|
|
149
|
+
'typosquat:impersonation-exact-skeleton': "This skill's name closely resembles a well-known skill and may be attempting impersonation.",
|
|
150
|
+
'typosquat:levenshtein': "This skill's name is very similar to a well-known skill — double-check that you have the right one before installing.",
|
|
151
|
+
'typosquat:authority-affix': "This skill's name claims an official or verified status it has not been granted.",
|
|
152
|
+
};
|
|
153
|
+
/** Fallback copy for a typosquat finding whose category isn't in the lookup table above. */
|
|
154
|
+
const TYPOSQUAT_FALLBACK_MESSAGE = 'This skill has a naming pattern that may be attempting to impersonate another skill.';
|
|
155
|
+
/**
|
|
156
|
+
* SMI-595: translate typosquat findings into user-facing copy.
|
|
157
|
+
*
|
|
158
|
+
* Banned-vocabulary rule (enforced by a dedicated test, not just this
|
|
159
|
+
* function's implementation): the strings this function returns must never
|
|
160
|
+
* contain "homoglyph", "confusable", "skeleton", or internal ladder-step/rule
|
|
161
|
+
* numbers — those describe the *detection method*, which means nothing to a
|
|
162
|
+
* skill installer and could help an attacker tune around the detector.
|
|
163
|
+
*
|
|
164
|
+
* @param findings - Any findings array; non-typosquat findings are ignored.
|
|
165
|
+
* @returns One user-facing string per typosquat finding, in input order.
|
|
166
|
+
*/
|
|
167
|
+
export function describeSignals(findings) {
|
|
168
|
+
return findings
|
|
169
|
+
.filter((finding) => finding.type === 'typosquat')
|
|
170
|
+
.map((finding) => TYPOSQUAT_SIGNAL_COPY[finding.category ?? ''] ?? TYPOSQUAT_FALLBACK_MESSAGE);
|
|
171
|
+
}
|
|
139
172
|
//# sourceMappingURL=SecurityScanner.formatters.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SecurityScanner.formatters.js","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.formatters.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SecurityScanner.formatters.js","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.formatters.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-595: describeSignals() — banned-vocabulary translation-layer tests.
|
|
3
|
+
*
|
|
4
|
+
* User-facing security copy must describe *what's wrong*, not leak internal
|
|
5
|
+
* detection-method jargon (which means nothing to a skill installer and
|
|
6
|
+
* could help an attacker tune around the detector).
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=SecurityScanner.formatters.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecurityScanner.formatters.test.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.formatters.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMI-595: describeSignals() — banned-vocabulary translation-layer tests.
|
|
3
|
+
*
|
|
4
|
+
* User-facing security copy must describe *what's wrong*, not leak internal
|
|
5
|
+
* detection-method jargon (which means nothing to a skill installer and
|
|
6
|
+
* could help an attacker tune around the detector).
|
|
7
|
+
*/
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { describeSignals } from './SecurityScanner.formatters.js';
|
|
10
|
+
import { scanTyposquat } from './typosquat.js';
|
|
11
|
+
/** Terms that must never appear in describeSignals() output (case-insensitive). */
|
|
12
|
+
const BANNED_VOCABULARY = [
|
|
13
|
+
'homoglyph',
|
|
14
|
+
'confusable',
|
|
15
|
+
'skeleton',
|
|
16
|
+
'rule 1',
|
|
17
|
+
'rule 2',
|
|
18
|
+
'rule 3',
|
|
19
|
+
'ladder',
|
|
20
|
+
];
|
|
21
|
+
describe('describeSignals (SMI-595 §7)', () => {
|
|
22
|
+
it('produces one message per typosquat finding', () => {
|
|
23
|
+
const findings = scanTyposquat('anthropic-mcp-official', new Set(['anthropic']));
|
|
24
|
+
const messages = describeSignals(findings);
|
|
25
|
+
expect(messages).toHaveLength(findings.length);
|
|
26
|
+
expect(messages.length).toBeGreaterThan(0);
|
|
27
|
+
});
|
|
28
|
+
it('never contains banned detection-method vocabulary, across all three rules', () => {
|
|
29
|
+
const impersonation = scanTyposquat('аnthropic', new Set(['anthropic'])); // Cyrillic а
|
|
30
|
+
const levenshtein = scanTyposquat('claude-cod', new Set(['claude-code']));
|
|
31
|
+
const authorityAffix = scanTyposquat('anthropic-mcp-official', new Set(['anthropic']));
|
|
32
|
+
const allFindings = [...impersonation, ...levenshtein, ...authorityAffix];
|
|
33
|
+
expect(allFindings.length).toBeGreaterThan(0);
|
|
34
|
+
const messages = describeSignals(allFindings);
|
|
35
|
+
expect(messages.length).toBe(allFindings.length);
|
|
36
|
+
for (const message of messages) {
|
|
37
|
+
const lower = message.toLowerCase();
|
|
38
|
+
for (const banned of BANNED_VOCABULARY) {
|
|
39
|
+
expect(lower.includes(banned)).toBe(false);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
it('ignores non-typosquat findings', () => {
|
|
44
|
+
const jailbreakFinding = {
|
|
45
|
+
type: 'jailbreak',
|
|
46
|
+
severity: 'critical',
|
|
47
|
+
message: 'Ignore previous instructions',
|
|
48
|
+
};
|
|
49
|
+
expect(describeSignals([jailbreakFinding])).toHaveLength(0);
|
|
50
|
+
});
|
|
51
|
+
it('falls back to a safe generic message for an unrecognized typosquat category', () => {
|
|
52
|
+
const finding = {
|
|
53
|
+
type: 'typosquat',
|
|
54
|
+
severity: 'medium',
|
|
55
|
+
message: 'internal message',
|
|
56
|
+
category: 'typosquat:some-future-rule',
|
|
57
|
+
};
|
|
58
|
+
const messages = describeSignals([finding]);
|
|
59
|
+
expect(messages).toHaveLength(1);
|
|
60
|
+
for (const banned of BANNED_VOCABULARY) {
|
|
61
|
+
expect(messages[0].toLowerCase().includes(banned)).toBe(false);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
//# sourceMappingURL=SecurityScanner.formatters.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecurityScanner.formatters.test.js","sourceRoot":"","sources":["../../../../src/security/scanner/SecurityScanner.formatters.test.ts"],"names":[],"mappings":"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"}
|