oc-pilot 0.5.1 → 0.88.1

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 (481) hide show
  1. package/CHANGELOG.md +683 -0
  2. package/CONTRIBUTING.md +4 -2
  3. package/README.md +588 -20
  4. package/README.ru.md +572 -10
  5. package/dist/cli.js +185 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/cli.test.js +1211 -1
  8. package/dist/cli.test.js.map +1 -1
  9. package/dist/commands/anti-regression.js +97 -0
  10. package/dist/commands/anti-regression.js.map +1 -0
  11. package/dist/commands/arch-diff.js +80 -0
  12. package/dist/commands/arch-diff.js.map +1 -0
  13. package/dist/commands/authz.js +167 -0
  14. package/dist/commands/authz.js.map +1 -0
  15. package/dist/commands/autopsy.js +64 -0
  16. package/dist/commands/autopsy.js.map +1 -0
  17. package/dist/commands/bankruptcy.js +60 -0
  18. package/dist/commands/bankruptcy.js.map +1 -0
  19. package/dist/commands/battle.js +119 -0
  20. package/dist/commands/battle.js.map +1 -0
  21. package/dist/commands/boundaries.js +114 -0
  22. package/dist/commands/boundaries.js.map +1 -0
  23. package/dist/commands/bounty.js +56 -0
  24. package/dist/commands/bounty.js.map +1 -0
  25. package/dist/commands/brag.js +62 -0
  26. package/dist/commands/brag.js.map +1 -0
  27. package/dist/commands/cache.js +62 -0
  28. package/dist/commands/cache.js.map +1 -0
  29. package/dist/commands/cinema.js +61 -0
  30. package/dist/commands/cinema.js.map +1 -0
  31. package/dist/commands/cleanup.js +110 -0
  32. package/dist/commands/cleanup.js.map +1 -0
  33. package/dist/commands/commit.js +177 -0
  34. package/dist/commands/commit.js.map +1 -0
  35. package/dist/commands/commit.test.js +160 -0
  36. package/dist/commands/commit.test.js.map +1 -0
  37. package/dist/commands/compliance.js +121 -0
  38. package/dist/commands/compliance.js.map +1 -0
  39. package/dist/commands/contract-drift.js +97 -0
  40. package/dist/commands/contract-drift.js.map +1 -0
  41. package/dist/commands/coverage.js +137 -0
  42. package/dist/commands/coverage.js.map +1 -0
  43. package/dist/commands/day.js +116 -0
  44. package/dist/commands/day.js.map +1 -0
  45. package/dist/commands/day.test.js +45 -0
  46. package/dist/commands/day.test.js.map +1 -0
  47. package/dist/commands/deps.js +245 -0
  48. package/dist/commands/deps.js.map +1 -0
  49. package/dist/commands/docgen.js +4 -2
  50. package/dist/commands/docgen.js.map +1 -1
  51. package/dist/commands/doctor.js +146 -2
  52. package/dist/commands/doctor.js.map +1 -1
  53. package/dist/commands/doctor.test.js +69 -0
  54. package/dist/commands/doctor.test.js.map +1 -0
  55. package/dist/commands/echo.js +78 -0
  56. package/dist/commands/echo.js.map +1 -0
  57. package/dist/commands/ecosystem-verify.js +113 -0
  58. package/dist/commands/ecosystem-verify.js.map +1 -0
  59. package/dist/commands/env-sync.js +102 -0
  60. package/dist/commands/env-sync.js.map +1 -0
  61. package/dist/commands/explain.js +115 -0
  62. package/dist/commands/explain.js.map +1 -0
  63. package/dist/commands/fix.js +145 -3
  64. package/dist/commands/fix.js.map +1 -1
  65. package/dist/commands/flaky.js +111 -0
  66. package/dist/commands/flaky.js.map +1 -0
  67. package/dist/commands/fuzz.js +119 -0
  68. package/dist/commands/fuzz.js.map +1 -0
  69. package/dist/commands/hivemind.js +124 -0
  70. package/dist/commands/hivemind.js.map +1 -0
  71. package/dist/commands/impact.js +73 -0
  72. package/dist/commands/impact.js.map +1 -0
  73. package/dist/commands/init.js +73 -11
  74. package/dist/commands/init.js.map +1 -1
  75. package/dist/commands/init.test.js +138 -0
  76. package/dist/commands/init.test.js.map +1 -0
  77. package/dist/commands/install-hook.js +161 -0
  78. package/dist/commands/install-hook.js.map +1 -0
  79. package/dist/commands/install-hook.test.js +100 -0
  80. package/dist/commands/install-hook.test.js.map +1 -0
  81. package/dist/commands/invariants.js +103 -0
  82. package/dist/commands/invariants.js.map +1 -0
  83. package/dist/commands/mock-leak.js +97 -0
  84. package/dist/commands/mock-leak.js.map +1 -0
  85. package/dist/commands/mutate.js +99 -0
  86. package/dist/commands/mutate.js.map +1 -0
  87. package/dist/commands/pack.js +144 -0
  88. package/dist/commands/pack.js.map +1 -0
  89. package/dist/commands/preflight.js +137 -0
  90. package/dist/commands/preflight.js.map +1 -0
  91. package/dist/commands/query.js +57 -0
  92. package/dist/commands/query.js.map +1 -0
  93. package/dist/commands/ready.js +152 -0
  94. package/dist/commands/ready.js.map +1 -0
  95. package/dist/commands/receipt.js +289 -0
  96. package/dist/commands/receipt.js.map +1 -0
  97. package/dist/commands/replay.js +112 -0
  98. package/dist/commands/replay.js.map +1 -0
  99. package/dist/commands/replay.test.js +125 -0
  100. package/dist/commands/replay.test.js.map +1 -0
  101. package/dist/commands/restore.js +54 -0
  102. package/dist/commands/restore.js.map +1 -0
  103. package/dist/commands/review.js +263 -4
  104. package/dist/commands/review.js.map +1 -1
  105. package/dist/commands/review.test.js +160 -0
  106. package/dist/commands/review.test.js.map +1 -0
  107. package/dist/commands/reviewers.js +86 -0
  108. package/dist/commands/reviewers.js.map +1 -0
  109. package/dist/commands/roast.js +102 -0
  110. package/dist/commands/roast.js.map +1 -0
  111. package/dist/commands/sandbox.js +82 -0
  112. package/dist/commands/sandbox.js.map +1 -0
  113. package/dist/commands/sast.js +162 -0
  114. package/dist/commands/sast.js.map +1 -0
  115. package/dist/commands/scan.js +47 -12
  116. package/dist/commands/scan.js.map +1 -1
  117. package/dist/commands/slop.js +145 -0
  118. package/dist/commands/slop.js.map +1 -0
  119. package/dist/commands/slop.test.js +135 -0
  120. package/dist/commands/slop.test.js.map +1 -0
  121. package/dist/commands/stats.js +61 -0
  122. package/dist/commands/stats.js.map +1 -0
  123. package/dist/commands/test-gen.js +372 -0
  124. package/dist/commands/test-gen.js.map +1 -0
  125. package/dist/commands/test-guard.js +89 -0
  126. package/dist/commands/test-guard.js.map +1 -0
  127. package/dist/commands/test-plan.js +133 -0
  128. package/dist/commands/test-plan.js.map +1 -0
  129. package/dist/commands/zoom-symbol.js +52 -0
  130. package/dist/commands/zoom-symbol.js.map +1 -0
  131. package/dist/core/agent-rules.js +35 -0
  132. package/dist/core/agent-rules.js.map +1 -0
  133. package/dist/core/anthropic.test.js +116 -0
  134. package/dist/core/anthropic.test.js.map +1 -0
  135. package/dist/core/anti-regression.js +161 -0
  136. package/dist/core/anti-regression.js.map +1 -0
  137. package/dist/core/anti-regression.test.js +154 -0
  138. package/dist/core/anti-regression.test.js.map +1 -0
  139. package/dist/core/aql.js +220 -0
  140. package/dist/core/aql.js.map +1 -0
  141. package/dist/core/aql.test.js +64 -0
  142. package/dist/core/aql.test.js.map +1 -0
  143. package/dist/core/arch-diff.js +122 -0
  144. package/dist/core/arch-diff.js.map +1 -0
  145. package/dist/core/arch-diff.test.js +93 -0
  146. package/dist/core/arch-diff.test.js.map +1 -0
  147. package/dist/core/authz.js +292 -0
  148. package/dist/core/authz.js.map +1 -0
  149. package/dist/core/authz.test.js +115 -0
  150. package/dist/core/authz.test.js.map +1 -0
  151. package/dist/core/autopsy.js +230 -0
  152. package/dist/core/autopsy.js.map +1 -0
  153. package/dist/core/autopsy.test.js +106 -0
  154. package/dist/core/autopsy.test.js.map +1 -0
  155. package/dist/core/bankruptcy.js +232 -0
  156. package/dist/core/bankruptcy.js.map +1 -0
  157. package/dist/core/bankruptcy.test.js +91 -0
  158. package/dist/core/bankruptcy.test.js.map +1 -0
  159. package/dist/core/baseline.js +131 -0
  160. package/dist/core/baseline.js.map +1 -0
  161. package/dist/core/baseline.test.js +91 -0
  162. package/dist/core/baseline.test.js.map +1 -0
  163. package/dist/core/battle.js +213 -0
  164. package/dist/core/battle.js.map +1 -0
  165. package/dist/core/battle.test.js +116 -0
  166. package/dist/core/battle.test.js.map +1 -0
  167. package/dist/core/boundaries.js +200 -0
  168. package/dist/core/boundaries.js.map +1 -0
  169. package/dist/core/boundaries.test.js +62 -0
  170. package/dist/core/boundaries.test.js.map +1 -0
  171. package/dist/core/bounty.js +128 -0
  172. package/dist/core/bounty.js.map +1 -0
  173. package/dist/core/bounty.test.js +82 -0
  174. package/dist/core/bounty.test.js.map +1 -0
  175. package/dist/core/brag.js +174 -0
  176. package/dist/core/brag.js.map +1 -0
  177. package/dist/core/brag.test.js +43 -0
  178. package/dist/core/brag.test.js.map +1 -0
  179. package/dist/core/cache.js +189 -0
  180. package/dist/core/cache.js.map +1 -0
  181. package/dist/core/cache.test.js +159 -0
  182. package/dist/core/cache.test.js.map +1 -0
  183. package/dist/core/cinema.js +242 -0
  184. package/dist/core/cinema.js.map +1 -0
  185. package/dist/core/cinema.test.js +73 -0
  186. package/dist/core/cinema.test.js.map +1 -0
  187. package/dist/core/cleanup.js +107 -0
  188. package/dist/core/cleanup.js.map +1 -0
  189. package/dist/core/compliance.js +193 -0
  190. package/dist/core/compliance.js.map +1 -0
  191. package/dist/core/compliance.test.js +123 -0
  192. package/dist/core/compliance.test.js.map +1 -0
  193. package/dist/core/config.js +78 -0
  194. package/dist/core/config.js.map +1 -1
  195. package/dist/core/config.test.js +66 -0
  196. package/dist/core/config.test.js.map +1 -1
  197. package/dist/core/contract-drift.js +296 -0
  198. package/dist/core/contract-drift.js.map +1 -0
  199. package/dist/core/contract-drift.test.js +166 -0
  200. package/dist/core/contract-drift.test.js.map +1 -0
  201. package/dist/core/cost.test.js +103 -0
  202. package/dist/core/cost.test.js.map +1 -0
  203. package/dist/core/coverage.js +335 -0
  204. package/dist/core/coverage.js.map +1 -0
  205. package/dist/core/coverage.test.js +190 -0
  206. package/dist/core/coverage.test.js.map +1 -0
  207. package/dist/core/deps-fix.js +169 -0
  208. package/dist/core/deps-fix.js.map +1 -0
  209. package/dist/core/deps-fix.test.js +176 -0
  210. package/dist/core/deps-fix.test.js.map +1 -0
  211. package/dist/core/deps-health.js +353 -0
  212. package/dist/core/deps-health.js.map +1 -0
  213. package/dist/core/deps-health.test.js +230 -0
  214. package/dist/core/deps-health.test.js.map +1 -0
  215. package/dist/core/deps-why.js +260 -0
  216. package/dist/core/deps-why.js.map +1 -0
  217. package/dist/core/deps-why.test.js +175 -0
  218. package/dist/core/deps-why.test.js.map +1 -0
  219. package/dist/core/deps.js +179 -0
  220. package/dist/core/deps.js.map +1 -0
  221. package/dist/core/deps.test.js +88 -0
  222. package/dist/core/deps.test.js.map +1 -0
  223. package/dist/core/diff-source.js +4 -2
  224. package/dist/core/diff-source.js.map +1 -1
  225. package/dist/core/echo.js +151 -0
  226. package/dist/core/echo.js.map +1 -0
  227. package/dist/core/echo.test.js +90 -0
  228. package/dist/core/echo.test.js.map +1 -0
  229. package/dist/core/ecosystem-verify.js +331 -0
  230. package/dist/core/ecosystem-verify.js.map +1 -0
  231. package/dist/core/ecosystem-verify.test.js +158 -0
  232. package/dist/core/ecosystem-verify.test.js.map +1 -0
  233. package/dist/core/engine-preset.test.js +60 -0
  234. package/dist/core/engine-preset.test.js.map +1 -0
  235. package/dist/core/engine.js +278 -32
  236. package/dist/core/engine.js.map +1 -1
  237. package/dist/core/engine.test.js +63 -0
  238. package/dist/core/engine.test.js.map +1 -1
  239. package/dist/core/env-sync.js +122 -0
  240. package/dist/core/env-sync.js.map +1 -0
  241. package/dist/core/env-sync.test.js +99 -0
  242. package/dist/core/env-sync.test.js.map +1 -0
  243. package/dist/core/file-context.js +4 -2
  244. package/dist/core/file-context.js.map +1 -1
  245. package/dist/core/findings-baseline.js +60 -0
  246. package/dist/core/findings-baseline.js.map +1 -0
  247. package/dist/core/findings-baseline.test.js +77 -0
  248. package/dist/core/findings-baseline.test.js.map +1 -0
  249. package/dist/core/findings.js +68 -21
  250. package/dist/core/findings.js.map +1 -1
  251. package/dist/core/flaky.js +137 -0
  252. package/dist/core/flaky.js.map +1 -0
  253. package/dist/core/flaky.test.js +97 -0
  254. package/dist/core/flaky.test.js.map +1 -0
  255. package/dist/core/format.js +5 -3
  256. package/dist/core/format.js.map +1 -1
  257. package/dist/core/fuzz.js +306 -0
  258. package/dist/core/fuzz.js.map +1 -0
  259. package/dist/core/fuzz.test.js +121 -0
  260. package/dist/core/fuzz.test.js.map +1 -0
  261. package/dist/core/git.js +22 -0
  262. package/dist/core/git.js.map +1 -1
  263. package/dist/core/gitlab.js +176 -0
  264. package/dist/core/gitlab.js.map +1 -0
  265. package/dist/core/gitlab.test.js +199 -0
  266. package/dist/core/gitlab.test.js.map +1 -0
  267. package/dist/core/hivemind.js +250 -0
  268. package/dist/core/hivemind.js.map +1 -0
  269. package/dist/core/hivemind.test.js +79 -0
  270. package/dist/core/hivemind.test.js.map +1 -0
  271. package/dist/core/hologram.js +463 -0
  272. package/dist/core/hologram.js.map +1 -0
  273. package/dist/core/hologram.test.js +124 -0
  274. package/dist/core/hologram.test.js.map +1 -0
  275. package/dist/core/impact.js +240 -0
  276. package/dist/core/impact.js.map +1 -0
  277. package/dist/core/impact.test.js +105 -0
  278. package/dist/core/impact.test.js.map +1 -0
  279. package/dist/core/import-graph.js +170 -0
  280. package/dist/core/import-graph.js.map +1 -0
  281. package/dist/core/import-graph.test.js +52 -0
  282. package/dist/core/import-graph.test.js.map +1 -0
  283. package/dist/core/injection.js +55 -0
  284. package/dist/core/injection.js.map +1 -0
  285. package/dist/core/injection.test.js +45 -0
  286. package/dist/core/injection.test.js.map +1 -0
  287. package/dist/core/invariants.js +286 -0
  288. package/dist/core/invariants.js.map +1 -0
  289. package/dist/core/invariants.test.js +87 -0
  290. package/dist/core/invariants.test.js.map +1 -0
  291. package/dist/core/last-review.js +92 -0
  292. package/dist/core/last-review.js.map +1 -0
  293. package/dist/core/last-review.test.js +97 -0
  294. package/dist/core/last-review.test.js.map +1 -0
  295. package/dist/core/lockfile-support.test.js +112 -0
  296. package/dist/core/lockfile-support.test.js.map +1 -0
  297. package/dist/core/lockfile.js +284 -0
  298. package/dist/core/lockfile.js.map +1 -0
  299. package/dist/core/lockfile.test.js +203 -0
  300. package/dist/core/lockfile.test.js.map +1 -0
  301. package/dist/core/mock-leak.js +120 -0
  302. package/dist/core/mock-leak.js.map +1 -0
  303. package/dist/core/mock-leak.test.js +79 -0
  304. package/dist/core/mock-leak.test.js.map +1 -0
  305. package/dist/core/mutation.js +206 -0
  306. package/dist/core/mutation.js.map +1 -0
  307. package/dist/core/mutation.test.js +104 -0
  308. package/dist/core/mutation.test.js.map +1 -0
  309. package/dist/core/opencode.js +15 -2
  310. package/dist/core/opencode.js.map +1 -1
  311. package/dist/core/opencode.test.js +10 -1
  312. package/dist/core/opencode.test.js.map +1 -1
  313. package/dist/core/options.js +51 -3
  314. package/dist/core/options.js.map +1 -1
  315. package/dist/core/osv.js +202 -0
  316. package/dist/core/osv.js.map +1 -0
  317. package/dist/core/osv.test.js +173 -0
  318. package/dist/core/osv.test.js.map +1 -0
  319. package/dist/core/output-format.test.js +102 -0
  320. package/dist/core/output-format.test.js.map +1 -0
  321. package/dist/core/pack.js +212 -0
  322. package/dist/core/pack.js.map +1 -0
  323. package/dist/core/pack.test.js +129 -0
  324. package/dist/core/pack.test.js.map +1 -0
  325. package/dist/core/phantom.js +155 -0
  326. package/dist/core/phantom.js.map +1 -0
  327. package/dist/core/phantom.test.js +58 -0
  328. package/dist/core/phantom.test.js.map +1 -0
  329. package/dist/core/pr-comment.js +116 -0
  330. package/dist/core/pr-comment.js.map +1 -0
  331. package/dist/core/pr-comment.test.js +53 -0
  332. package/dist/core/pr-comment.test.js.map +1 -0
  333. package/dist/core/pr-review.js +160 -0
  334. package/dist/core/pr-review.js.map +1 -0
  335. package/dist/core/pr-review.test.js +62 -0
  336. package/dist/core/pr-review.test.js.map +1 -0
  337. package/dist/core/preflight-run.js +106 -0
  338. package/dist/core/preflight-run.js.map +1 -0
  339. package/dist/core/preflight.js +173 -0
  340. package/dist/core/preflight.js.map +1 -0
  341. package/dist/core/preflight.test.js +187 -0
  342. package/dist/core/preflight.test.js.map +1 -0
  343. package/dist/core/presets.js +408 -0
  344. package/dist/core/presets.js.map +1 -0
  345. package/dist/core/presets.test.js +79 -0
  346. package/dist/core/presets.test.js.map +1 -0
  347. package/dist/core/prompt.golden.test.js +74 -0
  348. package/dist/core/prompt.golden.test.js.map +1 -0
  349. package/dist/core/prompt.js +43 -4
  350. package/dist/core/prompt.js.map +1 -1
  351. package/dist/core/prompt.test.js +16 -0
  352. package/dist/core/prompt.test.js.map +1 -1
  353. package/dist/core/readiness-fix.js +111 -0
  354. package/dist/core/readiness-fix.js.map +1 -0
  355. package/dist/core/readiness-fix.test.js +99 -0
  356. package/dist/core/readiness-fix.test.js.map +1 -0
  357. package/dist/core/readiness.js +189 -0
  358. package/dist/core/readiness.js.map +1 -0
  359. package/dist/core/readiness.test.js +199 -0
  360. package/dist/core/readiness.test.js.map +1 -0
  361. package/dist/core/receipt.js +193 -0
  362. package/dist/core/receipt.js.map +1 -0
  363. package/dist/core/receipt.test.js +193 -0
  364. package/dist/core/receipt.test.js.map +1 -0
  365. package/dist/core/reviewers.js +179 -0
  366. package/dist/core/reviewers.js.map +1 -0
  367. package/dist/core/reviewers.test.js +105 -0
  368. package/dist/core/reviewers.test.js.map +1 -0
  369. package/dist/core/roast.js +27 -0
  370. package/dist/core/roast.js.map +1 -0
  371. package/dist/core/roast.test.js +19 -0
  372. package/dist/core/roast.test.js.map +1 -0
  373. package/dist/core/router.js +8 -0
  374. package/dist/core/router.js.map +1 -0
  375. package/dist/core/router.test.js +157 -0
  376. package/dist/core/router.test.js.map +1 -0
  377. package/dist/core/rules.js +2 -2
  378. package/dist/core/rules.js.map +1 -1
  379. package/dist/core/run.js +322 -20
  380. package/dist/core/run.js.map +1 -1
  381. package/dist/core/sarif.js +125 -0
  382. package/dist/core/sarif.js.map +1 -0
  383. package/dist/core/sarif.test.js +60 -0
  384. package/dist/core/sarif.test.js.map +1 -0
  385. package/dist/core/sast.js +324 -0
  386. package/dist/core/sast.js.map +1 -0
  387. package/dist/core/sast.test.js +105 -0
  388. package/dist/core/sast.test.js.map +1 -0
  389. package/dist/core/sbom.js +121 -0
  390. package/dist/core/sbom.js.map +1 -0
  391. package/dist/core/sbom.test.js +81 -0
  392. package/dist/core/sbom.test.js.map +1 -0
  393. package/dist/core/second-opinion.js +88 -0
  394. package/dist/core/second-opinion.js.map +1 -0
  395. package/dist/core/second-opinion.test.js +89 -0
  396. package/dist/core/second-opinion.test.js.map +1 -0
  397. package/dist/core/secrets.js +120 -5
  398. package/dist/core/secrets.js.map +1 -1
  399. package/dist/core/secrets.test.js +74 -1
  400. package/dist/core/secrets.test.js.map +1 -1
  401. package/dist/core/shadow.js +94 -0
  402. package/dist/core/shadow.js.map +1 -0
  403. package/dist/core/shadow.test.js +50 -0
  404. package/dist/core/shadow.test.js.map +1 -0
  405. package/dist/core/slicing.js +110 -0
  406. package/dist/core/slicing.js.map +1 -0
  407. package/dist/core/slicing.test.js +287 -0
  408. package/dist/core/slicing.test.js.map +1 -0
  409. package/dist/core/slop/churn.js +49 -0
  410. package/dist/core/slop/churn.js.map +1 -0
  411. package/dist/core/slop/collect.js +186 -0
  412. package/dist/core/slop/collect.js.map +1 -0
  413. package/dist/core/slop/dead-exports.js +101 -0
  414. package/dist/core/slop/dead-exports.js.map +1 -0
  415. package/dist/core/slop/duplicates.js +155 -0
  416. package/dist/core/slop/duplicates.js.map +1 -0
  417. package/dist/core/slop/index.js +72 -0
  418. package/dist/core/slop/index.js.map +1 -0
  419. package/dist/core/slop/model.js +2 -0
  420. package/dist/core/slop/model.js.map +1 -0
  421. package/dist/core/slop/score.js +185 -0
  422. package/dist/core/slop/score.js.map +1 -0
  423. package/dist/core/slop/slop.test.js +160 -0
  424. package/dist/core/slop/slop.test.js.map +1 -0
  425. package/dist/core/slop/source.js +129 -0
  426. package/dist/core/slop/source.js.map +1 -0
  427. package/dist/core/slop/test-debt.js +178 -0
  428. package/dist/core/slop/test-debt.js.map +1 -0
  429. package/dist/core/slop/test-debt.test.js +114 -0
  430. package/dist/core/slop/test-debt.test.js.map +1 -0
  431. package/dist/core/slop/todos.js +117 -0
  432. package/dist/core/slop/todos.js.map +1 -0
  433. package/dist/core/snapshot.js +214 -0
  434. package/dist/core/snapshot.js.map +1 -0
  435. package/dist/core/snapshot.test.js +101 -0
  436. package/dist/core/snapshot.test.js.map +1 -0
  437. package/dist/core/stats.js +119 -0
  438. package/dist/core/stats.js.map +1 -0
  439. package/dist/core/stats.test.js +86 -0
  440. package/dist/core/stats.test.js.map +1 -0
  441. package/dist/core/suppressions.js +96 -0
  442. package/dist/core/suppressions.js.map +1 -0
  443. package/dist/core/suppressions.test.js +90 -0
  444. package/dist/core/suppressions.test.js.map +1 -0
  445. package/dist/core/test-delta.js +53 -0
  446. package/dist/core/test-delta.js.map +1 -0
  447. package/dist/core/test-delta.test.js +41 -0
  448. package/dist/core/test-delta.test.js.map +1 -0
  449. package/dist/core/test-plan.js +212 -0
  450. package/dist/core/test-plan.js.map +1 -0
  451. package/dist/core/test-plan.test.js +94 -0
  452. package/dist/core/test-plan.test.js.map +1 -0
  453. package/dist/core/test-weakening.js +191 -0
  454. package/dist/core/test-weakening.js.map +1 -0
  455. package/dist/core/test-weakening.test.js +114 -0
  456. package/dist/core/test-weakening.test.js.map +1 -0
  457. package/dist/core/tree-scan.js +77 -0
  458. package/dist/core/tree-scan.js.map +1 -0
  459. package/dist/core/tree-scan.test.js +58 -0
  460. package/dist/core/tree-scan.test.js.map +1 -0
  461. package/dist/mcp-prompts.test.js +133 -0
  462. package/dist/mcp-prompts.test.js.map +1 -0
  463. package/dist/mcp.js +3303 -240
  464. package/dist/mcp.js.map +1 -1
  465. package/dist/mcp.test.js +1739 -1
  466. package/dist/mcp.test.js.map +1 -1
  467. package/dist/skills.test.js +221 -0
  468. package/dist/skills.test.js.map +1 -0
  469. package/dist/version.js +1 -1
  470. package/dist/version.js.map +1 -1
  471. package/docs/integrations.md +151 -7
  472. package/package.json +2 -1
  473. package/prompts/oc-pilot.en.md +8 -1
  474. package/prompts/oc-pilot.ru.md +8 -1
  475. package/server.json +2 -2
  476. package/skills/agent-self-review/SKILL.md +87 -0
  477. package/skills/commit-discipline/SKILL.md +86 -0
  478. package/skills/oc-pilot/SKILL.md +86 -0
  479. package/skills/secure-diff-gate/SKILL.md +101 -0
  480. package/skills/stack-review-packs/SKILL.md +76 -0
  481. package/skills/test-failure-triage/SKILL.md +88 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif.js","sourceRoot":"","sources":["../../src/core/sarif.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import { CRITICAL_RULE_ID, SARIF_VERSION, SECRET_RULE_ID, WARNING_RULE_ID, analysisFindingsToSarifResults, buildSarif, secretFindingsToSarifResults, } from "./sarif.js";
