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,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 fs = __toESM(require("fs"));
|
|
26
|
+
var path = __toESM(require("path"));
|
|
27
|
+
var os = __toESM(require("os"));
|
|
28
|
+
var import_client = require("./client");
|
|
29
|
+
var import_database = require("../../db/database");
|
|
30
|
+
(0, import_vitest.describe)("VulnerabilityDatabaseClient", () => {
|
|
31
|
+
let db;
|
|
32
|
+
let tmpDir;
|
|
33
|
+
(0, import_vitest.beforeEach)(() => {
|
|
34
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "kirograph-vuln-client-test-"));
|
|
35
|
+
db = new import_database.GraphDatabase(tmpDir);
|
|
36
|
+
db.applySecuritySchema();
|
|
37
|
+
});
|
|
38
|
+
(0, import_vitest.afterEach)(() => {
|
|
39
|
+
db.close();
|
|
40
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
41
|
+
});
|
|
42
|
+
function createDependencyNode(name, ecosystem, version) {
|
|
43
|
+
const nodeId = `dep:${ecosystem}:${name}`;
|
|
44
|
+
const rawDb = db.getRawDb();
|
|
45
|
+
const node = {
|
|
46
|
+
id: nodeId,
|
|
47
|
+
kind: "dependency",
|
|
48
|
+
name,
|
|
49
|
+
qualifiedName: `${ecosystem}/${name}`,
|
|
50
|
+
filePath: "",
|
|
51
|
+
language: "unknown",
|
|
52
|
+
startLine: 0,
|
|
53
|
+
endLine: 0,
|
|
54
|
+
startColumn: 0,
|
|
55
|
+
endColumn: 0,
|
|
56
|
+
isExported: false,
|
|
57
|
+
isAsync: false,
|
|
58
|
+
isStatic: false,
|
|
59
|
+
isAbstract: false,
|
|
60
|
+
updatedAt: Date.now()
|
|
61
|
+
};
|
|
62
|
+
db.upsertNode(node);
|
|
63
|
+
rawDb.run(
|
|
64
|
+
`INSERT OR REPLACE INTO sec_dependencies
|
|
65
|
+
(node_id, ecosystem, package_name, declared_constraint, resolved_version, scope, source_manifests)
|
|
66
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`,
|
|
67
|
+
[nodeId, ecosystem, name, `^${version}`, version, "production", JSON.stringify(["package.json"])]
|
|
68
|
+
);
|
|
69
|
+
return nodeId;
|
|
70
|
+
}
|
|
71
|
+
function createMockAdapter(name, results) {
|
|
72
|
+
return {
|
|
73
|
+
name,
|
|
74
|
+
async query(ecosystem, packageName, version) {
|
|
75
|
+
const key = `${ecosystem}:${packageName}:${version}`;
|
|
76
|
+
return results.get(key) ?? [];
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function createFailingAdapter(name, errorMessage) {
|
|
81
|
+
return {
|
|
82
|
+
name,
|
|
83
|
+
async query() {
|
|
84
|
+
throw new Error(errorMessage);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
const sampleCve = {
|
|
89
|
+
id: "CVE-2023-12345",
|
|
90
|
+
severity: 7.5,
|
|
91
|
+
affectedVersionRanges: [{ introduced: "1.0.0", fixed: "1.2.0" }],
|
|
92
|
+
fixedVersion: "1.2.0",
|
|
93
|
+
summary: "A critical vulnerability in express"
|
|
94
|
+
};
|
|
95
|
+
const sampleCve2 = {
|
|
96
|
+
id: "CVE-2023-67890",
|
|
97
|
+
severity: 5,
|
|
98
|
+
affectedVersionRanges: [{ introduced: "0.9.0", fixed: "1.1.0" }],
|
|
99
|
+
fixedVersion: "1.1.0",
|
|
100
|
+
summary: "A moderate vulnerability"
|
|
101
|
+
};
|
|
102
|
+
(0, import_vitest.describe)("enrichAll()", () => {
|
|
103
|
+
(0, import_vitest.it)("should return zero counts when no dependencies exist", async () => {
|
|
104
|
+
const adapter = createMockAdapter("OSV", /* @__PURE__ */ new Map());
|
|
105
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter], db);
|
|
106
|
+
const result = await client.enrichAll();
|
|
107
|
+
(0, import_vitest.expect)(result.vulnerabilitiesFound).toBe(0);
|
|
108
|
+
(0, import_vitest.expect)(result.dependenciesChecked).toBe(0);
|
|
109
|
+
(0, import_vitest.expect)(result.errors).toHaveLength(0);
|
|
110
|
+
(0, import_vitest.expect)(result.staleNodes).toHaveLength(0);
|
|
111
|
+
});
|
|
112
|
+
(0, import_vitest.it)("should create Vulnerability_Nodes for matching CVEs", async () => {
|
|
113
|
+
const depNodeId = createDependencyNode("express", "npm", "1.0.5");
|
|
114
|
+
const results = /* @__PURE__ */ new Map();
|
|
115
|
+
results.set("npm:express:1.0.5", [sampleCve]);
|
|
116
|
+
const adapter = createMockAdapter("OSV", results);
|
|
117
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter], db);
|
|
118
|
+
const result = await client.enrichAll();
|
|
119
|
+
(0, import_vitest.expect)(result.vulnerabilitiesFound).toBe(1);
|
|
120
|
+
(0, import_vitest.expect)(result.dependenciesChecked).toBe(1);
|
|
121
|
+
const vulnNode = db.getNode("vuln:CVE-2023-12345");
|
|
122
|
+
(0, import_vitest.expect)(vulnNode).not.toBeNull();
|
|
123
|
+
(0, import_vitest.expect)(vulnNode.kind).toBe("vulnerability");
|
|
124
|
+
(0, import_vitest.expect)(vulnNode.name).toBe("CVE-2023-12345");
|
|
125
|
+
const rawDb = db.getRawDb();
|
|
126
|
+
const vulnRow = rawDb.get(
|
|
127
|
+
"SELECT * FROM sec_vulnerabilities WHERE cve_id = ?",
|
|
128
|
+
["CVE-2023-12345"]
|
|
129
|
+
);
|
|
130
|
+
(0, import_vitest.expect)(vulnRow).not.toBeNull();
|
|
131
|
+
(0, import_vitest.expect)(vulnRow.severity_score).toBe(7.5);
|
|
132
|
+
(0, import_vitest.expect)(vulnRow.fixed_version).toBe("1.2.0");
|
|
133
|
+
(0, import_vitest.expect)(vulnRow.source_database).toBe("OSV");
|
|
134
|
+
const edges = db.getEdgesForNodes([depNodeId]);
|
|
135
|
+
const vulnEdge = edges.find(
|
|
136
|
+
(e) => e.source === depNodeId && e.target === "vuln:CVE-2023-12345" && e.kind === "has_vulnerability"
|
|
137
|
+
);
|
|
138
|
+
(0, import_vitest.expect)(vulnEdge).toBeDefined();
|
|
139
|
+
});
|
|
140
|
+
(0, import_vitest.it)("should update last_vuln_check timestamp on queried dependencies", async () => {
|
|
141
|
+
createDependencyNode("express", "npm", "1.0.5");
|
|
142
|
+
const adapter = createMockAdapter("OSV", /* @__PURE__ */ new Map());
|
|
143
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter], db);
|
|
144
|
+
const before = Date.now();
|
|
145
|
+
await client.enrichAll();
|
|
146
|
+
const after = Date.now();
|
|
147
|
+
const rawDb = db.getRawDb();
|
|
148
|
+
const row = rawDb.get(
|
|
149
|
+
"SELECT last_vuln_check FROM sec_dependencies WHERE package_name = ?",
|
|
150
|
+
["express"]
|
|
151
|
+
);
|
|
152
|
+
(0, import_vitest.expect)(row.last_vuln_check).toBeGreaterThanOrEqual(before);
|
|
153
|
+
(0, import_vitest.expect)(row.last_vuln_check).toBeLessThanOrEqual(after);
|
|
154
|
+
});
|
|
155
|
+
(0, import_vitest.it)("should deduplicate CVEs by ID across multiple databases", async () => {
|
|
156
|
+
createDependencyNode("express", "npm", "1.0.5");
|
|
157
|
+
const results1 = /* @__PURE__ */ new Map();
|
|
158
|
+
results1.set("npm:express:1.0.5", [sampleCve]);
|
|
159
|
+
const results2 = /* @__PURE__ */ new Map();
|
|
160
|
+
results2.set("npm:express:1.0.5", [
|
|
161
|
+
{ ...sampleCve, summary: "Different summary from DB2" }
|
|
162
|
+
]);
|
|
163
|
+
const adapter1 = createMockAdapter("OSV", results1);
|
|
164
|
+
const adapter2 = createMockAdapter("NVD", results2);
|
|
165
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter1, adapter2], db);
|
|
166
|
+
const result = await client.enrichAll();
|
|
167
|
+
(0, import_vitest.expect)(result.vulnerabilitiesFound).toBe(1);
|
|
168
|
+
const rawDb = db.getRawDb();
|
|
169
|
+
const vulnRows = rawDb.all("SELECT * FROM sec_vulnerabilities WHERE cve_id = ?", ["CVE-2023-12345"]);
|
|
170
|
+
(0, import_vitest.expect)(vulnRows).toHaveLength(1);
|
|
171
|
+
});
|
|
172
|
+
(0, import_vitest.it)("should handle multiple unique CVEs from multiple databases", async () => {
|
|
173
|
+
createDependencyNode("express", "npm", "1.0.5");
|
|
174
|
+
const results1 = /* @__PURE__ */ new Map();
|
|
175
|
+
results1.set("npm:express:1.0.5", [sampleCve]);
|
|
176
|
+
const results2 = /* @__PURE__ */ new Map();
|
|
177
|
+
results2.set("npm:express:1.0.5", [sampleCve2]);
|
|
178
|
+
const adapter1 = createMockAdapter("OSV", results1);
|
|
179
|
+
const adapter2 = createMockAdapter("NVD", results2);
|
|
180
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter1, adapter2], db);
|
|
181
|
+
const result = await client.enrichAll();
|
|
182
|
+
(0, import_vitest.expect)(result.vulnerabilitiesFound).toBe(2);
|
|
183
|
+
(0, import_vitest.expect)(db.getNode("vuln:CVE-2023-12345")).not.toBeNull();
|
|
184
|
+
(0, import_vitest.expect)(db.getNode("vuln:CVE-2023-67890")).not.toBeNull();
|
|
185
|
+
});
|
|
186
|
+
(0, import_vitest.it)("should handle unreachable databases: set vulnDataStale flag", async () => {
|
|
187
|
+
const depNodeId = createDependencyNode("express", "npm", "1.0.5");
|
|
188
|
+
const failingAdapter = createFailingAdapter("OSV", "Network error");
|
|
189
|
+
const client = new import_client.VulnerabilityDatabaseClient([failingAdapter], db);
|
|
190
|
+
const result = await client.enrichAll();
|
|
191
|
+
(0, import_vitest.expect)(result.errors).toHaveLength(1);
|
|
192
|
+
(0, import_vitest.expect)(result.errors[0].dependency).toBe("express");
|
|
193
|
+
(0, import_vitest.expect)(result.errors[0].database).toBe("OSV");
|
|
194
|
+
(0, import_vitest.expect)(result.errors[0].error).toBe("Network error");
|
|
195
|
+
(0, import_vitest.expect)(result.staleNodes).toContain(depNodeId);
|
|
196
|
+
const rawDb = db.getRawDb();
|
|
197
|
+
const row = rawDb.get(
|
|
198
|
+
"SELECT vuln_data_stale, vuln_data_stale_since FROM sec_dependencies WHERE node_id = ?",
|
|
199
|
+
[depNodeId]
|
|
200
|
+
);
|
|
201
|
+
(0, import_vitest.expect)(row.vuln_data_stale).toBe(1);
|
|
202
|
+
(0, import_vitest.expect)(row.vuln_data_stale_since).toBeGreaterThan(0);
|
|
203
|
+
});
|
|
204
|
+
(0, import_vitest.it)("should continue processing other dependencies when one adapter fails", async () => {
|
|
205
|
+
createDependencyNode("express", "npm", "1.0.5");
|
|
206
|
+
createDependencyNode("lodash", "npm", "4.17.21");
|
|
207
|
+
const adapter = {
|
|
208
|
+
name: "OSV",
|
|
209
|
+
async query(_eco, pkg) {
|
|
210
|
+
if (pkg === "express") throw new Error("Timeout");
|
|
211
|
+
return [];
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter], db);
|
|
215
|
+
const result = await client.enrichAll();
|
|
216
|
+
(0, import_vitest.expect)(result.dependenciesChecked).toBe(2);
|
|
217
|
+
(0, import_vitest.expect)(result.errors).toHaveLength(1);
|
|
218
|
+
});
|
|
219
|
+
(0, import_vitest.it)("should truncate summary to 500 characters", async () => {
|
|
220
|
+
createDependencyNode("express", "npm", "1.0.5");
|
|
221
|
+
const longSummary = "A".repeat(600);
|
|
222
|
+
const cveWithLongSummary = {
|
|
223
|
+
...sampleCve,
|
|
224
|
+
summary: longSummary
|
|
225
|
+
};
|
|
226
|
+
const results = /* @__PURE__ */ new Map();
|
|
227
|
+
results.set("npm:express:1.0.5", [cveWithLongSummary]);
|
|
228
|
+
const adapter = createMockAdapter("OSV", results);
|
|
229
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter], db);
|
|
230
|
+
await client.enrichAll();
|
|
231
|
+
const rawDb = db.getRawDb();
|
|
232
|
+
const row = rawDb.get(
|
|
233
|
+
"SELECT summary FROM sec_vulnerabilities WHERE cve_id = ?",
|
|
234
|
+
["CVE-2023-12345"]
|
|
235
|
+
);
|
|
236
|
+
(0, import_vitest.expect)(row.summary).toHaveLength(500);
|
|
237
|
+
});
|
|
238
|
+
});
|
|
239
|
+
(0, import_vitest.describe)("enrichOne()", () => {
|
|
240
|
+
(0, import_vitest.it)("should return empty array for non-existent dependency", async () => {
|
|
241
|
+
const adapter = createMockAdapter("OSV", /* @__PURE__ */ new Map());
|
|
242
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter], db);
|
|
243
|
+
const result = await client.enrichOne("dep:npm:nonexistent");
|
|
244
|
+
(0, import_vitest.expect)(result).toHaveLength(0);
|
|
245
|
+
});
|
|
246
|
+
(0, import_vitest.it)("should enrich a single dependency and return CVE records", async () => {
|
|
247
|
+
const depNodeId = createDependencyNode("express", "npm", "1.0.5");
|
|
248
|
+
const results = /* @__PURE__ */ new Map();
|
|
249
|
+
results.set("npm:express:1.0.5", [sampleCve, sampleCve2]);
|
|
250
|
+
const adapter = createMockAdapter("OSV", results);
|
|
251
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter], db);
|
|
252
|
+
const cves = await client.enrichOne(depNodeId);
|
|
253
|
+
(0, import_vitest.expect)(cves).toHaveLength(2);
|
|
254
|
+
(0, import_vitest.expect)(cves[0].id).toBe("CVE-2023-12345");
|
|
255
|
+
(0, import_vitest.expect)(cves[1].id).toBe("CVE-2023-67890");
|
|
256
|
+
(0, import_vitest.expect)(db.getNode("vuln:CVE-2023-12345")).not.toBeNull();
|
|
257
|
+
(0, import_vitest.expect)(db.getNode("vuln:CVE-2023-67890")).not.toBeNull();
|
|
258
|
+
});
|
|
259
|
+
(0, import_vitest.it)("should update last_vuln_check for the single dependency", async () => {
|
|
260
|
+
const depNodeId = createDependencyNode("express", "npm", "1.0.5");
|
|
261
|
+
const adapter = createMockAdapter("OSV", /* @__PURE__ */ new Map());
|
|
262
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter], db);
|
|
263
|
+
const before = Date.now();
|
|
264
|
+
await client.enrichOne(depNodeId);
|
|
265
|
+
const after = Date.now();
|
|
266
|
+
const rawDb = db.getRawDb();
|
|
267
|
+
const row = rawDb.get(
|
|
268
|
+
"SELECT last_vuln_check FROM sec_dependencies WHERE node_id = ?",
|
|
269
|
+
[depNodeId]
|
|
270
|
+
);
|
|
271
|
+
(0, import_vitest.expect)(row.last_vuln_check).toBeGreaterThanOrEqual(before);
|
|
272
|
+
(0, import_vitest.expect)(row.last_vuln_check).toBeLessThanOrEqual(after);
|
|
273
|
+
});
|
|
274
|
+
(0, import_vitest.it)("should deduplicate CVEs when enriching a single dependency", async () => {
|
|
275
|
+
const depNodeId = createDependencyNode("express", "npm", "1.0.5");
|
|
276
|
+
const results1 = /* @__PURE__ */ new Map();
|
|
277
|
+
results1.set("npm:express:1.0.5", [sampleCve]);
|
|
278
|
+
const results2 = /* @__PURE__ */ new Map();
|
|
279
|
+
results2.set("npm:express:1.0.5", [sampleCve]);
|
|
280
|
+
const adapter1 = createMockAdapter("OSV", results1);
|
|
281
|
+
const adapter2 = createMockAdapter("NVD", results2);
|
|
282
|
+
const client = new import_client.VulnerabilityDatabaseClient([adapter1, adapter2], db);
|
|
283
|
+
const cves = await client.enrichOne(depNodeId);
|
|
284
|
+
(0, import_vitest.expect)(cves).toHaveLength(1);
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
//# sourceMappingURL=client.test.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/security/vuln/client.test.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Tests for VulnerabilityDatabaseClient\n *\n * Verifies:\n * - Querying configured databases and creating Vulnerability_Nodes\n * - Deduplication by CVE identifier across multiple databases\n * - Recording lastVulnCheck timestamp on queried Dependency_Nodes\n * - Handling unreachable databases: log error, set vulnDataStale flag\n * - enrichOne() for single dependency enrichment\n */\nimport { describe, it, expect, beforeEach, afterEach } from 'vitest';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { VulnerabilityDatabaseClient } from './client';\nimport type { VulnDatabaseAdapter } from './types';\nimport { GraphDatabase } from '../../db/database';\nimport type { Node, Edge } from '../../types';\nimport type { CVERecord } from '../types';\n\ndescribe('VulnerabilityDatabaseClient', () => {\n let db: GraphDatabase;\n let tmpDir: string;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kirograph-vuln-client-test-'));\n db = new GraphDatabase(tmpDir);\n db.applySecuritySchema();\n });\n\n afterEach(() => {\n db.close();\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n function createDependencyNode(\n name: string,\n ecosystem: string,\n version: string,\n ): string {\n const nodeId = `dep:${ecosystem}:${name}`;\n const rawDb = db.getRawDb();\n\n const node: Node = {\n id: nodeId,\n kind: 'dependency',\n name,\n qualifiedName: `${ecosystem}/${name}`,\n filePath: '',\n language: 'unknown',\n startLine: 0,\n endLine: 0,\n startColumn: 0,\n endColumn: 0,\n isExported: false,\n isAsync: false,\n isStatic: false,\n isAbstract: false,\n updatedAt: Date.now(),\n };\n\n db.upsertNode(node);\n\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 [nodeId, ecosystem, name, `^${version}`, version, 'production', JSON.stringify(['package.json'])],\n );\n\n return nodeId;\n }\n\n function createMockAdapter(\n name: string,\n results: Map<string, CVERecord[]>,\n ): VulnDatabaseAdapter {\n return {\n name,\n async query(ecosystem: string, packageName: string, version: string): Promise<CVERecord[]> {\n const key = `${ecosystem}:${packageName}:${version}`;\n return results.get(key) ?? [];\n },\n };\n }\n\n function createFailingAdapter(name: string, errorMessage: string): VulnDatabaseAdapter {\n return {\n name,\n async query(): Promise<CVERecord[]> {\n throw new Error(errorMessage);\n },\n };\n }\n\n const sampleCve: CVERecord = {\n id: 'CVE-2023-12345',\n severity: 7.5,\n affectedVersionRanges: [{ introduced: '1.0.0', fixed: '1.2.0' }],\n fixedVersion: '1.2.0',\n summary: 'A critical vulnerability in express',\n };\n\n const sampleCve2: CVERecord = {\n id: 'CVE-2023-67890',\n severity: 5.0,\n affectedVersionRanges: [{ introduced: '0.9.0', fixed: '1.1.0' }],\n fixedVersion: '1.1.0',\n summary: 'A moderate vulnerability',\n };\n\n describe('enrichAll()', () => {\n it('should return zero counts when no dependencies exist', async () => {\n const adapter = createMockAdapter('OSV', new Map());\n const client = new VulnerabilityDatabaseClient([adapter], db);\n\n const result = await client.enrichAll();\n\n expect(result.vulnerabilitiesFound).toBe(0);\n expect(result.dependenciesChecked).toBe(0);\n expect(result.errors).toHaveLength(0);\n expect(result.staleNodes).toHaveLength(0);\n });\n\n it('should create Vulnerability_Nodes for matching CVEs', async () => {\n const depNodeId = createDependencyNode('express', 'npm', '1.0.5');\n\n const results = new Map<string, CVERecord[]>();\n results.set('npm:express:1.0.5', [sampleCve]);\n\n const adapter = createMockAdapter('OSV', results);\n const client = new VulnerabilityDatabaseClient([adapter], db);\n\n const result = await client.enrichAll();\n\n expect(result.vulnerabilitiesFound).toBe(1);\n expect(result.dependenciesChecked).toBe(1);\n\n // Verify Vulnerability_Node was created\n const vulnNode = db.getNode('vuln:CVE-2023-12345');\n expect(vulnNode).not.toBeNull();\n expect(vulnNode!.kind).toBe('vulnerability');\n expect(vulnNode!.name).toBe('CVE-2023-12345');\n\n // Verify sec_vulnerabilities row\n const rawDb = db.getRawDb();\n const vulnRow = rawDb.get(\n 'SELECT * FROM sec_vulnerabilities WHERE cve_id = ?',\n ['CVE-2023-12345'],\n );\n expect(vulnRow).not.toBeNull();\n expect(vulnRow.severity_score).toBe(7.5);\n expect(vulnRow.fixed_version).toBe('1.2.0');\n expect(vulnRow.source_database).toBe('OSV');\n\n // Verify has_vulnerability edge\n const edges = db.getEdgesForNodes([depNodeId]);\n const vulnEdge = edges.find(\n e => e.source === depNodeId && e.target === 'vuln:CVE-2023-12345' && e.kind === 'has_vulnerability',\n );\n expect(vulnEdge).toBeDefined();\n });\n\n it('should update last_vuln_check timestamp on queried dependencies', async () => {\n createDependencyNode('express', 'npm', '1.0.5');\n\n const adapter = createMockAdapter('OSV', new Map());\n const client = new VulnerabilityDatabaseClient([adapter], db);\n\n const before = Date.now();\n await client.enrichAll();\n const after = Date.now();\n\n const rawDb = db.getRawDb();\n const row = rawDb.get(\n 'SELECT last_vuln_check FROM sec_dependencies WHERE package_name = ?',\n ['express'],\n );\n expect(row.last_vuln_check).toBeGreaterThanOrEqual(before);\n expect(row.last_vuln_check).toBeLessThanOrEqual(after);\n });\n\n it('should deduplicate CVEs by ID across multiple databases', async () => {\n createDependencyNode('express', 'npm', '1.0.5');\n\n // Both adapters return the same CVE\n const results1 = new Map<string, CVERecord[]>();\n results1.set('npm:express:1.0.5', [sampleCve]);\n\n const results2 = new Map<string, CVERecord[]>();\n results2.set('npm:express:1.0.5', [\n { ...sampleCve, summary: 'Different summary from DB2' },\n ]);\n\n const adapter1 = createMockAdapter('OSV', results1);\n const adapter2 = createMockAdapter('NVD', results2);\n const client = new VulnerabilityDatabaseClient([adapter1, adapter2], db);\n\n const result = await client.enrichAll();\n\n // Only one Vulnerability_Node should be created despite two databases reporting it\n expect(result.vulnerabilitiesFound).toBe(1);\n\n const rawDb = db.getRawDb();\n const vulnRows = rawDb.all('SELECT * FROM sec_vulnerabilities WHERE cve_id = ?', ['CVE-2023-12345']);\n expect(vulnRows).toHaveLength(1);\n });\n\n it('should handle multiple unique CVEs from multiple databases', async () => {\n createDependencyNode('express', 'npm', '1.0.5');\n\n const results1 = new Map<string, CVERecord[]>();\n results1.set('npm:express:1.0.5', [sampleCve]);\n\n const results2 = new Map<string, CVERecord[]>();\n results2.set('npm:express:1.0.5', [sampleCve2]);\n\n const adapter1 = createMockAdapter('OSV', results1);\n const adapter2 = createMockAdapter('NVD', results2);\n const client = new VulnerabilityDatabaseClient([adapter1, adapter2], db);\n\n const result = await client.enrichAll();\n\n expect(result.vulnerabilitiesFound).toBe(2);\n\n expect(db.getNode('vuln:CVE-2023-12345')).not.toBeNull();\n expect(db.getNode('vuln:CVE-2023-67890')).not.toBeNull();\n });\n\n it('should handle unreachable databases: set vulnDataStale flag', async () => {\n const depNodeId = createDependencyNode('express', 'npm', '1.0.5');\n\n const failingAdapter = createFailingAdapter('OSV', 'Network error');\n const client = new VulnerabilityDatabaseClient([failingAdapter], db);\n\n const result = await client.enrichAll();\n\n expect(result.errors).toHaveLength(1);\n expect(result.errors[0].dependency).toBe('express');\n expect(result.errors[0].database).toBe('OSV');\n expect(result.errors[0].error).toBe('Network error');\n\n expect(result.staleNodes).toContain(depNodeId);\n\n // Verify vuln_data_stale flag is set\n const rawDb = db.getRawDb();\n const row = rawDb.get(\n 'SELECT vuln_data_stale, vuln_data_stale_since FROM sec_dependencies WHERE node_id = ?',\n [depNodeId],\n );\n expect(row.vuln_data_stale).toBe(1);\n expect(row.vuln_data_stale_since).toBeGreaterThan(0);\n });\n\n it('should continue processing other dependencies when one adapter fails', async () => {\n createDependencyNode('express', 'npm', '1.0.5');\n createDependencyNode('lodash', 'npm', '4.17.21');\n\n // Adapter that fails for express but succeeds for lodash\n const adapter: VulnDatabaseAdapter = {\n name: 'OSV',\n async query(_eco: string, pkg: string): Promise<CVERecord[]> {\n if (pkg === 'express') throw new Error('Timeout');\n return [];\n },\n };\n\n const client = new VulnerabilityDatabaseClient([adapter], db);\n const result = await client.enrichAll();\n\n // Both dependencies should be checked\n expect(result.dependenciesChecked).toBe(2);\n expect(result.errors).toHaveLength(1);\n });\n\n it('should truncate summary to 500 characters', async () => {\n createDependencyNode('express', 'npm', '1.0.5');\n\n const longSummary = 'A'.repeat(600);\n const cveWithLongSummary: CVERecord = {\n ...sampleCve,\n summary: longSummary,\n };\n\n const results = new Map<string, CVERecord[]>();\n results.set('npm:express:1.0.5', [cveWithLongSummary]);\n\n const adapter = createMockAdapter('OSV', results);\n const client = new VulnerabilityDatabaseClient([adapter], db);\n\n await client.enrichAll();\n\n const rawDb = db.getRawDb();\n const row = rawDb.get(\n 'SELECT summary FROM sec_vulnerabilities WHERE cve_id = ?',\n ['CVE-2023-12345'],\n );\n expect(row.summary).toHaveLength(500);\n });\n });\n\n describe('enrichOne()', () => {\n it('should return empty array for non-existent dependency', async () => {\n const adapter = createMockAdapter('OSV', new Map());\n const client = new VulnerabilityDatabaseClient([adapter], db);\n\n const result = await client.enrichOne('dep:npm:nonexistent');\n\n expect(result).toHaveLength(0);\n });\n\n it('should enrich a single dependency and return CVE records', async () => {\n const depNodeId = createDependencyNode('express', 'npm', '1.0.5');\n\n const results = new Map<string, CVERecord[]>();\n results.set('npm:express:1.0.5', [sampleCve, sampleCve2]);\n\n const adapter = createMockAdapter('OSV', results);\n const client = new VulnerabilityDatabaseClient([adapter], db);\n\n const cves = await client.enrichOne(depNodeId);\n\n expect(cves).toHaveLength(2);\n expect(cves[0].id).toBe('CVE-2023-12345');\n expect(cves[1].id).toBe('CVE-2023-67890');\n\n // Verify nodes were created\n expect(db.getNode('vuln:CVE-2023-12345')).not.toBeNull();\n expect(db.getNode('vuln:CVE-2023-67890')).not.toBeNull();\n });\n\n it('should update last_vuln_check for the single dependency', async () => {\n const depNodeId = createDependencyNode('express', 'npm', '1.0.5');\n\n const adapter = createMockAdapter('OSV', new Map());\n const client = new VulnerabilityDatabaseClient([adapter], db);\n\n const before = Date.now();\n await client.enrichOne(depNodeId);\n const after = Date.now();\n\n const rawDb = db.getRawDb();\n const row = rawDb.get(\n 'SELECT last_vuln_check FROM sec_dependencies WHERE node_id = ?',\n [depNodeId],\n );\n expect(row.last_vuln_check).toBeGreaterThanOrEqual(before);\n expect(row.last_vuln_check).toBeLessThanOrEqual(after);\n });\n\n it('should deduplicate CVEs when enriching a single dependency', async () => {\n const depNodeId = createDependencyNode('express', 'npm', '1.0.5');\n\n const results1 = new Map<string, CVERecord[]>();\n results1.set('npm:express:1.0.5', [sampleCve]);\n\n const results2 = new Map<string, CVERecord[]>();\n results2.set('npm:express:1.0.5', [sampleCve]);\n\n const adapter1 = createMockAdapter('OSV', results1);\n const adapter2 = createMockAdapter('NVD', results2);\n const client = new VulnerabilityDatabaseClient([adapter1, adapter2], db);\n\n const cves = await client.enrichOne(depNodeId);\n\n expect(cves).toHaveLength(1);\n });\n });\n});\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
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 epss_client_exports = {};
|
|
20
|
+
__export(epss_client_exports, {
|
|
21
|
+
EpssClient: () => EpssClient
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(epss_client_exports);
|
|
24
|
+
var import_errors = require("../../errors");
|
|
25
|
+
const EPSS_API_URL = "https://api.first.org/data/v1/epss";
|
|
26
|
+
const CHUNK_SIZE = 500;
|
|
27
|
+
const TIMEOUT_MS = 3e4;
|
|
28
|
+
class EpssClient {
|
|
29
|
+
/**
|
|
30
|
+
* Fetch EPSS scores for a list of CVE IDs.
|
|
31
|
+
*
|
|
32
|
+
* CVEs are batched into chunks of 500 per request. Missing CVEs simply do
|
|
33
|
+
* not appear in the response and are omitted from the returned map.
|
|
34
|
+
*
|
|
35
|
+
* @param cveIds - List of CVE identifiers (e.g. ["CVE-2021-44228", ...])
|
|
36
|
+
* @returns Map from CVE ID to { score, percentile } (floats in [0, 1])
|
|
37
|
+
*/
|
|
38
|
+
async fetchScores(cveIds) {
|
|
39
|
+
const result = /* @__PURE__ */ new Map();
|
|
40
|
+
if (cveIds.length === 0) {
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
for (let i = 0; i < cveIds.length; i += CHUNK_SIZE) {
|
|
44
|
+
const chunk = cveIds.slice(i, i + CHUNK_SIZE);
|
|
45
|
+
try {
|
|
46
|
+
const chunkResult = await this.fetchChunk(chunk);
|
|
47
|
+
for (const [cve, scores] of chunkResult) {
|
|
48
|
+
result.set(cve, scores);
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
52
|
+
(0, import_errors.logWarn)(`[sec:epss] Failed to fetch EPSS scores for chunk of ${chunk.length} CVEs: ${msg}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
async fetchChunk(cveIds) {
|
|
58
|
+
const result = /* @__PURE__ */ new Map();
|
|
59
|
+
const controller = new AbortController();
|
|
60
|
+
const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
61
|
+
try {
|
|
62
|
+
const cveParam = cveIds.join(",");
|
|
63
|
+
const url = `${EPSS_API_URL}?cve=${encodeURIComponent(cveParam)}`;
|
|
64
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
65
|
+
if (!response.ok) {
|
|
66
|
+
(0, import_errors.logWarn)(`[sec:epss] EPSS API returned HTTP ${response.status}`);
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
const json = await response.json();
|
|
70
|
+
if (json.status !== "OK" || !Array.isArray(json.data)) {
|
|
71
|
+
(0, import_errors.logWarn)(`[sec:epss] Unexpected EPSS API response structure`);
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
for (const entry of json.data) {
|
|
75
|
+
const score = parseFloat(entry.epss);
|
|
76
|
+
const percentile = parseFloat(entry.percentile);
|
|
77
|
+
if (!isNaN(score) && !isNaN(percentile)) {
|
|
78
|
+
result.set(entry.cve, { score, percentile });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
83
|
+
(0, import_errors.logError)(`[sec:epss] Error fetching EPSS chunk: ${msg}`);
|
|
84
|
+
throw error;
|
|
85
|
+
} finally {
|
|
86
|
+
clearTimeout(timeout);
|
|
87
|
+
}
|
|
88
|
+
return result;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
92
|
+
0 && (module.exports = {
|
|
93
|
+
EpssClient
|
|
94
|
+
});
|
|
95
|
+
//# sourceMappingURL=epss-client.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/security/vuln/epss-client.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * EPSS (Exploit Prediction Scoring System) Client\n *\n * Fetches exploit prediction scores from the FIRST.org EPSS API for CVE IDs.\n * EPSS is non-critical: on error, an empty map is returned so the caller can\n * continue without scores.\n *\n * API: https://api.first.org/data/v1/epss\n */\n\nimport { logWarn, logError } from '../../errors';\n\nconst EPSS_API_URL = 'https://api.first.org/data/v1/epss';\nconst CHUNK_SIZE = 500;\nconst TIMEOUT_MS = 30000;\n\ninterface EpssApiResponse {\n status: string;\n status_code: number;\n data: Array<{\n cve: string;\n epss: string;\n percentile: string;\n date: string;\n }>;\n}\n\nexport class EpssClient {\n /**\n * Fetch EPSS scores for a list of CVE IDs.\n *\n * CVEs are batched into chunks of 500 per request. Missing CVEs simply do\n * not appear in the response and are omitted from the returned map.\n *\n * @param cveIds - List of CVE identifiers (e.g. [\"CVE-2021-44228\", ...])\n * @returns Map from CVE ID to { score, percentile } (floats in [0, 1])\n */\n async fetchScores(cveIds: string[]): Promise<Map<string, { score: number; percentile: number }>> {\n const result = new Map<string, { score: number; percentile: number }>();\n\n if (cveIds.length === 0) {\n return result;\n }\n\n for (let i = 0; i < cveIds.length; i += CHUNK_SIZE) {\n const chunk = cveIds.slice(i, i + CHUNK_SIZE);\n try {\n const chunkResult = await this.fetchChunk(chunk);\n for (const [cve, scores] of chunkResult) {\n result.set(cve, scores);\n }\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logWarn(`[sec:epss] Failed to fetch EPSS scores for chunk of ${chunk.length} CVEs: ${msg}`);\n // EPSS is non-critical \u2014 continue with partial results\n }\n }\n\n return result;\n }\n\n private async fetchChunk(\n cveIds: string[],\n ): Promise<Map<string, { score: number; percentile: number }>> {\n const result = new Map<string, { score: number; percentile: number }>();\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), TIMEOUT_MS);\n\n try {\n const cveParam = cveIds.join(',');\n const url = `${EPSS_API_URL}?cve=${encodeURIComponent(cveParam)}`;\n\n const response = await fetch(url, { signal: controller.signal });\n\n if (!response.ok) {\n logWarn(`[sec:epss] EPSS API returned HTTP ${response.status}`);\n return result;\n }\n\n const json = (await response.json()) as EpssApiResponse;\n\n if (json.status !== 'OK' || !Array.isArray(json.data)) {\n logWarn(`[sec:epss] Unexpected EPSS API response structure`);\n return result;\n }\n\n for (const entry of json.data) {\n const score = parseFloat(entry.epss);\n const percentile = parseFloat(entry.percentile);\n if (!isNaN(score) && !isNaN(percentile)) {\n result.set(entry.cve, { score, percentile });\n }\n }\n } catch (error: unknown) {\n const msg = error instanceof Error ? error.message : String(error);\n logError(`[sec:epss] Error fetching EPSS chunk: ${msg}`);\n throw error;\n } finally {\n clearTimeout(timeout);\n }\n\n return result;\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAUA,oBAAkC;AAElC,MAAM,eAAe;AACrB,MAAM,aAAa;AACnB,MAAM,aAAa;AAaZ,MAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUtB,MAAM,YAAY,QAA+E;AAC/F,UAAM,SAAS,oBAAI,IAAmD;AAEtE,QAAI,OAAO,WAAW,GAAG;AACvB,aAAO;AAAA,IACT;AAEA,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,YAAY;AAClD,YAAM,QAAQ,OAAO,MAAM,GAAG,IAAI,UAAU;AAC5C,UAAI;AACF,cAAM,cAAc,MAAM,KAAK,WAAW,KAAK;AAC/C,mBAAW,CAAC,KAAK,MAAM,KAAK,aAAa;AACvC,iBAAO,IAAI,KAAK,MAAM;AAAA,QACxB;AAAA,MACF,SAAS,OAAgB;AACvB,cAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACjE,mCAAQ,uDAAuD,MAAM,MAAM,UAAU,GAAG,EAAE;AAAA,MAE5F;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,WACZ,QAC6D;AAC7D,UAAM,SAAS,oBAAI,IAAmD;AAEtE,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,UAAU;AAE/D,QAAI;AACF,YAAM,WAAW,OAAO,KAAK,GAAG;AAChC,YAAM,MAAM,GAAG,YAAY,QAAQ,mBAAmB,QAAQ,CAAC;AAE/D,YAAM,WAAW,MAAM,MAAM,KAAK,EAAE,QAAQ,WAAW,OAAO,CAAC;AAE/D,UAAI,CAAC,SAAS,IAAI;AAChB,mCAAQ,qCAAqC,SAAS,MAAM,EAAE;AAC9D,eAAO;AAAA,MACT;AAEA,YAAM,OAAQ,MAAM,SAAS,KAAK;AAElC,UAAI,KAAK,WAAW,QAAQ,CAAC,MAAM,QAAQ,KAAK,IAAI,GAAG;AACrD,mCAAQ,mDAAmD;AAC3D,eAAO;AAAA,MACT;AAEA,iBAAW,SAAS,KAAK,MAAM;AAC7B,cAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,cAAM,aAAa,WAAW,MAAM,UAAU;AAC9C,YAAI,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,UAAU,GAAG;AACvC,iBAAO,IAAI,MAAM,KAAK,EAAE,OAAO,WAAW,CAAC;AAAA,QAC7C;AAAA,MACF;AAAA,IACF,SAAS,OAAgB;AACvB,YAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACjE,kCAAS,yCAAyC,GAAG,EAAE;AACvD,YAAM;AAAA,IACR,UAAE;AACA,mBAAa,OAAO;AAAA,IACtB;AAEA,WAAO;AAAA,EACT;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
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 vuln_exports = {};
|
|
20
|
+
__export(vuln_exports, {
|
|
21
|
+
VulnerabilityDatabaseClient: () => import_client.VulnerabilityDatabaseClient
|
|
22
|
+
});
|
|
23
|
+
module.exports = __toCommonJS(vuln_exports);
|
|
24
|
+
var import_client = require("./client");
|
|
25
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
26
|
+
0 && (module.exports = {
|
|
27
|
+
VulnerabilityDatabaseClient
|
|
28
|
+
});
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/security/vuln/index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Vulnerability database subsystem exports.\n */\n\nexport { VulnerabilityDatabaseClient } from './client';\nexport type { VulnDatabaseAdapter } from './types';\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,oBAA4C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|