kirograph 0.16.1 → 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.
- package/LICENSE +1 -1
- package/README.md +103 -1605
- package/dist/architecture/layers/dart.js +112 -0
- package/dist/architecture/layers/dart.js.map +7 -0
- package/dist/architecture/layers/index.js +3 -1
- package/dist/architecture/layers/index.js.map +2 -2
- package/dist/bin/banner.js +74 -6
- package/dist/bin/banner.js.map +2 -2
- package/dist/bin/commands/affected.js +40 -5
- package/dist/bin/commands/affected.js.map +2 -2
- package/dist/bin/commands/attack-surface.js +157 -0
- package/dist/bin/commands/attack-surface.js.map +7 -0
- package/dist/bin/commands/benchmark.js +206 -0
- package/dist/bin/commands/benchmark.js.map +7 -0
- package/dist/bin/commands/budget.js +70 -0
- package/dist/bin/commands/budget.js.map +7 -0
- package/dist/bin/commands/communities.js +77 -0
- package/dist/bin/commands/communities.js.map +7 -0
- package/dist/bin/commands/data.js +665 -0
- package/dist/bin/commands/data.js.map +7 -0
- package/dist/bin/commands/dep-confusion.js +119 -0
- package/dist/bin/commands/dep-confusion.js.map +7 -0
- package/dist/bin/commands/export.js +304 -10
- package/dist/bin/commands/export.js.map +2 -2
- package/dist/bin/commands/flows.js +97 -0
- package/dist/bin/commands/flows.js.map +7 -0
- package/dist/bin/commands/help.js +386 -100
- package/dist/bin/commands/help.js.map +2 -2
- package/dist/bin/commands/install.js +90 -8
- package/dist/bin/commands/install.js.map +2 -2
- package/dist/bin/commands/licenses.js +201 -0
- package/dist/bin/commands/licenses.js.map +7 -0
- package/dist/bin/commands/reachability.js +160 -0
- package/dist/bin/commands/reachability.js.map +7 -0
- package/dist/bin/commands/read.js +100 -0
- package/dist/bin/commands/read.js.map +7 -0
- package/dist/bin/commands/refactor.js +116 -0
- package/dist/bin/commands/refactor.js.map +7 -0
- package/dist/bin/commands/remediation.js +133 -0
- package/dist/bin/commands/remediation.js.map +7 -0
- package/dist/bin/commands/sbom.js +76 -0
- package/dist/bin/commands/sbom.js.map +7 -0
- package/dist/bin/commands/security-ci-report.js +267 -0
- package/dist/bin/commands/security-ci-report.js.map +7 -0
- package/dist/bin/commands/security-export.js +1349 -0
- package/dist/bin/commands/security-export.js.map +7 -0
- package/dist/bin/commands/security-flows.js +126 -0
- package/dist/bin/commands/security-flows.js.map +7 -0
- package/dist/bin/commands/security-secrets.js +170 -0
- package/dist/bin/commands/security-secrets.js.map +7 -0
- package/dist/bin/commands/security.js +193 -0
- package/dist/bin/commands/security.js.map +7 -0
- package/dist/bin/commands/staleness.js +144 -0
- package/dist/bin/commands/staleness.js.map +7 -0
- package/dist/bin/commands/status.js +102 -2
- package/dist/bin/commands/status.js.map +2 -2
- package/dist/bin/commands/supply-chain.js +138 -0
- package/dist/bin/commands/supply-chain.js.map +7 -0
- package/dist/bin/commands/uninit.js +109 -60
- package/dist/bin/commands/uninit.js.map +2 -2
- package/dist/bin/commands/vex.js +76 -0
- package/dist/bin/commands/vex.js.map +7 -0
- package/dist/bin/commands/vuln-suppress.js +95 -0
- package/dist/bin/commands/vuln-suppress.js.map +7 -0
- package/dist/bin/commands/vulns.js +382 -0
- package/dist/bin/commands/vulns.js.map +7 -0
- package/dist/bin/installer/auto-detect.js +125 -0
- package/dist/bin/installer/auto-detect.js.map +7 -0
- package/dist/bin/installer/cli-agent.js +28 -5
- package/dist/bin/installer/cli-agent.js.map +2 -2
- package/dist/bin/installer/common.js +47 -0
- package/dist/bin/installer/common.js.map +2 -2
- package/dist/bin/installer/config-prompt.js +39 -1
- package/dist/bin/installer/config-prompt.js.map +2 -2
- package/dist/bin/installer/detect.js +309 -0
- package/dist/bin/installer/detect.js.map +7 -0
- package/dist/bin/installer/index.js +36 -1
- package/dist/bin/installer/index.js.map +2 -2
- package/dist/bin/installer/instructions.js +195 -3
- package/dist/bin/installer/instructions.js.map +2 -2
- package/dist/bin/installer/steering.js +440 -0
- package/dist/bin/installer/steering.js.map +2 -2
- package/dist/bin/installer/targets/aider.js +3 -3
- package/dist/bin/installer/targets/aider.js.map +2 -2
- package/dist/bin/installer/targets/amp.js +3 -3
- package/dist/bin/installer/targets/amp.js.map +2 -2
- package/dist/bin/installer/targets/antigravity.js +35 -6
- package/dist/bin/installer/targets/antigravity.js.map +2 -2
- package/dist/bin/installer/targets/augment.js +3 -3
- package/dist/bin/installer/targets/augment.js.map +2 -2
- package/dist/bin/installer/targets/claude.js +49 -5
- package/dist/bin/installer/targets/claude.js.map +2 -2
- package/dist/bin/installer/targets/cline.js +32 -6
- package/dist/bin/installer/targets/cline.js.map +2 -2
- package/dist/bin/installer/targets/codex.js +64 -12
- package/dist/bin/installer/targets/codex.js.map +2 -2
- package/dist/bin/installer/targets/continue.js +2 -2
- package/dist/bin/installer/targets/continue.js.map +2 -2
- package/dist/bin/installer/targets/copilot-cli.js +101 -0
- package/dist/bin/installer/targets/copilot-cli.js.map +7 -0
- package/dist/bin/installer/targets/copilot.js +31 -5
- package/dist/bin/installer/targets/copilot.js.map +2 -2
- package/dist/bin/installer/targets/cursor.js +4 -4
- package/dist/bin/installer/targets/cursor.js.map +2 -2
- package/dist/bin/installer/targets/devin.js +2 -2
- package/dist/bin/installer/targets/devin.js.map +2 -2
- package/dist/bin/installer/targets/gemini-cli.js +2 -2
- package/dist/bin/installer/targets/gemini-cli.js.map +2 -2
- package/dist/bin/installer/targets/generic.js +2 -14
- package/dist/bin/installer/targets/generic.js.map +2 -2
- package/dist/bin/installer/targets/goose.js +3 -3
- package/dist/bin/installer/targets/goose.js.map +2 -2
- package/dist/bin/installer/targets/index.js +250 -0
- package/dist/bin/installer/targets/index.js.map +2 -2
- package/dist/bin/installer/targets/junie.js +2 -2
- package/dist/bin/installer/targets/junie.js.map +2 -2
- package/dist/bin/installer/targets/kilo.js +2 -2
- package/dist/bin/installer/targets/kilo.js.map +2 -2
- package/dist/bin/installer/targets/kiro.js +3 -3
- package/dist/bin/installer/targets/kiro.js.map +2 -2
- package/dist/bin/installer/targets/opencode.js +2 -2
- package/dist/bin/installer/targets/opencode.js.map +2 -2
- package/dist/bin/installer/targets/openhands.js +3 -3
- package/dist/bin/installer/targets/openhands.js.map +2 -2
- package/dist/bin/installer/targets/qoder.js +91 -0
- package/dist/bin/installer/targets/qoder.js.map +7 -0
- package/dist/bin/installer/targets/qwen.js +96 -0
- package/dist/bin/installer/targets/qwen.js.map +7 -0
- package/dist/bin/installer/targets/replit.js +3 -3
- package/dist/bin/installer/targets/replit.js.map +2 -2
- package/dist/bin/installer/targets/roo.js +2 -2
- package/dist/bin/installer/targets/roo.js.map +2 -2
- package/dist/bin/installer/targets/tabnine.js +3 -3
- package/dist/bin/installer/targets/tabnine.js.map +2 -2
- package/dist/bin/installer/targets/trae.js +5 -5
- package/dist/bin/installer/targets/trae.js.map +2 -2
- package/dist/bin/installer/targets/warp.js +2 -2
- package/dist/bin/installer/targets/warp.js.map +2 -2
- package/dist/bin/installer/targets/windsurf.js +35 -6
- package/dist/bin/installer/targets/windsurf.js.map +2 -2
- package/dist/bin/kirograph.js +74 -6
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/bin/progress.js +30 -0
- package/dist/bin/progress.js.map +2 -2
- package/dist/compression/naive-cost.js +25 -0
- package/dist/compression/naive-cost.js.map +2 -2
- package/dist/compression/tracker.js +91 -2
- package/dist/compression/tracker.js.map +2 -2
- package/dist/compression/types.js.map +1 -1
- package/dist/config.js +126 -3
- package/dist/config.js.map +2 -2
- package/dist/core/pipeline.js +92 -4
- package/dist/core/pipeline.js.map +2 -2
- package/dist/data/filters.js +105 -0
- package/dist/data/filters.js.map +7 -0
- package/dist/data/indexer.js +225 -0
- package/dist/data/indexer.js.map +7 -0
- package/dist/data/linker.js +150 -0
- package/dist/data/linker.js.map +7 -0
- package/dist/data/lint.js +109 -0
- package/dist/data/lint.js.map +7 -0
- package/dist/data/parsers/csv.js +132 -0
- package/dist/data/parsers/csv.js.map +7 -0
- package/dist/data/parsers/excel.js +88 -0
- package/dist/data/parsers/excel.js.map +7 -0
- package/dist/data/parsers/index.js +79 -0
- package/dist/data/parsers/index.js.map +7 -0
- package/dist/data/parsers/json-array.js +89 -0
- package/dist/data/parsers/json-array.js.map +7 -0
- package/dist/data/parsers/jsonl.js +84 -0
- package/dist/data/parsers/jsonl.js.map +7 -0
- package/dist/data/parsers/parquet.js +95 -0
- package/dist/data/parsers/parquet.js.map +7 -0
- package/dist/data/profiler.js +182 -0
- package/dist/data/profiler.js.map +7 -0
- package/dist/data/queries.js +512 -0
- package/dist/data/queries.js.map +7 -0
- package/dist/data/types.js +17 -0
- package/dist/data/types.js.map +7 -0
- package/dist/db/data-schema.sql +61 -0
- package/dist/db/database.js +67 -5
- package/dist/db/database.js.map +2 -2
- package/dist/db/memory-schema.sql +5 -1
- package/dist/db/schema.sql +3 -1
- package/dist/db/security-schema.sql +72 -0
- package/dist/extraction/extractor.js +81 -0
- package/dist/extraction/extractor.js.map +2 -2
- package/dist/extraction/grammars.js +32 -8
- package/dist/extraction/grammars.js.map +2 -2
- package/dist/extraction/languages.js +43 -1
- package/dist/extraction/languages.js.map +2 -2
- package/dist/extraction/notebook.js +300 -0
- package/dist/extraction/notebook.js.map +7 -0
- package/dist/extraction/wasm/tree-sitter-astro.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-gdscript.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-julia.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-nix.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-perl.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-powershell.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-r.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-sql.wasm +0 -0
- package/dist/extraction/wasm/tree-sitter-verilog.wasm +0 -0
- package/dist/frameworks/flutter.js +130 -0
- package/dist/frameworks/flutter.js.map +7 -0
- package/dist/frameworks/index.js +7 -1
- package/dist/frameworks/index.js.map +3 -3
- package/dist/graph/communities.js +317 -0
- package/dist/graph/communities.js.map +7 -0
- package/dist/graph/flows.js +138 -0
- package/dist/graph/flows.js.map +7 -0
- package/dist/graph/refactor.js +155 -0
- package/dist/graph/refactor.js.map +7 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +2 -2
- package/dist/mcp/cache.js +172 -0
- package/dist/mcp/cache.js.map +7 -0
- package/dist/mcp/read-modes.js +295 -0
- package/dist/mcp/read-modes.js.map +7 -0
- package/dist/mcp/server.js +2 -1
- package/dist/mcp/server.js.map +2 -2
- package/dist/mcp/tool-names.js +29 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +1588 -6
- package/dist/mcp/tools.js.map +3 -3
- package/dist/memory/database.js +32 -2
- package/dist/memory/database.js.map +2 -2
- package/dist/memory/types.js.map +1 -1
- package/dist/resolution/bridges/android-rn.js +298 -0
- package/dist/resolution/bridges/android-rn.js.map +7 -0
- package/dist/resolution/bridges/expo-modules.js +155 -0
- package/dist/resolution/bridges/expo-modules.js.map +7 -0
- package/dist/resolution/bridges/flutter-channel.js +385 -0
- package/dist/resolution/bridges/flutter-channel.js.map +7 -0
- package/dist/resolution/bridges/index.js +80 -0
- package/dist/resolution/bridges/index.js.map +7 -0
- package/dist/resolution/bridges/native-events.js +188 -0
- package/dist/resolution/bridges/native-events.js.map +7 -0
- package/dist/resolution/bridges/native-views.js +244 -0
- package/dist/resolution/bridges/native-views.js.map +7 -0
- package/dist/resolution/bridges/react-native.js +161 -0
- package/dist/resolution/bridges/react-native.js.map +7 -0
- package/dist/resolution/bridges/swift-objc.js +227 -0
- package/dist/resolution/bridges/swift-objc.js.map +7 -0
- package/dist/resolution/bridges/turbomodules.js +216 -0
- package/dist/resolution/bridges/turbomodules.js.map +7 -0
- package/dist/resolution/index.js +61 -2
- package/dist/resolution/index.js.map +2 -2
- package/dist/security/attack-surface.js +164 -0
- package/dist/security/attack-surface.js.map +7 -0
- package/dist/security/context-warnings.js +123 -0
- package/dist/security/context-warnings.js.map +7 -0
- package/dist/security/context-warnings.test.js +300 -0
- package/dist/security/context-warnings.test.js.map +7 -0
- package/dist/security/data-flows.js +228 -0
- package/dist/security/data-flows.js.map +7 -0
- package/dist/security/dep-confusion.js +255 -0
- package/dist/security/dep-confusion.js.map +7 -0
- package/dist/security/errors.js +51 -0
- package/dist/security/errors.js.map +7 -0
- package/dist/security/export/fix-suggestions.js +58 -0
- package/dist/security/export/fix-suggestions.js.map +7 -0
- package/dist/security/export/fix-suggestions.test.js +74 -0
- package/dist/security/export/fix-suggestions.test.js.map +7 -0
- package/dist/security/export/sbom.js +230 -0
- package/dist/security/export/sbom.js.map +7 -0
- package/dist/security/export/sbom.test.js +288 -0
- package/dist/security/export/sbom.test.js.map +7 -0
- package/dist/security/export/serialization.js +142 -0
- package/dist/security/export/serialization.js.map +7 -0
- package/dist/security/export/serialization.test.js +310 -0
- package/dist/security/export/serialization.test.js.map +7 -0
- package/dist/security/export/vex.js +220 -0
- package/dist/security/export/vex.js.map +7 -0
- package/dist/security/export/vex.test.js +278 -0
- package/dist/security/export/vex.test.js.map +7 -0
- package/dist/security/index.js +67 -0
- package/dist/security/index.js.map +7 -0
- package/dist/security/integrator-transitives.test.js +359 -0
- package/dist/security/integrator-transitives.test.js.map +7 -0
- package/dist/security/integrator.js +491 -0
- package/dist/security/integrator.js.map +7 -0
- package/dist/security/integrator.test.js +237 -0
- package/dist/security/integrator.test.js.map +7 -0
- package/dist/security/license.js +70 -0
- package/dist/security/license.js.map +7 -0
- package/dist/security/manifest/adapter.js +243 -0
- package/dist/security/manifest/adapter.js.map +7 -0
- package/dist/security/manifest/adapter.test.js +272 -0
- package/dist/security/manifest/adapter.test.js.map +7 -0
- package/dist/security/manifest/parser.js +376 -0
- package/dist/security/manifest/parser.js.map +7 -0
- package/dist/security/manifest/parser.test.js +110 -0
- package/dist/security/manifest/parser.test.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.js +233 -0
- package/dist/security/manifest/plugins/cargo.js.map +7 -0
- package/dist/security/manifest/plugins/cargo.test.js +304 -0
- package/dist/security/manifest/plugins/cargo.test.js.map +7 -0
- package/dist/security/manifest/plugins/composer.js +152 -0
- package/dist/security/manifest/plugins/composer.js.map +7 -0
- package/dist/security/manifest/plugins/go.js +146 -0
- package/dist/security/manifest/plugins/go.js.map +7 -0
- package/dist/security/manifest/plugins/go.test.js +341 -0
- package/dist/security/manifest/plugins/go.test.js.map +7 -0
- package/dist/security/manifest/plugins/gradle.js +159 -0
- package/dist/security/manifest/plugins/gradle.js.map +7 -0
- package/dist/security/manifest/plugins/hex.js +159 -0
- package/dist/security/manifest/plugins/hex.js.map +7 -0
- package/dist/security/manifest/plugins/maven.js +128 -0
- package/dist/security/manifest/plugins/maven.js.map +7 -0
- package/dist/security/manifest/plugins/maven.test.js +389 -0
- package/dist/security/manifest/plugins/maven.test.js.map +7 -0
- package/dist/security/manifest/plugins/npm.js +284 -0
- package/dist/security/manifest/plugins/npm.js.map +7 -0
- package/dist/security/manifest/plugins/npm.test.js +298 -0
- package/dist/security/manifest/plugins/npm.test.js.map +7 -0
- package/dist/security/manifest/plugins/nuget.js +186 -0
- package/dist/security/manifest/plugins/nuget.js.map +7 -0
- package/dist/security/manifest/plugins/pip.js +123 -0
- package/dist/security/manifest/plugins/pip.js.map +7 -0
- package/dist/security/manifest/plugins/pip.test.js +208 -0
- package/dist/security/manifest/plugins/pip.test.js.map +7 -0
- package/dist/security/manifest/plugins/pubspec.js +212 -0
- package/dist/security/manifest/plugins/pubspec.js.map +7 -0
- package/dist/security/manifest/plugins/pyproject.js +261 -0
- package/dist/security/manifest/plugins/pyproject.js.map +7 -0
- package/dist/security/manifest/plugins/rubygems.js +185 -0
- package/dist/security/manifest/plugins/rubygems.js.map +7 -0
- package/dist/security/manifest/plugins/swift.js +200 -0
- package/dist/security/manifest/plugins/swift.js.map +7 -0
- package/dist/security/owasp.js +104 -0
- package/dist/security/owasp.js.map +7 -0
- package/dist/security/pipeline.js +125 -0
- package/dist/security/pipeline.js.map +7 -0
- package/dist/security/reachability.js +276 -0
- package/dist/security/reachability.js.map +7 -0
- package/dist/security/reachability.test.js +394 -0
- package/dist/security/reachability.test.js.map +7 -0
- package/dist/security/remediation.js +126 -0
- package/dist/security/remediation.js.map +7 -0
- package/dist/security/secrets.js +176 -0
- package/dist/security/secrets.js.map +7 -0
- package/dist/security/staleness.js +231 -0
- package/dist/security/staleness.js.map +7 -0
- package/dist/security/supply-chain.js +301 -0
- package/dist/security/supply-chain.js.map +7 -0
- package/dist/security/suppressions.js +106 -0
- package/dist/security/suppressions.js.map +7 -0
- package/dist/security/types.js +17 -0
- package/dist/security/types.js.map +7 -0
- package/dist/security/vuln/client.js +355 -0
- package/dist/security/vuln/client.js.map +7 -0
- package/dist/security/vuln/client.test.js +288 -0
- package/dist/security/vuln/client.test.js.map +7 -0
- package/dist/security/vuln/epss-client.js +95 -0
- package/dist/security/vuln/epss-client.js.map +7 -0
- package/dist/security/vuln/index.js +29 -0
- package/dist/security/vuln/index.js.map +7 -0
- package/dist/security/vuln/osv-adapter.js +321 -0
- package/dist/security/vuln/osv-adapter.js.map +7 -0
- package/dist/security/vuln/osv-adapter.test.js +391 -0
- package/dist/security/vuln/osv-adapter.test.js.map +7 -0
- package/dist/security/vuln/types.js +17 -0
- package/dist/security/vuln/types.js.map +7 -0
- package/dist/types.js.map +2 -2
- package/package.json +5 -2
|
@@ -0,0 +1,230 @@
|
|
|
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 sbom_exports = {};
|
|
30
|
+
__export(sbom_exports, {
|
|
31
|
+
SBOMExporter: () => SBOMExporter
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(sbom_exports);
|
|
34
|
+
var fs = __toESM(require("fs"));
|
|
35
|
+
var path = __toESM(require("path"));
|
|
36
|
+
const ECOSYSTEM_PURL_MAP = {
|
|
37
|
+
npm: "npm",
|
|
38
|
+
maven: "maven",
|
|
39
|
+
go: "golang",
|
|
40
|
+
pypi: "pypi",
|
|
41
|
+
python: "pypi",
|
|
42
|
+
cargo: "cargo"
|
|
43
|
+
};
|
|
44
|
+
function buildPurl(ecosystem, name, version) {
|
|
45
|
+
const purlType = ECOSYSTEM_PURL_MAP[ecosystem] ?? ecosystem;
|
|
46
|
+
return `pkg:${purlType}/${name}@${version}`;
|
|
47
|
+
}
|
|
48
|
+
const TOOL_NAME = "kirograph-sec";
|
|
49
|
+
function getToolVersion() {
|
|
50
|
+
try {
|
|
51
|
+
let dir = __dirname;
|
|
52
|
+
for (let i = 0; i < 5; i++) {
|
|
53
|
+
const pkgPath = path.join(dir, "package.json");
|
|
54
|
+
if (fs.existsSync(pkgPath)) {
|
|
55
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
56
|
+
if (pkg.version) return pkg.version;
|
|
57
|
+
}
|
|
58
|
+
dir = path.dirname(dir);
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
return "0.1.0";
|
|
63
|
+
}
|
|
64
|
+
function getProjectName(projectRoot) {
|
|
65
|
+
try {
|
|
66
|
+
const pkgJsonPath = path.join(projectRoot, "package.json");
|
|
67
|
+
if (fs.existsSync(pkgJsonPath)) {
|
|
68
|
+
const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
|
|
69
|
+
if (pkg.name) return pkg.name;
|
|
70
|
+
}
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const cargoPath = path.join(projectRoot, "Cargo.toml");
|
|
75
|
+
if (fs.existsSync(cargoPath)) {
|
|
76
|
+
const content = fs.readFileSync(cargoPath, "utf-8");
|
|
77
|
+
const match = content.match(/^\s*name\s*=\s*"([^"]+)"/m);
|
|
78
|
+
if (match) return match[1];
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const goModPath = path.join(projectRoot, "go.mod");
|
|
84
|
+
if (fs.existsSync(goModPath)) {
|
|
85
|
+
const content = fs.readFileSync(goModPath, "utf-8");
|
|
86
|
+
const match = content.match(/^module\s+(\S+)/m);
|
|
87
|
+
if (match) return match[1];
|
|
88
|
+
}
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const pomPath = path.join(projectRoot, "pom.xml");
|
|
93
|
+
if (fs.existsSync(pomPath)) {
|
|
94
|
+
const content = fs.readFileSync(pomPath, "utf-8");
|
|
95
|
+
const match = content.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
96
|
+
if (match) return match[1];
|
|
97
|
+
}
|
|
98
|
+
} catch {
|
|
99
|
+
}
|
|
100
|
+
return path.basename(projectRoot);
|
|
101
|
+
}
|
|
102
|
+
class SBOMExporter {
|
|
103
|
+
constructor(db, projectRoot) {
|
|
104
|
+
this.db = db;
|
|
105
|
+
this.projectRoot = projectRoot;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Generate a CycloneDX 1.5 SBOM object.
|
|
109
|
+
*/
|
|
110
|
+
export() {
|
|
111
|
+
const rawDb = this.db.getRawDb();
|
|
112
|
+
const depRows = rawDb.all(
|
|
113
|
+
"SELECT node_id, ecosystem, package_name, declared_constraint, resolved_version, scope, source_manifests FROM sec_dependencies"
|
|
114
|
+
);
|
|
115
|
+
const directNodeIds = /* @__PURE__ */ new Set();
|
|
116
|
+
if (depRows.length > 0) {
|
|
117
|
+
const declaredInEdges = rawDb.all(
|
|
118
|
+
`SELECT source, target, kind FROM edges WHERE kind = 'declared_in'`
|
|
119
|
+
);
|
|
120
|
+
for (const edge of declaredInEdges) {
|
|
121
|
+
directNodeIds.add(edge.source);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const components = depRows.map((row) => {
|
|
125
|
+
const version = row.resolved_version ?? row.declared_constraint;
|
|
126
|
+
const purl = buildPurl(row.ecosystem, row.package_name, version);
|
|
127
|
+
const isDirect = directNodeIds.has(row.node_id);
|
|
128
|
+
const component = {
|
|
129
|
+
type: "library",
|
|
130
|
+
name: row.package_name,
|
|
131
|
+
version,
|
|
132
|
+
purl,
|
|
133
|
+
scope: isDirect ? "required" : "optional",
|
|
134
|
+
properties: [
|
|
135
|
+
{ name: "kirograph:classification", value: isDirect ? "direct" : "transitive" },
|
|
136
|
+
{ name: "kirograph:ecosystem", value: row.ecosystem }
|
|
137
|
+
]
|
|
138
|
+
};
|
|
139
|
+
return component;
|
|
140
|
+
});
|
|
141
|
+
const dependencies = [];
|
|
142
|
+
if (depRows.length > 0) {
|
|
143
|
+
const dependsOnEdges = rawDb.all(
|
|
144
|
+
`SELECT source, target, kind FROM edges WHERE kind = 'depends_on'`
|
|
145
|
+
);
|
|
146
|
+
const depNodeIds = new Set(depRows.map((r) => r.node_id));
|
|
147
|
+
const depsMap = /* @__PURE__ */ new Map();
|
|
148
|
+
for (const edge of dependsOnEdges) {
|
|
149
|
+
if (depNodeIds.has(edge.source) && depNodeIds.has(edge.target)) {
|
|
150
|
+
const existing = depsMap.get(edge.source);
|
|
151
|
+
if (existing) {
|
|
152
|
+
existing.push(edge.target);
|
|
153
|
+
} else {
|
|
154
|
+
depsMap.set(edge.source, [edge.target]);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
for (const row of depRows) {
|
|
159
|
+
const version = row.resolved_version ?? row.declared_constraint;
|
|
160
|
+
const ref = buildPurl(row.ecosystem, row.package_name, version);
|
|
161
|
+
const deps = depsMap.get(row.node_id);
|
|
162
|
+
const entry = { ref };
|
|
163
|
+
if (deps && deps.length > 0) {
|
|
164
|
+
entry.dependsOn = deps.map((targetId) => {
|
|
165
|
+
const targetRow = depRows.find((r) => r.node_id === targetId);
|
|
166
|
+
if (targetRow) {
|
|
167
|
+
const targetVersion = targetRow.resolved_version ?? targetRow.declared_constraint;
|
|
168
|
+
return buildPurl(targetRow.ecosystem, targetRow.package_name, targetVersion);
|
|
169
|
+
}
|
|
170
|
+
return targetId;
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
dependencies.push(entry);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const metadata = this.buildMetadata();
|
|
177
|
+
const sbom = {
|
|
178
|
+
bomFormat: "CycloneDX",
|
|
179
|
+
specVersion: "1.5",
|
|
180
|
+
version: 1,
|
|
181
|
+
metadata,
|
|
182
|
+
components,
|
|
183
|
+
dependencies
|
|
184
|
+
};
|
|
185
|
+
return sbom;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Export the SBOM as a pretty-printed JSON string.
|
|
189
|
+
*/
|
|
190
|
+
exportJSON() {
|
|
191
|
+
const sbom = this.export();
|
|
192
|
+
return JSON.stringify(sbom, null, 2);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Build CycloneDX metadata section.
|
|
196
|
+
*/
|
|
197
|
+
buildMetadata() {
|
|
198
|
+
const toolVersion = getToolVersion();
|
|
199
|
+
const projectName = getProjectName(this.projectRoot);
|
|
200
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
201
|
+
const meta = {
|
|
202
|
+
toolName: TOOL_NAME,
|
|
203
|
+
toolVersion,
|
|
204
|
+
timestamp,
|
|
205
|
+
projectName
|
|
206
|
+
};
|
|
207
|
+
return {
|
|
208
|
+
timestamp: meta.timestamp,
|
|
209
|
+
tools: {
|
|
210
|
+
components: [
|
|
211
|
+
{
|
|
212
|
+
type: "application",
|
|
213
|
+
name: meta.toolName,
|
|
214
|
+
version: meta.toolVersion
|
|
215
|
+
}
|
|
216
|
+
]
|
|
217
|
+
},
|
|
218
|
+
component: {
|
|
219
|
+
type: "application",
|
|
220
|
+
name: meta.projectName,
|
|
221
|
+
version: ""
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
227
|
+
0 && (module.exports = {
|
|
228
|
+
SBOMExporter
|
|
229
|
+
});
|
|
230
|
+
//# sourceMappingURL=sbom.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/security/export/sbom.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * SBOMExporter \u2014 CycloneDX 1.5 SBOM Generation\n *\n * Generates a CycloneDX 1.5 JSON Software Bill of Materials from the\n * Dependency_Graph stored in the KiroGraph database. Each Dependency_Node\n * becomes a component of type \"library\" with purl, scope, and\n * direct/transitive classification.\n *\n * Requirements: 6.1, 6.2, 6.3, 6.4, 6.5, 6.6, 6.7\n */\nimport type { GraphDatabase } from '../../db/database';\nimport type {\n CycloneDXSBOM,\n CycloneDXComponent,\n CycloneDXDependencyEntry,\n SBOMMetadata,\n} from '../types';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n// \u2500\u2500 Ecosystem \u2192 purl type mapping \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst ECOSYSTEM_PURL_MAP: Record<string, string> = {\n npm: 'npm',\n maven: 'maven',\n go: 'golang',\n pypi: 'pypi',\n python: 'pypi',\n cargo: 'cargo',\n};\n\n/**\n * Build a Package URL (purl) for a dependency.\n * Format: pkg:<type>/<name>@<version>\n */\nfunction buildPurl(ecosystem: string, name: string, version: string): string {\n const purlType = ECOSYSTEM_PURL_MAP[ecosystem] ?? ecosystem;\n return `pkg:${purlType}/${name}@${version}`;\n}\n\n// \u2500\u2500 Metadata helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst TOOL_NAME = 'kirograph-sec';\n\n/**\n * Read the tool version from package.json, falling back to \"0.1.0\".\n */\nfunction getToolVersion(): string {\n try {\n // Walk up from this file to find the project root package.json\n let dir = __dirname;\n for (let i = 0; i < 5; i++) {\n const pkgPath = path.join(dir, 'package.json');\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));\n if (pkg.version) return pkg.version;\n }\n dir = path.dirname(dir);\n }\n } catch {\n // Ignore errors \u2014 fall back to default\n }\n return '0.1.0';\n}\n\n/**\n * Detect the project name from the root manifest file.\n * Checks package.json, Cargo.toml, go.mod, pom.xml in order.\n */\nfunction getProjectName(projectRoot: string): string {\n try {\n const pkgJsonPath = path.join(projectRoot, 'package.json');\n if (fs.existsSync(pkgJsonPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));\n if (pkg.name) return pkg.name;\n }\n } catch { /* ignore */ }\n\n try {\n const cargoPath = path.join(projectRoot, 'Cargo.toml');\n if (fs.existsSync(cargoPath)) {\n const content = fs.readFileSync(cargoPath, 'utf-8');\n const match = content.match(/^\\s*name\\s*=\\s*\"([^\"]+)\"/m);\n if (match) return match[1];\n }\n } catch { /* ignore */ }\n\n try {\n const goModPath = path.join(projectRoot, 'go.mod');\n if (fs.existsSync(goModPath)) {\n const content = fs.readFileSync(goModPath, 'utf-8');\n const match = content.match(/^module\\s+(\\S+)/m);\n if (match) return match[1];\n }\n } catch { /* ignore */ }\n\n try {\n const pomPath = path.join(projectRoot, 'pom.xml');\n if (fs.existsSync(pomPath)) {\n const content = fs.readFileSync(pomPath, 'utf-8');\n const match = content.match(/<artifactId>([^<]+)<\\/artifactId>/);\n if (match) return match[1];\n }\n } catch { /* ignore */ }\n\n // Fallback: use the directory name\n return path.basename(projectRoot);\n}\n\n// \u2500\u2500 Database row types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface SecDependencyRow {\n node_id: string;\n ecosystem: string;\n package_name: string;\n declared_constraint: string;\n resolved_version: string | null;\n scope: string;\n source_manifests: string;\n}\n\ninterface EdgeRow {\n source: string;\n target: string;\n kind: string;\n}\n\n// \u2500\u2500 SBOMExporter \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Generates CycloneDX 1.5 SBOM documents from the dependency graph.\n */\nexport class SBOMExporter {\n private readonly db: GraphDatabase;\n private readonly projectRoot: string;\n\n constructor(db: GraphDatabase, projectRoot: string) {\n this.db = db;\n this.projectRoot = projectRoot;\n }\n\n /**\n * Generate a CycloneDX 1.5 SBOM object.\n */\n export(): CycloneDXSBOM {\n const rawDb = this.db.getRawDb();\n\n // Query all dependency nodes\n const depRows: SecDependencyRow[] = rawDb.all(\n 'SELECT node_id, ecosystem, package_name, declared_constraint, resolved_version, scope, source_manifests FROM sec_dependencies',\n );\n\n // Determine which dependencies are direct (have a declared_in edge)\n const directNodeIds = new Set<string>();\n if (depRows.length > 0) {\n const declaredInEdges: EdgeRow[] = rawDb.all(\n `SELECT source, target, kind FROM edges WHERE kind = 'declared_in'`,\n );\n for (const edge of declaredInEdges) {\n directNodeIds.add(edge.source);\n }\n }\n\n // Build components\n const components: CycloneDXComponent[] = depRows.map((row) => {\n const version = row.resolved_version ?? row.declared_constraint;\n const purl = buildPurl(row.ecosystem, row.package_name, version);\n const isDirect = directNodeIds.has(row.node_id);\n\n const component: CycloneDXComponent = {\n type: 'library',\n name: row.package_name,\n version,\n purl,\n scope: isDirect ? 'required' : 'optional',\n properties: [\n { name: 'kirograph:classification', value: isDirect ? 'direct' : 'transitive' },\n { name: 'kirograph:ecosystem', value: row.ecosystem },\n ],\n };\n\n return component;\n });\n\n // Build dependency relationships from depends_on edges\n const dependencies: CycloneDXDependencyEntry[] = [];\n\n if (depRows.length > 0) {\n const dependsOnEdges: EdgeRow[] = rawDb.all(\n `SELECT source, target, kind FROM edges WHERE kind = 'depends_on'`,\n );\n\n // Group depends_on edges by source\n const depNodeIds = new Set(depRows.map((r) => r.node_id));\n const depsMap = new Map<string, string[]>();\n\n for (const edge of dependsOnEdges) {\n // Only include edges where both source and target are dependency nodes\n if (depNodeIds.has(edge.source) && depNodeIds.has(edge.target)) {\n const existing = depsMap.get(edge.source);\n if (existing) {\n existing.push(edge.target);\n } else {\n depsMap.set(edge.source, [edge.target]);\n }\n }\n }\n\n // Create an entry for each dependency node\n for (const row of depRows) {\n const version = row.resolved_version ?? row.declared_constraint;\n const ref = buildPurl(row.ecosystem, row.package_name, version);\n const deps = depsMap.get(row.node_id);\n\n const entry: CycloneDXDependencyEntry = { ref };\n if (deps && deps.length > 0) {\n // Resolve target node_ids to purls\n entry.dependsOn = deps.map((targetId) => {\n const targetRow = depRows.find((r) => r.node_id === targetId);\n if (targetRow) {\n const targetVersion = targetRow.resolved_version ?? targetRow.declared_constraint;\n return buildPurl(targetRow.ecosystem, targetRow.package_name, targetVersion);\n }\n return targetId; // fallback to node_id if not found\n });\n }\n\n dependencies.push(entry);\n }\n }\n\n // Build metadata\n const metadata = this.buildMetadata();\n\n const sbom: CycloneDXSBOM = {\n bomFormat: 'CycloneDX',\n specVersion: '1.5',\n version: 1,\n metadata,\n components,\n dependencies,\n };\n\n return sbom;\n }\n\n /**\n * Export the SBOM as a pretty-printed JSON string.\n */\n exportJSON(): string {\n const sbom = this.export();\n return JSON.stringify(sbom, null, 2);\n }\n\n /**\n * Build CycloneDX metadata section.\n */\n private buildMetadata(): Record<string, unknown> {\n const toolVersion = getToolVersion();\n const projectName = getProjectName(this.projectRoot);\n const timestamp = new Date().toISOString();\n\n const meta: SBOMMetadata = {\n toolName: TOOL_NAME,\n toolVersion,\n timestamp,\n projectName,\n };\n\n return {\n timestamp: meta.timestamp,\n tools: {\n components: [\n {\n type: 'application',\n name: meta.toolName,\n version: meta.toolVersion,\n },\n ],\n },\n component: {\n type: 'application',\n name: meta.projectName,\n version: '',\n },\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,288 @@
|
|
|
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 import_sbom = require("./sbom");
|
|
26
|
+
var import_database = require("../../db/database");
|
|
27
|
+
var fs = __toESM(require("fs"));
|
|
28
|
+
var path = __toESM(require("path"));
|
|
29
|
+
var os = __toESM(require("os"));
|
|
30
|
+
function createTempProject() {
|
|
31
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "sbom-test-"));
|
|
32
|
+
fs.writeFileSync(
|
|
33
|
+
path.join(dir, "package.json"),
|
|
34
|
+
JSON.stringify({ name: "test-project", version: "1.0.0" })
|
|
35
|
+
);
|
|
36
|
+
return dir;
|
|
37
|
+
}
|
|
38
|
+
function setupDb(projectRoot) {
|
|
39
|
+
const db = new import_database.GraphDatabase(projectRoot);
|
|
40
|
+
db.applySecuritySchema();
|
|
41
|
+
return db;
|
|
42
|
+
}
|
|
43
|
+
function insertDependency(db, opts) {
|
|
44
|
+
const rawDb = db.getRawDb();
|
|
45
|
+
db.upsertNode({
|
|
46
|
+
id: opts.nodeId,
|
|
47
|
+
kind: "dependency",
|
|
48
|
+
name: opts.packageName,
|
|
49
|
+
qualifiedName: `${opts.ecosystem}/${opts.packageName}`,
|
|
50
|
+
filePath: opts.sourceManifests?.[0] ?? "package.json",
|
|
51
|
+
language: "unknown",
|
|
52
|
+
startLine: 0,
|
|
53
|
+
endLine: 0,
|
|
54
|
+
startColumn: 0,
|
|
55
|
+
endColumn: 0,
|
|
56
|
+
updatedAt: Date.now()
|
|
57
|
+
});
|
|
58
|
+
rawDb.run(
|
|
59
|
+
`INSERT OR REPLACE INTO sec_dependencies
|
|
60
|
+
(node_id, ecosystem, package_name, declared_constraint, resolved_version, scope, source_manifests)
|
|
61
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
62
|
+
[
|
|
63
|
+
opts.nodeId,
|
|
64
|
+
opts.ecosystem,
|
|
65
|
+
opts.packageName,
|
|
66
|
+
opts.declaredConstraint,
|
|
67
|
+
opts.resolvedVersion ?? null,
|
|
68
|
+
opts.scope ?? "production",
|
|
69
|
+
JSON.stringify(opts.sourceManifests ?? ["package.json"])
|
|
70
|
+
]
|
|
71
|
+
);
|
|
72
|
+
if (opts.isDirect !== false) {
|
|
73
|
+
db.insertEdge({
|
|
74
|
+
source: opts.nodeId,
|
|
75
|
+
target: opts.sourceManifests?.[0] ?? "package.json",
|
|
76
|
+
kind: "declared_in"
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function insertDependsOnEdge(db, source, target) {
|
|
81
|
+
db.insertEdge({ source, target, kind: "depends_on" });
|
|
82
|
+
}
|
|
83
|
+
(0, import_vitest.describe)("SBOMExporter", () => {
|
|
84
|
+
let projectRoot;
|
|
85
|
+
let db;
|
|
86
|
+
(0, import_vitest.beforeEach)(() => {
|
|
87
|
+
projectRoot = createTempProject();
|
|
88
|
+
db = setupDb(projectRoot);
|
|
89
|
+
});
|
|
90
|
+
(0, import_vitest.afterEach)(() => {
|
|
91
|
+
db.close();
|
|
92
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
93
|
+
});
|
|
94
|
+
(0, import_vitest.describe)("empty dependency graph", () => {
|
|
95
|
+
(0, import_vitest.it)("generates a valid CycloneDX 1.5 document with empty components", () => {
|
|
96
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
97
|
+
const sbom = exporter.export();
|
|
98
|
+
(0, import_vitest.expect)(sbom.bomFormat).toBe("CycloneDX");
|
|
99
|
+
(0, import_vitest.expect)(sbom.specVersion).toBe("1.5");
|
|
100
|
+
(0, import_vitest.expect)(sbom.version).toBe(1);
|
|
101
|
+
(0, import_vitest.expect)(sbom.components).toEqual([]);
|
|
102
|
+
(0, import_vitest.expect)(sbom.dependencies).toEqual([]);
|
|
103
|
+
(0, import_vitest.expect)(sbom.metadata).toBeDefined();
|
|
104
|
+
});
|
|
105
|
+
(0, import_vitest.it)("includes metadata with tool name, timestamp, and project name", () => {
|
|
106
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
107
|
+
const sbom = exporter.export();
|
|
108
|
+
const meta = sbom.metadata;
|
|
109
|
+
(0, import_vitest.expect)(meta.timestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
110
|
+
(0, import_vitest.expect)(meta.tools.components[0].name).toBe("kirograph-sec");
|
|
111
|
+
(0, import_vitest.expect)(meta.tools.components[0].version).toBeDefined();
|
|
112
|
+
(0, import_vitest.expect)(meta.component.name).toBe("test-project");
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
(0, import_vitest.describe)("with dependencies", () => {
|
|
116
|
+
(0, import_vitest.beforeEach)(() => {
|
|
117
|
+
insertDependency(db, {
|
|
118
|
+
nodeId: "dep:npm:express",
|
|
119
|
+
ecosystem: "npm",
|
|
120
|
+
packageName: "express",
|
|
121
|
+
declaredConstraint: "^4.18.0",
|
|
122
|
+
resolvedVersion: "4.18.2",
|
|
123
|
+
scope: "production",
|
|
124
|
+
isDirect: true
|
|
125
|
+
});
|
|
126
|
+
insertDependency(db, {
|
|
127
|
+
nodeId: "dep:npm:lodash",
|
|
128
|
+
ecosystem: "npm",
|
|
129
|
+
packageName: "lodash",
|
|
130
|
+
declaredConstraint: "^4.17.0",
|
|
131
|
+
resolvedVersion: "4.17.21",
|
|
132
|
+
scope: "production",
|
|
133
|
+
isDirect: false
|
|
134
|
+
// transitive — no declared_in edge
|
|
135
|
+
});
|
|
136
|
+
insertDependsOnEdge(db, "dep:npm:express", "dep:npm:lodash");
|
|
137
|
+
});
|
|
138
|
+
(0, import_vitest.it)("includes all dependencies as components of type library", () => {
|
|
139
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
140
|
+
const sbom = exporter.export();
|
|
141
|
+
(0, import_vitest.expect)(sbom.components).toHaveLength(2);
|
|
142
|
+
(0, import_vitest.expect)(sbom.components.every((c) => c.type === "library")).toBe(true);
|
|
143
|
+
});
|
|
144
|
+
(0, import_vitest.it)("includes correct purl for each component", () => {
|
|
145
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
146
|
+
const sbom = exporter.export();
|
|
147
|
+
const express = sbom.components.find((c) => c.name === "express");
|
|
148
|
+
(0, import_vitest.expect)(express?.purl).toBe("pkg:npm/express@4.18.2");
|
|
149
|
+
const lodash = sbom.components.find((c) => c.name === "lodash");
|
|
150
|
+
(0, import_vitest.expect)(lodash?.purl).toBe("pkg:npm/lodash@4.17.21");
|
|
151
|
+
});
|
|
152
|
+
(0, import_vitest.it)('sets scope to "required" for direct and "optional" for transitive', () => {
|
|
153
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
154
|
+
const sbom = exporter.export();
|
|
155
|
+
const express = sbom.components.find((c) => c.name === "express");
|
|
156
|
+
(0, import_vitest.expect)(express?.scope).toBe("required");
|
|
157
|
+
const lodash = sbom.components.find((c) => c.name === "lodash");
|
|
158
|
+
(0, import_vitest.expect)(lodash?.scope).toBe("optional");
|
|
159
|
+
});
|
|
160
|
+
(0, import_vitest.it)("includes direct/transitive classification in properties", () => {
|
|
161
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
162
|
+
const sbom = exporter.export();
|
|
163
|
+
const express = sbom.components.find((c) => c.name === "express");
|
|
164
|
+
const expressClassification = express?.properties?.find(
|
|
165
|
+
(p) => p.name === "kirograph:classification"
|
|
166
|
+
);
|
|
167
|
+
(0, import_vitest.expect)(expressClassification?.value).toBe("direct");
|
|
168
|
+
const lodash = sbom.components.find((c) => c.name === "lodash");
|
|
169
|
+
const lodashClassification = lodash?.properties?.find(
|
|
170
|
+
(p) => p.name === "kirograph:classification"
|
|
171
|
+
);
|
|
172
|
+
(0, import_vitest.expect)(lodashClassification?.value).toBe("transitive");
|
|
173
|
+
});
|
|
174
|
+
(0, import_vitest.it)("includes dependency relationships reflecting depends_on edges", () => {
|
|
175
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
176
|
+
const sbom = exporter.export();
|
|
177
|
+
const expressDep = sbom.dependencies.find(
|
|
178
|
+
(d) => d.ref === "pkg:npm/express@4.18.2"
|
|
179
|
+
);
|
|
180
|
+
(0, import_vitest.expect)(expressDep).toBeDefined();
|
|
181
|
+
(0, import_vitest.expect)(expressDep?.dependsOn).toContain("pkg:npm/lodash@4.17.21");
|
|
182
|
+
const lodashDep = sbom.dependencies.find(
|
|
183
|
+
(d) => d.ref === "pkg:npm/lodash@4.17.21"
|
|
184
|
+
);
|
|
185
|
+
(0, import_vitest.expect)(lodashDep).toBeDefined();
|
|
186
|
+
(0, import_vitest.expect)(lodashDep?.dependsOn).toBeUndefined();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
(0, import_vitest.describe)("version fallback", () => {
|
|
190
|
+
(0, import_vitest.it)("uses declared constraint when resolved version is not available", () => {
|
|
191
|
+
insertDependency(db, {
|
|
192
|
+
nodeId: "dep:npm:some-pkg",
|
|
193
|
+
ecosystem: "npm",
|
|
194
|
+
packageName: "some-pkg",
|
|
195
|
+
declaredConstraint: ">=2.0.0",
|
|
196
|
+
resolvedVersion: void 0,
|
|
197
|
+
isDirect: true
|
|
198
|
+
});
|
|
199
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
200
|
+
const sbom = exporter.export();
|
|
201
|
+
const comp = sbom.components.find((c) => c.name === "some-pkg");
|
|
202
|
+
(0, import_vitest.expect)(comp?.version).toBe(">=2.0.0");
|
|
203
|
+
(0, import_vitest.expect)(comp?.purl).toBe("pkg:npm/some-pkg@>=2.0.0");
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
(0, import_vitest.describe)("ecosystem purl mapping", () => {
|
|
207
|
+
(0, import_vitest.it)("maps go ecosystem to golang in purl", () => {
|
|
208
|
+
insertDependency(db, {
|
|
209
|
+
nodeId: "dep:go:github.com/gin-gonic/gin",
|
|
210
|
+
ecosystem: "go",
|
|
211
|
+
packageName: "github.com/gin-gonic/gin",
|
|
212
|
+
declaredConstraint: "v1.9.1",
|
|
213
|
+
resolvedVersion: "v1.9.1",
|
|
214
|
+
isDirect: true
|
|
215
|
+
});
|
|
216
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
217
|
+
const sbom = exporter.export();
|
|
218
|
+
const comp = sbom.components.find((c) => c.name === "github.com/gin-gonic/gin");
|
|
219
|
+
(0, import_vitest.expect)(comp?.purl).toBe("pkg:golang/github.com/gin-gonic/gin@v1.9.1");
|
|
220
|
+
});
|
|
221
|
+
(0, import_vitest.it)("maps python ecosystem to pypi in purl", () => {
|
|
222
|
+
insertDependency(db, {
|
|
223
|
+
nodeId: "dep:python:django",
|
|
224
|
+
ecosystem: "python",
|
|
225
|
+
packageName: "django",
|
|
226
|
+
declaredConstraint: "==4.2.0",
|
|
227
|
+
resolvedVersion: "4.2.0",
|
|
228
|
+
isDirect: true
|
|
229
|
+
});
|
|
230
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
231
|
+
const sbom = exporter.export();
|
|
232
|
+
const comp = sbom.components.find((c) => c.name === "django");
|
|
233
|
+
(0, import_vitest.expect)(comp?.purl).toBe("pkg:pypi/django@4.2.0");
|
|
234
|
+
});
|
|
235
|
+
(0, import_vitest.it)("maps cargo ecosystem correctly in purl", () => {
|
|
236
|
+
insertDependency(db, {
|
|
237
|
+
nodeId: "dep:cargo:serde",
|
|
238
|
+
ecosystem: "cargo",
|
|
239
|
+
packageName: "serde",
|
|
240
|
+
declaredConstraint: "1.0.188",
|
|
241
|
+
resolvedVersion: "1.0.188",
|
|
242
|
+
isDirect: true
|
|
243
|
+
});
|
|
244
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
245
|
+
const sbom = exporter.export();
|
|
246
|
+
const comp = sbom.components.find((c) => c.name === "serde");
|
|
247
|
+
(0, import_vitest.expect)(comp?.purl).toBe("pkg:cargo/serde@1.0.188");
|
|
248
|
+
});
|
|
249
|
+
(0, import_vitest.it)("maps maven ecosystem correctly in purl", () => {
|
|
250
|
+
insertDependency(db, {
|
|
251
|
+
nodeId: "dep:maven:org.apache.logging.log4j/log4j-core",
|
|
252
|
+
ecosystem: "maven",
|
|
253
|
+
packageName: "org.apache.logging.log4j/log4j-core",
|
|
254
|
+
declaredConstraint: "2.17.0",
|
|
255
|
+
resolvedVersion: "2.17.0",
|
|
256
|
+
isDirect: true
|
|
257
|
+
});
|
|
258
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
259
|
+
const sbom = exporter.export();
|
|
260
|
+
const comp = sbom.components.find(
|
|
261
|
+
(c) => c.name === "org.apache.logging.log4j/log4j-core"
|
|
262
|
+
);
|
|
263
|
+
(0, import_vitest.expect)(comp?.purl).toBe(
|
|
264
|
+
"pkg:maven/org.apache.logging.log4j/log4j-core@2.17.0"
|
|
265
|
+
);
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
(0, import_vitest.describe)("exportJSON", () => {
|
|
269
|
+
(0, import_vitest.it)("returns a pretty-printed JSON string", () => {
|
|
270
|
+
insertDependency(db, {
|
|
271
|
+
nodeId: "dep:npm:express",
|
|
272
|
+
ecosystem: "npm",
|
|
273
|
+
packageName: "express",
|
|
274
|
+
declaredConstraint: "^4.18.0",
|
|
275
|
+
resolvedVersion: "4.18.2",
|
|
276
|
+
isDirect: true
|
|
277
|
+
});
|
|
278
|
+
const exporter = new import_sbom.SBOMExporter(db, projectRoot);
|
|
279
|
+
const json = exporter.exportJSON();
|
|
280
|
+
const parsed = JSON.parse(json);
|
|
281
|
+
(0, import_vitest.expect)(parsed.bomFormat).toBe("CycloneDX");
|
|
282
|
+
(0, import_vitest.expect)(parsed.specVersion).toBe("1.5");
|
|
283
|
+
(0, import_vitest.expect)(json).toContain("\n");
|
|
284
|
+
(0, import_vitest.expect)(json).toContain(" ");
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
//# sourceMappingURL=sbom.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/security/export/sbom.test.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Unit tests for SBOMExporter \u2014 CycloneDX 1.5 SBOM generation\n */\nimport { describe, it, expect, beforeEach, afterEach } from 'vitest';\nimport { SBOMExporter } from './sbom';\nimport { GraphDatabase } from '../../db/database';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\n\n// \u2500\u2500 Test Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction createTempProject(): string {\n const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sbom-test-'));\n // Create a package.json so the project name can be detected\n fs.writeFileSync(\n path.join(dir, 'package.json'),\n JSON.stringify({ name: 'test-project', version: '1.0.0' }),\n );\n return dir;\n}\n\nfunction setupDb(projectRoot: string): GraphDatabase {\n const db = new GraphDatabase(projectRoot);\n db.applySecuritySchema();\n return db;\n}\n\nfunction insertDependency(\n db: GraphDatabase,\n opts: {\n nodeId: string;\n ecosystem: string;\n packageName: string;\n declaredConstraint: string;\n resolvedVersion?: string;\n scope?: string;\n sourceManifests?: string[];\n isDirect?: boolean;\n },\n): void {\n const rawDb = db.getRawDb();\n\n // Insert into nodes table\n db.upsertNode({\n id: opts.nodeId,\n kind: 'dependency',\n name: opts.packageName,\n qualifiedName: `${opts.ecosystem}/${opts.packageName}`,\n filePath: opts.sourceManifests?.[0] ?? 'package.json',\n language: 'unknown',\n startLine: 0,\n endLine: 0,\n startColumn: 0,\n endColumn: 0,\n updatedAt: Date.now(),\n });\n\n // Insert into sec_dependencies\n rawDb.run(\n `INSERT OR REPLACE INTO sec_dependencies\n (node_id, ecosystem, package_name, declared_constraint, resolved_version, scope, source_manifests)\n VALUES (?, ?, ?, ?, ?, ?, ?)`,\n [\n opts.nodeId,\n opts.ecosystem,\n opts.packageName,\n opts.declaredConstraint,\n opts.resolvedVersion ?? null,\n opts.scope ?? 'production',\n JSON.stringify(opts.sourceManifests ?? ['package.json']),\n ],\n );\n\n // Create declared_in edge if direct\n if (opts.isDirect !== false) {\n db.insertEdge({\n source: opts.nodeId,\n target: opts.sourceManifests?.[0] ?? 'package.json',\n kind: 'declared_in',\n });\n }\n}\n\nfunction insertDependsOnEdge(db: GraphDatabase, source: string, target: string): void {\n db.insertEdge({ source, target, kind: 'depends_on' });\n}\n\n// \u2500\u2500 Tests \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ndescribe('SBOMExporter', () => {\n let projectRoot: string;\n let db: GraphDatabase;\n\n beforeEach(() => {\n projectRoot = createTempProject();\n db = setupDb(projectRoot);\n });\n\n afterEach(() => {\n db.close();\n fs.rmSync(projectRoot, { recursive: true, force: true });\n });\n\n describe('empty dependency graph', () => {\n it('generates a valid CycloneDX 1.5 document with empty components', () => {\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n expect(sbom.bomFormat).toBe('CycloneDX');\n expect(sbom.specVersion).toBe('1.5');\n expect(sbom.version).toBe(1);\n expect(sbom.components).toEqual([]);\n expect(sbom.dependencies).toEqual([]);\n expect(sbom.metadata).toBeDefined();\n });\n\n it('includes metadata with tool name, timestamp, and project name', () => {\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const meta = sbom.metadata as any;\n expect(meta.timestamp).toMatch(/^\\d{4}-\\d{2}-\\d{2}T/);\n expect(meta.tools.components[0].name).toBe('kirograph-sec');\n expect(meta.tools.components[0].version).toBeDefined();\n expect(meta.component.name).toBe('test-project');\n });\n });\n\n describe('with dependencies', () => {\n beforeEach(() => {\n insertDependency(db, {\n nodeId: 'dep:npm:express',\n ecosystem: 'npm',\n packageName: 'express',\n declaredConstraint: '^4.18.0',\n resolvedVersion: '4.18.2',\n scope: 'production',\n isDirect: true,\n });\n\n insertDependency(db, {\n nodeId: 'dep:npm:lodash',\n ecosystem: 'npm',\n packageName: 'lodash',\n declaredConstraint: '^4.17.0',\n resolvedVersion: '4.17.21',\n scope: 'production',\n isDirect: false, // transitive \u2014 no declared_in edge\n });\n\n // express depends on lodash (transitive)\n insertDependsOnEdge(db, 'dep:npm:express', 'dep:npm:lodash');\n });\n\n it('includes all dependencies as components of type library', () => {\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n expect(sbom.components).toHaveLength(2);\n expect(sbom.components.every((c) => c.type === 'library')).toBe(true);\n });\n\n it('includes correct purl for each component', () => {\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const express = sbom.components.find((c) => c.name === 'express');\n expect(express?.purl).toBe('pkg:npm/express@4.18.2');\n\n const lodash = sbom.components.find((c) => c.name === 'lodash');\n expect(lodash?.purl).toBe('pkg:npm/lodash@4.17.21');\n });\n\n it('sets scope to \"required\" for direct and \"optional\" for transitive', () => {\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const express = sbom.components.find((c) => c.name === 'express');\n expect(express?.scope).toBe('required');\n\n const lodash = sbom.components.find((c) => c.name === 'lodash');\n expect(lodash?.scope).toBe('optional');\n });\n\n it('includes direct/transitive classification in properties', () => {\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const express = sbom.components.find((c) => c.name === 'express');\n const expressClassification = express?.properties?.find(\n (p) => p.name === 'kirograph:classification',\n );\n expect(expressClassification?.value).toBe('direct');\n\n const lodash = sbom.components.find((c) => c.name === 'lodash');\n const lodashClassification = lodash?.properties?.find(\n (p) => p.name === 'kirograph:classification',\n );\n expect(lodashClassification?.value).toBe('transitive');\n });\n\n it('includes dependency relationships reflecting depends_on edges', () => {\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const expressDep = sbom.dependencies.find(\n (d) => d.ref === 'pkg:npm/express@4.18.2',\n );\n expect(expressDep).toBeDefined();\n expect(expressDep?.dependsOn).toContain('pkg:npm/lodash@4.17.21');\n\n // lodash has no depends_on\n const lodashDep = sbom.dependencies.find(\n (d) => d.ref === 'pkg:npm/lodash@4.17.21',\n );\n expect(lodashDep).toBeDefined();\n expect(lodashDep?.dependsOn).toBeUndefined();\n });\n });\n\n describe('version fallback', () => {\n it('uses declared constraint when resolved version is not available', () => {\n insertDependency(db, {\n nodeId: 'dep:npm:some-pkg',\n ecosystem: 'npm',\n packageName: 'some-pkg',\n declaredConstraint: '>=2.0.0',\n resolvedVersion: undefined,\n isDirect: true,\n });\n\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const comp = sbom.components.find((c) => c.name === 'some-pkg');\n expect(comp?.version).toBe('>=2.0.0');\n expect(comp?.purl).toBe('pkg:npm/some-pkg@>=2.0.0');\n });\n });\n\n describe('ecosystem purl mapping', () => {\n it('maps go ecosystem to golang in purl', () => {\n insertDependency(db, {\n nodeId: 'dep:go:github.com/gin-gonic/gin',\n ecosystem: 'go',\n packageName: 'github.com/gin-gonic/gin',\n declaredConstraint: 'v1.9.1',\n resolvedVersion: 'v1.9.1',\n isDirect: true,\n });\n\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const comp = sbom.components.find((c) => c.name === 'github.com/gin-gonic/gin');\n expect(comp?.purl).toBe('pkg:golang/github.com/gin-gonic/gin@v1.9.1');\n });\n\n it('maps python ecosystem to pypi in purl', () => {\n insertDependency(db, {\n nodeId: 'dep:python:django',\n ecosystem: 'python',\n packageName: 'django',\n declaredConstraint: '==4.2.0',\n resolvedVersion: '4.2.0',\n isDirect: true,\n });\n\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const comp = sbom.components.find((c) => c.name === 'django');\n expect(comp?.purl).toBe('pkg:pypi/django@4.2.0');\n });\n\n it('maps cargo ecosystem correctly in purl', () => {\n insertDependency(db, {\n nodeId: 'dep:cargo:serde',\n ecosystem: 'cargo',\n packageName: 'serde',\n declaredConstraint: '1.0.188',\n resolvedVersion: '1.0.188',\n isDirect: true,\n });\n\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const comp = sbom.components.find((c) => c.name === 'serde');\n expect(comp?.purl).toBe('pkg:cargo/serde@1.0.188');\n });\n\n it('maps maven ecosystem correctly in purl', () => {\n insertDependency(db, {\n nodeId: 'dep:maven:org.apache.logging.log4j/log4j-core',\n ecosystem: 'maven',\n packageName: 'org.apache.logging.log4j/log4j-core',\n declaredConstraint: '2.17.0',\n resolvedVersion: '2.17.0',\n isDirect: true,\n });\n\n const exporter = new SBOMExporter(db, projectRoot);\n const sbom = exporter.export();\n\n const comp = sbom.components.find(\n (c) => c.name === 'org.apache.logging.log4j/log4j-core',\n );\n expect(comp?.purl).toBe(\n 'pkg:maven/org.apache.logging.log4j/log4j-core@2.17.0',\n );\n });\n });\n\n describe('exportJSON', () => {\n it('returns a pretty-printed JSON string', () => {\n insertDependency(db, {\n nodeId: 'dep:npm:express',\n ecosystem: 'npm',\n packageName: 'express',\n declaredConstraint: '^4.18.0',\n resolvedVersion: '4.18.2',\n isDirect: true,\n });\n\n const exporter = new SBOMExporter(db, projectRoot);\n const json = exporter.exportJSON();\n\n // Should be valid JSON\n const parsed = JSON.parse(json);\n expect(parsed.bomFormat).toBe('CycloneDX');\n expect(parsed.specVersion).toBe('1.5');\n\n // Should be pretty-printed (contains newlines and indentation)\n expect(json).toContain('\\n');\n expect(json).toContain(' ');\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|