4
+ import { VERSION } from "../version.js";
5
+ function secretFinding(overrides = {}) {
6
+ return {
7
+ file: "src/config.ts",
8
+ line: 3,
9
+ kind: "AWS Access Key ID",
10
+ severity: "critical",
11
+ preview: "[REDACTED:AWS Access Key ID]",
12
+ ...overrides,
13
+ };
14
+ }
15
+ test("secretFindingsToSarifResults maps severity to levels and rule id", () => {
16
+ const results = secretFindingsToSarifResults([
17
+ secretFinding(),
18
+ secretFinding({ severity: "warning", kind: "Generic secret", line: 9 }),
19
+ ]);
20
+ assert.equal(results[0]?.ruleId, SECRET_RULE_ID);
21
+ assert.equal(results[0]?.level, "error");
22
+ assert.equal(results[0]?.file, "src/config.ts");
23
+ assert.equal(results[0]?.line, 3);
24
+ assert.equal(results[1]?.level, "warning");
25
+ });
26
+ test("secret sarif message never contains a raw secret", () => {
27
+ const results = secretFindingsToSarifResults([secretFinding({ preview: "[REDACTED:AWS]" })]);
28
+ assert.ok(results[0].message.includes("[REDACTED:AWS]"));
29
+ assert.ok(!results[0].message.includes("AKIA"));
30
+ });
31
+ test("analysisFindingsToSarifResults maps severities and parses locations", () => {
32
+ const findings = [
33
+ { severity: "critical", location: "src/a.ts:12", text: "SQL injection" },
34
+ { severity: "warning", location: "src/a.ts:30:5", text: "Missing await" },
35
+ { severity: "style", text: "No location" },
36
+ ];
37
+ const results = analysisFindingsToSarifResults(findings, process.cwd());
38
+ assert.equal(results[0]?.ruleId, CRITICAL_RULE_ID);
39
+ assert.equal(results[0]?.level, "error");
40
+ assert.equal(results[0]?.file, "src/a.ts");
41
+ assert.equal(results[0]?.line, 12);
42
+ assert.equal(results[1]?.ruleId, WARNING_RULE_ID);
43
+ assert.equal(results[1]?.level, "warning");
44
+ assert.equal(results[1]?.line, 30);
45
+ assert.equal(results[2]?.level, "note");
46
+ assert.equal(results[2]?.file, undefined);
47
+ });
48
+ test("buildSarif produces a 2.1.0 log with driver and rules", () => {
49
+ const log = buildSarif(secretFindingsToSarifResults([secretFinding()]));
50
+ assert.equal(log.version, SARIF_VERSION);
51
+ assert.equal(log.version, "2.1.0");
52
+ assert.equal(log.runs[0]?.tool.driver.name, "oc-pilot");
53
+ assert.equal(log.runs[0]?.tool.driver.version, VERSION);
54
+ const ruleIds = log.runs[0].tool.driver.rules.map((rule) => rule.id);
55
+ assert.deepEqual(ruleIds.sort(), [CRITICAL_RULE_ID, SECRET_RULE_ID, WARNING_RULE_ID].sort());
56
+ const result = log.runs[0].results[0];
57
+ assert.equal(result.locations?.[0]?.physicalLocation.artifactLocation.uri, "src/config.ts");
58
+ assert.equal(result.locations?.[0]?.physicalLocation.region.startLine, 3);
59
+ });
60
+ //# sourceMappingURL=sarif.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sarif.test.js","sourceRoot":"","sources":["../../src/core/sarif.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,324 @@
1
+ import { SARIF_SCHEMA, SARIF_VERSION } from "./sarif.js";
2
+ import { VERSION } from "../version.js";
3
+ /**
4
+ * Deterministic rules, not a model. Each sink is a call that becomes dangerous
5
+ * when a request-derived value reaches it; `taint` keeps the rules quiet on
6
+ * constant arguments.
7
+ */
8
+ const RULES = [
9
+ {
10
+ id: "command-injection",
11
+ severity: "high",
12
+ message: "request data reaches a shell command; use execFile/spawn with an argument array",
13
+ sink: /\b(?:exec|execSync)\s*\(/,
14
+ },
15
+ {
16
+ id: "shell-true",
17
+ severity: "medium",
18
+ message: "spawn with shell:true turns arguments into shell syntax",
19
+ sink: /\bshell\s*:\s*true\b/,
20
+ taint: false,
21
+ },
22
+ {
23
+ id: "sql-injection",
24
+ severity: "high",
25
+ message: "request data is concatenated into a SQL query; use placeholders",
26
+ sink: /\.(?:query|execute|raw)\s*\(/,
27
+ },
28
+ {
29
+ id: "path-traversal",
30
+ severity: "high",
31
+ message: "request data reaches the filesystem; resolve and confine the path first",
32
+ sink: /\b(?:readFile|readFileSync|writeFile|writeFileSync|createReadStream|createWriteStream|unlink|unlinkSync|rm|rmSync)\s*\(/,
33
+ },
34
+ {
35
+ id: "ssrf",
36
+ severity: "high",
37
+ message: "request data becomes an outbound URL; allowlist hosts before the request",
38
+ sink: /\b(?:fetch|axios\.(?:get|post|put|delete)|got|request)\s*\(/,
39
+ },
40
+ {
41
+ id: "code-injection",
42
+ severity: "high",
43
+ message: "request data is evaluated as code",
44
+ sink: /\beval\s*\(|\bnew\s+Function\s*\(|\bvm\.runIn(?:NewContext|ThisContext|Context)\s*\(/,
45
+ },
46
+ {
47
+ id: "xss",
48
+ severity: "high",
49
+ message: "request data is written as HTML without escaping",
50
+ sink: /\.innerHTML\s*=|\bdangerouslySetInnerHTML\b/,
51
+ },
52
+ {
53
+ id: "open-redirect",
54
+ severity: "medium",
55
+ message: "request data controls a redirect target; allowlist paths",
56
+ sink: /\bres\.redirect\s*\(/,
57
+ },
58
+ {
59
+ id: "tls-verification",
60
+ severity: "high",
61
+ message: "TLS certificate verification is disabled",
62
+ sink: /rejectUnauthorized\s*:\s*false|NODE_TLS_REJECT_UNAUTHORIZED\s*=\s*["']?0/,
63
+ taint: false,
64
+ },
65
+ {
66
+ id: "cors-wildcard",
67
+ severity: "low",
68
+ message: "wildcard CORS origin exposes the endpoint to any site",
69
+ sink: /origin\s*:\s*["']\*["']/,
70
+ taint: false,
71
+ },
72
+ {
73
+ id: "prototype-pollution",
74
+ severity: "medium",
75
+ message: "request data is merged into an object without key filtering",
76
+ sink: /Object\.assign\s*\(\s*[^,]+,\s*(?:req|request)\.|\.merge\s*\(\s*[^,]+,\s*(?:req|request)\./,
77
+ taint: false,
78
+ },
79
+ {
80
+ id: "weak-hash",
81
+ severity: "low",
82
+ message: "MD5/SHA-1 are not collision resistant; use SHA-256 or better",
83
+ sink: /createHash\s*\(\s*["'](?:md5|sha1)["']/i,
84
+ taint: false,
85
+ },
86
+ {
87
+ id: "jwt-none",
88
+ severity: "high",
89
+ message: "the JWT 'none' algorithm accepts unsigned tokens",
90
+ sink: /algorithms\s*:\s*\[[^\]]*["']none["']/i,
91
+ taint: false,
92
+ },
93
+ ];
94
+ const TAINT = /\b(?:req|request)\.(?:query|body|params|headers|cookies)\b|\buserInput\b|\buntrusted\w*\b|\b(?:query|body|params)\.\w+/;
95
+ const SANITIZER = /\bpath\.(?:resolve|normalize|basename|join)\b/;
96
+ const SUPPRESSION = /oc-pilot:sast-ignore(?:\s+([\w-]+))?\s*:\s*(\S.*)$/;
97
+ const SEVERITY_RANK = { low: 1, medium: 2, high: 3 };
98
+ export function severityAtLeast(value, threshold) {
99
+ return SEVERITY_RANK[value] >= SEVERITY_RANK[threshold];
100
+ }
101
+ export function parseSastSeverity(value) {
102
+ const normalized = value.trim().toLowerCase();
103
+ return normalized === "high" || normalized === "medium" || normalized === "low" ? normalized : undefined;
104
+ }
105
+ /** Removes comments but keeps string contents: SQL and HTML sinks live in strings. */
106
+ export function stripComments(text) {
107
+ let out = "";
108
+ let state = "code";
109
+ for (let index = 0; index < text.length; index += 1) {
110
+ const char = text[index];
111
+ const next = text[index + 1];
112
+ if (state === "code") {
113
+ if (char === "/" && next === "/") {
114
+ state = "line";
115
+ out += " ";
116
+ index += 1;
117
+ continue;
118
+ }
119
+ if (char === "/" && next === "*") {
120
+ state = "block";
121
+ out += " ";
122
+ index += 1;
123
+ continue;
124
+ }
125
+ if (char === "'")
126
+ state = "single";
127
+ else if (char === '"')
128
+ state = "double";
129
+ else if (char === "`")
130
+ state = "template";
131
+ out += char;
132
+ continue;
133
+ }
134
+ if (state === "line") {
135
+ if (char === "\n") {
136
+ state = "code";
137
+ out += char;
138
+ }
139
+ else {
140
+ out += " ";
141
+ }
142
+ continue;
143
+ }
144
+ if (state === "block") {
145
+ if (char === "*" && next === "/") {
146
+ state = "code";
147
+ out += " ";
148
+ index += 1;
149
+ }
150
+ else {
151
+ out += char === "\n" ? "\n" : " ";
152
+ }
153
+ continue;
154
+ }
155
+ if (char === "\\") {
156
+ out += char + (next ?? "");
157
+ index += 1;
158
+ continue;
159
+ }
160
+ if ((state === "single" && char === "'") || (state === "double" && char === '"') || (state === "template" && char === "`")) {
161
+ state = "code";
162
+ }
163
+ out += char;
164
+ }
165
+ return out;
166
+ }
167
+ function lineStarts(text) {
168
+ const starts = [0];
169
+ for (let index = 0; index < text.length; index += 1) {
170
+ if (text[index] === "\n")
171
+ starts.push(index + 1);
172
+ }
173
+ return starts;
174
+ }
175
+ function locate(starts, offset) {
176
+ let low = 0;
177
+ let high = starts.length - 1;
178
+ while (low <= high) {
179
+ const middle = (low + high) >> 1;
180
+ if (starts[middle] <= offset)
181
+ low = middle + 1;
182
+ else
183
+ high = middle - 1;
184
+ }
185
+ const lineIndex = Math.max(0, high);
186
+ return { line: lineIndex + 1, column: offset - starts[lineIndex] + 1 };
187
+ }
188
+ function suppressionAt(lines, lineIndex, rule) {
189
+ for (const index of [lineIndex, lineIndex - 1]) {
190
+ const line = lines[index];
191
+ if (!line)
192
+ continue;
193
+ const match = line.match(SUPPRESSION);
194
+ if (!match)
195
+ continue;
196
+ const scopedRule = match[1];
197
+ const reason = match[2]?.trim() ?? "";
198
+ if (reason.length < 3)
199
+ continue;
200
+ if (scopedRule && scopedRule !== rule)
201
+ continue;
202
+ return true;
203
+ }
204
+ return false;
205
+ }
206
+ /**
207
+ * Scans one source file. The rules are intentionally conservative: a sink is
208
+ * reported only when request-derived data appears in the same expression.
209
+ */
210
+ export function scanSource(file, text, options = {}) {
211
+ const clean = stripComments(text);
212
+ const starts = lineStarts(clean);
213
+ const lines = text.split(/\r?\n/);
214
+ const findings = [];
215
+ const seen = new Set();
216
+ let suppressed = 0;
217
+ for (const rule of RULES) {
218
+ const pattern = new RegExp(rule.sink.source, rule.sink.flags.includes("g") ? rule.sink.flags : `${rule.sink.flags}g`);
219
+ for (let match = pattern.exec(clean); match; match = pattern.exec(clean)) {
220
+ const offset = match.index ?? 0;
221
+ const window = clean.slice(offset, offset + 260);
222
+ if (rule.taint !== false && !TAINT.test(window))
223
+ continue;
224
+ if (rule.id === "path-traversal" && SANITIZER.test(window))
225
+ continue;
226
+ const { line, column } = locate(starts, offset);
227
+ const key = `${rule.id}:${String(line)}:${String(column)}`;
228
+ if (seen.has(key))
229
+ continue;
230
+ seen.add(key);
231
+ if (options.suppressions !== false && suppressionAt(lines, line - 1, rule.id)) {
232
+ suppressed += 1;
233
+ continue;
234
+ }
235
+ findings.push({
236
+ rule: rule.id,
237
+ severity: rule.severity,
238
+ file,
239
+ line,
240
+ column,
241
+ message: rule.message,
242
+ snippet: (lines[line - 1] ?? "").trim().slice(0, 200),
243
+ });
244
+ }
245
+ }
246
+ findings.sort((a, b) => a.line - b.line || a.column - b.column || a.rule.localeCompare(b.rule));
247
+ return { findings, suppressed };
248
+ }
249
+ export function buildSastReport(files, results) {
250
+ const findings = results.flatMap((result) => result.findings);
251
+ const summary = { high: 0, medium: 0, low: 0 };
252
+ for (const finding of findings)
253
+ summary[finding.severity] += 1;
254
+ return {
255
+ files,
256
+ findings,
257
+ suppressed: results.reduce((total, result) => total + result.suppressed, 0),
258
+ summary,
259
+ };
260
+ }
261
+ export function renderSast(report) {
262
+ const lines = [
263
+ `Файлов проверено: ${String(report.files)}.`,
264
+ `Найдено: ${String(report.findings.length)} (high ${String(report.summary.high)}, medium ${String(report.summary.medium)}, low ${String(report.summary.low)}), подавлено: ${String(report.suppressed)}.`,
265
+ ];
266
+ if (report.findings.length === 0) {
267
+ lines.push("Проблем не найдено.");
268
+ }
269
+ else {
270
+ for (const finding of report.findings) {
271
+ lines.push(`${finding.severity.toUpperCase().padEnd(6)} ${finding.rule.padEnd(20)} ${finding.file}:${String(finding.line)} — ${finding.message}`);
272
+ }
273
+ }
274
+ return `${lines.join("\n")}\n`;
275
+ }
276
+ /** SARIF 2.1.0 for code scanning uploads. */
277
+ export function sastSarif(report) {
278
+ const levels = {
279
+ high: "error",
280
+ medium: "warning",
281
+ low: "note",
282
+ };
283
+ const rules = [...new Set(report.findings.map((finding) => finding.rule))].map((id) => {
284
+ const finding = report.findings.find((candidate) => candidate.rule === id);
285
+ return {
286
+ id: `oc-pilot/${id}`,
287
+ name: id,
288
+ shortDescription: { text: finding.message },
289
+ defaultConfiguration: { level: levels[finding.severity] },
290
+ };
291
+ });
292
+ const results = report.findings.map((finding) => ({
293
+ ruleId: `oc-pilot/${finding.rule}`,
294
+ level: levels[finding.severity],
295
+ message: { text: `${finding.message}: ${finding.snippet}` },
296
+ locations: [
297
+ {
298
+ physicalLocation: {
299
+ artifactLocation: { uri: finding.file.replace(/\\/g, "/") },
300
+ region: { startLine: finding.line, startColumn: finding.column },
301
+ },
302
+ },
303
+ ],
304
+ properties: { severity: finding.severity },
305
+ }));
306
+ return `${JSON.stringify({
307
+ $schema: SARIF_SCHEMA,
308
+ version: SARIF_VERSION,
309
+ runs: [
310
+ {
311
+ tool: {
312
+ driver: {
313
+ name: "oc-pilot",
314
+ informationUri: "https://github.com/oc-pilot/oc-pilot",
315
+ version: VERSION,
316
+ rules,
317
+ },
318
+ },
319
+ results,
320
+ },
321
+ ],
322
+ }, null, 2)}\n`;
323
+ }
324
+ //# sourceMappingURL=sast.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sast.js","sourceRoot":"","sources":["../../src/core/sast.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ import assert from "node:assert/strict";
2
+ import { test } from "node:test";
3
+ import { buildSastReport, parseSastSeverity, renderSast, sastSarif, scanSource, severityAtLeast, stripComments, } from "./sast.js";
4
+ test("scanSource reports request data reaching dangerous sinks", () => {
5
+ const source = [
6
+ 'const { exec } = require("node:child_process");',
7
+ "exec(`rm -rf ${req.query.dir}`);",
8
+ 'db.query("SELECT * FROM users WHERE id = " + req.query.id);',
9
+ "readFile(req.query.file);",
10
+ "fetch(req.body.url);",
11
+ "eval(userInput);",
12
+ "el.innerHTML = req.query.html;",
13
+ "https.request({ rejectUnauthorized: false });",
14
+ "",
15
+ ].join("\n");
16
+ const { findings, suppressed } = scanSource("src/app.js", source);
17
+ const rules = findings.map((finding) => finding.rule).sort();
18
+ assert.deepEqual(rules, [
19
+ "code-injection",
20
+ "command-injection",
21
+ "path-traversal",
22
+ "sql-injection",
23
+ "ssrf",
24
+ "tls-verification",
25
+ "xss",
26
+ ]);
27
+ assert.equal(suppressed, 0);
28
+ assert.equal(findings.find((finding) => finding.rule === "sql-injection")?.line, 3);
29
+ assert.match(findings.find((finding) => finding.rule === "command-injection")?.snippet ?? "", /exec/);
30
+ });
31
+ test("constant arguments do not trigger the taint rules", () => {
32
+ const source = [
33
+ 'exec("ls -la");',
34
+ 'db.query("SELECT 1");',
35
+ 'readFile("/etc/hosts");',
36
+ 'fetch("https://example.com");',
37
+ 'el.innerHTML = "<b>static</b>";',
38
+ "",
39
+ ].join("\n");
40
+ const { findings } = scanSource("src/safe.js", source);
41
+ assert.deepEqual(findings, []);
42
+ });
43
+ test("path.resolve silences the path-traversal rule", () => {
44
+ const source = 'readFile(path.resolve("/data", req.query.file));\n';
45
+ const { findings } = scanSource("src/files.js", source);
46
+ assert.deepEqual(findings, []);
47
+ });
48
+ test("stripComments removes comments but keeps string contents and offsets", () => {
49
+ const source = [
50
+ "// exec(req.query.x)",
51
+ "/* db.query(req.body.y) */",
52
+ 'const value = "exec(req.query.z)";',
53
+ "",
54
+ ].join("\n");
55
+ const clean = stripComments(source);
56
+ assert.equal(clean.includes("exec(req.query.x)"), false);
57
+ assert.equal(clean.includes("db.query(req.body.y)"), false);
58
+ assert.equal(clean.includes("exec(req.query.z)"), true);
59
+ assert.equal(clean.split("\n").length, source.split("\n").length);
60
+ assert.equal(clean.length, source.length);
61
+ });
62
+ test("inline suppressions need a reason and may be scoped to a rule", () => {
63
+ const suppressed = scanSource("src/admin.js", "exec(req.query.cmd); // oc-pilot:sast-ignore command-injection: trusted admin command\n");
64
+ assert.deepEqual(suppressed.findings, []);
65
+ assert.equal(suppressed.suppressed, 1);
66
+ const noReason = scanSource("src/admin.js", "exec(req.query.cmd); // oc-pilot:sast-ignore\n");
67
+ assert.equal(noReason.findings.length, 1);
68
+ const wrongRule = scanSource("src/admin.js", "exec(req.query.cmd); // oc-pilot:sast-ignore sql-injection: wrong rule\n");
69
+ assert.equal(wrongRule.findings.length, 1);
70
+ });
71
+ test("severity helpers order the levels", () => {
72
+ assert.equal(parseSastSeverity(" HIGH "), "high");
73
+ assert.equal(parseSastSeverity("critical"), undefined);
74
+ assert.equal(severityAtLeast("high", "medium"), true);
75
+ assert.equal(severityAtLeast("medium", "high"), false);
76
+ assert.equal(severityAtLeast("low", "low"), true);
77
+ });
78
+ test("buildSastReport and renderSast summarize the scan", () => {
79
+ const high = scanSource("src/a.js", "exec(req.query.cmd);\n");
80
+ const medium = scanSource("src/b.js", "res.redirect(req.query.next);\n");
81
+ const report = buildSastReport(2, [high, medium]);
82
+ assert.equal(report.files, 2);
83
+ assert.equal(report.findings.length, 2);
84
+ assert.deepEqual(report.summary, { high: 1, medium: 1, low: 0 });
85
+ const text = renderSast(report);
86
+ assert.match(text, /Файлов проверено: 2\./);
87
+ assert.match(text, /Найдено: 2 \(high 1, medium 1, low 0\), подавлено: 0\./);
88
+ assert.match(text, /HIGH\s+command-injection\s+src\/a\.js:1/);
89
+ assert.equal(text.endsWith("\n"), true);
90
+ const empty = renderSast(buildSastReport(1, [scanSource("src/safe.js", "const a = 1;\n")]));
91
+ assert.match(empty, /Проблем не найдено\./);
92
+ });
93
+ test("sastSarif emits SARIF 2.1.0 with rule metadata", () => {
94
+ const report = buildSastReport(1, [scanSource("src/a.js", 'db.query("SELECT " + req.query.id);\n')]);
95
+ const sarif = JSON.parse(sastSarif(report));
96
+ assert.equal(sarif.version, "2.1.0");
97
+ assert.equal(sarif.runs[0]?.tool.driver.name, "oc-pilot");
98
+ assert.deepEqual(sarif.runs[0]?.tool.driver.rules.map((rule) => rule.id), ["oc-pilot/sql-injection"]);
99
+ assert.equal(sarif.runs[0]?.results[0]?.ruleId, "oc-pilot/sql-injection");
100
+ assert.equal(sarif.runs[0]?.results[0]?.level, "error");
101
+ assert.equal(sarif.runs[0]?.results[0]?.locations[0]?.physicalLocation.artifactLocation.uri, "src/a.js");
102
+ assert.equal(sarif.runs[0]?.results[0]?.locations[0]?.physicalLocation.region.startLine, 1);
103
+ assert.equal(sarif.runs[0]?.results[0]?.properties.severity, "high");
104
+ });
105
+ //# sourceMappingURL=sast.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sast.test.js","sourceRoot":"","sources":["../../src/core/sast.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * License audit and CycloneDX SBOM for the resolved lockfile. Both work
3
+ * offline: the SBOM needs only the lockfile, and licenses are read from the
4
+ * installed packages when `node_modules` is present.
5
+ */
6
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7
+ import { dirname, isAbsolute, join, resolve } from "node:path";
8
+ import { VERSION } from "../version.js";
9
+ const PERMISSIVE = ["mit", "isc", "bsd", "apache", "0bsd", "unlicense", "cc0", "wtfpl", "zlib", "python-2.0"];
10
+ const WEAK_COPYLEFT = ["lgpl", "mpl", "epl", "cddl"];
11
+ export function licenseRisk(license) {
12
+ const value = license.trim().toLowerCase();
13
+ if (!value || value === "unknown" || value === "noassertion")
14
+ return "unknown";
15
+ if (value.includes("unlicensed") || value.includes("proprietary") || value.includes("see license"))
16
+ return "proprietary";
17
+ if (value.includes("agpl") || (value.includes("gpl") && !value.includes("lgpl")))
18
+ return "strong-copyleft";
19
+ if (WEAK_COPYLEFT.some((marker) => value.includes(marker)))
20
+ return "weak-copyleft";
21
+ if (PERMISSIVE.some((marker) => value.includes(marker)))
22
+ return "permissive";
23
+ return "unknown";
24
+ }
25
+ function licenseOf(value) {
26
+ if (typeof value === "string" && value.trim().length > 0)
27
+ return value.trim();
28
+ if (value && typeof value === "object" && !Array.isArray(value)) {
29
+ const type = value.type;
30
+ if (typeof type === "string" && type.trim().length > 0)
31
+ return type.trim();
32
+ }
33
+ if (Array.isArray(value)) {
34
+ for (const item of value) {
35
+ const found = licenseOf(item);
36
+ if (found)
37
+ return found;
38
+ }
39
+ }
40
+ return undefined;
41
+ }
42
+ /** Reads licenses from installed packages; absent `node_modules` is reported, not guessed. */
43
+ export function auditLicenses(cwd, packages) {
44
+ const unique = new Map();
45
+ for (const pkg of packages) {
46
+ if (pkg.name && pkg.version)
47
+ unique.set(`${pkg.name}@${pkg.version}`, pkg);
48
+ }
49
+ let checked = 0;
50
+ let missing = 0;
51
+ const findings = [];
52
+ for (const pkg of unique.values()) {
53
+ const manifestPath = join(cwd, "node_modules", pkg.name, "package.json");
54
+ if (!existsSync(manifestPath)) {
55
+ missing += 1;
56
+ continue;
57
+ }
58
+ let manifest;
59
+ try {
60
+ manifest = JSON.parse(readFileSync(manifestPath, "utf8").replace(/^\uFEFF/, ""));
61
+ }
62
+ catch {
63
+ missing += 1;
64
+ continue;
65
+ }
66
+ checked += 1;
67
+ const license = licenseOf(manifest.license) ?? licenseOf(manifest.licenses) ?? "unknown";
68
+ const risk = licenseRisk(license);
69
+ if (risk !== "permissive")
70
+ findings.push({ name: pkg.name, version: pkg.version, license, risk });
71
+ }
72
+ return { checked, missing, findings };
73
+ }
74
+ function purl(name, version) {
75
+ const encoded = name.startsWith("@") ? `%40${name.slice(1)}` : name;
76
+ return `pkg:npm/${encoded}@${version}`;
77
+ }
78
+ export function buildSbom(packages, packageName, timestamp = new Date().toISOString()) {
79
+ const unique = new Map();
80
+ for (const pkg of packages) {
81
+ if (pkg.name && pkg.version)
82
+ unique.set(`${pkg.name}@${pkg.version}`, pkg);
83
+ }
84
+ const components = [...unique.values()]
85
+ .map((pkg) => ({ type: "library", name: pkg.name, version: pkg.version, purl: purl(pkg.name, pkg.version) }))
86
+ .sort((a, b) => a.purl.localeCompare(b.purl));
87
+ return {
88
+ bomFormat: "CycloneDX",
89
+ specVersion: "1.5",
90
+ version: 1,
91
+ metadata: {
92
+ timestamp,
93
+ tools: [{ vendor: "oc-pilot", name: "oc-pilot", version: VERSION }],
94
+ component: { type: "application", name: packageName },
95
+ },
96
+ components,
97
+ };
98
+ }
99
+ export function writeSbom(cwd, file, sbom) {
100
+ const absolute = isAbsolute(file) ? file : resolve(cwd, file);
101
+ mkdirSync(dirname(absolute), { recursive: true });
102
+ writeFileSync(absolute, `${JSON.stringify(sbom, null, 2)}\n`, "utf8");
103
+ return absolute;
104
+ }
105
+ export function renderLicenses(audit) {
106
+ const lines = [
107
+ `Лицензии: проверено ${String(audit.checked)} пакетов` +
108
+ (audit.missing > 0 ? `, без package.json ${String(audit.missing)} (node_modules не установлен?)` : "") +
109
+ ".",
110
+ ];
111
+ if (audit.findings.length === 0) {
112
+ lines.push("Лицензионных рисков не найдено.");
113
+ return `${lines.join("\n")}\n`;
114
+ }
115
+ lines.push("Лицензионные риски:");
116
+ for (const finding of audit.findings) {
117
+ lines.push(`${finding.risk.padEnd(15)} ${finding.name}@${finding.version} — ${finding.license}`);
118
+ }
119
+ return `${lines.join("\n")}\n`;
120
+ }
121
+ //# sourceMappingURL=sbom.js.map