@skillsmith/core 0.11.7 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/CHANGELOG.md +200 -0
  2. package/README.md +2 -2
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/src/api/client.types.d.ts +9 -0
  5. package/dist/src/api/client.types.d.ts.map +1 -1
  6. package/dist/src/api/schemas.d.ts +6 -0
  7. package/dist/src/api/schemas.d.ts.map +1 -1
  8. package/dist/src/api/schemas.js +5 -0
  9. package/dist/src/api/schemas.js.map +1 -1
  10. package/dist/src/api/security-summary.d.ts +10 -1
  11. package/dist/src/api/security-summary.d.ts.map +1 -1
  12. package/dist/src/api/security-summary.js +9 -0
  13. package/dist/src/api/security-summary.js.map +1 -1
  14. package/dist/src/api/security-summary.test.js +79 -1
  15. package/dist/src/api/security-summary.test.js.map +1 -1
  16. package/dist/src/api/types.d.ts +9 -0
  17. package/dist/src/api/types.d.ts.map +1 -1
  18. package/dist/src/exports/services.d.ts +1 -1
  19. package/dist/src/exports/services.d.ts.map +1 -1
  20. package/dist/src/exports/services.js +3 -1
  21. package/dist/src/exports/services.js.map +1 -1
  22. package/dist/src/index.d.ts +2 -2
  23. package/dist/src/index.d.ts.map +1 -1
  24. package/dist/src/index.js +2 -2
  25. package/dist/src/index.js.map +1 -1
  26. package/dist/src/install/agent-config-merge.json-array.d.ts +8 -0
  27. package/dist/src/install/agent-config-merge.json-array.d.ts.map +1 -1
  28. package/dist/src/install/agent-config-merge.json-array.js +22 -2
  29. package/dist/src/install/agent-config-merge.json-array.js.map +1 -1
  30. package/dist/src/install/agent-pack-installer.cursor-hooks.d.ts.map +1 -1
  31. package/dist/src/install/agent-pack-installer.cursor-hooks.js +122 -3
  32. package/dist/src/install/agent-pack-installer.cursor-hooks.js.map +1 -1
  33. package/dist/src/install/agent-pack-installer.cursor-hooks.test.js +146 -1
  34. package/dist/src/install/agent-pack-installer.cursor-hooks.test.js.map +1 -1
  35. package/dist/src/install/agent-pack-installer.harness.d.ts +8 -0
  36. package/dist/src/install/agent-pack-installer.harness.d.ts.map +1 -1
  37. package/dist/src/install/agent-pack-installer.harness.js +8 -1
  38. package/dist/src/install/agent-pack-installer.harness.js.map +1 -1
  39. package/dist/src/scripts/__tests__/scan-imported-skills.test.js +25 -0
  40. package/dist/src/scripts/__tests__/scan-imported-skills.test.js.map +1 -1
  41. package/dist/src/scripts/skill-scanner/trust-scorer.js +1 -1
  42. package/dist/src/scripts/skill-scanner/trust-scorer.js.map +1 -1
  43. package/dist/src/security/scanner/SecurityScanner.archive.d.ts +34 -0
  44. package/dist/src/security/scanner/SecurityScanner.archive.d.ts.map +1 -0
  45. package/dist/src/security/scanner/SecurityScanner.archive.js +218 -0
  46. package/dist/src/security/scanner/SecurityScanner.archive.js.map +1 -0
  47. package/dist/src/security/scanner/SecurityScanner.compound.d.ts +1 -0
  48. package/dist/src/security/scanner/SecurityScanner.compound.d.ts.map +1 -1
  49. package/dist/src/security/scanner/SecurityScanner.compound.js +117 -38
  50. package/dist/src/security/scanner/SecurityScanner.compound.js.map +1 -1
  51. package/dist/src/security/scanner/SecurityScanner.d.ts +41 -4
  52. package/dist/src/security/scanner/SecurityScanner.d.ts.map +1 -1
  53. package/dist/src/security/scanner/SecurityScanner.decoy.d.ts +72 -0
  54. package/dist/src/security/scanner/SecurityScanner.decoy.d.ts.map +1 -0
  55. package/dist/src/security/scanner/SecurityScanner.decoy.js +265 -0
  56. package/dist/src/security/scanner/SecurityScanner.decoy.js.map +1 -0
  57. package/dist/src/security/scanner/SecurityScanner.encoding.d.ts +56 -0
  58. package/dist/src/security/scanner/SecurityScanner.encoding.d.ts.map +1 -0
  59. package/dist/src/security/scanner/SecurityScanner.encoding.js +270 -0
  60. package/dist/src/security/scanner/SecurityScanner.encoding.js.map +1 -0
  61. package/dist/src/security/scanner/SecurityScanner.exec.d.ts +23 -20
  62. package/dist/src/security/scanner/SecurityScanner.exec.d.ts.map +1 -1
  63. package/dist/src/security/scanner/SecurityScanner.exec.js +207 -52
  64. package/dist/src/security/scanner/SecurityScanner.exec.js.map +1 -1
  65. package/dist/src/security/scanner/SecurityScanner.fetch-correlation.d.ts +115 -0
  66. package/dist/src/security/scanner/SecurityScanner.fetch-correlation.d.ts.map +1 -0
  67. package/dist/src/security/scanner/SecurityScanner.fetch-correlation.js +181 -0
  68. package/dist/src/security/scanner/SecurityScanner.fetch-correlation.js.map +1 -0
  69. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts +14 -11
  70. package/dist/src/security/scanner/SecurityScanner.helpers.d.ts.map +1 -1
  71. package/dist/src/security/scanner/SecurityScanner.helpers.js +26 -124
  72. package/dist/src/security/scanner/SecurityScanner.helpers.js.map +1 -1
  73. package/dist/src/security/scanner/SecurityScanner.js +114 -36
  74. package/dist/src/security/scanner/SecurityScanner.js.map +1 -1
  75. package/dist/src/security/scanner/SecurityScanner.paste-host.d.ts +69 -0
  76. package/dist/src/security/scanner/SecurityScanner.paste-host.d.ts.map +1 -0
  77. package/dist/src/security/scanner/SecurityScanner.paste-host.js +342 -0
  78. package/dist/src/security/scanner/SecurityScanner.paste-host.js.map +1 -0
  79. package/dist/src/security/scanner/SecurityScanner.risk-score.d.ts +27 -0
  80. package/dist/src/security/scanner/SecurityScanner.risk-score.d.ts.map +1 -0
  81. package/dist/src/security/scanner/SecurityScanner.risk-score.js +178 -0
  82. package/dist/src/security/scanner/SecurityScanner.risk-score.js.map +1 -0
  83. package/dist/src/security/scanner/SecurityScanner.urls.d.ts +20 -0
  84. package/dist/src/security/scanner/SecurityScanner.urls.d.ts.map +1 -0
  85. package/dist/src/security/scanner/SecurityScanner.urls.js +26 -0
  86. package/dist/src/security/scanner/SecurityScanner.urls.js.map +1 -0
  87. package/dist/src/security/scanner/index.d.ts +2 -2
  88. package/dist/src/security/scanner/index.d.ts.map +1 -1
  89. package/dist/src/security/scanner/index.js +5 -1
  90. package/dist/src/security/scanner/index.js.map +1 -1
  91. package/dist/src/security/scanner/multiline-category-closure.test.js +6 -0
  92. package/dist/src/security/scanner/multiline-category-closure.test.js.map +1 -1
  93. package/dist/src/security/scanner/patterns.d.ts +41 -21
  94. package/dist/src/security/scanner/patterns.d.ts.map +1 -1
  95. package/dist/src/security/scanner/patterns.exec.d.ts +89 -0
  96. package/dist/src/security/scanner/patterns.exec.d.ts.map +1 -0
  97. package/dist/src/security/scanner/patterns.exec.js +115 -0
  98. package/dist/src/security/scanner/patterns.exec.js.map +1 -0
  99. package/dist/src/security/scanner/patterns.js +78 -41
  100. package/dist/src/security/scanner/patterns.js.map +1 -1
  101. package/dist/src/security/scanner/types.d.ts +15 -1
  102. package/dist/src/security/scanner/types.d.ts.map +1 -1
  103. package/dist/src/security/scanner/typosquat.d.ts +10 -0
  104. package/dist/src/security/scanner/typosquat.d.ts.map +1 -1
  105. package/dist/src/security/scanner/typosquat.js +5 -1
  106. package/dist/src/security/scanner/typosquat.js.map +1 -1
  107. package/dist/src/security/scanner/weights.d.ts +12 -3
  108. package/dist/src/security/scanner/weights.d.ts.map +1 -1
  109. package/dist/src/security/scanner/weights.js +50 -1
  110. package/dist/src/security/scanner/weights.js.map +1 -1
  111. package/dist/src/services/agent-pack/prompt-source.js +1 -1
  112. package/dist/src/services/agent-pack/prompt-source.js.map +1 -1
  113. package/dist/src/services/bundled-sibling-scan.d.ts +87 -13
  114. package/dist/src/services/bundled-sibling-scan.d.ts.map +1 -1
  115. package/dist/src/services/bundled-sibling-scan.js +177 -33
  116. package/dist/src/services/bundled-sibling-scan.js.map +1 -1
  117. package/dist/src/services/recommend-guard.d.ts.map +1 -1
  118. package/dist/src/services/recommend-guard.js +8 -1
  119. package/dist/src/services/recommend-guard.js.map +1 -1
  120. package/dist/src/services/recommend-guard.test.js +6 -2
  121. package/dist/src/services/recommend-guard.test.js.map +1 -1
  122. package/dist/src/sync/index.d.ts +1 -0
  123. package/dist/src/sync/index.d.ts.map +1 -1
  124. package/dist/src/sync/index.js +2 -0
  125. package/dist/src/sync/index.js.map +1 -1
  126. package/dist/src/sync/license-status-client.d.ts +56 -0
  127. package/dist/src/sync/license-status-client.d.ts.map +1 -0
  128. package/dist/src/sync/license-status-client.js +118 -0
  129. package/dist/src/sync/license-status-client.js.map +1 -0
  130. package/dist/src/sync/license-status-client.test.d.ts +14 -0
  131. package/dist/src/sync/license-status-client.test.d.ts.map +1 -0
  132. package/dist/src/sync/license-status-client.test.js +136 -0
  133. package/dist/src/sync/license-status-client.test.js.map +1 -0
  134. package/dist/src/types.d.ts +13 -0
  135. package/dist/src/types.d.ts.map +1 -1
  136. package/dist/src/utils/version-check.d.ts +58 -5
  137. package/dist/src/utils/version-check.d.ts.map +1 -1
  138. package/dist/src/utils/version-check.js +73 -2
  139. package/dist/src/utils/version-check.js.map +1 -1
  140. package/dist/src/utils/version-check.test.js +49 -10
  141. package/dist/src/utils/version-check.test.js.map +1 -1
  142. package/dist/tests/SecurityScanner.scoring.test.js +123 -0
  143. package/dist/tests/SecurityScanner.scoring.test.js.map +1 -1
  144. package/dist/tests/security/advisory-severity-guard.test.d.ts +20 -0
  145. package/dist/tests/security/advisory-severity-guard.test.d.ts.map +1 -0
  146. package/dist/tests/security/advisory-severity-guard.test.js +70 -0
  147. package/dist/tests/security/advisory-severity-guard.test.js.map +1 -0
  148. package/dist/tests/security/archive-evasion.test.d.ts +2 -0
  149. package/dist/tests/security/archive-evasion.test.d.ts.map +1 -0
  150. package/dist/tests/security/archive-evasion.test.js +126 -0
  151. package/dist/tests/security/archive-evasion.test.js.map +1 -0
  152. package/dist/tests/security/clawhavoc-e2e.fixtures.d.ts +71 -0
  153. package/dist/tests/security/clawhavoc-e2e.fixtures.d.ts.map +1 -0
  154. package/dist/tests/security/clawhavoc-e2e.fixtures.js +135 -0
  155. package/dist/tests/security/clawhavoc-e2e.fixtures.js.map +1 -0
  156. package/dist/tests/security/co-signal-escalation.test.d.ts +30 -0
  157. package/dist/tests/security/co-signal-escalation.test.d.ts.map +1 -0
  158. package/dist/tests/security/co-signal-escalation.test.js +494 -0
  159. package/dist/tests/security/co-signal-escalation.test.js.map +1 -0
  160. package/dist/tests/security/decoy-misdirection.test.d.ts +2 -0
  161. package/dist/tests/security/decoy-misdirection.test.d.ts.map +1 -0
  162. package/dist/tests/security/decoy-misdirection.test.js +130 -0
  163. package/dist/tests/security/decoy-misdirection.test.js.map +1 -0
  164. package/dist/tests/security/encoded-payload.test.d.ts +2 -0
  165. package/dist/tests/security/encoded-payload.test.d.ts.map +1 -0
  166. package/dist/tests/security/encoded-payload.test.js +199 -0
  167. package/dist/tests/security/encoded-payload.test.js.map +1 -0
  168. package/dist/tests/security/gatekeeper-bypass.test.d.ts +2 -0
  169. package/dist/tests/security/gatekeeper-bypass.test.d.ts.map +1 -0
  170. package/dist/tests/security/gatekeeper-bypass.test.js +167 -0
  171. package/dist/tests/security/gatekeeper-bypass.test.js.map +1 -0
  172. package/dist/tests/security/paste-host-fetch.test.d.ts +2 -0
  173. package/dist/tests/security/paste-host-fetch.test.d.ts.map +1 -0
  174. package/dist/tests/security/paste-host-fetch.test.js +199 -0
  175. package/dist/tests/security/paste-host-fetch.test.js.map +1 -0
  176. package/dist/tests/security/scanner-regression-guard.test.js +5 -0
  177. package/dist/tests/security/scanner-regression-guard.test.js.map +1 -1
  178. package/dist/tests/services/aidefence-feedback.test.js +5 -0
  179. package/dist/tests/services/aidefence-feedback.test.js.map +1 -1
  180. package/dist/tests/services/bundled-sibling-scan.test.js +171 -10
  181. package/dist/tests/services/bundled-sibling-scan.test.js.map +1 -1
  182. package/dist/tests/skill-scanner/allowlist.test.js +5 -0
  183. package/dist/tests/skill-scanner/allowlist.test.js.map +1 -1
  184. package/package.json +1 -1
