kirograph 0.17.0 → 0.19.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 (329) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +103 -1822
  3. package/dist/architecture/layers/dart.js +112 -0
  4. package/dist/architecture/layers/dart.js.map +7 -0
  5. package/dist/architecture/layers/index.js +3 -1
  6. package/dist/architecture/layers/index.js.map +2 -2
  7. package/dist/bin/banner.js +74 -6
  8. package/dist/bin/banner.js.map +2 -2
  9. package/dist/bin/commands/attack-surface.js +157 -0
  10. package/dist/bin/commands/attack-surface.js.map +7 -0
  11. package/dist/bin/commands/benchmark.js +206 -0
  12. package/dist/bin/commands/benchmark.js.map +7 -0
  13. package/dist/bin/commands/budget.js +70 -0
  14. package/dist/bin/commands/budget.js.map +7 -0
  15. package/dist/bin/commands/communities.js +77 -0
  16. package/dist/bin/commands/communities.js.map +7 -0
  17. package/dist/bin/commands/dep-confusion.js +119 -0
  18. package/dist/bin/commands/dep-confusion.js.map +7 -0
  19. package/dist/bin/commands/export.js +304 -10
  20. package/dist/bin/commands/export.js.map +2 -2
  21. package/dist/bin/commands/flows.js +97 -0
  22. package/dist/bin/commands/flows.js.map +7 -0
  23. package/dist/bin/commands/help.js +382 -101
  24. package/dist/bin/commands/help.js.map +2 -2
  25. package/dist/bin/commands/install.js +90 -8
  26. package/dist/bin/commands/install.js.map +2 -2
  27. package/dist/bin/commands/licenses.js +201 -0
  28. package/dist/bin/commands/licenses.js.map +7 -0
  29. package/dist/bin/commands/reachability.js +160 -0
  30. package/dist/bin/commands/reachability.js.map +7 -0
  31. package/dist/bin/commands/read.js +100 -0
  32. package/dist/bin/commands/read.js.map +7 -0
  33. package/dist/bin/commands/refactor.js +116 -0
  34. package/dist/bin/commands/refactor.js.map +7 -0
  35. package/dist/bin/commands/remediation.js +133 -0
  36. package/dist/bin/commands/remediation.js.map +7 -0
  37. package/dist/bin/commands/sbom.js +76 -0
  38. package/dist/bin/commands/sbom.js.map +7 -0
  39. package/dist/bin/commands/security-ci-report.js +267 -0
  40. package/dist/bin/commands/security-ci-report.js.map +7 -0
  41. package/dist/bin/commands/security-export.js +1349 -0
  42. package/dist/bin/commands/security-export.js.map +7 -0
  43. package/dist/bin/commands/security-flows.js +126 -0
  44. package/dist/bin/commands/security-flows.js.map +7 -0
  45. package/dist/bin/commands/security-secrets.js +170 -0
  46. package/dist/bin/commands/security-secrets.js.map +7 -0
  47. package/dist/bin/commands/security.js +193 -0
  48. package/dist/bin/commands/security.js.map +7 -0
  49. package/dist/bin/commands/staleness.js +144 -0
  50. package/dist/bin/commands/staleness.js.map +7 -0
  51. package/dist/bin/commands/status.js +102 -2
  52. package/dist/bin/commands/status.js.map +2 -2
  53. package/dist/bin/commands/supply-chain.js +138 -0
  54. package/dist/bin/commands/supply-chain.js.map +7 -0
  55. package/dist/bin/commands/uninit.js +109 -60
  56. package/dist/bin/commands/uninit.js.map +2 -2
  57. package/dist/bin/commands/vex.js +76 -0
  58. package/dist/bin/commands/vex.js.map +7 -0
  59. package/dist/bin/commands/vuln-suppress.js +95 -0
  60. package/dist/bin/commands/vuln-suppress.js.map +7 -0
  61. package/dist/bin/commands/vulns.js +382 -0
  62. package/dist/bin/commands/vulns.js.map +7 -0
  63. package/dist/bin/installer/auto-detect.js +125 -0
  64. package/dist/bin/installer/auto-detect.js.map +7 -0
  65. package/dist/bin/installer/cli-agent.js +28 -5
  66. package/dist/bin/installer/cli-agent.js.map +2 -2
  67. package/dist/bin/installer/common.js +47 -0
  68. package/dist/bin/installer/common.js.map +2 -2
  69. package/dist/bin/installer/config-prompt.js +12 -1
  70. package/dist/bin/installer/config-prompt.js.map +2 -2
  71. package/dist/bin/installer/detect.js +309 -0
  72. package/dist/bin/installer/detect.js.map +7 -0
  73. package/dist/bin/installer/index.js +9 -1
  74. package/dist/bin/installer/index.js.map +2 -2
  75. package/dist/bin/installer/instructions.js +195 -3
  76. package/dist/bin/installer/instructions.js.map +2 -2
  77. package/dist/bin/installer/steering.js +407 -0
  78. package/dist/bin/installer/steering.js.map +2 -2
  79. package/dist/bin/installer/targets/aider.js +3 -3
  80. package/dist/bin/installer/targets/aider.js.map +2 -2
  81. package/dist/bin/installer/targets/amp.js +3 -3
  82. package/dist/bin/installer/targets/amp.js.map +2 -2
  83. package/dist/bin/installer/targets/antigravity.js +35 -6
  84. package/dist/bin/installer/targets/antigravity.js.map +2 -2
  85. package/dist/bin/installer/targets/augment.js +3 -3
  86. package/dist/bin/installer/targets/augment.js.map +2 -2
  87. package/dist/bin/installer/targets/claude.js +49 -5
  88. package/dist/bin/installer/targets/claude.js.map +2 -2
  89. package/dist/bin/installer/targets/cline.js +32 -6
  90. package/dist/bin/installer/targets/cline.js.map +2 -2
  91. package/dist/bin/installer/targets/codex.js +64 -12
  92. package/dist/bin/installer/targets/codex.js.map +2 -2
  93. package/dist/bin/installer/targets/continue.js +2 -2
  94. package/dist/bin/installer/targets/continue.js.map +2 -2
  95. package/dist/bin/installer/targets/copilot-cli.js +101 -0
  96. package/dist/bin/installer/targets/copilot-cli.js.map +7 -0
  97. package/dist/bin/installer/targets/copilot.js +31 -5
  98. package/dist/bin/installer/targets/copilot.js.map +2 -2
  99. package/dist/bin/installer/targets/cursor.js +4 -4
  100. package/dist/bin/installer/targets/cursor.js.map +2 -2
  101. package/dist/bin/installer/targets/devin.js +2 -2
  102. package/dist/bin/installer/targets/devin.js.map +2 -2
  103. package/dist/bin/installer/targets/gemini-cli.js +2 -2
  104. package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
  105. package/dist/bin/installer/targets/generic.js +2 -14
  106. package/dist/bin/installer/targets/generic.js.map +2 -2
  107. package/dist/bin/installer/targets/goose.js +3 -3
  108. package/dist/bin/installer/targets/goose.js.map +2 -2
  109. package/dist/bin/installer/targets/index.js +250 -0
  110. package/dist/bin/installer/targets/index.js.map +2 -2
  111. package/dist/bin/installer/targets/junie.js +2 -2
  112. package/dist/bin/installer/targets/junie.js.map +2 -2
  113. package/dist/bin/installer/targets/kilo.js +2 -2
  114. package/dist/bin/installer/targets/kilo.js.map +2 -2
  115. package/dist/bin/installer/targets/kiro.js +3 -3
  116. package/dist/bin/installer/targets/kiro.js.map +2 -2
  117. package/dist/bin/installer/targets/opencode.js +2 -2
  118. package/dist/bin/installer/targets/opencode.js.map +2 -2
  119. package/dist/bin/installer/targets/openhands.js +3 -3
  120. package/dist/bin/installer/targets/openhands.js.map +2 -2
  121. package/dist/bin/installer/targets/qoder.js +91 -0
  122. package/dist/bin/installer/targets/qoder.js.map +7 -0
  123. package/dist/bin/installer/targets/qwen.js +96 -0
  124. package/dist/bin/installer/targets/qwen.js.map +7 -0
  125. package/dist/bin/installer/targets/replit.js +3 -3
  126. package/dist/bin/installer/targets/replit.js.map +2 -2
  127. package/dist/bin/installer/targets/roo.js +2 -2
  128. package/dist/bin/installer/targets/roo.js.map +2 -2
  129. package/dist/bin/installer/targets/tabnine.js +3 -3
  130. package/dist/bin/installer/targets/tabnine.js.map +2 -2
  131. package/dist/bin/installer/targets/trae.js +5 -5
  132. package/dist/bin/installer/targets/trae.js.map +2 -2
  133. package/dist/bin/installer/targets/warp.js +2 -2
  134. package/dist/bin/installer/targets/warp.js.map +2 -2
  135. package/dist/bin/installer/targets/windsurf.js +35 -6
  136. package/dist/bin/installer/targets/windsurf.js.map +2 -2
  137. package/dist/bin/kirograph.js +72 -6
  138. package/dist/bin/kirograph.js.map +3 -3
  139. package/dist/compression/tracker.js +80 -0
  140. package/dist/compression/tracker.js.map +2 -2
  141. package/dist/config.js +90 -3
  142. package/dist/config.js.map +2 -2
  143. package/dist/core/pipeline.js +36 -2
  144. package/dist/core/pipeline.js.map +2 -2
  145. package/dist/db/database.js +56 -5
  146. package/dist/db/database.js.map +2 -2
  147. package/dist/db/memory-schema.sql +5 -1
  148. package/dist/db/schema.sql +3 -1
  149. package/dist/db/security-schema.sql +72 -0
  150. package/dist/extraction/extractor.js +81 -0
  151. package/dist/extraction/extractor.js.map +2 -2
  152. package/dist/extraction/grammars.js +32 -8
  153. package/dist/extraction/grammars.js.map +2 -2
  154. package/dist/extraction/languages.js +43 -1
  155. package/dist/extraction/languages.js.map +2 -2
  156. package/dist/extraction/notebook.js +300 -0
  157. package/dist/extraction/notebook.js.map +7 -0
  158. package/dist/extraction/wasm/tree-sitter-astro.wasm +0 -0
  159. package/dist/extraction/wasm/tree-sitter-gdscript.wasm +0 -0
  160. package/dist/extraction/wasm/tree-sitter-julia.wasm +0 -0
  161. package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
  162. package/dist/extraction/wasm/tree-sitter-perl.wasm +0 -0
  163. package/dist/extraction/wasm/tree-sitter-powershell.wasm +0 -0
  164. package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
  165. package/dist/extraction/wasm/tree-sitter-sql.wasm +0 -0
  166. package/dist/extraction/wasm/tree-sitter-verilog.wasm +0 -0
  167. package/dist/frameworks/flutter.js +130 -0
  168. package/dist/frameworks/flutter.js.map +7 -0
  169. package/dist/frameworks/index.js +7 -1
  170. package/dist/frameworks/index.js.map +3 -3
  171. package/dist/graph/communities.js +317 -0
  172. package/dist/graph/communities.js.map +7 -0
  173. package/dist/graph/flows.js +138 -0
  174. package/dist/graph/flows.js.map +7 -0
  175. package/dist/graph/refactor.js +155 -0
  176. package/dist/graph/refactor.js.map +7 -0
  177. package/dist/index.js +1 -1
  178. package/dist/index.js.map +2 -2
  179. package/dist/mcp/cache.js +172 -0
  180. package/dist/mcp/cache.js.map +7 -0
  181. package/dist/mcp/read-modes.js +295 -0
  182. package/dist/mcp/read-modes.js.map +7 -0
  183. package/dist/mcp/server.js +2 -1
  184. package/dist/mcp/server.js.map +2 -2
  185. package/dist/mcp/tool-names.js +21 -1
  186. package/dist/mcp/tool-names.js.map +2 -2
  187. package/dist/mcp/tools.js +1160 -1
  188. package/dist/mcp/tools.js.map +3 -3
  189. package/dist/memory/database.js +32 -2
  190. package/dist/memory/database.js.map +2 -2
  191. package/dist/memory/types.js.map +1 -1
  192. package/dist/resolution/bridges/android-rn.js +298 -0
  193. package/dist/resolution/bridges/android-rn.js.map +7 -0
  194. package/dist/resolution/bridges/expo-modules.js +155 -0
  195. package/dist/resolution/bridges/expo-modules.js.map +7 -0
  196. package/dist/resolution/bridges/flutter-channel.js +385 -0
  197. package/dist/resolution/bridges/flutter-channel.js.map +7 -0
  198. package/dist/resolution/bridges/index.js +80 -0
  199. package/dist/resolution/bridges/index.js.map +7 -0
  200. package/dist/resolution/bridges/native-events.js +188 -0
  201. package/dist/resolution/bridges/native-events.js.map +7 -0
  202. package/dist/resolution/bridges/native-views.js +244 -0
  203. package/dist/resolution/bridges/native-views.js.map +7 -0
  204. package/dist/resolution/bridges/react-native.js +161 -0
  205. package/dist/resolution/bridges/react-native.js.map +7 -0
  206. package/dist/resolution/bridges/swift-objc.js +227 -0
  207. package/dist/resolution/bridges/swift-objc.js.map +7 -0
  208. package/dist/resolution/bridges/turbomodules.js +216 -0
  209. package/dist/resolution/bridges/turbomodules.js.map +7 -0
  210. package/dist/resolution/index.js +61 -2
  211. package/dist/resolution/index.js.map +2 -2
  212. package/dist/security/attack-surface.js +164 -0
  213. package/dist/security/attack-surface.js.map +7 -0
  214. package/dist/security/context-warnings.js +123 -0
  215. package/dist/security/context-warnings.js.map +7 -0
  216. package/dist/security/context-warnings.test.js +300 -0
  217. package/dist/security/context-warnings.test.js.map +7 -0
  218. package/dist/security/data-flows.js +228 -0
  219. package/dist/security/data-flows.js.map +7 -0
  220. package/dist/security/dep-confusion.js +255 -0
  221. package/dist/security/dep-confusion.js.map +7 -0
  222. package/dist/security/errors.js +51 -0
  223. package/dist/security/errors.js.map +7 -0
  224. package/dist/security/export/fix-suggestions.js +58 -0
  225. package/dist/security/export/fix-suggestions.js.map +7 -0
  226. package/dist/security/export/fix-suggestions.test.js +74 -0
  227. package/dist/security/export/fix-suggestions.test.js.map +7 -0
  228. package/dist/security/export/sbom.js +230 -0
  229. package/dist/security/export/sbom.js.map +7 -0
  230. package/dist/security/export/sbom.test.js +288 -0
  231. package/dist/security/export/sbom.test.js.map +7 -0
  232. package/dist/security/export/serialization.js +142 -0
  233. package/dist/security/export/serialization.js.map +7 -0
  234. package/dist/security/export/serialization.test.js +310 -0
  235. package/dist/security/export/serialization.test.js.map +7 -0
  236. package/dist/security/export/vex.js +220 -0
  237. package/dist/security/export/vex.js.map +7 -0
  238. package/dist/security/export/vex.test.js +278 -0
  239. package/dist/security/export/vex.test.js.map +7 -0
  240. package/dist/security/index.js +67 -0
  241. package/dist/security/index.js.map +7 -0
  242. package/dist/security/integrator-transitives.test.js +359 -0
  243. package/dist/security/integrator-transitives.test.js.map +7 -0
  244. package/dist/security/integrator.js +491 -0
  245. package/dist/security/integrator.js.map +7 -0
  246. package/dist/security/integrator.test.js +237 -0
  247. package/dist/security/integrator.test.js.map +7 -0
  248. package/dist/security/license.js +70 -0
  249. package/dist/security/license.js.map +7 -0
  250. package/dist/security/manifest/adapter.js +243 -0
  251. package/dist/security/manifest/adapter.js.map +7 -0
  252. package/dist/security/manifest/adapter.test.js +272 -0
  253. package/dist/security/manifest/adapter.test.js.map +7 -0
  254. package/dist/security/manifest/parser.js +376 -0
  255. package/dist/security/manifest/parser.js.map +7 -0
  256. package/dist/security/manifest/parser.test.js +110 -0
  257. package/dist/security/manifest/parser.test.js.map +7 -0
  258. package/dist/security/manifest/plugins/cargo.js +233 -0
  259. package/dist/security/manifest/plugins/cargo.js.map +7 -0
  260. package/dist/security/manifest/plugins/cargo.test.js +304 -0
  261. package/dist/security/manifest/plugins/cargo.test.js.map +7 -0
  262. package/dist/security/manifest/plugins/composer.js +152 -0
  263. package/dist/security/manifest/plugins/composer.js.map +7 -0
  264. package/dist/security/manifest/plugins/go.js +146 -0
  265. package/dist/security/manifest/plugins/go.js.map +7 -0
  266. package/dist/security/manifest/plugins/go.test.js +341 -0
  267. package/dist/security/manifest/plugins/go.test.js.map +7 -0
  268. package/dist/security/manifest/plugins/gradle.js +159 -0
  269. package/dist/security/manifest/plugins/gradle.js.map +7 -0
  270. package/dist/security/manifest/plugins/hex.js +159 -0
  271. package/dist/security/manifest/plugins/hex.js.map +7 -0
  272. package/dist/security/manifest/plugins/maven.js +128 -0
  273. package/dist/security/manifest/plugins/maven.js.map +7 -0
  274. package/dist/security/manifest/plugins/maven.test.js +389 -0
  275. package/dist/security/manifest/plugins/maven.test.js.map +7 -0
  276. package/dist/security/manifest/plugins/npm.js +284 -0
  277. package/dist/security/manifest/plugins/npm.js.map +7 -0
  278. package/dist/security/manifest/plugins/npm.test.js +298 -0
  279. package/dist/security/manifest/plugins/npm.test.js.map +7 -0
  280. package/dist/security/manifest/plugins/nuget.js +186 -0
  281. package/dist/security/manifest/plugins/nuget.js.map +7 -0
  282. package/dist/security/manifest/plugins/pip.js +123 -0
  283. package/dist/security/manifest/plugins/pip.js.map +7 -0
  284. package/dist/security/manifest/plugins/pip.test.js +208 -0
  285. package/dist/security/manifest/plugins/pip.test.js.map +7 -0
  286. package/dist/security/manifest/plugins/pubspec.js +212 -0
  287. package/dist/security/manifest/plugins/pubspec.js.map +7 -0
  288. package/dist/security/manifest/plugins/pyproject.js +261 -0
  289. package/dist/security/manifest/plugins/pyproject.js.map +7 -0
  290. package/dist/security/manifest/plugins/rubygems.js +185 -0
  291. package/dist/security/manifest/plugins/rubygems.js.map +7 -0
  292. package/dist/security/manifest/plugins/swift.js +200 -0
  293. package/dist/security/manifest/plugins/swift.js.map +7 -0
  294. package/dist/security/owasp.js +104 -0
  295. package/dist/security/owasp.js.map +7 -0
  296. package/dist/security/pipeline.js +125 -0
  297. package/dist/security/pipeline.js.map +7 -0
  298. package/dist/security/reachability.js +276 -0
  299. package/dist/security/reachability.js.map +7 -0
  300. package/dist/security/reachability.test.js +394 -0
  301. package/dist/security/reachability.test.js.map +7 -0
  302. package/dist/security/remediation.js +126 -0
  303. package/dist/security/remediation.js.map +7 -0
  304. package/dist/security/secrets.js +176 -0
  305. package/dist/security/secrets.js.map +7 -0
  306. package/dist/security/staleness.js +231 -0
  307. package/dist/security/staleness.js.map +7 -0
  308. package/dist/security/supply-chain.js +301 -0
  309. package/dist/security/supply-chain.js.map +7 -0
  310. package/dist/security/suppressions.js +106 -0
  311. package/dist/security/suppressions.js.map +7 -0
  312. package/dist/security/types.js +17 -0
  313. package/dist/security/types.js.map +7 -0
  314. package/dist/security/vuln/client.js +355 -0
  315. package/dist/security/vuln/client.js.map +7 -0
  316. package/dist/security/vuln/client.test.js +288 -0
  317. package/dist/security/vuln/client.test.js.map +7 -0
  318. package/dist/security/vuln/epss-client.js +95 -0
  319. package/dist/security/vuln/epss-client.js.map +7 -0
  320. package/dist/security/vuln/index.js +29 -0
  321. package/dist/security/vuln/index.js.map +7 -0
  322. package/dist/security/vuln/osv-adapter.js +321 -0
  323. package/dist/security/vuln/osv-adapter.js.map +7 -0
  324. package/dist/security/vuln/osv-adapter.test.js +391 -0
  325. package/dist/security/vuln/osv-adapter.test.js.map +7 -0
  326. package/dist/security/vuln/types.js +17 -0
  327. package/dist/security/vuln/types.js.map +7 -0
  328. package/dist/types.js.map +2 -2
  329. package/package.json +2 -1
