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.
- package/LICENSE +1 -1
- package/README.md +103 -1822
- 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/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/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 +382 -101
- 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 +12 -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 +9 -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 +407 -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 +72 -6
- package/dist/bin/kirograph.js.map +3 -3
- package/dist/compression/tracker.js +80 -0
- package/dist/compression/tracker.js.map +2 -2
- package/dist/config.js +90 -3
- package/dist/config.js.map +2 -2
- package/dist/core/pipeline.js +36 -2
- package/dist/core/pipeline.js.map +2 -2
- package/dist/db/database.js +56 -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 +21 -1
- package/dist/mcp/tool-names.js.map +2 -2
- package/dist/mcp/tools.js +1160 -1
- 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 +2 -1
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var pipeline_exports = {};
|
|
20
|
+
__export(pipeline_exports, {
|
|
21
|
+
SecurityPipeline: () => SecurityPipeline
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(pipeline_exports);
|
|
24
|
+
var import_parser = require("./manifest/parser");
|
|
25
|
+
var import_integrator = require("./integrator");
|
|
26
|
+
var import_client = require("./vuln/client");
|
|
27
|
+
var import_osv_adapter = require("./vuln/osv-adapter");
|
|
28
|
+
var import_reachability = require("./reachability");
|
|
29
|
+
var import_errors = require("../errors");
|
|
30
|
+
class SecurityPipeline {
|
|
31
|
+
constructor(db, config, projectRoot) {
|
|
32
|
+
this.db = db;
|
|
33
|
+
this.config = config;
|
|
34
|
+
this.projectRoot = projectRoot;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Run the full security analysis pipeline.
|
|
38
|
+
*
|
|
39
|
+
* @param onProgress - Optional callback reporting (phase, current, total)
|
|
40
|
+
* @returns SecurityResult with counts and duration
|
|
41
|
+
*/
|
|
42
|
+
async run(onProgress) {
|
|
43
|
+
const start = Date.now();
|
|
44
|
+
const result = {
|
|
45
|
+
manifestsDiscovered: 0,
|
|
46
|
+
dependenciesCreated: 0,
|
|
47
|
+
vulnerabilitiesFound: 0,
|
|
48
|
+
affectedCount: 0,
|
|
49
|
+
notAffectedCount: 0,
|
|
50
|
+
underInvestigationCount: 0,
|
|
51
|
+
duration: 0
|
|
52
|
+
};
|
|
53
|
+
onProgress?.("manifest-parsing", 0, 4);
|
|
54
|
+
const parser = new import_parser.ManifestParser(this.db, this.projectRoot);
|
|
55
|
+
const parseResult = await parser.parseAll();
|
|
56
|
+
result.manifestsDiscovered = parseResult.manifestsParsed;
|
|
57
|
+
result.dependenciesCreated = parseResult.dependenciesCreated;
|
|
58
|
+
onProgress?.("manifest-parsing", 1, 4);
|
|
59
|
+
onProgress?.("dependency-integration", 1, 4);
|
|
60
|
+
const integrator = new import_integrator.DependencyGraphIntegrator(this.db, this.projectRoot);
|
|
61
|
+
await integrator.integrate();
|
|
62
|
+
await integrator.resolveTransitives(10);
|
|
63
|
+
await integrator.cleanup();
|
|
64
|
+
onProgress?.("dependency-integration", 2, 4);
|
|
65
|
+
if (this.config.securityAutoEnrich) {
|
|
66
|
+
onProgress?.("vulnerability-enrichment", 2, 4);
|
|
67
|
+
const adapters = this.createVulnAdapters();
|
|
68
|
+
const vulnClient = new import_client.VulnerabilityDatabaseClient(adapters, this.db);
|
|
69
|
+
const enrichResult = await vulnClient.enrichAll();
|
|
70
|
+
result.vulnerabilitiesFound = enrichResult.vulnerabilitiesFound;
|
|
71
|
+
onProgress?.("vulnerability-enrichment", 3, 4);
|
|
72
|
+
} else {
|
|
73
|
+
onProgress?.("vulnerability-enrichment", 3, 4);
|
|
74
|
+
}
|
|
75
|
+
onProgress?.("reachability-analysis", 3, 4);
|
|
76
|
+
const reachability = new import_reachability.ReachabilityAnalyzer(this.db, this.config);
|
|
77
|
+
const reachResults = await reachability.analyzeAll();
|
|
78
|
+
for (const [, reachResult] of reachResults) {
|
|
79
|
+
switch (reachResult.verdict) {
|
|
80
|
+
case "affected":
|
|
81
|
+
result.affectedCount++;
|
|
82
|
+
break;
|
|
83
|
+
case "not_affected":
|
|
84
|
+
result.notAffectedCount++;
|
|
85
|
+
break;
|
|
86
|
+
case "under_investigation":
|
|
87
|
+
result.underInvestigationCount++;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const [vulnNodeId, reachResult] of reachResults) {
|
|
92
|
+
if (reachResult.verdict === "affected") {
|
|
93
|
+
await reachability.getImpactSummary(vulnNodeId);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
onProgress?.("reachability-analysis", 4, 4);
|
|
97
|
+
result.duration = Date.now() - start;
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create vulnerability database adapters based on config.
|
|
102
|
+
*/
|
|
103
|
+
createVulnAdapters() {
|
|
104
|
+
const adapters = [];
|
|
105
|
+
for (const dbName of this.config.securityDatabases) {
|
|
106
|
+
switch (dbName.toUpperCase()) {
|
|
107
|
+
case "OSV":
|
|
108
|
+
adapters.push(new import_osv_adapter.OsvAdapter());
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
(0, import_errors.logWarn)(`[sec:pipeline] Unknown vulnerability database "${dbName}", skipping`);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (adapters.length === 0) {
|
|
116
|
+
adapters.push(new import_osv_adapter.OsvAdapter());
|
|
117
|
+
}
|
|
118
|
+
return adapters;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
122
|
+
0 && (module.exports = {
|
|
123
|
+
SecurityPipeline
|
|
124
|
+
});
|
|
125
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/security/pipeline.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * SecurityPipeline \u2014 Orchestrates the security analysis phases.\n *\n * Phases (in order):\n * 1. Manifest parsing \u2014 discover and parse dependency manifests\n * 2. Dependency integration \u2014 link dependencies to code symbols\n * 3. Vulnerability enrichment \u2014 query databases for CVEs (if securityAutoEnrich)\n * 4. Reachability analysis \u2014 determine if vulnerable code is reachable\n *\n * Reports progress via callback and returns a SecurityResult with counts and duration.\n *\n * Requirements: 9.3, 9.5\n */\n\nimport type { GraphDatabase } from '../db/database';\nimport type { KiroGraphConfig } from '../config';\nimport type { SecurityResult } from './types';\nimport { ManifestParser } from './manifest/parser';\nimport { DependencyGraphIntegrator } from './integrator';\nimport { VulnerabilityDatabaseClient } from './vuln/client';\nimport { OsvAdapter } from './vuln/osv-adapter';\nimport { ReachabilityAnalyzer } from './reachability';\nimport { logWarn } from '../errors';\n\n/**\n * SecurityPipeline orchestrates the full security analysis workflow.\n */\nexport class SecurityPipeline {\n private readonly db: GraphDatabase;\n private readonly config: KiroGraphConfig;\n private readonly projectRoot: string;\n\n constructor(db: GraphDatabase, config: KiroGraphConfig, projectRoot: string) {\n this.db = db;\n this.config = config;\n this.projectRoot = projectRoot;\n }\n\n /**\n * Run the full security analysis pipeline.\n *\n * @param onProgress - Optional callback reporting (phase, current, total)\n * @returns SecurityResult with counts and duration\n */\n async run(\n onProgress?: (phase: string, current: number, total: number) => void,\n ): Promise<SecurityResult> {\n const start = Date.now();\n\n const result: SecurityResult = {\n manifestsDiscovered: 0,\n dependenciesCreated: 0,\n vulnerabilitiesFound: 0,\n affectedCount: 0,\n notAffectedCount: 0,\n underInvestigationCount: 0,\n duration: 0,\n };\n\n // \u2500\u2500 Phase 1: Manifest Parsing \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 onProgress?.('manifest-parsing', 0, 4);\n\n const parser = new ManifestParser(this.db, this.projectRoot);\n const parseResult = await parser.parseAll();\n\n result.manifestsDiscovered = parseResult.manifestsParsed;\n result.dependenciesCreated = parseResult.dependenciesCreated;\n\n onProgress?.('manifest-parsing', 1, 4);\n\n // \u2500\u2500 Phase 2: Dependency Integration \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 onProgress?.('dependency-integration', 1, 4);\n\n const integrator = new DependencyGraphIntegrator(this.db, this.projectRoot);\n await integrator.integrate();\n await integrator.resolveTransitives(10);\n await integrator.cleanup();\n\n onProgress?.('dependency-integration', 2, 4);\n\n // \u2500\u2500 Phase 3: Vulnerability Enrichment (conditional) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (this.config.securityAutoEnrich) {\n onProgress?.('vulnerability-enrichment', 2, 4);\n\n const adapters = this.createVulnAdapters();\n const vulnClient = new VulnerabilityDatabaseClient(adapters, this.db);\n const enrichResult = await vulnClient.enrichAll();\n\n result.vulnerabilitiesFound = enrichResult.vulnerabilitiesFound;\n\n onProgress?.('vulnerability-enrichment', 3, 4);\n } else {\n onProgress?.('vulnerability-enrichment', 3, 4);\n }\n\n // \u2500\u2500 Phase 4: Reachability Analysis \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 onProgress?.('reachability-analysis', 3, 4);\n\n const reachability = new ReachabilityAnalyzer(this.db, this.config);\n const reachResults = await reachability.analyzeAll();\n\n // Count verdicts\n for (const [, reachResult] of reachResults) {\n switch (reachResult.verdict) {\n case 'affected':\n result.affectedCount++;\n break;\n case 'not_affected':\n result.notAffectedCount++;\n break;\n case 'under_investigation':\n result.underInvestigationCount++;\n break;\n }\n }\n\n // Generate impact summaries for affected vulnerabilities\n for (const [vulnNodeId, reachResult] of reachResults) {\n if (reachResult.verdict === 'affected') {\n await reachability.getImpactSummary(vulnNodeId);\n }\n }\n\n onProgress?.('reachability-analysis', 4, 4);\n\n result.duration = Date.now() - start;\n return result;\n }\n\n /**\n * Create vulnerability database adapters based on config.\n */\n private createVulnAdapters(): import('./vuln/types').VulnDatabaseAdapter[] {\n const adapters: import('./vuln/types').VulnDatabaseAdapter[] = [];\n\n for (const dbName of this.config.securityDatabases) {\n switch (dbName.toUpperCase()) {\n case 'OSV':\n adapters.push(new OsvAdapter());\n break;\n default:\n logWarn(`[sec:pipeline] Unknown vulnerability database \"${dbName}\", skipping`);\n break;\n }\n }\n\n // Fallback to OSV if no valid adapters configured\n if (adapters.length === 0) {\n adapters.push(new OsvAdapter());\n }\n\n return adapters;\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBA,oBAA+B;AAC/B,wBAA0C;AAC1C,oBAA4C;AAC5C,yBAA2B;AAC3B,0BAAqC;AACrC,oBAAwB;AAKjB,MAAM,iBAAiB;AAAA,EAK5B,YAAY,IAAmB,QAAyB,aAAqB;AAC3E,SAAK,KAAK;AACV,SAAK,SAAS;AACd,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,IACJ,YACyB;AACzB,UAAM,QAAQ,KAAK,IAAI;AAEvB,UAAM,SAAyB;AAAA,MAC7B,qBAAqB;AAAA,MACrB,qBAAqB;AAAA,MACrB,sBAAsB;AAAA,MACtB,eAAe;AAAA,MACf,kBAAkB;AAAA,MAClB,yBAAyB;AAAA,MACzB,UAAU;AAAA,IACZ;AAGA,iBAAa,oBAAoB,GAAG,CAAC;AAErC,UAAM,SAAS,IAAI,6BAAe,KAAK,IAAI,KAAK,WAAW;AAC3D,UAAM,cAAc,MAAM,OAAO,SAAS;AAE1C,WAAO,sBAAsB,YAAY;AACzC,WAAO,sBAAsB,YAAY;AAEzC,iBAAa,oBAAoB,GAAG,CAAC;AAGrC,iBAAa,0BAA0B,GAAG,CAAC;AAE3C,UAAM,aAAa,IAAI,4CAA0B,KAAK,IAAI,KAAK,WAAW;AAC1E,UAAM,WAAW,UAAU;AAC3B,UAAM,WAAW,mBAAmB,EAAE;AACtC,UAAM,WAAW,QAAQ;AAEzB,iBAAa,0BAA0B,GAAG,CAAC;AAG3C,QAAI,KAAK,OAAO,oBAAoB;AAClC,mBAAa,4BAA4B,GAAG,CAAC;AAE7C,YAAM,WAAW,KAAK,mBAAmB;AACzC,YAAM,aAAa,IAAI,0CAA4B,UAAU,KAAK,EAAE;AACpE,YAAM,eAAe,MAAM,WAAW,UAAU;AAEhD,aAAO,uBAAuB,aAAa;AAE3C,mBAAa,4BAA4B,GAAG,CAAC;AAAA,IAC/C,OAAO;AACL,mBAAa,4BAA4B,GAAG,CAAC;AAAA,IAC/C;AAGA,iBAAa,yBAAyB,GAAG,CAAC;AAE1C,UAAM,eAAe,IAAI,yCAAqB,KAAK,IAAI,KAAK,MAAM;AAClE,UAAM,eAAe,MAAM,aAAa,WAAW;AAGnD,eAAW,CAAC,EAAE,WAAW,KAAK,cAAc;AAC1C,cAAQ,YAAY,SAAS;AAAA,QAC3B,KAAK;AACH,iBAAO;AACP;AAAA,QACF,KAAK;AACH,iBAAO;AACP;AAAA,QACF,KAAK;AACH,iBAAO;AACP;AAAA,MACJ;AAAA,IACF;AAGA,eAAW,CAAC,YAAY,WAAW,KAAK,cAAc;AACpD,UAAI,YAAY,YAAY,YAAY;AACtC,cAAM,aAAa,iBAAiB,UAAU;AAAA,MAChD;AAAA,IACF;AAEA,iBAAa,yBAAyB,GAAG,CAAC;AAE1C,WAAO,WAAW,KAAK,IAAI,IAAI;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,qBAAmE;AACzE,UAAM,WAAyD,CAAC;AAEhE,eAAW,UAAU,KAAK,OAAO,mBAAmB;AAClD,cAAQ,OAAO,YAAY,GAAG;AAAA,QAC5B,KAAK;AACH,mBAAS,KAAK,IAAI,8BAAW,CAAC;AAC9B;AAAA,QACF;AACE,qCAAQ,kDAAkD,MAAM,aAAa;AAC7E;AAAA,MACJ;AAAA,IACF;AAGA,QAAI,SAAS,WAAW,GAAG;AACzB,eAAS,KAAK,IAAI,8BAAW,CAAC;AAAA,IAChC;AAEA,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var reachability_exports = {};
|
|
20
|
+
__export(reachability_exports, {
|
|
21
|
+
ReachabilityAnalyzer: () => ReachabilityAnalyzer
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(reachability_exports);
|
|
24
|
+
const TRAVERSAL_EDGE_KINDS = ["calls", "imports", "references"];
|
|
25
|
+
const MAX_UNRESOLVED_SYMBOLS = 50;
|
|
26
|
+
const MAX_DISTINCT_PATHS = 100;
|
|
27
|
+
class ReachabilityAnalyzer {
|
|
28
|
+
constructor(db, config) {
|
|
29
|
+
this.db = db;
|
|
30
|
+
this.config = config;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Analyze reachability for a single Vulnerability_Node.
|
|
34
|
+
*
|
|
35
|
+
* 1. Find the Dependency_Node linked via `has_vulnerability` edge
|
|
36
|
+
* 2. Find all Entry_Points in the graph
|
|
37
|
+
* 3. BFS from each entry point toward the dependency node
|
|
38
|
+
* 4. Assign verdict based on path existence and unresolved imports
|
|
39
|
+
*
|
|
40
|
+
* Requirements: 4.1, 4.2, 4.3, 4.4, 4.6
|
|
41
|
+
*/
|
|
42
|
+
async analyze(vulnerabilityNodeId) {
|
|
43
|
+
const rawDb = this.db.getRawDb();
|
|
44
|
+
const depEdge = rawDb.get(
|
|
45
|
+
`SELECT source FROM edges WHERE target = ? AND kind = 'has_vulnerability'`,
|
|
46
|
+
[vulnerabilityNodeId]
|
|
47
|
+
);
|
|
48
|
+
if (!depEdge) {
|
|
49
|
+
return {
|
|
50
|
+
verdict: "under_investigation",
|
|
51
|
+
paths: [],
|
|
52
|
+
unresolvedSymbols: [],
|
|
53
|
+
reachingEntryPointCount: 0
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const dependencyNodeId = depEdge.source;
|
|
57
|
+
const entryPoints = rawDb.all(
|
|
58
|
+
`SELECT id FROM nodes WHERE kind = 'route'
|
|
59
|
+
UNION
|
|
60
|
+
SELECT id FROM nodes WHERE kind = 'function' AND is_exported = 1`
|
|
61
|
+
);
|
|
62
|
+
if (entryPoints.length === 0) {
|
|
63
|
+
return {
|
|
64
|
+
verdict: "not_affected",
|
|
65
|
+
paths: [],
|
|
66
|
+
unresolvedSymbols: [],
|
|
67
|
+
reachingEntryPointCount: 0
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const reachingPaths = [];
|
|
71
|
+
const allUnresolvedSymbols = /* @__PURE__ */ new Set();
|
|
72
|
+
for (const ep of entryPoints) {
|
|
73
|
+
const result2 = this.bfsFromEntryPoint(rawDb, ep.id, dependencyNodeId);
|
|
74
|
+
if (result2.path) {
|
|
75
|
+
reachingPaths.push({
|
|
76
|
+
entryPoint: ep.id,
|
|
77
|
+
path: result2.path
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
for (const sym of result2.unresolvedSymbols) {
|
|
81
|
+
allUnresolvedSymbols.add(sym);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const unresolvedSymbols = Array.from(allUnresolvedSymbols).slice(0, MAX_UNRESOLVED_SYMBOLS);
|
|
85
|
+
let verdict;
|
|
86
|
+
if (reachingPaths.length > 0) {
|
|
87
|
+
verdict = "affected";
|
|
88
|
+
} else if (allUnresolvedSymbols.size > 0) {
|
|
89
|
+
verdict = "under_investigation";
|
|
90
|
+
} else {
|
|
91
|
+
verdict = "not_affected";
|
|
92
|
+
}
|
|
93
|
+
const result = {
|
|
94
|
+
verdict,
|
|
95
|
+
paths: reachingPaths,
|
|
96
|
+
unresolvedSymbols: verdict === "under_investigation" ? unresolvedSymbols : [],
|
|
97
|
+
reachingEntryPointCount: reachingPaths.length
|
|
98
|
+
};
|
|
99
|
+
this.storeReachabilityResult(rawDb, vulnerabilityNodeId, result);
|
|
100
|
+
return result;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Produce impact summary for an affected vulnerability.
|
|
104
|
+
*
|
|
105
|
+
* Returns null if the vulnerability is not affected.
|
|
106
|
+
* Reads layer assignments from `arch_file_layers` table.
|
|
107
|
+
*
|
|
108
|
+
* Requirements: 5.1, 5.2, 5.3, 5.4, 5.5
|
|
109
|
+
*/
|
|
110
|
+
async getImpactSummary(vulnerabilityNodeId) {
|
|
111
|
+
const rawDb = this.db.getRawDb();
|
|
112
|
+
const reachRow = rawDb.get(
|
|
113
|
+
`SELECT verdict, paths, reaching_entry_point_count FROM sec_reachability WHERE vulnerability_node_id = ?`,
|
|
114
|
+
[vulnerabilityNodeId]
|
|
115
|
+
);
|
|
116
|
+
if (!reachRow || reachRow.verdict !== "affected") {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const paths = reachRow.paths ? JSON.parse(reachRow.paths) : [];
|
|
120
|
+
if (paths.length === 0) {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const allNodeIds = /* @__PURE__ */ new Set();
|
|
124
|
+
const affectedEntryPoints = [];
|
|
125
|
+
for (const p of paths) {
|
|
126
|
+
affectedEntryPoints.push(p.entryPoint);
|
|
127
|
+
for (const nodeId of p.path) {
|
|
128
|
+
allNodeIds.add(nodeId);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
const affectedLayers = /* @__PURE__ */ new Set();
|
|
132
|
+
if (this.config.enableArchitecture) {
|
|
133
|
+
const nodeIds = Array.from(allNodeIds);
|
|
134
|
+
if (nodeIds.length > 0) {
|
|
135
|
+
const placeholders = nodeIds.map(() => "?").join(",");
|
|
136
|
+
const nodeFiles = rawDb.all(
|
|
137
|
+
`SELECT id, file_path FROM nodes WHERE id IN (${placeholders})`,
|
|
138
|
+
nodeIds
|
|
139
|
+
);
|
|
140
|
+
const filePaths = [...new Set(nodeFiles.map((n) => n.file_path).filter(Boolean))];
|
|
141
|
+
if (filePaths.length > 0) {
|
|
142
|
+
const filePlaceholders = filePaths.map(() => "?").join(",");
|
|
143
|
+
const layerRows = rawDb.all(
|
|
144
|
+
`SELECT file_path, layer_id FROM arch_file_layers WHERE file_path IN (${filePlaceholders})`,
|
|
145
|
+
filePaths
|
|
146
|
+
);
|
|
147
|
+
const fileToLayer = /* @__PURE__ */ new Map();
|
|
148
|
+
for (const row of layerRows) {
|
|
149
|
+
fileToLayer.set(row.file_path, row.layer_id);
|
|
150
|
+
}
|
|
151
|
+
for (const nf of nodeFiles) {
|
|
152
|
+
if (nf.file_path) {
|
|
153
|
+
const layer = fileToLayer.get(nf.file_path);
|
|
154
|
+
affectedLayers.add(layer ?? "unclassified");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const distinctPathSet = /* @__PURE__ */ new Set();
|
|
161
|
+
for (const p of paths) {
|
|
162
|
+
const pathKey = p.path.join("\u2192");
|
|
163
|
+
distinctPathSet.add(pathKey);
|
|
164
|
+
if (distinctPathSet.size >= MAX_DISTINCT_PATHS) break;
|
|
165
|
+
}
|
|
166
|
+
const summary = {
|
|
167
|
+
affectedLayers: Array.from(affectedLayers),
|
|
168
|
+
affectedEntryPoints,
|
|
169
|
+
distinctPathCount: Math.min(distinctPathSet.size, MAX_DISTINCT_PATHS)
|
|
170
|
+
};
|
|
171
|
+
this.storeImpactSummary(rawDb, vulnerabilityNodeId, summary);
|
|
172
|
+
return summary;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Analyze all Vulnerability_Nodes in the graph.
|
|
176
|
+
*
|
|
177
|
+
* Iterates all vulnerability nodes and calls analyze() for each.
|
|
178
|
+
*/
|
|
179
|
+
async analyzeAll() {
|
|
180
|
+
const rawDb = this.db.getRawDb();
|
|
181
|
+
const results = /* @__PURE__ */ new Map();
|
|
182
|
+
const vulnRows = rawDb.all(
|
|
183
|
+
`SELECT node_id FROM sec_vulnerabilities`
|
|
184
|
+
);
|
|
185
|
+
for (const row of vulnRows) {
|
|
186
|
+
const result = await this.analyze(row.node_id);
|
|
187
|
+
results.set(row.node_id, result);
|
|
188
|
+
}
|
|
189
|
+
return results;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* BFS from a single entry point toward the dependency node.
|
|
193
|
+
*
|
|
194
|
+
* Uses outgoing edges of types: calls, imports, references.
|
|
195
|
+
* Visits each node at most once (handles cycles).
|
|
196
|
+
* Tracks unresolved imports encountered during traversal.
|
|
197
|
+
*
|
|
198
|
+
* Returns the shortest path if found, plus any unresolved symbols encountered.
|
|
199
|
+
*/
|
|
200
|
+
bfsFromEntryPoint(rawDb, entryPointId, dependencyNodeId) {
|
|
201
|
+
const visited = /* @__PURE__ */ new Set();
|
|
202
|
+
const unresolvedSymbols = [];
|
|
203
|
+
const queue = [[entryPointId, [entryPointId]]];
|
|
204
|
+
visited.add(entryPointId);
|
|
205
|
+
const edgeKinds = TRAVERSAL_EDGE_KINDS.map((k) => `'${k}'`).join(",");
|
|
206
|
+
while (queue.length > 0) {
|
|
207
|
+
const [currentId, currentPath] = queue.shift();
|
|
208
|
+
const outEdges = rawDb.all(
|
|
209
|
+
`SELECT target, kind FROM edges WHERE source = ? AND kind IN (${edgeKinds})`,
|
|
210
|
+
[currentId]
|
|
211
|
+
);
|
|
212
|
+
if (outEdges.length === 0) {
|
|
213
|
+
const nodeRow = rawDb.get(
|
|
214
|
+
`SELECT kind FROM nodes WHERE id = ?`,
|
|
215
|
+
[currentId]
|
|
216
|
+
);
|
|
217
|
+
if (nodeRow && nodeRow.kind === "import") {
|
|
218
|
+
unresolvedSymbols.push(currentId);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
for (const edge of outEdges) {
|
|
222
|
+
const targetId = edge.target;
|
|
223
|
+
if (targetId === dependencyNodeId) {
|
|
224
|
+
return {
|
|
225
|
+
path: [...currentPath, targetId],
|
|
226
|
+
unresolvedSymbols
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
if (visited.has(targetId)) continue;
|
|
230
|
+
visited.add(targetId);
|
|
231
|
+
queue.push([targetId, [...currentPath, targetId]]);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return { path: null, unresolvedSymbols };
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Store reachability result in sec_reachability table.
|
|
238
|
+
*/
|
|
239
|
+
storeReachabilityResult(rawDb, vulnerabilityNodeId, result) {
|
|
240
|
+
rawDb.run(
|
|
241
|
+
`INSERT OR REPLACE INTO sec_reachability
|
|
242
|
+
(vulnerability_node_id, verdict, paths, unresolved_symbols, reaching_entry_point_count, analyzed_at)
|
|
243
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
244
|
+
[
|
|
245
|
+
vulnerabilityNodeId,
|
|
246
|
+
result.verdict,
|
|
247
|
+
result.paths.length > 0 ? JSON.stringify(result.paths) : null,
|
|
248
|
+
result.unresolvedSymbols.length > 0 ? JSON.stringify(result.unresolvedSymbols) : null,
|
|
249
|
+
result.reachingEntryPointCount,
|
|
250
|
+
Date.now()
|
|
251
|
+
]
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Store impact summary in sec_impact table.
|
|
256
|
+
*/
|
|
257
|
+
storeImpactSummary(rawDb, vulnerabilityNodeId, summary) {
|
|
258
|
+
rawDb.run(
|
|
259
|
+
`INSERT OR REPLACE INTO sec_impact
|
|
260
|
+
(vulnerability_node_id, affected_layers, affected_entry_points, distinct_path_count, analyzed_at)
|
|
261
|
+
VALUES (?, ?, ?, ?, ?)`,
|
|
262
|
+
[
|
|
263
|
+
vulnerabilityNodeId,
|
|
264
|
+
JSON.stringify(summary.affectedLayers),
|
|
265
|
+
JSON.stringify(summary.affectedEntryPoints),
|
|
266
|
+
summary.distinctPathCount,
|
|
267
|
+
Date.now()
|
|
268
|
+
]
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
273
|
+
0 && (module.exports = {
|
|
274
|
+
ReachabilityAnalyzer
|
|
275
|
+
});
|
|
276
|
+
//# sourceMappingURL=reachability.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/security/reachability.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph-Sec Reachability Analyzer\n *\n * Traverses the knowledge graph from application entry points to determine\n * whether vulnerable dependencies are reachable through actual code paths.\n * Produces reachability verdicts and impact summaries enriched with\n * architectural layer context.\n *\n * Requirements: 4.1, 4.2, 4.3, 4.4, 4.6, 5.1, 5.2, 5.3, 5.4, 5.5\n */\n\nimport type { GraphDatabase } from '../db/database';\nimport type { KiroGraphConfig } from '../config';\nimport type {\n ReachabilityVerdict,\n ReachabilityPath,\n ReachabilityResult,\n ImpactSummary,\n} from './types';\n\n/** Edge kinds used for reachability traversal */\nconst TRAVERSAL_EDGE_KINDS = ['calls', 'imports', 'references'] as const;\n\n/** Maximum unresolved symbols to report */\nconst MAX_UNRESOLVED_SYMBOLS = 50;\n\n/** Maximum distinct paths to report in impact summary */\nconst MAX_DISTINCT_PATHS = 100;\n\n/**\n * ReachabilityAnalyzer traverses the knowledge graph from entry points\n * toward affected dependency nodes to determine reachability verdicts.\n */\nexport class ReachabilityAnalyzer {\n private readonly db: GraphDatabase;\n private readonly config: KiroGraphConfig;\n\n constructor(db: GraphDatabase, config: KiroGraphConfig) {\n this.db = db;\n this.config = config;\n }\n\n /**\n * Analyze reachability for a single Vulnerability_Node.\n *\n * 1. Find the Dependency_Node linked via `has_vulnerability` edge\n * 2. Find all Entry_Points in the graph\n * 3. BFS from each entry point toward the dependency node\n * 4. Assign verdict based on path existence and unresolved imports\n *\n * Requirements: 4.1, 4.2, 4.3, 4.4, 4.6\n */\n async analyze(vulnerabilityNodeId: string): Promise<ReachabilityResult> {\n const rawDb = this.db.getRawDb();\n\n // Step 1: Find the Dependency_Node linked to this vulnerability\n const depEdge = rawDb.get(\n `SELECT source FROM edges WHERE target = ? AND kind = 'has_vulnerability'`,\n [vulnerabilityNodeId],\n );\n\n if (!depEdge) {\n // No dependency linked \u2014 cannot determine reachability\n return {\n verdict: 'under_investigation',\n paths: [],\n unresolvedSymbols: [],\n reachingEntryPointCount: 0,\n };\n }\n\n const dependencyNodeId: string = depEdge.source;\n\n // Step 2: Find all Entry_Points\n // Entry points are: nodes with kind='route' OR nodes with kind='function' that are exported\n const entryPoints: Array<{ id: string }> = rawDb.all(\n `SELECT id FROM nodes WHERE kind = 'route'\n UNION\n SELECT id FROM nodes WHERE kind = 'function' AND is_exported = 1`,\n );\n\n if (entryPoints.length === 0) {\n // No entry points \u2014 cannot determine reachability\n return {\n verdict: 'not_affected',\n paths: [],\n unresolvedSymbols: [],\n reachingEntryPointCount: 0,\n };\n }\n\n // Step 3: BFS from each entry point toward the dependency node\n const reachingPaths: ReachabilityPath[] = [];\n const allUnresolvedSymbols = new Set<string>();\n\n for (const ep of entryPoints) {\n const result = this.bfsFromEntryPoint(rawDb, ep.id, dependencyNodeId);\n\n if (result.path) {\n reachingPaths.push({\n entryPoint: ep.id,\n path: result.path,\n });\n }\n\n for (const sym of result.unresolvedSymbols) {\n allUnresolvedSymbols.add(sym);\n }\n }\n\n // Step 4: Assign verdict\n const unresolvedSymbols = Array.from(allUnresolvedSymbols).slice(0, MAX_UNRESOLVED_SYMBOLS);\n\n let verdict: ReachabilityVerdict;\n if (reachingPaths.length > 0) {\n verdict = 'affected';\n } else if (allUnresolvedSymbols.size > 0) {\n verdict = 'under_investigation';\n } else {\n verdict = 'not_affected';\n }\n\n const result: ReachabilityResult = {\n verdict,\n paths: reachingPaths,\n unresolvedSymbols: verdict === 'under_investigation' ? unresolvedSymbols : [],\n reachingEntryPointCount: reachingPaths.length,\n };\n\n // Store result in sec_reachability table\n this.storeReachabilityResult(rawDb, vulnerabilityNodeId, result);\n\n return result;\n }\n\n /**\n * Produce impact summary for an affected vulnerability.\n *\n * Returns null if the vulnerability is not affected.\n * Reads layer assignments from `arch_file_layers` table.\n *\n * Requirements: 5.1, 5.2, 5.3, 5.4, 5.5\n */\n async getImpactSummary(vulnerabilityNodeId: string): Promise<ImpactSummary | null> {\n const rawDb = this.db.getRawDb();\n\n // Check if the vulnerability has verdict 'affected'\n const reachRow = rawDb.get(\n `SELECT verdict, paths, reaching_entry_point_count FROM sec_reachability WHERE vulnerability_node_id = ?`,\n [vulnerabilityNodeId],\n );\n\n if (!reachRow || reachRow.verdict !== 'affected') {\n return null;\n }\n\n const paths: ReachabilityPath[] = reachRow.paths ? JSON.parse(reachRow.paths) : [];\n\n if (paths.length === 0) {\n return null;\n }\n\n // Collect all node IDs on reachable paths\n const allNodeIds = new Set<string>();\n const affectedEntryPoints: string[] = [];\n\n for (const p of paths) {\n affectedEntryPoints.push(p.entryPoint);\n for (const nodeId of p.path) {\n allNodeIds.add(nodeId);\n }\n }\n\n // Identify architectural layers on reachable paths\n const affectedLayers = new Set<string>();\n\n if (this.config.enableArchitecture) {\n // Get file paths for all nodes on reachable paths\n const nodeIds = Array.from(allNodeIds);\n if (nodeIds.length > 0) {\n const placeholders = nodeIds.map(() => '?').join(',');\n const nodeFiles: Array<{ id: string; file_path: string }> = rawDb.all(\n `SELECT id, file_path FROM nodes WHERE id IN (${placeholders})`,\n nodeIds,\n );\n\n // Get layer assignments for those files from arch_file_layers\n const filePaths = [...new Set(nodeFiles.map(n => n.file_path).filter(Boolean))];\n if (filePaths.length > 0) {\n const filePlaceholders = filePaths.map(() => '?').join(',');\n const layerRows: Array<{ file_path: string; layer_id: string }> = rawDb.all(\n `SELECT file_path, layer_id FROM arch_file_layers WHERE file_path IN (${filePlaceholders})`,\n filePaths,\n );\n\n const fileToLayer = new Map<string, string>();\n for (const row of layerRows) {\n fileToLayer.set(row.file_path, row.layer_id);\n }\n\n // Assign layers to nodes; classify as \"unclassified\" if no layer assigned\n for (const nf of nodeFiles) {\n if (nf.file_path) {\n const layer = fileToLayer.get(nf.file_path);\n affectedLayers.add(layer ?? 'unclassified');\n }\n }\n }\n }\n }\n // When enableArchitecture is false, omit layer classification (Requirement 5.4)\n\n // Count distinct paths (capped at MAX_DISTINCT_PATHS)\n // Two paths are distinct if they differ by at least one intermediate symbol\n const distinctPathSet = new Set<string>();\n for (const p of paths) {\n // Use the full path as a key for distinctness\n const pathKey = p.path.join('\u2192');\n distinctPathSet.add(pathKey);\n if (distinctPathSet.size >= MAX_DISTINCT_PATHS) break;\n }\n\n const summary: ImpactSummary = {\n affectedLayers: Array.from(affectedLayers),\n affectedEntryPoints,\n distinctPathCount: Math.min(distinctPathSet.size, MAX_DISTINCT_PATHS),\n };\n\n // Store impact summary in sec_impact table\n this.storeImpactSummary(rawDb, vulnerabilityNodeId, summary);\n\n return summary;\n }\n\n /**\n * Analyze all Vulnerability_Nodes in the graph.\n *\n * Iterates all vulnerability nodes and calls analyze() for each.\n */\n async analyzeAll(): Promise<Map<string, ReachabilityResult>> {\n const rawDb = this.db.getRawDb();\n const results = new Map<string, ReachabilityResult>();\n\n const vulnRows: Array<{ node_id: string }> = rawDb.all(\n `SELECT node_id FROM sec_vulnerabilities`,\n );\n\n for (const row of vulnRows) {\n const result = await this.analyze(row.node_id);\n results.set(row.node_id, result);\n }\n\n return results;\n }\n\n /**\n * BFS from a single entry point toward the dependency node.\n *\n * Uses outgoing edges of types: calls, imports, references.\n * Visits each node at most once (handles cycles).\n * Tracks unresolved imports encountered during traversal.\n *\n * Returns the shortest path if found, plus any unresolved symbols encountered.\n */\n private bfsFromEntryPoint(\n rawDb: any,\n entryPointId: string,\n dependencyNodeId: string,\n ): { path: string[] | null; unresolvedSymbols: string[] } {\n const visited = new Set<string>();\n const unresolvedSymbols: string[] = [];\n\n // BFS queue: each entry is [nodeId, path from entry point]\n const queue: Array<[string, string[]]> = [[entryPointId, [entryPointId]]];\n visited.add(entryPointId);\n\n // Prepare edge kind filter\n const edgeKinds = TRAVERSAL_EDGE_KINDS.map(k => `'${k}'`).join(',');\n\n while (queue.length > 0) {\n const [currentId, currentPath] = queue.shift()!;\n\n // Get outgoing edges from current node (calls, imports, references)\n const outEdges: Array<{ target: string; kind: string }> = rawDb.all(\n `SELECT target, kind FROM edges WHERE source = ? AND kind IN (${edgeKinds})`,\n [currentId],\n );\n\n // Check if current node is an unresolved import\n // An unresolved import is a node with kind='import' that has no outgoing edges\n if (outEdges.length === 0) {\n const nodeRow = rawDb.get(\n `SELECT kind FROM nodes WHERE id = ?`,\n [currentId],\n );\n if (nodeRow && nodeRow.kind === 'import') {\n unresolvedSymbols.push(currentId);\n }\n }\n\n for (const edge of outEdges) {\n const targetId = edge.target;\n\n // Check if we reached the dependency node\n if (targetId === dependencyNodeId) {\n return {\n path: [...currentPath, targetId],\n unresolvedSymbols,\n };\n }\n\n // Skip already visited nodes (cycle handling)\n if (visited.has(targetId)) continue;\n visited.add(targetId);\n\n queue.push([targetId, [...currentPath, targetId]]);\n }\n }\n\n // No path found from this entry point\n return { path: null, unresolvedSymbols };\n }\n\n /**\n * Store reachability result in sec_reachability table.\n */\n private storeReachabilityResult(\n rawDb: any,\n vulnerabilityNodeId: string,\n result: ReachabilityResult,\n ): void {\n rawDb.run(\n `INSERT OR REPLACE INTO sec_reachability\n (vulnerability_node_id, verdict, paths, unresolved_symbols, reaching_entry_point_count, analyzed_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n [\n vulnerabilityNodeId,\n result.verdict,\n result.paths.length > 0 ? JSON.stringify(result.paths) : null,\n result.unresolvedSymbols.length > 0 ? JSON.stringify(result.unresolvedSymbols) : null,\n result.reachingEntryPointCount,\n Date.now(),\n ],\n );\n }\n\n /**\n * Store impact summary in sec_impact table.\n */\n private storeImpactSummary(\n rawDb: any,\n vulnerabilityNodeId: string,\n summary: ImpactSummary,\n ): void {\n rawDb.run(\n `INSERT OR REPLACE INTO sec_impact\n (vulnerability_node_id, affected_layers, affected_entry_points, distinct_path_count, analyzed_at)\n VALUES (?, ?, ?, ?, ?)`,\n [\n vulnerabilityNodeId,\n JSON.stringify(summary.affectedLayers),\n JSON.stringify(summary.affectedEntryPoints),\n summary.distinctPathCount,\n Date.now(),\n ],\n );\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["result"]
|
|
7
|
+
}
|