@@ -64,6 +64,16 @@ export type { TyposquatEnforcementMode };
64
64
  * cross-boundary-import constraint.
65
65
  */
66
66
  export declare const BRAND_ALIASES: Readonly<Record<string, string>>;
67
+ /**
68
+ * A small, curated "claims official status" affix list. Deliberately narrow —
69
+ * benign functional affixes (`mcp`, `tools`, `community`, `helper`, ...)
70
+ * simply aren't in this list, so they never trigger this check.
71
+ *
72
+ * SMI-6033 Wave 1 (Gap 6): exported (was module-private) so the decoy/
73
+ * misdirection detector (`SecurityScanner.decoy.ts`, Wave 4) can reuse the
74
+ * same curated affix corpus rather than duplicating it.
75
+ */
76
+ export declare const AUTHORITY_CLAIMING_AFFIXES: ReadonlySet<string>;
67
77
  /**
68
78
  * Standard iterative-DP Levenshtein edit distance (insertion/deletion/
69
79
  * substitution). Inputs here are short skill-name strings, so an O(n*m) table
@@ -1 +1 @@
1
- {"version":3,"file":"typosquat.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAG3E,YAAY,EAAE,wBAAwB,EAAE,CAAA;AAMxC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO1D,CAAA;AAmDD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBhE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,eAAe,EAAE,CAgEnB;AAMD;gFACgF;AAChF,eAAO,MAAM,kCAAkC,EAAE,wBAAiC,CAAA;AAElF,+EAA+E;AAC/E,wBAAgB,+BAA+B,CAC7C,IAAI,CAAC,EAAE,wBAAwB,GAC9B,wBAAwB,CAE1B;AAID;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,eAAe,EAAE,EAC3B,IAAI,GAAE,wBAA6D,GAClE,eAAe,EAAE,CAQnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,EACnC,IAAI,GAAE,wBAA6D,GAClE,eAAe,EAAE,CAEnB"}
1
+ {"version":3,"file":"typosquat.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AAG3E,YAAY,EAAE,wBAAwB,EAAE,CAAA;AAMxC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,aAAa,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAO1D,CAAA;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,EAAE,WAAW,CAAC,MAAM,CAKzD,CAAA;AAmCF;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAyBhE;AAMD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,eAAe,EAAE,CAgEnB;AAMD;gFACgF;AAChF,eAAO,MAAM,kCAAkC,EAAE,wBAAiC,CAAA;AAElF,+EAA+E;AAC/E,wBAAgB,+BAA+B,CAC7C,IAAI,CAAC,EAAE,wBAAwB,GAC9B,wBAAwB,CAE1B;AAID;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,QAAQ,EAAE,eAAe,EAAE,EAC3B,IAAI,GAAE,wBAA6D,GAClE,eAAe,EAAE,CAQnB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,EACnC,IAAI,GAAE,wBAA6D,GAClE,eAAe,EAAE,CAEnB"}
@@ -80,8 +80,12 @@ export const BRAND_ALIASES = {
80
80
  * A small, curated "claims official status" affix list. Deliberately narrow —
81
81
  * benign functional affixes (`mcp`, `tools`, `community`, `helper`, ...)
82
82
  * simply aren't in this list, so they never trigger this check.
83
+ *
84
+ * SMI-6033 Wave 1 (Gap 6): exported (was module-private) so the decoy/
85
+ * misdirection detector (`SecurityScanner.decoy.ts`, Wave 4) can reuse the
86
+ * same curated affix corpus rather than duplicating it.
83
87
  */