@@ -0,0 +1,233 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+ var cargo_exports = {};
30
+ __export(cargo_exports, {
31
+ parseCargoManifest: () => parseCargoManifest
32
+ });
33
+ module.exports = __toCommonJS(cargo_exports);
34
+ var fs = __toESM(require("fs"));
35
+ var path = __toESM(require("path"));
36
+ var import_errors = require("../../../errors");
37
+ const SECTION_SCOPES = [
38
+ { section: "dependencies", scope: "production" },
39
+ { section: "dev-dependencies", scope: "development" },
40
+ { section: "build-dependencies", scope: "development" }
41
+ ];
42
+ async function parseCargoManifest(manifestPath, projectRoot) {
43
+ const manifestDir = path.dirname(manifestPath);
44
+ const relativeManifest = path.relative(projectRoot, manifestPath).replace(/\\/g, "/");
45
+ let content;
46
+ try {
47
+ content = fs.readFileSync(manifestPath, "utf8");
48
+ } catch (err) {
49
+ (0, import_errors.logWarn)(`[sec:cargo] Failed to read ${relativeManifest}: ${err instanceof Error ? err.message : String(err)}`);
50
+ return [];
51
+ }
52
+ const resolvedVersions = loadResolvedVersions(manifestDir);
53
+ const license = extractCargoLicense(content);
54
+ const dependencies = [];
55
+ for (const { section, scope } of SECTION_SCOPES) {
56
+ const sectionDeps = extractSectionDependencies(content, section, relativeManifest);
57
+ for (const dep of sectionDeps) {
58
+ const resolvedVersion = resolvedVersions.get(dep.name);
59
+ dependencies.push({
60
+ name: dep.name,
61
+ declaredConstraint: dep.constraint,
62
+ resolvedVersion,
63
+ scope,
64
+ ecosystem: "cargo",
65
+ sourceManifest: relativeManifest,
66
+ ...license !== void 0 ? { license } : {}
67
+ });
68
+ }
69
+ }
70
+ return dependencies;
71
+ }
72
+ function extractSectionDependencies(content, section, relativeManifest) {
73
+ const deps = [];
74
+ const sectionRegex = new RegExp(`^\\[${escapeRegex(section)}\\]\\s*$`, "m");
75
+ const sectionMatch = sectionRegex.exec(content);
76
+ if (sectionMatch) {
77
+ const startIdx = sectionMatch.index + sectionMatch[0].length;
78
+ const remaining = content.slice(startIdx);
79
+ const nextSectionMatch = remaining.match(/^\[/m);
80
+ const sectionContent = nextSectionMatch ? remaining.slice(0, nextSectionMatch.index) : remaining;
81
+ const lines = sectionContent.split("\n");
82
+ for (const line of lines) {
83
+ const trimmed = line.trim();
84
+ if (trimmed === "" || trimmed.startsWith("#")) continue;
85
+ const dep = parseDependencyLine(trimmed, relativeManifest, section);
86
+ if (dep) {
87
+ deps.push(dep);
88
+ }
89
+ }
90
+ }
91
+ const tableRegex = new RegExp(`^\\[${escapeRegex(section)}\\.([^\\]]+)\\]\\s*$`, "gm");
92
+ let tableMatch;
93
+ while ((tableMatch = tableRegex.exec(content)) !== null) {
94
+ const depName = tableMatch[1].trim();
95
+ const startIdx = tableMatch.index + tableMatch[0].length;
96
+ const remaining = content.slice(startIdx);
97
+ const nextSectionMatch = remaining.match(/^\[/m);
98
+ const tableContent = nextSectionMatch ? remaining.slice(0, nextSectionMatch.index) : remaining;
99
+ const version = extractVersionFromTable(tableContent);
100
+ if (version) {
101
+ deps.push({ name: depName, constraint: version });
102
+ } else {
103
+ deps.push({ name: depName, constraint: "*" });
104
+ }
105
+ }
106
+ return deps;
107
+ }
108
+ function parseDependencyLine(line, relativeManifest, section) {
109
+ const simpleMatch = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*"([^"]*)"$/);
110
+ if (simpleMatch) {
111
+ const name = simpleMatch[1];
112
+ const constraint = simpleMatch[2];
113
+ if (!isValidCrateName(name)) {
114
+ (0, import_errors.logWarn)(`[sec:cargo] Invalid crate name "${name}" in ${relativeManifest} [${section}] \u2014 skipping`);
115
+ return null;
116
+ }
117
+ if (constraint.trim() === "") {
118
+ (0, import_errors.logWarn)(`[sec:cargo] Empty version constraint for "${name}" in ${relativeManifest} [${section}] \u2014 skipping`);
119
+ return null;
120
+ }
121
+ return { name, constraint };
122
+ }
123
+ const tableMatch = line.match(/^([a-zA-Z0-9_-]+)\s*=\s*\{(.*)\}\s*$/);
124
+ if (tableMatch) {
125
+ const name = tableMatch[1];
126
+ const tableContent = tableMatch[2];
127
+ if (!isValidCrateName(name)) {
128
+ (0, import_errors.logWarn)(`[sec:cargo] Invalid crate name "${name}" in ${relativeManifest} [${section}] \u2014 skipping`);
129
+ return null;
130
+ }
131
+ const version = extractVersionFromInlineTable(tableContent);
132
+ if (version) {
133
+ return { name, constraint: version };
134
+ }
135
+ return { name, constraint: "*" };
136
+ }
137
+ if (line.includes("=") && !line.startsWith("#")) {
138
+ const possibleName = line.split("=")[0].trim();
139
+ if (possibleName && /^[a-zA-Z0-9_-]+$/.test(possibleName)) {
140
+ (0, import_errors.logWarn)(`[sec:cargo] Could not parse dependency line in ${relativeManifest} [${section}]: "${line}" \u2014 skipping`);
141
+ }
142
+ }
143
+ return null;
144
+ }
145
+ function extractVersionFromInlineTable(tableContent) {
146
+ const versionMatch = tableContent.match(/version\s*=\s*"([^"]*)"/);
147
+ if (versionMatch && versionMatch[1].trim() !== "") {
148
+ return versionMatch[1];
149
+ }
150
+ return null;
151
+ }
152
+ function extractVersionFromTable(tableContent) {
153
+ const lines = tableContent.split("\n");
154
+ for (const line of lines) {
155
+ const trimmed = line.trim();
156
+ const versionMatch = trimmed.match(/^version\s*=\s*"([^"]*)"$/);
157
+ if (versionMatch && versionMatch[1].trim() !== "") {
158
+ return versionMatch[1];
159
+ }
160
+ }
161
+ return null;
162
+ }
163
+ function loadResolvedVersions(manifestDir) {
164
+ const map = /* @__PURE__ */ new Map();
165
+ const lockPath = path.join(manifestDir, "Cargo.lock");
166
+ if (!fs.existsSync(lockPath)) {
167
+ return map;
168
+ }
169
+ try {
170
+ const lockContent = fs.readFileSync(lockPath, "utf8");
171
+ extractFromCargoLock(lockContent, map);
172
+ } catch (err) {
173
+ (0, import_errors.logWarn)(`[sec:cargo] Failed to parse Cargo.lock: ${err instanceof Error ? err.message : String(err)}`);
174
+ }
175
+ return map;
176
+ }
177
+ function extractFromCargoLock(content, map) {
178
+ const lines = content.split("\n");
179
+ let currentName = null;
180
+ for (const line of lines) {
181
+ const trimmed = line.trim();
182
+ if (trimmed === "[[package]]") {
183
+ currentName = null;
184
+ continue;
185
+ }
186
+ if (currentName === null) {
187
+ const nameMatch = trimmed.match(/^name\s*=\s*"([^"]+)"$/);
188
+ if (nameMatch) {
189
+ currentName = nameMatch[1];
190
+ continue;
191
+ }
192
+ }
193
+ if (currentName !== null) {
194
+ const versionMatch = trimmed.match(/^version\s*=\s*"([^"]+)"$/);
195
+ if (versionMatch) {
196
+ if (!map.has(currentName)) {
197
+ map.set(currentName, versionMatch[1]);
198
+ }
199
+ currentName = null;
200
+ continue;
201
+ }
202
+ }
203
+ if (trimmed === "" || trimmed.startsWith("[")) {
204
+ currentName = null;
205
+ }
206
+ }
207
+ }
208
+ function isValidCrateName(name) {
209
+ if (!name || typeof name !== "string") return false;
210
+ if (name.length > 64) return false;
211
+ return /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name);
212
+ }
213
+ function escapeRegex(str) {
214
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
215
+ }
216
+ function extractCargoLicense(content) {
217
+ const packageSectionMatch = /^\[package\]\s*$/m.exec(content);
218
+ if (!packageSectionMatch) return void 0;
219
+ const startIdx = packageSectionMatch.index + packageSectionMatch[0].length;
220
+ const remaining = content.slice(startIdx);
221
+ const nextSection = remaining.match(/^\[/m);
222
+ const sectionContent = nextSection ? remaining.slice(0, nextSection.index) : remaining;
223
+ const licenseMatch = sectionContent.match(/^license\s*=\s*"([^"]+)"/m);
224
+ if (licenseMatch && licenseMatch[1].trim() !== "") {
225
+ return licenseMatch[1].trim();
226
+ }
227
+ return void 0;
228
+ }
229
+ // Annotate the CommonJS export names for ESM import in node:
230
+ 0 && (module.exports = {
231
+ parseCargoManifest
232
+ });
233
+ //# sourceMappingURL=cargo.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/security/manifest/plugins/cargo.ts"],
4
+ "sourcesContent": ["/**\n * Cargo Version Extraction Plugin for KiroGraph-Sec\n *\n * Extends the existing architecture cargo parser to extract version constraints,\n * resolved versions (from Cargo.lock), and dependency scopes for security analysis.\n *\n * Reuses `cargoParser` from `src/architecture/manifest/cargo.ts` for discovery and\n * basic parsing, then layers on version/scope extraction.\n *\n * Handles both simple format (`serde = \"1.0\"`) and table format\n * (`serde = { version = \"1.0\", features = [\"derive\"] }`).\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type { ParsedDependency } from '../../types';\nimport { logWarn } from '../../../errors';\n\n/**\n * Dependency section headers in Cargo.toml mapped to their ParsedDependency scope.\n */\nconst SECTION_SCOPES: Array<{ section: string; scope: ParsedDependency['scope'] }> = [\n { section: 'dependencies', scope: 'production' },\n { section: 'dev-dependencies', scope: 'development' },\n { section: 'build-dependencies', scope: 'development' },\n];\n\n/**\n * Resolved version map: package name \u2192 resolved version string.\n * Built from Cargo.lock.\n */\ntype ResolvedVersionMap = Map<string, string>;\n\n/**\n * Parse a Cargo.toml manifest and extract dependency declarations\n * with version constraints, scopes, and resolved versions from Cargo.lock.\n *\n * @param manifestPath - Absolute path to the Cargo.toml file\n * @param projectRoot - Absolute path to the project root directory\n * @returns Array of parsed dependencies with version and scope information\n */\nexport async function parseCargoManifest(\n manifestPath: string,\n projectRoot: string,\n): Promise<ParsedDependency[]> {\n const manifestDir = path.dirname(manifestPath);\n const relativeManifest = path.relative(projectRoot, manifestPath).replace(/\\\\/g, '/');\n\n // Read the Cargo.toml content\n let content: string;\n try {\n content = fs.readFileSync(manifestPath, 'utf8');\n } catch (err) {\n logWarn(`[sec:cargo] Failed to read ${relativeManifest}: ${err instanceof Error ? err.message : String(err)}`);\n return [];\n }\n\n // Attempt to load resolved versions from Cargo.lock\n const resolvedVersions = loadResolvedVersions(manifestDir);\n\n // Extract the package-level license from [package] section\n const license = extractCargoLicense(content);\n\n // Extract dependencies from each section\n const dependencies: ParsedDependency[] = [];\n\n for (const { section, scope } of SECTION_SCOPES) {\n const sectionDeps = extractSectionDependencies(content, section, relativeManifest);\n for (const dep of sectionDeps) {\n const resolvedVersion = resolvedVersions.get(dep.name);\n dependencies.push({\n name: dep.name,\n declaredConstraint: dep.constraint,\n resolvedVersion,\n scope,\n ecosystem: 'cargo',\n sourceManifest: relativeManifest,\n ...(license !== undefined ? { license } : {}),\n });\n }\n }\n\n return dependencies;\n}\n\n/**\n * A raw dependency entry extracted from a TOML section.\n */\ninterface RawDependency {\n name: string;\n constraint: string;\n}\n\n/**\n * Extract dependencies from a specific section of a Cargo.toml file.\n * Handles both simple (`name = \"version\"`) and table (`name = { version = \"...\" }`) formats.\n * Also handles `[<section>.<name>]` table headers for individual dependency configuration.\n */\nfunction extractSectionDependencies(\n content: string,\n section: string,\n relativeManifest: string,\n): RawDependency[] {\n const deps: RawDependency[] = [];\n\n // Pattern 1: Inline section [dependencies] / [dev-dependencies] / [build-dependencies]\n // Match the section header and capture everything until the next section header\n const sectionRegex = new RegExp(`^\\\\[${escapeRegex(section)}\\\\]\\\\s*$`, 'm');\n const sectionMatch = sectionRegex.exec(content);\n\n if (sectionMatch) {\n const startIdx = sectionMatch.index + sectionMatch[0].length;\n // Find the next section header (line starting with [)\n const remaining = content.slice(startIdx);\n const nextSectionMatch = remaining.match(/^\\[/m);\n const sectionContent = nextSectionMatch\n ? remaining.slice(0, nextSectionMatch.index)\n : remaining;\n\n const lines = sectionContent.split('\\n');\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed === '' || trimmed.startsWith('#')) continue;\n\n const dep = parseDependencyLine(trimmed, relativeManifest, section);\n if (dep) {\n deps.push(dep);\n }\n }\n }\n\n // Pattern 2: Table-style individual dependency sections\n // e.g., [dependencies.serde] or [dev-dependencies.tokio]\n const tableRegex = new RegExp(`^\\\\[${escapeRegex(section)}\\\\.([^\\\\]]+)\\\\]\\\\s*$`, 'gm');\n let tableMatch: RegExpExecArray | null;\n while ((tableMatch = tableRegex.exec(content)) !== null) {\n const depName = tableMatch[1].trim();\n const startIdx = tableMatch.index + tableMatch[0].length;\n const remaining = content.slice(startIdx);\n const nextSectionMatch = remaining.match(/^\\[/m);\n const tableContent = nextSectionMatch\n ? remaining.slice(0, nextSectionMatch.index)\n : remaining;\n\n const version = extractVersionFromTable(tableContent);\n if (version) {\n deps.push({ name: depName, constraint: version });\n } else {\n // If no version specified, it might use path/git \u2014 use \"*\" as constraint\n deps.push({ name: depName, constraint: '*' });\n }\n }\n\n return deps;\n}\n\n/**\n * Parse a single dependency line from a Cargo.toml section.\n *\n * Handles:\n * - Simple string: `serde = \"1.0\"`\n * - Inline table: `serde = { version = \"1.0\", features = [\"derive\"] }`\n * - Path/git dependencies without version: `my-lib = { path = \"../my-lib\" }`\n */\nfunction parseDependencyLine(\n line: string,\n relativeManifest: string,\n section: string,\n): RawDependency | null {\n // Match: name = \"version\" or name = '...'\n const simpleMatch = line.match(/^([a-zA-Z0-9_-]+)\\s*=\\s*\"([^\"]*)\"$/);\n if (simpleMatch) {\n const name = simpleMatch[1];\n const constraint = simpleMatch[2];\n if (!isValidCrateName(name)) {\n logWarn(`[sec:cargo] Invalid crate name \"${name}\" in ${relativeManifest} [${section}] \u2014 skipping`);\n return null;\n }\n if (constraint.trim() === '') {\n logWarn(`[sec:cargo] Empty version constraint for \"${name}\" in ${relativeManifest} [${section}] \u2014 skipping`);\n return null;\n }\n return { name, constraint };\n }\n\n // Match: name = { version = \"...\", ... }\n const tableMatch = line.match(/^([a-zA-Z0-9_-]+)\\s*=\\s*\\{(.*)\\}\\s*$/);\n if (tableMatch) {\n const name = tableMatch[1];\n const tableContent = tableMatch[2];\n if (!isValidCrateName(name)) {\n logWarn(`[sec:cargo] Invalid crate name \"${name}\" in ${relativeManifest} [${section}] \u2014 skipping`);\n return null;\n }\n\n const version = extractVersionFromInlineTable(tableContent);\n if (version) {\n return { name, constraint: version };\n }\n\n // No version field \u2014 path or git dependency, use \"*\" as constraint\n return { name, constraint: '*' };\n }\n\n // Line doesn't match expected formats \u2014 could be a comment or malformed\n // Only warn if it looks like it was intended to be a dependency\n if (line.includes('=') && !line.startsWith('#')) {\n const possibleName = line.split('=')[0].trim();\n if (possibleName && /^[a-zA-Z0-9_-]+$/.test(possibleName)) {\n logWarn(`[sec:cargo] Could not parse dependency line in ${relativeManifest} [${section}]: \"${line}\" \u2014 skipping`);\n }\n }\n\n return null;\n}\n\n/**\n * Extract the version field from an inline table string.\n * e.g., from `version = \"1.0\", features = [\"derive\"]` \u2192 \"1.0\"\n */\nfunction extractVersionFromInlineTable(tableContent: string): string | null {\n const versionMatch = tableContent.match(/version\\s*=\\s*\"([^\"]*)\"/);\n if (versionMatch && versionMatch[1].trim() !== '') {\n return versionMatch[1];\n }\n return null;\n}\n\n/**\n * Extract the version field from a multi-line table section.\n * e.g., from lines like `version = \"1.0\"`\n */\nfunction extractVersionFromTable(tableContent: string): string | null {\n const lines = tableContent.split('\\n');\n for (const line of lines) {\n const trimmed = line.trim();\n const versionMatch = trimmed.match(/^version\\s*=\\s*\"([^\"]*)\"$/);\n if (versionMatch && versionMatch[1].trim() !== '') {\n return versionMatch[1];\n }\n }\n return null;\n}\n\n/**\n * Attempt to load resolved versions from Cargo.lock in the given directory.\n * Returns an empty map if Cargo.lock is not available.\n *\n * Cargo.lock format uses [[package]] blocks:\n * ```\n * [[package]]\n * name = \"serde\"\n * version = \"1.0.193\"\n * ```\n */\nfunction loadResolvedVersions(manifestDir: string): ResolvedVersionMap {\n const map: ResolvedVersionMap = new Map();\n\n const lockPath = path.join(manifestDir, 'Cargo.lock');\n if (!fs.existsSync(lockPath)) {\n return map;\n }\n\n try {\n const lockContent = fs.readFileSync(lockPath, 'utf8');\n extractFromCargoLock(lockContent, map);\n } catch (err) {\n logWarn(`[sec:cargo] Failed to parse Cargo.lock: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n return map;\n}\n\n/**\n * Extract resolved versions from Cargo.lock content.\n *\n * Cargo.lock uses [[package]] blocks with name and version fields:\n * ```\n * [[package]]\n * name = \"serde\"\n * version = \"1.0.193\"\n * source = \"registry+https://github.com/rust-lang/crates.io-index\"\n * ```\n */\nfunction extractFromCargoLock(content: string, map: ResolvedVersionMap): void {\n const lines = content.split('\\n');\n let currentName: string | null = null;\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n // New package block\n if (trimmed === '[[package]]') {\n currentName = null;\n continue;\n }\n\n // Name field within a package block\n if (currentName === null) {\n const nameMatch = trimmed.match(/^name\\s*=\\s*\"([^\"]+)\"$/);\n if (nameMatch) {\n currentName = nameMatch[1];\n continue;\n }\n }\n\n // Version field within a package block (after name is set)\n if (currentName !== null) {\n const versionMatch = trimmed.match(/^version\\s*=\\s*\"([^\"]+)\"$/);\n if (versionMatch) {\n // Only store the first occurrence (highest version is typically last,\n // but Cargo.lock lists the resolved version for each unique instance)\n if (!map.has(currentName)) {\n map.set(currentName, versionMatch[1]);\n }\n currentName = null;\n continue;\n }\n }\n\n // Reset on empty line or new section\n if (trimmed === '' || trimmed.startsWith('[')) {\n currentName = null;\n }\n }\n}\n\n/**\n * Validate that a crate name follows Rust/Cargo naming conventions.\n * Crate names must be non-empty and contain only alphanumeric characters,\n * hyphens, or underscores.\n */\nfunction isValidCrateName(name: string): boolean {\n if (!name || typeof name !== 'string') return false;\n if (name.length > 64) return false;\n return /^[a-zA-Z][a-zA-Z0-9_-]*$/.test(name);\n}\n\n/**\n * Escape special regex characters in a string.\n */\nfunction escapeRegex(str: string): string {\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\n/**\n * Extract the license field from the [package] section of a Cargo.toml.\n * Handles: license = \"MIT\" or license = \"MIT OR Apache-2.0\"\n */\nfunction extractCargoLicense(content: string): string | undefined {\n // Find the [package] section\n const packageSectionMatch = /^\\[package\\]\\s*$/m.exec(content);\n if (!packageSectionMatch) return undefined;\n\n const startIdx = packageSectionMatch.index + packageSectionMatch[0].length;\n const remaining = content.slice(startIdx);\n // Grab content up to the next section header\n const nextSection = remaining.match(/^\\[/m);\n const sectionContent = nextSection ? remaining.slice(0, nextSection.index) : remaining;\n\n const licenseMatch = sectionContent.match(/^license\\s*=\\s*\"([^\"]+)\"/m);\n if (licenseMatch && licenseMatch[1].trim() !== '') {\n return licenseMatch[1].trim();\n }\n return undefined;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+ var import_vitest = require("vitest");
25
+ var fs = __toESM(require("fs"));
26
+ var path = __toESM(require("path"));
27
+ var os = __toESM(require("os"));
28
+ var import_cargo = require("./cargo");
29
+ (0, import_vitest.describe)("parseCargoManifest", () => {
30
+ let tmpDir;
31
+ (0, import_vitest.beforeEach)(() => {
32
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "kirograph-cargo-test-"));
33
+ });
34
+ (0, import_vitest.afterEach)(() => {
35
+ fs.rmSync(tmpDir, { recursive: true, force: true });
36
+ });
37
+ function writeCargoToml(content, dir) {
38
+ const targetDir = dir ?? tmpDir;
39
+ fs.mkdirSync(targetDir, { recursive: true });
40
+ const filePath = path.join(targetDir, "Cargo.toml");
41
+ fs.writeFileSync(filePath, content);
42
+ return filePath;
43
+ }
44
+ (0, import_vitest.it)("extracts simple string dependencies with production scope", async () => {
45
+ const manifestPath = writeCargoToml(`
46
+ [package]
47
+ name = "my-app"
48
+ version = "0.1.0"
49
+
50
+ [dependencies]
51
+ serde = "1.0"
52
+ tokio = "1.35"
53
+ `);
54
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
55
+ (0, import_vitest.expect)(deps).toHaveLength(2);
56
+ const serde = deps.find((d) => d.name === "serde");
57
+ (0, import_vitest.expect)(serde).toBeDefined();
58
+ (0, import_vitest.expect)(serde.declaredConstraint).toBe("1.0");
59
+ (0, import_vitest.expect)(serde.scope).toBe("production");
60
+ (0, import_vitest.expect)(serde.ecosystem).toBe("cargo");
61
+ const tokio = deps.find((d) => d.name === "tokio");
62
+ (0, import_vitest.expect)(tokio).toBeDefined();
63
+ (0, import_vitest.expect)(tokio.declaredConstraint).toBe("1.35");
64
+ (0, import_vitest.expect)(tokio.scope).toBe("production");
65
+ });
66
+ (0, import_vitest.it)("extracts table format dependencies with version field", async () => {
67
+ const manifestPath = writeCargoToml(`
68
+ [package]
69
+ name = "my-app"
70
+ version = "0.1.0"
71
+
72
+ [dependencies]
73
+ serde = { version = "1.0", features = ["derive"] }
74
+ tokio = { version = "1.35", features = ["full"] }
75
+ `);
76
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
77
+ (0, import_vitest.expect)(deps).toHaveLength(2);
78
+ (0, import_vitest.expect)(deps.find((d) => d.name === "serde").declaredConstraint).toBe("1.0");
79
+ (0, import_vitest.expect)(deps.find((d) => d.name === "tokio").declaredConstraint).toBe("1.35");
80
+ });
81
+ (0, import_vitest.it)("extracts dev-dependencies with development scope", async () => {
82
+ const manifestPath = writeCargoToml(`
83
+ [package]
84
+ name = "my-app"
85
+ version = "0.1.0"
86
+
87
+ [dev-dependencies]
88
+ criterion = "0.5"
89
+ proptest = "1.4"
90
+ `);
91
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
92
+ (0, import_vitest.expect)(deps).toHaveLength(2);
93
+ (0, import_vitest.expect)(deps.every((d) => d.scope === "development")).toBe(true);
94
+ });
95
+ (0, import_vitest.it)("extracts build-dependencies with development scope", async () => {
96
+ const manifestPath = writeCargoToml(`
97
+ [package]
98
+ name = "my-app"
99
+ version = "0.1.0"
100
+
101
+ [build-dependencies]
102
+ cc = "1.0"
103
+ bindgen = "0.69"
104
+ `);
105
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
106
+ (0, import_vitest.expect)(deps).toHaveLength(2);
107
+ (0, import_vitest.expect)(deps.every((d) => d.scope === "development")).toBe(true);
108
+ (0, import_vitest.expect)(deps.find((d) => d.name === "cc").declaredConstraint).toBe("1.0");
109
+ });
110
+ (0, import_vitest.it)("handles all dependency sections together", async () => {
111
+ const manifestPath = writeCargoToml(`
112
+ [package]
113
+ name = "my-app"
114
+ version = "0.1.0"
115
+
116
+ [dependencies]
117
+ serde = "1.0"
118
+
119
+ [dev-dependencies]
120
+ criterion = "0.5"
121
+
122
+ [build-dependencies]
123
+ cc = "1.0"
124
+ `);
125
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
126
+ (0, import_vitest.expect)(deps).toHaveLength(3);
127
+ (0, import_vitest.expect)(deps.find((d) => d.name === "serde").scope).toBe("production");
128
+ (0, import_vitest.expect)(deps.find((d) => d.name === "criterion").scope).toBe("development");
129
+ (0, import_vitest.expect)(deps.find((d) => d.name === "cc").scope).toBe("development");
130
+ });
131
+ (0, import_vitest.it)("resolves versions from Cargo.lock", async () => {
132
+ writeCargoToml(`
133
+ [package]
134
+ name = "my-app"
135
+ version = "0.1.0"
136
+
137
+ [dependencies]
138
+ serde = "1.0"
139
+ tokio = "1"
140
+ `);
141
+ const lockContent = `# This file is automatically @generated by Cargo.
142
+ # It is not intended for manual editing.
143
+ version = 3
144
+
145
+ [[package]]
146
+ name = "serde"
147
+ version = "1.0.193"
148
+ source = "registry+https://github.com/rust-lang/crates.io-index"
149
+
150
+ [[package]]
151
+ name = "tokio"
152
+ version = "1.35.1"
153
+ source = "registry+https://github.com/rust-lang/crates.io-index"
154
+ `;
155
+ fs.writeFileSync(path.join(tmpDir, "Cargo.lock"), lockContent);
156
+ const manifestPath = path.join(tmpDir, "Cargo.toml");
157
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
158
+ (0, import_vitest.expect)(deps).toHaveLength(2);
159
+ (0, import_vitest.expect)(deps.find((d) => d.name === "serde").resolvedVersion).toBe("1.0.193");
160
+ (0, import_vitest.expect)(deps.find((d) => d.name === "tokio").resolvedVersion).toBe("1.35.1");
161
+ });
162
+ (0, import_vitest.it)("leaves resolvedVersion undefined when no Cargo.lock exists", async () => {
163
+ const manifestPath = writeCargoToml(`
164
+ [package]
165
+ name = "my-app"
166
+ version = "0.1.0"
167
+
168
+ [dependencies]
169
+ serde = "1.0"
170
+ `);
171
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
172
+ (0, import_vitest.expect)(deps).toHaveLength(1);
173
+ (0, import_vitest.expect)(deps[0].resolvedVersion).toBeUndefined();
174
+ });
175
+ (0, import_vitest.it)("handles path dependencies with no version (uses * constraint)", async () => {
176
+ const manifestPath = writeCargoToml(`
177
+ [package]
178
+ name = "my-app"
179
+ version = "0.1.0"
180
+
181
+ [dependencies]
182
+ my-lib = { path = "../my-lib" }
183
+ `);
184
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
185
+ (0, import_vitest.expect)(deps).toHaveLength(1);
186
+ (0, import_vitest.expect)(deps[0].name).toBe("my-lib");
187
+ (0, import_vitest.expect)(deps[0].declaredConstraint).toBe("*");
188
+ });
189
+ (0, import_vitest.it)("handles git dependencies with no version (uses * constraint)", async () => {
190
+ const manifestPath = writeCargoToml(`
191
+ [package]
192
+ name = "my-app"
193
+ version = "0.1.0"
194
+
195
+ [dependencies]
196
+ my-crate = { git = "https://github.com/user/repo" }
197
+ `);
198
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
199
+ (0, import_vitest.expect)(deps).toHaveLength(1);
200
+ (0, import_vitest.expect)(deps[0].name).toBe("my-crate");
201
+ (0, import_vitest.expect)(deps[0].declaredConstraint).toBe("*");
202
+ });
203
+ (0, import_vitest.it)("handles table-style individual dependency sections", async () => {
204
+ const manifestPath = writeCargoToml(`
205
+ [package]
206
+ name = "my-app"
207
+ version = "0.1.0"
208
+
209
+ [dependencies.serde]
210
+ version = "1.0"
211
+ features = ["derive"]
212
+
213
+ [dependencies.tokio]
214
+ version = "1.35"
215
+ features = ["full"]
216
+ `);
217
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
218
+ (0, import_vitest.expect)(deps).toHaveLength(2);
219
+ (0, import_vitest.expect)(deps.find((d) => d.name === "serde").declaredConstraint).toBe("1.0");
220
+ (0, import_vitest.expect)(deps.find((d) => d.name === "tokio").declaredConstraint).toBe("1.35");
221
+ (0, import_vitest.expect)(deps.every((d) => d.scope === "production")).toBe(true);
222
+ });
223
+ (0, import_vitest.it)("returns empty array for unreadable file", async () => {
224
+ const fakePath = path.join(tmpDir, "nonexistent", "Cargo.toml");
225
+ const deps = await (0, import_cargo.parseCargoManifest)(fakePath, tmpDir);
226
+ (0, import_vitest.expect)(deps).toEqual([]);
227
+ });
228
+ (0, import_vitest.it)("returns empty array for Cargo.toml with no dependency sections", async () => {
229
+ const manifestPath = writeCargoToml(`
230
+ [package]
231
+ name = "my-app"
232
+ version = "0.1.0"
233
+ edition = "2021"
234
+ `);
235
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
236
+ (0, import_vitest.expect)(deps).toEqual([]);
237
+ });
238
+ (0, import_vitest.it)("skips comment lines in dependency sections", async () => {
239
+ const manifestPath = writeCargoToml(`
240
+ [package]
241
+ name = "my-app"
242
+ version = "0.1.0"
243
+
244
+ [dependencies]
245
+ # This is a comment
246
+ serde = "1.0"
247
+ # Another comment
248
+ tokio = "1.35"
249
+ `);
250
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
251
+ (0, import_vitest.expect)(deps).toHaveLength(2);
252
+ (0, import_vitest.expect)(deps.find((d) => d.name === "serde")).toBeDefined();
253
+ (0, import_vitest.expect)(deps.find((d) => d.name === "tokio")).toBeDefined();
254
+ });
255
+ (0, import_vitest.it)("sets sourceManifest as relative path", async () => {
256
+ const subDir = path.join(tmpDir, "crates", "sub");
257
+ const manifestPath = writeCargoToml(`
258
+ [package]
259
+ name = "sub-crate"
260
+ version = "0.1.0"
261
+
262
+ [dependencies]
263
+ serde = "1.0"
264
+ `, subDir);
265
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
266
+ (0, import_vitest.expect)(deps).toHaveLength(1);
267
+ (0, import_vitest.expect)(deps[0].sourceManifest).toBe("crates/sub/Cargo.toml");
268
+ });
269
+ (0, import_vitest.it)("handles mixed simple and table format in same section", async () => {
270
+ const manifestPath = writeCargoToml(`
271
+ [package]
272
+ name = "my-app"
273
+ version = "0.1.0"
274
+
275
+ [dependencies]
276
+ log = "0.4"
277
+ serde = { version = "1.0", features = ["derive"] }
278
+ rand = "0.8"
279
+ `);
280
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
281
+ (0, import_vitest.expect)(deps).toHaveLength(3);
282
+ (0, import_vitest.expect)(deps.find((d) => d.name === "log").declaredConstraint).toBe("0.4");
283
+ (0, import_vitest.expect)(deps.find((d) => d.name === "serde").declaredConstraint).toBe("1.0");
284
+ (0, import_vitest.expect)(deps.find((d) => d.name === "rand").declaredConstraint).toBe("0.8");
285
+ });
286
+ (0, import_vitest.it)("handles version constraints with operators", async () => {
287
+ const manifestPath = writeCargoToml(`
288
+ [package]
289
+ name = "my-app"
290
+ version = "0.1.0"
291
+
292
+ [dependencies]
293
+ serde = ">=1.0, <2.0"
294
+ tokio = "~1.35"
295
+ rand = "^0.8"
296
+ `);
297
+ const deps = await (0, import_cargo.parseCargoManifest)(manifestPath, tmpDir);
298
+ (0, import_vitest.expect)(deps).toHaveLength(3);
299
+ (0, import_vitest.expect)(deps.find((d) => d.name === "serde").declaredConstraint).toBe(">=1.0, <2.0");
300
+ (0, import_vitest.expect)(deps.find((d) => d.name === "tokio").declaredConstraint).toBe("~1.35");
301
+ (0, import_vitest.expect)(deps.find((d) => d.name === "rand").declaredConstraint).toBe("^0.8");
302
+ });
303
+ });
304
+ //# sourceMappingURL=cargo.test.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/security/manifest/plugins/cargo.test.ts"],
4
+ "sourcesContent": ["/**\n * Unit tests for the Cargo security manifest plugin.\n */\nimport { describe, it, expect, beforeEach, afterEach } from 'vitest';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { parseCargoManifest } from './cargo';\n\ndescribe('parseCargoManifest', () => {\n let tmpDir: string;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kirograph-cargo-test-'));\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n function writeCargoToml(content: string, dir?: string): string {\n const targetDir = dir ?? tmpDir;\n fs.mkdirSync(targetDir, { recursive: true });\n const filePath = path.join(targetDir, 'Cargo.toml');\n fs.writeFileSync(filePath, content);\n return filePath;\n }\n\n it('extracts simple string dependencies with production scope', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nserde = \"1.0\"\ntokio = \"1.35\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(2);\n const serde = deps.find(d => d.name === 'serde');\n expect(serde).toBeDefined();\n expect(serde!.declaredConstraint).toBe('1.0');\n expect(serde!.scope).toBe('production');\n expect(serde!.ecosystem).toBe('cargo');\n\n const tokio = deps.find(d => d.name === 'tokio');\n expect(tokio).toBeDefined();\n expect(tokio!.declaredConstraint).toBe('1.35');\n expect(tokio!.scope).toBe('production');\n });\n\n it('extracts table format dependencies with version field', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nserde = { version = \"1.0\", features = [\"derive\"] }\ntokio = { version = \"1.35\", features = [\"full\"] }\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(2);\n expect(deps.find(d => d.name === 'serde')!.declaredConstraint).toBe('1.0');\n expect(deps.find(d => d.name === 'tokio')!.declaredConstraint).toBe('1.35');\n });\n\n it('extracts dev-dependencies with development scope', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dev-dependencies]\ncriterion = \"0.5\"\nproptest = \"1.4\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(2);\n expect(deps.every(d => d.scope === 'development')).toBe(true);\n });\n\n it('extracts build-dependencies with development scope', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[build-dependencies]\ncc = \"1.0\"\nbindgen = \"0.69\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(2);\n expect(deps.every(d => d.scope === 'development')).toBe(true);\n expect(deps.find(d => d.name === 'cc')!.declaredConstraint).toBe('1.0');\n });\n\n it('handles all dependency sections together', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nserde = \"1.0\"\n\n[dev-dependencies]\ncriterion = \"0.5\"\n\n[build-dependencies]\ncc = \"1.0\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(3);\n expect(deps.find(d => d.name === 'serde')!.scope).toBe('production');\n expect(deps.find(d => d.name === 'criterion')!.scope).toBe('development');\n expect(deps.find(d => d.name === 'cc')!.scope).toBe('development');\n });\n\n it('resolves versions from Cargo.lock', async () => {\n writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nserde = \"1.0\"\ntokio = \"1\"\n`);\n\n const lockContent = `# This file is automatically @generated by Cargo.\n# It is not intended for manual editing.\nversion = 3\n\n[[package]]\nname = \"serde\"\nversion = \"1.0.193\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\n\n[[package]]\nname = \"tokio\"\nversion = \"1.35.1\"\nsource = \"registry+https://github.com/rust-lang/crates.io-index\"\n`;\n fs.writeFileSync(path.join(tmpDir, 'Cargo.lock'), lockContent);\n\n const manifestPath = path.join(tmpDir, 'Cargo.toml');\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(2);\n expect(deps.find(d => d.name === 'serde')!.resolvedVersion).toBe('1.0.193');\n expect(deps.find(d => d.name === 'tokio')!.resolvedVersion).toBe('1.35.1');\n });\n\n it('leaves resolvedVersion undefined when no Cargo.lock exists', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nserde = \"1.0\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(1);\n expect(deps[0].resolvedVersion).toBeUndefined();\n });\n\n it('handles path dependencies with no version (uses * constraint)', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nmy-lib = { path = \"../my-lib\" }\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(1);\n expect(deps[0].name).toBe('my-lib');\n expect(deps[0].declaredConstraint).toBe('*');\n });\n\n it('handles git dependencies with no version (uses * constraint)', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nmy-crate = { git = \"https://github.com/user/repo\" }\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(1);\n expect(deps[0].name).toBe('my-crate');\n expect(deps[0].declaredConstraint).toBe('*');\n });\n\n it('handles table-style individual dependency sections', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies.serde]\nversion = \"1.0\"\nfeatures = [\"derive\"]\n\n[dependencies.tokio]\nversion = \"1.35\"\nfeatures = [\"full\"]\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(2);\n expect(deps.find(d => d.name === 'serde')!.declaredConstraint).toBe('1.0');\n expect(deps.find(d => d.name === 'tokio')!.declaredConstraint).toBe('1.35');\n expect(deps.every(d => d.scope === 'production')).toBe(true);\n });\n\n it('returns empty array for unreadable file', async () => {\n const fakePath = path.join(tmpDir, 'nonexistent', 'Cargo.toml');\n const deps = await parseCargoManifest(fakePath, tmpDir);\n expect(deps).toEqual([]);\n });\n\n it('returns empty array for Cargo.toml with no dependency sections', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\nedition = \"2021\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n expect(deps).toEqual([]);\n });\n\n it('skips comment lines in dependency sections', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\n# This is a comment\nserde = \"1.0\"\n# Another comment\ntokio = \"1.35\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(2);\n expect(deps.find(d => d.name === 'serde')).toBeDefined();\n expect(deps.find(d => d.name === 'tokio')).toBeDefined();\n });\n\n it('sets sourceManifest as relative path', async () => {\n const subDir = path.join(tmpDir, 'crates', 'sub');\n const manifestPath = writeCargoToml(`\n[package]\nname = \"sub-crate\"\nversion = \"0.1.0\"\n\n[dependencies]\nserde = \"1.0\"\n`, subDir);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(1);\n expect(deps[0].sourceManifest).toBe('crates/sub/Cargo.toml');\n });\n\n it('handles mixed simple and table format in same section', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nlog = \"0.4\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nrand = \"0.8\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(3);\n expect(deps.find(d => d.name === 'log')!.declaredConstraint).toBe('0.4');\n expect(deps.find(d => d.name === 'serde')!.declaredConstraint).toBe('1.0');\n expect(deps.find(d => d.name === 'rand')!.declaredConstraint).toBe('0.8');\n });\n\n it('handles version constraints with operators', async () => {\n const manifestPath = writeCargoToml(`\n[package]\nname = \"my-app\"\nversion = \"0.1.0\"\n\n[dependencies]\nserde = \">=1.0, <2.0\"\ntokio = \"~1.35\"\nrand = \"^0.8\"\n`);\n\n const deps = await parseCargoManifest(manifestPath, tmpDir);\n\n expect(deps).toHaveLength(3);\n expect(deps.find(d => d.name === 'serde')!.declaredConstraint).toBe('>=1.0, <2.0');\n expect(deps.find(d => d.name === 'tokio')!.declaredConstraint).toBe('~1.35');\n expect(deps.find(d => d.name === 'rand')!.declaredConstraint).toBe('^0.8');\n });\n});\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }