@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.
Files changed (235) hide show
  1. package/CHANGELOG.md +36 -9
  2. package/README.md +6 -7
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/src/analysis/McpReferenceExtractor.d.ts +59 -5
  5. package/dist/src/analysis/McpReferenceExtractor.d.ts.map +1 -1
  6. package/dist/src/analysis/McpReferenceExtractor.hardening.test.d.ts +2 -0
  7. package/dist/src/analysis/McpReferenceExtractor.hardening.test.d.ts.map +1 -0
  8. package/dist/src/analysis/McpReferenceExtractor.hardening.test.js +278 -0
  9. package/dist/src/analysis/McpReferenceExtractor.hardening.test.js.map +1 -0
  10. package/dist/src/analysis/McpReferenceExtractor.js +276 -20
  11. package/dist/src/analysis/McpReferenceExtractor.js.map +1 -1
  12. package/dist/src/analysis/McpReferenceExtractor.test.js +4 -0
  13. package/dist/src/analysis/McpReferenceExtractor.test.js.map +1 -1
  14. package/dist/src/config/config-atomic-write.d.ts +58 -0
  15. package/dist/src/config/config-atomic-write.d.ts.map +1 -0
  16. package/dist/src/config/config-atomic-write.js +151 -0
  17. package/dist/src/config/config-atomic-write.js.map +1 -0
  18. package/dist/src/config/config-atomic-write.test.d.ts +9 -0
  19. package/dist/src/config/config-atomic-write.test.d.ts.map +1 -0
  20. package/dist/src/config/config-atomic-write.test.js +92 -0
  21. package/dist/src/config/config-atomic-write.test.js.map +1 -0
  22. package/dist/src/config/device-identity.d.ts +16 -0
  23. package/dist/src/config/device-identity.d.ts.map +1 -1
  24. package/dist/src/config/device-identity.js +82 -14
  25. package/dist/src/config/device-identity.js.map +1 -1
  26. package/dist/src/config/device-identity.test.js +158 -1
  27. package/dist/src/config/device-identity.test.js.map +1 -1
  28. package/dist/src/config/index.d.ts +14 -0
  29. package/dist/src/config/index.d.ts.map +1 -1
  30. package/dist/src/config/index.js +31 -15
  31. package/dist/src/config/index.js.map +1 -1
  32. package/dist/src/exports/services.d.ts +2 -1
  33. package/dist/src/exports/services.d.ts.map +1 -1
  34. package/dist/src/exports/services.js +1 -0
  35. package/dist/src/exports/services.js.map +1 -1
  36. package/dist/src/index.d.ts +4 -1
  37. package/dist/src/index.d.ts.map +1 -1
  38. package/dist/src/index.js +13 -1
  39. package/dist/src/index.js.map +1 -1
  40. package/dist/src/indexer/SwarmIndexer.d.ts +1 -1
  41. package/dist/src/indexer/SwarmIndexer.js +2 -2
  42. package/dist/src/indexer/SwarmIndexer.js.map +1 -1
  43. package/dist/src/install/paths.d.ts +1 -1
  44. package/dist/src/install/paths.d.ts.map +1 -1
  45. package/dist/src/install/paths.js +2 -0
  46. package/dist/src/install/paths.js.map +1 -1
  47. package/dist/src/install/paths.test.js +10 -1
  48. package/dist/src/install/paths.test.js.map +1 -1
  49. package/dist/src/logging/logger.test.js +57 -0
  50. package/dist/src/logging/logger.test.js.map +1 -1
  51. package/dist/src/logging/rotation.d.ts +27 -2
  52. package/dist/src/logging/rotation.d.ts.map +1 -1
  53. package/dist/src/logging/rotation.js +44 -3
  54. package/dist/src/logging/rotation.js.map +1 -1
  55. package/dist/src/logging/rotation.test.js +69 -0
  56. package/dist/src/logging/rotation.test.js.map +1 -1
  57. package/dist/src/logging/types.d.ts +8 -2
  58. package/dist/src/logging/types.d.ts.map +1 -1
  59. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +5 -0
  60. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  61. package/dist/src/security/audit-types.d.ts +1 -1
  62. package/dist/src/security/audit-types.d.ts.map +1 -1
  63. package/dist/src/security/audit-types.js.map +1 -1
  64. package/dist/src/security/index.d.ts +2 -1
  65. package/dist/src/security/index.d.ts.map +1 -1
  66. package/dist/src/security/index.js +3 -0
  67. package/dist/src/security/index.js.map +1 -1
  68. package/dist/src/security/scanner/SecurityScanner.d.ts +2 -2
  69. package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
  70. package/dist/src/security/scanner/SecurityScanner.evidence.d.ts +76 -0
  71. package/dist/src/security/scanner/SecurityScanner.evidence.d.ts.map +1 -0
  72. package/dist/src/security/scanner/SecurityScanner.evidence.js +176 -0
  73. package/dist/src/security/scanner/SecurityScanner.evidence.js.map +1 -0
  74. package/dist/src/security/scanner/SecurityScanner.exec.d.ts +15 -1
  75. package/dist/src/security/scanner/SecurityScanner.exec.d.ts.map +1 -1
  76. package/dist/src/security/scanner/SecurityScanner.exec.js +68 -76
  77. package/dist/src/security/scanner/SecurityScanner.exec.js.map +1 -1
  78. package/dist/src/security/scanner/SecurityScanner.formatters.d.ts +14 -1
  79. package/dist/src/security/scanner/SecurityScanner.formatters.d.ts.map +1 -1
  80. package/dist/src/security/scanner/SecurityScanner.formatters.js +33 -0
  81. package/dist/src/security/scanner/SecurityScanner.formatters.js.map +1 -1
  82. package/dist/src/security/scanner/SecurityScanner.formatters.test.d.ts +9 -0
  83. package/dist/src/security/scanner/SecurityScanner.formatters.test.d.ts.map +1 -0
  84. package/dist/src/security/scanner/SecurityScanner.formatters.test.js +65 -0
  85. package/dist/src/security/scanner/SecurityScanner.formatters.test.js.map +1 -0
  86. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts +35 -10
  87. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
  88. package/dist/src/security/scanner/SecurityScanner.helpers.js +121 -60
  89. package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
  90. package/dist/src/security/scanner/SecurityScanner.js +41 -13
  91. package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
  92. package/dist/src/security/scanner/SecurityScanner.pii.d.ts.map +1 -1
  93. package/dist/src/security/scanner/SecurityScanner.pii.js +23 -1
  94. package/dist/src/security/scanner/SecurityScanner.pii.js.map +1 -1
  95. package/dist/src/security/scanner/SecurityScanner.scanners.d.ts.map +1 -1
  96. package/dist/src/security/scanner/SecurityScanner.scanners.js +15 -3
  97. package/dist/src/security/scanner/SecurityScanner.scanners.js.map +1 -1
  98. package/dist/src/security/scanner/SecurityScanner.ssrf.d.ts +11 -1
  99. package/dist/src/security/scanner/SecurityScanner.ssrf.d.ts.map +1 -1
  100. package/dist/src/security/scanner/SecurityScanner.ssrf.js +18 -7
  101. package/dist/src/security/scanner/SecurityScanner.ssrf.js.map +1 -1
  102. package/dist/src/security/scanner/confusables.d.ts +28 -0
  103. package/dist/src/security/scanner/confusables.d.ts.map +1 -0
  104. package/dist/src/security/scanner/confusables.js +87 -0
  105. package/dist/src/security/scanner/confusables.js.map +1 -0
  106. package/dist/src/security/scanner/confusables.test.d.ts +10 -0
  107. package/dist/src/security/scanner/confusables.test.d.ts.map +1 -0
  108. package/dist/src/security/scanner/confusables.test.js +44 -0
  109. package/dist/src/security/scanner/confusables.test.js.map +1 -0
  110. package/dist/src/security/scanner/index.d.ts +9 -3
  111. package/dist/src/security/scanner/index.d.ts.map +1 -1
  112. package/dist/src/security/scanner/index.js +18 -2
  113. package/dist/src/security/scanner/index.js.map +1 -1
  114. package/dist/src/security/scanner/patterns.d.ts +41 -14
  115. package/dist/src/security/scanner/patterns.d.ts.map +1 -1
  116. package/dist/src/security/scanner/patterns.jailbreak.d.ts +41 -0
  117. package/dist/src/security/scanner/patterns.jailbreak.d.ts.map +1 -0
  118. package/dist/src/security/scanner/patterns.jailbreak.evidence.d.ts +35 -0
  119. package/dist/src/security/scanner/patterns.jailbreak.evidence.d.ts.map +1 -0
  120. package/dist/src/security/scanner/patterns.jailbreak.evidence.js +119 -0
  121. package/dist/src/security/scanner/patterns.jailbreak.evidence.js.map +1 -0
  122. package/dist/src/security/scanner/patterns.jailbreak.js +370 -0
  123. package/dist/src/security/scanner/patterns.jailbreak.js.map +1 -0
  124. package/dist/src/security/scanner/patterns.js +101 -82
  125. package/dist/src/security/scanner/patterns.js.map +1 -1
  126. package/dist/src/security/scanner/patterns.scope.d.ts +62 -0
  127. package/dist/src/security/scanner/patterns.scope.d.ts.map +1 -0
  128. package/dist/src/security/scanner/patterns.scope.js +207 -0
  129. package/dist/src/security/scanner/patterns.scope.js.map +1 -0
  130. package/dist/src/security/scanner/regex-utils.d.ts +20 -0
  131. package/dist/src/security/scanner/regex-utils.d.ts.map +1 -1
  132. package/dist/src/security/scanner/regex-utils.js +20 -0
  133. package/dist/src/security/scanner/regex-utils.js.map +1 -1
  134. package/dist/src/security/scanner/types.d.ts +49 -1
  135. package/dist/src/security/scanner/types.d.ts.map +1 -1
  136. package/dist/src/security/scanner/typosquat-reference-list.d.ts +54 -0
  137. package/dist/src/security/scanner/typosquat-reference-list.d.ts.map +1 -0
  138. package/dist/src/security/scanner/typosquat-reference-list.js +55 -0
  139. package/dist/src/security/scanner/typosquat-reference-list.js.map +1 -0
  140. package/dist/src/security/scanner/typosquat-reference-list.test.d.ts +5 -0
  141. package/dist/src/security/scanner/typosquat-reference-list.test.d.ts.map +1 -0
  142. package/dist/src/security/scanner/typosquat-reference-list.test.js +52 -0
  143. package/dist/src/security/scanner/typosquat-reference-list.test.js.map +1 -0
  144. package/dist/src/security/scanner/typosquat.d.ts +100 -0
  145. package/dist/src/security/scanner/typosquat.d.ts.map +1 -0
  146. package/dist/src/security/scanner/typosquat.js +256 -0
  147. package/dist/src/security/scanner/typosquat.js.map +1 -0
  148. package/dist/src/security/scanner/typosquat.test.d.ts +12 -0
  149. package/dist/src/security/scanner/typosquat.test.d.ts.map +1 -0
  150. package/dist/src/security/scanner/typosquat.test.js +140 -0
  151. package/dist/src/security/scanner/typosquat.test.js.map +1 -0
  152. package/dist/src/security/scanner/weights.d.ts.map +1 -1
  153. package/dist/src/security/scanner/weights.js +10 -0
  154. package/dist/src/security/scanner/weights.js.map +1 -1
  155. package/dist/src/services/skill-installation.helpers.d.ts +21 -0
  156. package/dist/src/services/skill-installation.helpers.d.ts.map +1 -1
  157. package/dist/src/services/skill-installation.helpers.js +46 -2
  158. package/dist/src/services/skill-installation.helpers.js.map +1 -1
  159. package/dist/src/services/skill-installation.helpers.test.d.ts +2 -0
  160. package/dist/src/services/skill-installation.helpers.test.d.ts.map +1 -0
  161. package/dist/src/services/skill-installation.helpers.test.js +80 -0
  162. package/dist/src/services/skill-installation.helpers.test.js.map +1 -0
  163. package/dist/src/services/skill-installation.io.d.ts.map +1 -1
  164. package/dist/src/services/skill-installation.io.js +12 -1
  165. package/dist/src/services/skill-installation.io.js.map +1 -1
  166. package/dist/src/sync/inventory-collector.d.ts +15 -4
  167. package/dist/src/sync/inventory-collector.d.ts.map +1 -1
  168. package/dist/src/sync/inventory-collector.js +75 -26
  169. package/dist/src/sync/inventory-collector.js.map +1 -1
  170. package/dist/src/sync/inventory-collector.test.d.ts +15 -1
  171. package/dist/src/sync/inventory-collector.test.d.ts.map +1 -1
  172. package/dist/src/sync/inventory-collector.test.js +75 -5
  173. package/dist/src/sync/inventory-collector.test.js.map +1 -1
  174. package/dist/src/telemetry/tracer-imports.d.ts +1 -1
  175. package/dist/src/telemetry/tracer-imports.js +1 -1
  176. package/dist/src/telemetry/tracer.js +1 -1
  177. package/dist/tests/SecurityScanner.ai-defence.test.js +32 -7
  178. package/dist/tests/SecurityScanner.ai-defence.test.js.map +1 -1
  179. package/dist/tests/SecurityScanner.exec.test.js +79 -0
  180. package/dist/tests/SecurityScanner.exec.test.js.map +1 -1
  181. package/dist/tests/SecurityScanner.scoring.test.js +60 -0
  182. package/dist/tests/SecurityScanner.scoring.test.js.map +1 -1
  183. package/dist/tests/SwarmIndexer.test.js +4 -5
  184. package/dist/tests/SwarmIndexer.test.js.map +1 -1
  185. package/dist/tests/security/ContinuousSecurity.performance.test.js +6 -0
  186. package/dist/tests/security/ContinuousSecurity.performance.test.js.map +1 -1
  187. package/dist/tests/security/ContinuousSecurity.test.js +15 -0
  188. package/dist/tests/security/ContinuousSecurity.test.js.map +1 -1
  189. package/dist/tests/security/ad-crlf-injection.test.d.ts +18 -0
  190. package/dist/tests/security/ad-crlf-injection.test.d.ts.map +1 -0
  191. package/dist/tests/security/ad-crlf-injection.test.js +150 -0
  192. package/dist/tests/security/ad-crlf-injection.test.js.map +1 -0
  193. package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts +25 -0
  194. package/dist/tests/security/ai-defence-multiline-promotion.test.d.ts.map +1 -0
  195. package/dist/tests/security/ai-defence-multiline-promotion.test.js +138 -0
  196. package/dist/tests/security/ai-defence-multiline-promotion.test.js.map +1 -0
  197. package/dist/tests/security/nested-instruction-long-attribute.test.d.ts +15 -0
  198. package/dist/tests/security/nested-instruction-long-attribute.test.d.ts.map +1 -0
  199. package/dist/tests/security/nested-instruction-long-attribute.test.js +60 -0
  200. package/dist/tests/security/nested-instruction-long-attribute.test.js.map +1 -0
  201. package/dist/tests/security/pattern-scope.test.d.ts +17 -0
  202. package/dist/tests/security/pattern-scope.test.d.ts.map +1 -0
  203. package/dist/tests/security/pattern-scope.test.js +128 -0
  204. package/dist/tests/security/pattern-scope.test.js.map +1 -0
  205. package/dist/tests/security/pii-detection.test.js +39 -0
  206. package/dist/tests/security/pii-detection.test.js.map +1 -1
  207. package/dist/tests/security/scanner-evidence-tiers.test.d.ts +30 -0
  208. package/dist/tests/security/scanner-evidence-tiers.test.d.ts.map +1 -0
  209. package/dist/tests/security/scanner-evidence-tiers.test.js +258 -0
  210. package/dist/tests/security/scanner-evidence-tiers.test.js.map +1 -0
  211. package/dist/tests/security/scanner-privesc-severity.test.d.ts +23 -0
  212. package/dist/tests/security/scanner-privesc-severity.test.d.ts.map +1 -0
  213. package/dist/tests/security/scanner-privesc-severity.test.js +43 -0
  214. package/dist/tests/security/scanner-privesc-severity.test.js.map +1 -0
  215. package/dist/tests/security/scanner-regression-guard.exec-locality.test.d.ts +20 -0
  216. package/dist/tests/security/scanner-regression-guard.exec-locality.test.d.ts.map +1 -0
  217. package/dist/tests/security/scanner-regression-guard.exec-locality.test.js +71 -0
  218. package/dist/tests/security/scanner-regression-guard.exec-locality.test.js.map +1 -0
  219. package/dist/tests/security/scanner-regression-guard.test.d.ts +24 -0
  220. package/dist/tests/security/scanner-regression-guard.test.d.ts.map +1 -1
  221. package/dist/tests/security/scanner-regression-guard.test.js +60 -3
  222. package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
  223. package/dist/tests/security/scanner-ssrf-word-boundary.test.d.ts +19 -0
  224. package/dist/tests/security/scanner-ssrf-word-boundary.test.d.ts.map +1 -0
  225. package/dist/tests/security/scanner-ssrf-word-boundary.test.js +65 -0
  226. package/dist/tests/security/scanner-ssrf-word-boundary.test.js.map +1 -0
  227. package/dist/tests/security.test.js +11 -2
  228. package/dist/tests/security.test.js.map +1 -1
  229. package/dist/tests/services/aidefence-feedback.test.js +1 -0
  230. package/dist/tests/services/aidefence-feedback.test.js.map +1 -1
  231. package/dist/tests/skill-scanner/allowlist.test.js +16 -7
  232. package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
  233. package/dist/tests/unit/services/skill-installation.io.test.js +54 -1
  234. package/dist/tests/unit/services/skill-installation.io.test.js.map +1 -1
  235. 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":"AAAA;;;;;;;;;;GAUG;AASH,OAAO,EAAE,wBAAwB,EAAE,MAAM,kCAAkC,CAAA;AAE3E;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAS;IACxC,OAAO,wBAAwB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,wBAAwB,CAAA;AACpE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,aAAa,GAAiC;IACzD,OAAO,EAAE,CAAC;IACV,mBAAmB,EAAE,CAAC;IACtB,sBAAsB,EAAE,CAAC;IACzB,oBAAoB,EAAE,CAAC;IACvB,eAAe,EAAE,CAAC;CACnB,CAAA;AAED,yGAAyG;AACzG,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAA;AAElC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAMhC;IACF,OAAO,EAAE;QACP,MAAM,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;QAC9C,GAAG,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE;KAC5C;IACD,mBAAmB,EAAE;QACnB,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE;QAChD,GAAG,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE;KAClD;IACD,sBAAsB,EAAE;QACtB,MAAM,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE;QACpD,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE;KAChD;IACD,oBAAoB,EAAE;QACpB,MAAM,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE;QACpD,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE;KAChD;IACD,yEAAyE;IACzE,2EAA2E;IAC3E,eAAe,EAAE;QACf,MAAM,EAAE,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE;QACpD,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE;KAChD;CACF,CAAA;AAED,yFAAyF;AACzF,MAAM,UAAU,uBAAuB,CACrC,IAAkB,EAClB,sBAA+B;IAE/B,MAAM,KAAK,GAAG,uBAAuB,CAAC,IAAI,CAAC,CAAA;IAC3C,OAAO,sBAAsB,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAA;AAC1D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,qBAAqB,GAAqC,IAAI,GAAG,CAAC;IACtE,gBAAgB;IAChB,sBAAsB;IACtB,mBAAmB;IACnB,sBAAsB;IACtB,MAAM;CACP,CAAC,CAAA;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,+BAA+B,GAAG,EAAE,CAAA;AAE1C;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,4BAA4B,CAAC,QAA2B;IACtE,MAAM,aAAa,GAAG,QAAQ,CAAC,MAAM,CACnC,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,CAAC,QAAQ,KAAK,MAAM,IAAI,CAAC,CAAC,QAAQ,KAAK,UAAU,CAAC;QACpD,CAAC,CAAC,sBAAsB,KAAK,IAAI;QACjC,qBAAqB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;QACjC,OAAO,CAAC,CAAC,UAAU,KAAK,QAAQ,CACnC,CAAA;IACD,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC;QAAE,OAAM;IAEtC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAC/B,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS;YAAE,SAAQ;QAChD,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ;YAAE,SAAQ;QACpD,MAAM,YAAY,GAAG,aAAa,CAAC,IAAI,CACrC,CAAC,CAAC,EAAE,EAAE,CACJ,IAAI,CAAC,GAAG,CAAE,CAAC,CAAC,UAAqB,GAAI,OAAO,CAAC,UAAqB,CAAC;YACnE,+BAA+B,CAClC,CAAA;QACD,IAAI,CAAC,YAAY;YAAE,SAAQ;QAE3B,MAAM,KAAK,GAAG,OAAO,CAAC,sBAAsB,KAAK,IAAI,CAAA;QACrD,OAAO,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAA;QAC5C,OAAO,CAAC,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAA;QAC7C,OAAO,CAAC,YAAY,GAAG,IAAI,CAAA;QAC3B,OAAO,CAAC,OAAO;YACb,oDAAoD,YAAY,CAAC,IAAI,GAAG;gBACxE,mBAAmB,YAAY,CAAC,UAAU,MAAM,OAAO,CAAC,OAAO,EAAE,CAAA;IACrE,CAAC;AACH,CAAC"}
@@ -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. Mutates the finding in place. Requiring the co-signal to be
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;AA0H/D;;;;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;AAaD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,IAAI,CAgBvE"}
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
- * Conservative homoglyph map (a curated subset of UTS-#39 confusables): only the
50
- * unambiguous Cyrillic / Greek look-alikes that real homoglyph attacks use to
51
- * disguise Latin letters. Fullwidth Latin (offset 0xFEE0) and the Mathematical
52
- * Alphanumeric Symbols block (U+1D400-1D7FF, e.g. bold/italic/script 𝐢𝐠𝐧𝐨𝐫𝐞)
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
- const CONFUSABLES = {
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. Mutates the finding in place. Requiring the co-signal to be
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":"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"}
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;AAM5C;;;;;;;;;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"}
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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"}