84
- const AUTHORITY_CLAIMING_AFFIXES = new Set([
88
+ export const AUTHORITY_CLAIMING_AFFIXES = new Set([
85
89
  'official',
86
90
  'verified',
87
91
  'authentic',
@@ -1 +1 @@
1
- {"version":3,"file":"typosquat.js","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"typosquat.js","sourceRoot":"","sources":["../../../../src/security/scanner/typosquat.ts"],"names":[],"mappings":"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"}
@@ -3,13 +3,22 @@
3
3
  *
4
4
  * Weight constants for risk score calculation.
5
5
  */
6
- import type { SecuritySeverity } from './types.js';
6
+ import type { SecurityFindingType, SecuritySeverity } from './types.js';
7
7
  /**
8
8
  * Severity weights for risk score calculation
9
9
  */
10
10
  export declare const SEVERITY_WEIGHTS: Record<SecuritySeverity, number>;
11
11
  /**
12
- * Category weights for risk score calculation
12
+ * Category weights for risk score calculation.
13
+ *
14
+ * SMI-6033 Wave 2 gap-closing pass: typed against `SecurityFindingType`
15
+ * (was `Record<string, number>`) so a future new finding type that ships
16
+ * without a weight entry here is a TypeScript compile error, not a silent
17
+ * runtime `?? 1.0` fallback in SecurityScanner.helpers.ts's
18
+ * `calculateRiskScore` — the exact class of gap
19
+ * scripts/tests/indexer/parity.test.ts's new consolidated core<->edge
20
+ * coverage test (SMI-6033 Wave 2) was added to catch on the edge side; this
21
+ * closes the equivalent hole on the core side, one layer earlier.
13
22
  */
14
- export declare const CATEGORY_WEIGHTS: Record<string, number>;
23
+ export declare const CATEGORY_WEIGHTS: Record<SecurityFindingType, number>;
15
24
  //# sourceMappingURL=weights.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"weights.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/weights.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAK7D,CAAA;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA4BnD,CAAA"}
1
+ {"version":3,"file":"weights.d.ts","sourceRoot":"","sources":["../../../../src/security/scanner/weights.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAEvE;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAK7D,CAAA;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAoEhE,CAAA"}
@@ -13,7 +13,16 @@ export const SEVERITY_WEIGHTS = {
13
13
  critical: 50,
14
14
  };
15
15
  /**
16
- * Category weights for risk score calculation
16
+ * Category weights for risk score calculation.
17
+ *
18
+ * SMI-6033 Wave 2 gap-closing pass: typed against `SecurityFindingType`
19
+ * (was `Record<string, number>`) so a future new finding type that ships
20
+ * without a weight entry here is a TypeScript compile error, not a silent
21
+ * runtime `?? 1.0` fallback in SecurityScanner.helpers.ts's
22
+ * `calculateRiskScore` — the exact class of gap
23
+ * scripts/tests/indexer/parity.test.ts's new consolidated core<->edge
24
+ * coverage test (SMI-6033 Wave 2) was added to catch on the edge side; this
25
+ * closes the equivalent hole on the core side, one layer earlier.
17
26
  */
18
27
  export const CATEGORY_WEIGHTS = {
19
28
  jailbreak: 2.0,
@@ -43,5 +52,45 @@ export const CATEGORY_WEIGHTS = {
43
52
  // comfortably under the riskThreshold: 40 quarantine cutoff on its own. See the
44
53
  // stacked-risk test in SecurityScanner.scoring.test.ts.
45
54
  typosquat: 1.2,
55
+ // SMI-6033 Wave 2 (Gap 5/Gap 3): top-tier weight matching code_execution/
56
+ // obfuscated_directive — the SAME single weight is used for both the
57
+ // critical (standalone-quarantining) and medium (advisory, co-signal-
58
+ // eligible) forms of each type; severity alone (SEVERITY_WEIGHTS 50 vs 15)
59
+ // does the two-tier split, exactly as scanChmodFetchCompound's own
60
+ // privilege_escalation compound signal already does with ONE weight
61
+ // (1.9/0.11) across its high/low severities. Paired with the 0.40
62
+ // coefficient in calculateRiskScore: a single CRITICAL finding reaches
63
+ // exactly the 40 quarantine threshold on its own (50 * 2.0 * 1.0 = 100 ->
64
+ // capped 100 -> * 0.40 = 40); a single MEDIUM finding contributes 12
65
+ // (15 * 2.0 * 1.0 = 30 -> * 0.40 = 12) — well under threshold alone.
66
+ gatekeeper_bypass: 2.0,
67
+ archive_evasion: 2.0,
68
+ // SMI-6033 Wave 2 (Gap 4): same top-tier weight as gatekeeper_bypass/
69
+ // archive_evasion, for the same reason — a paste-host URL that is the
70
+ // TARGET of a fetch command reaches exactly the 40 quarantine threshold
71
+ // standalone (50 * 2.0 * 1.0 = 100 -> capped 100 -> * 0.40 = 40), while a
72
+ // paste-host URL that is merely linked never reaches this detector at all
73
+ // (see SecurityScanner.paste-host.ts) and stays covered by the existing
74
+ // url:medium finding instead.
75
+ paste_host_fetch: 2.0,
76
+ // SMI-6033 Wave 2 (Gap 2): the sensitive_path/typosquat tier (1.2/0.04 —
77
+ // NOT the 2.0/0.40 tier every other Wave 2 detector above uses). This
78
+ // detector's wrapper finding is deliberately advisory-only: the escalation
79
+ // Gap 2 achieves comes for free from the decoded content's OWN findings
80
+ // (e.g. a decoded `curl|bash` natively trips code_execution at ITS OWN
81
+ // top-tier weight), not from encoded_payload itself. A single medium,
82
+ // high-confidence finding scores 15 * 1.2 * 1.0 = 18 -> * 0.04 = 0.72
83
+ // (rounds to ~1); a saturated breakdown (capped at 100) contributes 4 —
84
+ // comfortably under the riskThreshold: 40 quarantine cutoff on its own.
85
+ encoded_payload: 1.2,
86
+ // SMI-6033 Wave 4 (Gap 6): same advisory tier as typosquat/encoded_payload/
87
+ // sensitive_path (1.2/0.04) — NOT the 2.0/0.40 top tier gatekeeper_bypass/
88
+ // archive_evasion/paste_host_fetch use, since this finding type must never
89
+ // be standalone-critical (plan §9: "N/A — never standalone... Approximate
90
+ // NL heuristic by construction; co-signal required"). A single medium,
91
+ // high-confidence finding scores 15 * 1.2 * 1.0 = 18 -> * 0.04 = 0.72
92
+ // (rounds to ~1); a saturated breakdown (capped at 100) contributes 4 —
93
+ // comfortably under the riskThreshold: 40 quarantine cutoff on its own.
94
+ decoy_misdirection: 1.2,
46
95
  };
47
96
  //# sourceMappingURL=weights.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"weights.js","sourceRoot":"","sources":["../../../../src/security/scanner/weights.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAqC;IAChE,GAAG,EAAE,CAAC;IACN,MAAM,EAAE,EAAE;IACV,IAAI,EAAE,EAAE;IACR,QAAQ,EAAE,EAAE;CACb,CAAA;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAA2B;IACtD,SAAS,EAAE,GAAG;IACd,kBAAkB,EAAE,GAAG;IACvB,cAAc,EAAE,GAAG;IACnB,iBAAiB,EAAE,GAAG;IACtB,oBAAoB,EAAE,GAAG;IACzB,kBAAkB,EAAE,GAAG;IACvB,cAAc,EAAE,GAAG;IACnB,GAAG,EAAE,GAAG;IACR,UAAU,EAAE,GAAG,EAAE,iDAAiD;IAClE,IAAI,EAAE,GAAG,EAAE,uCAAuC;IAClD,GAAG,EAAE,GAAG,EAAE,8CAA8C;IACxD,8EAA8E;IAC9E,iFAAiF;IACjF,kFAAkF;IAClF,kEAAkE;IAClE,cAAc,EAAE,GAAG;IACnB,oBAAoB,EAAE,GAAG;IACzB,+EAA+E;IAC/E,8EAA8E;IAC9E,8EAA8E;IAC9E,+DAA+D;IAC/D,+EAA+E;IAC/E,gFAAgF;IAChF,0EAA0E;IAC1E,gFAAgF;IAChF,wDAAwD;IACxD,SAAS,EAAE,GAAG;CACf,CAAA"}
1
+ {"version":3,"file":"weights.js","sourceRoot":"","sources":["../../../../src/security/scanner/weights.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAqC;IAChE,GAAG,EAAE,CAAC;IACN,MAAM,EAAE,EAAE;IACV,IAAI,EAAE,EAAE;IACR,QAAQ,EAAE,EAAE;CACb,CAAA;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAwC;IACnE,SAAS,EAAE,GAAG;IACd,kBAAkB,EAAE,GAAG;IACvB,cAAc,EAAE,GAAG;IACnB,iBAAiB,EAAE,GAAG;IACtB,oBAAoB,EAAE,GAAG;IACzB,kBAAkB,EAAE,GAAG;IACvB,cAAc,EAAE,GAAG;IACnB,GAAG,EAAE,GAAG;IACR,UAAU,EAAE,GAAG,EAAE,iDAAiD;IAClE,IAAI,EAAE,GAAG,EAAE,uCAAuC;IAClD,GAAG,EAAE,GAAG,EAAE,8CAA8C;IACxD,8EAA8E;IAC9E,iFAAiF;IACjF,kFAAkF;IAClF,kEAAkE;IAClE,cAAc,EAAE,GAAG;IACnB,oBAAoB,EAAE,GAAG;IACzB,+EAA+E;IAC/E,8EAA8E;IAC9E,8EAA8E;IAC9E,+DAA+D;IAC/D,+EAA+E;IAC/E,gFAAgF;IAChF,0EAA0E;IAC1E,gFAAgF;IAChF,wDAAwD;IACxD,SAAS,EAAE,GAAG;IACd,0EAA0E;IAC1E,qEAAqE;IACrE,sEAAsE;IACtE,2EAA2E;IAC3E,mEAAmE;IACnE,oEAAoE;IACpE,kEAAkE;IAClE,uEAAuE;IACvE,0EAA0E;IAC1E,qEAAqE;IACrE,qEAAqE;IACrE,iBAAiB,EAAE,GAAG;IACtB,eAAe,EAAE,GAAG;IACpB,sEAAsE;IACtE,sEAAsE;IACtE,wEAAwE;IACxE,0EAA0E;IAC1E,0EAA0E;IAC1E,wEAAwE;IACxE,8BAA8B;IAC9B,gBAAgB,EAAE,GAAG;IACrB,yEAAyE;IACzE,sEAAsE;IACtE,2EAA2E;IAC3E,wEAAwE;IACxE,uEAAuE;IACvE,sEAAsE;IACtE,sEAAsE;IACtE,wEAAwE;IACxE,wEAAwE;IACxE,eAAe,EAAE,GAAG;IACpB,4EAA4E;IAC5E,2EAA2E;IAC3E,2EAA2E;IAC3E,0EAA0E;IAC1E,uEAAuE;IACvE,sEAAsE;IACtE,wEAAwE;IACxE,wEAAwE;IACxE,kBAAkB,EAAE,GAAG;CACxB,CAAA"}
@@ -155,7 +155,7 @@ export const PAYWALL_TRIGGERS = [
155
155
  {
156
156
  id: 'T2',
157
157
  title: 'T2 - quota forecast (to Individual)',
158
- body: 'When usage is on track to exhaust the free 1,000-call monthly quota, you may note the forecast ("at this pace you reach the cap in about K days") and mention Individual\'s 10,000 calls. Use this sparingly; a quota nag reads as a tax.',
158
+ body: 'When usage is on track to exhaust the free 100-call monthly quota, you may note the forecast ("at this pace you reach the cap in about K days") and mention Individual\'s 1,000 calls. Use this sparingly; a quota nag reads as a tax.',
159
159
  },
160
160
  {
161
161
  id: 'T4',
@@ -1 +1 @@
1
- {"version":3,"file":"prompt-source.js","sourceRoot":"","sources":["../../../../src/services/agent-pack/prompt-source.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"prompt-source.js","sourceRoot":"","sources":["../../../../src/services/agent-pack/prompt-source.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,8 @@
4
4
  *
5
5
  * Walks an installed skill's bundle directory and scans its sibling bundled
6
6
  * files (`.mcp.json`, `.claude/settings*.json`, `package.json` lifecycle hooks,
7
- * `config.json`, `scripts/*.sh` + top-level `*.sh`) so `skill_rescan` can
7
+ * `config.json`, and executable-code files under `scripts/`/`src/`/`bin/`
8
+ * plus the skill's top level) so `skill_rescan` can
8
9
  * quarantine a skill whose MALICIOUS sibling — not its `SKILL.md` — carries the
9
10
  * threat (CVE-2025-59536 hook execution, `curl|bash` postinstall, a
10
11
  * remote-fetch-execute install script).
@@ -35,13 +36,35 @@
35
36
  * INHERITED DETECTION GAPS (FN, tracked SMI-5424): bare-interpreter hook
36
37
  * payloads (`node evil.js`, `python evil.py`, `bun`/`deno`), `&&`/`;`-chained
37
38
  * fetch-then-exec, `npx`, and JSON `\uXXXX`-escaped commands inside raw-scanned
38
- * structured files are NOT detected. `.js`/`.py`/`.mjs`/`.ts` payload files and
39
- * nested/non-`scripts/` `.sh` are a Phase-3 follow-up (recursive bundle walk).
39
+ * structured files are NOT detected.
40
+ *
41
+ * SMI-6033 Wave 2 (Gap 8) CLOSED PART OF THE PHASE-3 TODO: the glob is no
42
+ * longer `.sh`-only and no longer `scripts/`-only. It now covers every
43
+ * {@link EXECUTABLE_CODE_EXTENSIONS} file at the skill's top level and as a
44
+ * DIRECT child of `scripts/`, `src/` or `bin/` — the same SCOPE, RANKING and
45
+ * COUNT cap the indexer applies registry-side (`scripts/indexer/
46
+ * skill-processor.security.tree.ts`'s `MAX_EXTENDED_SIBLING_FILES = 20`), so
47
+ * a skill scanned locally and the same skill scanned in the registry select
48
+ * the same candidate FILES. The per-file BYTE cap is deliberately NOT
49
+ * unified: {@link MAX_SIBLING_FILE_BYTES} (512 KB) here vs the indexer's
50
+ * `MAX_SIBLING_CONTENT_BYTES` (256 KB, `skill-processor.security.ts`) — a
51
+ * pre-existing divergence this wave did not introduce and did not reconcile
52
+ * (adversarial review, 2026-08-16). A file between 256 KB and 512 KB is
53
+ * therefore scanned locally but `size_cap`-dropped registry-side; harmless
54
+ * (fail-open, surfaced via `scan_coverage_incomplete`/`skippedOversize`, never
55
+ * silent) but worth knowing before assuming the two verdicts always agree.
56
+ * Still deliberately shallow: a fully recursive bundle walk remains out of
57
+ * scope.
58
+ *
40
59
  * COUNT-CAP DECOY-PADDING (FN): the fixed bundled files are cap-exempt, but the
41
- * `.sh` glob is capped — an author can name the malicious script to sort last
42
- * and pad `scripts/` with cap-many benign decoys so it lands in `droppedForCount`
43
- * and is never scanned. This is surfaced (never a silent drop) but not blocked;
44
- * a cumulative-resource bound is a Phase-3 follow-up.
60
+ * executable-code glob is capped — an author can name the malicious script to
61
+ * rank last and pad the scanned directories with cap-many benign decoys so it
62
+ * lands in `droppedForCount` and is never scanned. Wave 2's four-tier ranking
63
+ * (SKILL.md-referenced, then entry-point-named, then shallow, then
64
+ * lexicographic) raises the bar — to land past the cap the payload must also
65
+ * be unreferenced from SKILL.md and not entry-point-named, which cuts against
66
+ * it actually being executed — but does not close the gap. Surfaced (never a
67
+ * silent drop), not blocked; a cumulative-resource bound is a follow-up.
45
68
  *
46
69
  * config.json IS scanned here (only doc-class siblings are skipped), whereas the
47
70
  * Phase-1 validate helper (validate-bundled-scan.ts) also skips `config` — rescan
@@ -49,16 +72,47 @@
49
72
  */
50
73
  import type { SecurityFinding } from '../security/scanner/types.js';
51
74
  import type { SecurityScanner } from '../security/index.js';
52
- /** Max `.sh` glob files scanned per skill (fixed bundled files are exempt). */
53
- export declare const MAX_SIBLING_SH_FILES = 50;
75
+ /**
76
+ * Operational-code extensions scanned by the glob (SMI-6033 Wave 2, Gap 8).
77
+ *
78
+ * Deliberately duplicated from `scripts/indexer/skill-processor.security.tree.ts`
79
+ * rather than shared: that module lives in the Node/Deno indexer trees, which
80
+ * cannot import `@skillsmith/core` (git-crypt boundary + Deno bundling — see
81
+ * that file's twin header). `parity.test.ts` pins the two lists equal, so the
82
+ * duplication cannot drift silently.
83
+ */
84
+ export declare const EXECUTABLE_CODE_EXTENSIONS: readonly [".sh", ".py", ".js", ".mjs", ".cjs", ".ts", ".rb", ".php", ".ps1", ".pl"];
85
+ /** Directories globbed for operational code, relative to the skill directory. */
86
+ export declare const EXTENDED_SCAN_DIRS: readonly ["scripts", "src", "bin"];
87
+ /** Basenames (extension-stripped, lowercased) that rank as entry points (tier 2). */
88
+ export declare const ENTRY_POINT_BASENAMES: Set<string>;
89
+ /**
90
+ * Max globbed operational-code files scanned per skill (fixed bundled files
91
+ * are exempt).
92
+ *
93
+ * SMI-6033 Wave 2 (Gap 8): narrowed from the previous `.sh`-only cap of 50 to
94
+ * 20, matching `MAX_EXTENDED_SIBLING_FILES` on the indexer side. Intentional
95
+ * behaviour change: the glob now covers ten extensions across four directories
96
+ * instead of one extension across two, so an uncapped-at-50 policy would fetch
97
+ * and scan far more per skill than before. 20 ranked files is the plan's
98
+ * chosen budget on BOTH surfaces, and keeping the two equal is what makes the
99
+ * local and registry verdicts comparable.
100
+ */
101
+ export declare const MAX_EXTENDED_SIBLING_FILES = 20;
54
102
  /** Per-file byte ceiling; larger siblings are skipped (recorded, never silent). */
55
103
  export declare const MAX_SIBLING_FILE_BYTES: number;
56
104
  /** Tunable caps for {@link scanLocalBundleSiblings}. */
57
105
  export interface BundledSiblingScanOptions {
58
- /** Override {@link MAX_SIBLING_SH_FILES}. */
59
- maxShFiles?: number;
106
+ /** Override {@link MAX_EXTENDED_SIBLING_FILES}. */
107
+ maxExtendedFiles?: number;
60
108
  /** Override {@link MAX_SIBLING_FILE_BYTES}. */
61
109
  maxBytesPerFile?: number;
110
+ /**
111
+ * The skill's own `SKILL.md` text, used only for the tier-1 ranking check
112
+ * ("is this file referenced by path from SKILL.md?"). Omitted, tier 1 simply
113
+ * never matches and ranking falls through to tiers 2-4 — never an error.
114
+ */
115
+ primaryContent?: string;
62
116
  }
63
117
  /**
64
118
  * Result of scanning a skill bundle's sibling files. All path fields are
@@ -83,19 +137,39 @@ export interface BundledSiblingScanResult {
83
137
  * mis-attribute a quarantine's surfaced score.
84
138
  */
85
139
  maxSiblingRiskScore: number;
86
- /** `.sh` files beyond the count cap — surfaced, never silently dropped. */
140
+ /** Executable-code files beyond the count cap — surfaced, never silently dropped. */
87
141
  droppedForCount: string[];
88
142
  /** Files skipped for exceeding the byte cap. */
89
143
  skippedOversize: string[];
90
144
  /** Symlink siblings resolving outside the skill dir (SMI-4287 guard). */
91
145
  skippedSymlinkEscape: string[];
92
146
  }
147
+ /**
148
+ * SMI-6033 Wave 2 (Gap 8): rank operational-code candidates so the count cap
149
+ * keeps the files most likely to actually be executed.
150
+ *
151
+ * Four tiers, in order: (1) referenced literally by path from `SKILL.md`,
152
+ * (2) entry-point basename, (3) shallower path first, (4) lexicographic.
153
+ *
154
+ * DETERMINISM is a required property, not incidental: the same candidate set
155
+ * must always produce the same order. Tier 4 is a TOTAL order over unique
156
+ * relative paths, so the result never depends on `Array.prototype.sort` being
157
+ * stable; and the compare uses raw `<`/`>` rather than `localeCompare`, whose
158
+ * collation is ICU/locale dependent. This mirrors the identical comparator in
159
+ * the indexer's `enumerateExtendedSiblingTargets`, so the two surfaces select
160
+ * the same files in the same order from the same bundle.
161
+ *
162
+ * NOTE: ranking does NOT defeat decoy-padding — see the module header. The
163
+ * dropped names are always surfaced in `droppedForCount` (no silent drop).
164
+ */
165
+ export declare function rankExecutableCodeFiles(relPaths: readonly string[], primaryContent: string): string[];
93
166
  /**
94
167
  * Scan a skill bundle's sibling files and return the structured result.
95
168
  *
96
169
  * Fixed {@link BUNDLED_SCAN_FILES} are always scanned (exempt from the count
97
170
  * cap, so a decoy-padding attack on `scripts/` cannot push the primary hook /
98
- * postinstall surface out of the scan window). The `.sh` glob is capped and
171
+ * postinstall surface out of the scan window). The executable-code glob is
172
+ * ranked then capped, and
99
173
  * any overflow is reported in `droppedForCount`.
100
174
  *
101
175
  * Doc-class siblings (`README.md`, `examples.md`) are intentionally NOT scanned:
@@ -1 +1 @@
1
- {"version":3,"file":"bundled-sibling-scan.d.ts","sourceRoot":"","sources":["../../../src/services/bundled-sibling-scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAc,MAAM,8BAA8B,CAAA;AAI/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAQ3D,+EAA+E;AAC/E,eAAO,MAAM,oBAAoB,KAAK,CAAA;AACtC,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,QAAa,CAAA;AAEhD,wDAAwD;AACxD,MAAM,WAAW,yBAAyB;IACxC,6CAA6C;IAC7C,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,2EAA2E;IAC3E,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,+EAA+E;IAC/E,UAAU,EAAE,OAAO,CAAA;IACnB,2DAA2D;IAC3D,kBAAkB,EAAE,eAAe,EAAE,CAAA;IACrC,2CAA2C;IAC3C,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,uCAAuC;IACvC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB;;;;;OAKG;IACH,mBAAmB,EAAE,MAAM,CAAA;IAC3B,2EAA2E;IAC3E,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,gDAAgD;IAChD,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,yEAAyE;IACzE,oBAAoB,EAAE,MAAM,EAAE,CAAA;CAC/B;AAgCD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,IAAI,GAAE,yBAA8B,GACnC,OAAO,CAAC,wBAAwB,CAAC,CAuEnC"}
1
+ {"version":3,"file":"bundled-sibling-scan.d.ts","sourceRoot":"","sources":["../../../src/services/bundled-sibling-scan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuEG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAc,MAAM,8BAA8B,CAAA;AAI/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAQ3D;;;;;;;;GAQG;AACH,eAAO,MAAM,0BAA0B,qFAW7B,CAAA;AAEV,iFAAiF;AACjF,eAAO,MAAM,kBAAkB,oCAAqC,CAAA;AAEpE,qFAAqF;AACrF,eAAO,MAAM,qBAAqB,aAOhC,CAAA;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,KAAK,CAAA;AAC5C,mFAAmF;AACnF,eAAO,MAAM,sBAAsB,QAAa,CAAA;AAEhD,wDAAwD;AACxD,MAAM,WAAW,yBAAyB;IACxC,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,+CAA+C;IAC/C,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,wBAAwB;IACvC,2EAA2E;IAC3E,QAAQ,EAAE,eAAe,EAAE,CAAA;IAC3B,+EAA+E;IAC/E,UAAU,EAAE,OAAO,CAAA;IACnB,2DAA2D;IAC3D,kBAAkB,EAAE,eAAe,EAAE,CAAA;IACrC,2CAA2C;IAC3C,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,uCAAuC;IACvC,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB;;;;;OAKG;IACH,mBAAmB,EAAE,MAAM,CAAA;IAC3B,qFAAqF;IACrF,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,gDAAgD;IAChD,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,yEAAyE;IACzE,oBAAoB,EAAE,MAAM,EAAE,CAAA;CAC/B;AAqCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,cAAc,EAAE,MAAM,GACrB,MAAM,EAAE,CAcV;AA8BD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,EACxB,IAAI,GAAE,yBAA8B,GACnC,OAAO,CAAC,wBAAwB,CAAC,CAqFnC"}
@@ -4,7 +4,8 @@
4
4
  *
5
5
  * Walks an installed skill's bundle directory and scans its sibling bundled
6
6
  * files (`.mcp.json`, `.claude/settings*.json`, `package.json` lifecycle hooks,
7
- * `config.json`, `scripts/*.sh` + top-level `*.sh`) so `skill_rescan` can
7
+ * `config.json`, and executable-code files under `scripts/`/`src/`/`bin/`
8
+ * plus the skill's top level) so `skill_rescan` can
8
9
  * quarantine a skill whose MALICIOUS sibling — not its `SKILL.md` — carries the
9
10
  * threat (CVE-2025-59536 hook execution, `curl|bash` postinstall, a
10
11
  * remote-fetch-execute install script).
@@ -35,13 +36,35 @@
35
36
  * INHERITED DETECTION GAPS (FN, tracked SMI-5424): bare-interpreter hook
36
37
  * payloads (`node evil.js`, `python evil.py`, `bun`/`deno`), `&&`/`;`-chained
37
38
  * fetch-then-exec, `npx`, and JSON `\uXXXX`-escaped commands inside raw-scanned
38
- * structured files are NOT detected. `.js`/`.py`/`.mjs`/`.ts` payload files and
39
- * nested/non-`scripts/` `.sh` are a Phase-3 follow-up (recursive bundle walk).
39
+ * structured files are NOT detected.
40
+ *
41
+ * SMI-6033 Wave 2 (Gap 8) CLOSED PART OF THE PHASE-3 TODO: the glob is no
42
+ * longer `.sh`-only and no longer `scripts/`-only. It now covers every
43
+ * {@link EXECUTABLE_CODE_EXTENSIONS} file at the skill's top level and as a
44
+ * DIRECT child of `scripts/`, `src/` or `bin/` — the same SCOPE, RANKING and
45
+ * COUNT cap the indexer applies registry-side (`scripts/indexer/
46
+ * skill-processor.security.tree.ts`'s `MAX_EXTENDED_SIBLING_FILES = 20`), so
47
+ * a skill scanned locally and the same skill scanned in the registry select
48
+ * the same candidate FILES. The per-file BYTE cap is deliberately NOT
49
+ * unified: {@link MAX_SIBLING_FILE_BYTES} (512 KB) here vs the indexer's
50
+ * `MAX_SIBLING_CONTENT_BYTES` (256 KB, `skill-processor.security.ts`) — a
51
+ * pre-existing divergence this wave did not introduce and did not reconcile
52
+ * (adversarial review, 2026-08-16). A file between 256 KB and 512 KB is
53
+ * therefore scanned locally but `size_cap`-dropped registry-side; harmless
54
+ * (fail-open, surfaced via `scan_coverage_incomplete`/`skippedOversize`, never
55
+ * silent) but worth knowing before assuming the two verdicts always agree.
56
+ * Still deliberately shallow: a fully recursive bundle walk remains out of
57
+ * scope.
58
+ *
40
59
  * COUNT-CAP DECOY-PADDING (FN): the fixed bundled files are cap-exempt, but the
41
- * `.sh` glob is capped — an author can name the malicious script to sort last
42
- * and pad `scripts/` with cap-many benign decoys so it lands in `droppedForCount`
43
- * and is never scanned. This is surfaced (never a silent drop) but not blocked;
44
- * a cumulative-resource bound is a Phase-3 follow-up.
60
+ * executable-code glob is capped — an author can name the malicious script to
61
+ * rank last and pad the scanned directories with cap-many benign decoys so it
62
+ * lands in `droppedForCount` and is never scanned. Wave 2's four-tier ranking
63
+ * (SKILL.md-referenced, then entry-point-named, then shallow, then
64
+ * lexicographic) raises the bar — to land past the cap the payload must also
65
+ * be unreferenced from SKILL.md and not entry-point-named, which cuts against
66
+ * it actually being executed — but does not close the gap. Surfaced (never a
67
+ * silent drop), not blocked; a cumulative-resource bound is a follow-up.
45
68
  *
46
69
  * config.json IS scanned here (only doc-class siblings are skipped), whereas the
47
70
  * Phase-1 validate helper (validate-bundled-scan.ts) also skips `config` — rescan
@@ -50,46 +73,158 @@
50
73
  import { join } from 'path';
51
74
  import { safeFs, resolveSafeRealpath } from '../sources/LocalFilesystemAdapter.helpers.js';
52
75
  import { BUNDLED_SCAN_FILES, classifyBundledFile, extractPackageJsonLifecycleScripts, } from './skill-installation.policy.js';
53
- /** Max `.sh` glob files scanned per skill (fixed bundled files are exempt). */
54
- export const MAX_SIBLING_SH_FILES = 50;
76
+ /**
77
+ * Operational-code extensions scanned by the glob (SMI-6033 Wave 2, Gap 8).
78
+ *
79
+ * Deliberately duplicated from `scripts/indexer/skill-processor.security.tree.ts`
80
+ * rather than shared: that module lives in the Node/Deno indexer trees, which
81
+ * cannot import `@skillsmith/core` (git-crypt boundary + Deno bundling — see
82
+ * that file's twin header). `parity.test.ts` pins the two lists equal, so the
83
+ * duplication cannot drift silently.
84
+ */
85
+ export const EXECUTABLE_CODE_EXTENSIONS = [
86
+ '.sh',
87
+ '.py',
88
+ '.js',
89
+ '.mjs',
90
+ '.cjs',
91
+ '.ts',
92
+ '.rb',
93
+ '.php',
94
+ '.ps1',
95
+ '.pl',
96
+ ];
97
+ /** Directories globbed for operational code, relative to the skill directory. */
98
+ export const EXTENDED_SCAN_DIRS = ['scripts', 'src', 'bin'];
99
+ /** Basenames (extension-stripped, lowercased) that rank as entry points (tier 2). */
100
+ export const ENTRY_POINT_BASENAMES = new Set([
101
+ 'install',
102
+ 'setup',
103
+ 'main',
104
+ 'index',
105
+ 'run',
106
+ 'postinstall',
107
+ ]);
108
+ /**
109
+ * Max globbed operational-code files scanned per skill (fixed bundled files
110
+ * are exempt).
111
+ *
112
+ * SMI-6033 Wave 2 (Gap 8): narrowed from the previous `.sh`-only cap of 50 to
113
+ * 20, matching `MAX_EXTENDED_SIBLING_FILES` on the indexer side. Intentional
114
+ * behaviour change: the glob now covers ten extensions across four directories
115
+ * instead of one extension across two, so an uncapped-at-50 policy would fetch
116
+ * and scan far more per skill than before. 20 ranked files is the plan's
117
+ * chosen budget on BOTH surfaces, and keeping the two equal is what makes the
118
+ * local and registry verdicts comparable.
119
+ */
120
+ export const MAX_EXTENDED_SIBLING_FILES = 20;
55
121
  /** Per-file byte ceiling; larger siblings are skipped (recorded, never silent). */
56
122
  export const MAX_SIBLING_FILE_BYTES = 512 * 1024;
57
- /** A finding is a quarantine driver only if it is a direct execution threat. */
58
- function isExecutionThreat(finding) {
59
- return finding.type === 'code_execution' || finding.type === 'obfuscated_directive';
123
+ /**
124
+ * A finding is a quarantine driver only if it is a direct execution threat.
125
+ *
126
+ * SMI-6033 Wave 2 (Gap 8) fix (adversarial review finding, 2026-08-16): for a
127
+ * file from the NEW extended (`scripts/`/`src/`/`bin/`/top-level executable
128
+ * code) surface, `code_execution` only drives rejection at `critical`
129
+ * severity — a bare `medium` code_execution finding (the severity
130
+ * `scanCodeExecution` assigns a lone `curl | bash` by design) must NOT
131
+ * standalone-quarantine an install script that uses the same idiom as
132
+ * rustup/Homebrew/nvm/bun. The original fixed `BUNDLED_SCAN_FILES` keep the
133
+ * unchanged, severity-agnostic rule — those are config/doc formats where a
134
+ * literal `curl | bash` string is inherently anomalous, not a real installer.
135
+ * `obfuscated_directive` remains rejectable at any severity on BOTH surfaces
136
+ * — it is delta-gated against a real decode step and has no legitimate
137
+ * installer-script shape.
138
+ */
139
+ function isExecutionThreat(finding, isExtended) {
140
+ if (finding.type === 'obfuscated_directive')
141
+ return true;
142
+ if (finding.type !== 'code_execution')
143
+ return false;
144
+ return isExtended ? finding.severity === 'critical' : true;
145
+ }
146
+ /** True when `path`'s final extension is in {@link EXECUTABLE_CODE_EXTENSIONS}. */
147
+ function hasExecutableCodeExtension(path) {
148
+ const lower = path.toLowerCase();
149
+ return EXECUTABLE_CODE_EXTENSIONS.some((ext) => lower.endsWith(ext));
150
+ }
151
+ /** Lowercased basename with its final extension removed (`Setup.SH` -> `setup`). */
152
+ function entryPointKey(path) {
153
+ const base = (path.split('/').pop() ?? path).toLowerCase();
154
+ const dotIdx = base.lastIndexOf('.');
155
+ return dotIdx > 0 ? base.slice(0, dotIdx) : base;
60
156
  }
61
157
  /**
62
- * Collect `*.sh` candidates: top-level then `scripts/`, regular files only
63
- * (symlinked scripts are not followed by the glob — Phase 3). Sorted so the cap
64
- * and `droppedForCount` are reproducible rather than dependent on readdir order.
65
- * NOTE: sorting does NOT defeat decoy-padding an author can name the malicious
66
- * file to sort last; that residual FN is documented in the module header and the
158
+ * SMI-6033 Wave 2 (Gap 8): rank operational-code candidates so the count cap
159
+ * keeps the files most likely to actually be executed.
160
+ *
161
+ * Four tiers, in order: (1) referenced literally by path from `SKILL.md`,
162
+ * (2) entry-point basename, (3) shallower path first, (4) lexicographic.
163
+ *
164
+ * DETERMINISM is a required property, not incidental: the same candidate set
165
+ * must always produce the same order. Tier 4 is a TOTAL order over unique
166
+ * relative paths, so the result never depends on `Array.prototype.sort` being
167
+ * stable; and the compare uses raw `<`/`>` rather than `localeCompare`, whose
168
+ * collation is ICU/locale dependent. This mirrors the identical comparator in
169
+ * the indexer's `enumerateExtendedSiblingTargets`, so the two surfaces select
170
+ * the same files in the same order from the same bundle.
171
+ *
172
+ * NOTE: ranking does NOT defeat decoy-padding — see the module header. The
67
173
  * dropped names are always surfaced in `droppedForCount` (no silent drop).
68
174
  */
69
- async function collectShFiles(skillDir) {
175
+ export function rankExecutableCodeFiles(relPaths, primaryContent) {
176
+ return [...relPaths].sort((a, b) => {
177
+ const aRef = primaryContent.includes(a);
178
+ const bRef = primaryContent.includes(b);
179
+ if (aRef !== bRef)
180
+ return aRef ? -1 : 1;
181
+ const aEntry = ENTRY_POINT_BASENAMES.has(entryPointKey(a));
182
+ const bEntry = ENTRY_POINT_BASENAMES.has(entryPointKey(b));
183
+ if (aEntry !== bEntry)
184
+ return aEntry ? -1 : 1;
185
+ const aDepth = a.split('/').length;
186
+ const bDepth = b.split('/').length;
187
+ if (aDepth !== bDepth)
188
+ return aDepth - bDepth;
189
+ if (a === b)
190
+ return 0;
191
+ return a < b ? -1 : 1;
192
+ });
193
+ }
194
+ /**
195
+ * Collect executable-code candidates: the skill's top level, then direct
196
+ * children of `scripts/`, `src/` and `bin/` — regular files only (symlinked
197
+ * scripts are not followed by the glob). Ranked by
198
+ * {@link rankExecutableCodeFiles} so the cap and `droppedForCount` are
199
+ * reproducible rather than dependent on readdir order.
200
+ */
201
+ async function collectExecutableCodeFiles(skillDir, primaryContent) {
70
202
  const out = [];
71
203
  const top = await safeFs.readdir(skillDir);
72
204
  if (top.ok) {
73
205
  for (const e of top.value) {
74
- if (e.isFile() && e.name.endsWith('.sh'))
206
+ if (e.isFile() && hasExecutableCodeExtension(e.name))
75
207
  out.push(e.name);
76
208
  }
77
209
  }
78
- const scripts = await safeFs.readdir(join(skillDir, 'scripts'));
79
- if (scripts.ok) {
80
- for (const e of scripts.value) {
81
- if (e.isFile() && e.name.endsWith('.sh'))
82
- out.push(join('scripts', e.name));
210
+ for (const sub of EXTENDED_SCAN_DIRS) {
211
+ const entries = await safeFs.readdir(join(skillDir, sub));
212
+ if (!entries.ok)
213
+ continue;
214
+ for (const e of entries.value) {
215
+ if (e.isFile() && hasExecutableCodeExtension(e.name))
216
+ out.push(`${sub}/${e.name}`);
83
217
  }
84
218
  }
85
- return out.sort();
219
+ return rankExecutableCodeFiles(out, primaryContent);
86
220
  }
87
221
  /**
88
222
  * Scan a skill bundle's sibling files and return the structured result.
89
223
  *
90
224
  * Fixed {@link BUNDLED_SCAN_FILES} are always scanned (exempt from the count
91
225
  * cap, so a decoy-padding attack on `scripts/` cannot push the primary hook /
92
- * postinstall surface out of the scan window). The `.sh` glob is capped and
226
+ * postinstall surface out of the scan window). The executable-code glob is
227
+ * ranked then capped, and
93
228
  * any overflow is reported in `droppedForCount`.
94
229
  *
95
230
  * Doc-class siblings (`README.md`, `examples.md`) are intentionally NOT scanned:
@@ -102,7 +237,7 @@ async function collectShFiles(skillDir) {
102
237
  * @param opts optional caps
103
238
  */
104
239
  export async function scanLocalBundleSiblings(skillDir, scanner, opts = {}) {
105
- const maxShFiles = opts.maxShFiles ?? MAX_SIBLING_SH_FILES;
240
+ const maxExtendedFiles = opts.maxExtendedFiles ?? MAX_EXTENDED_SIBLING_FILES;
106
241
  const maxBytes = opts.maxBytesPerFile ?? MAX_SIBLING_FILE_BYTES;
107
242
  const result = {
108
243
  findings: [],
@@ -115,10 +250,15 @@ export async function scanLocalBundleSiblings(skillDir, scanner, opts = {}) {
115
250
  skippedOversize: [],
116
251
  skippedSymlinkEscape: [],
117
252
  };
118
- const shFiles = await collectShFiles(skillDir);
119
- result.droppedForCount = shFiles.slice(maxShFiles);
120
- // Fixed files first (cap-exempt), then the capped, sorted .sh glob.
121
- const candidates = [...BUNDLED_SCAN_FILES, ...shFiles.slice(0, maxShFiles)];
253
+ const codeFiles = await collectExecutableCodeFiles(skillDir, opts.primaryContent ?? '');
254
+ result.droppedForCount = codeFiles.slice(maxExtendedFiles);
255
+ const extendedCandidates = codeFiles.slice(0, maxExtendedFiles);
256
+ // Fixed files first (cap-exempt), then the capped, ranked executable-code glob.
257
+ const candidates = [...BUNDLED_SCAN_FILES, ...extendedCandidates];
258
+ // SMI-6033 Wave 2 (Gap 8) fix: which candidates came from the new extended
259
+ // surface, so isExecutionThreat can apply its narrower rule to them without
260
+ // touching BUNDLED_SCAN_FILES's unchanged behavior at all.
261
+ const extendedCandidateSet = new Set(extendedCandidates);
122
262
  for (const rel of candidates) {
123
263
  const fileClass = classifyBundledFile(rel);
124
264
  if (fileClass === 'doc')
@@ -150,12 +290,16 @@ export async function scanLocalBundleSiblings(skillDir, scanner, opts = {}) {
150
290
  continue; // no install-time hooks — nothing risky
151
291
  textToScan = lifecycle;
152
292
  }
153
- const report = scanner.scan(`${skillDir}/${rel}`, textToScan);
293
+ // SMI-6033 Wave 2 (Gap 8) fix: extended candidates are real source files,
294
+ // not markdown — see SecurityScanner.scan()'s own header for why the
295
+ // markdown-only indented-code-block heuristic must be disabled for them.
296
+ // BUNDLED_SCAN_FILES keep the default (isMarkdown=true) unchanged.
297
+ const report = scanner.scan(`${skillDir}/${rel}`, textToScan, false, !extendedCandidateSet.has(rel));
154
298
  result.scannedFiles.push(rel);
155
299
  // Fresh objects (no mutation of the report's findings array) tagged with the file.
156
300
  const tagged = report.findings.map((f) => ({ ...f, location: rel }));
157
301
  result.findings.push(...tagged);
158
- const drivers = tagged.filter(isExecutionThreat);
302
+ const drivers = tagged.filter((f) => isExecutionThreat(f, extendedCandidateSet.has(rel)));
159
303
  if (drivers.length > 0) {
160
304
  result.rejectable = true;
161
305
  result.rejectableFindings.push(...drivers);