@vyuhlabs/dxkit 2.4.4 → 2.4.6
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/CHANGELOG.md +339 -0
- package/README.md +33 -19
- package/dist/analyzers/tests/index.d.ts.map +1 -1
- package/dist/analyzers/tests/index.js +2 -0
- package/dist/analyzers/tests/index.js.map +1 -1
- package/dist/analyzers/tools/coverage.d.ts +1 -1
- package/dist/analyzers/tools/coverage.d.ts.map +1 -1
- package/dist/analyzers/tools/coverage.js.map +1 -1
- package/dist/analyzers/tools/jacoco.d.ts +76 -0
- package/dist/analyzers/tools/jacoco.d.ts.map +1 -0
- package/dist/analyzers/tools/jacoco.js +228 -0
- package/dist/analyzers/tools/jacoco.js.map +1 -0
- package/dist/analyzers/tools/osv-scanner-deps.d.ts +47 -0
- package/dist/analyzers/tools/osv-scanner-deps.d.ts.map +1 -0
- package/dist/analyzers/tools/osv-scanner-deps.js +175 -0
- package/dist/analyzers/tools/osv-scanner-deps.js.map +1 -0
- package/dist/analyzers/tools/osv-scanner-fix.d.ts +20 -0
- package/dist/analyzers/tools/osv-scanner-fix.d.ts.map +1 -1
- package/dist/analyzers/tools/osv-scanner-fix.js +45 -20
- package/dist/analyzers/tools/osv-scanner-fix.js.map +1 -1
- package/dist/analyzers/tools/runner.d.ts +47 -0
- package/dist/analyzers/tools/runner.d.ts.map +1 -1
- package/dist/analyzers/tools/runner.js +79 -0
- package/dist/analyzers/tools/runner.js.map +1 -1
- package/dist/analyzers/tools/tool-registry.d.ts +10 -0
- package/dist/analyzers/tools/tool-registry.d.ts.map +1 -1
- package/dist/analyzers/tools/tool-registry.js +131 -0
- package/dist/analyzers/tools/tool-registry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +36 -2
- package/dist/cli.js.map +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/languages/index.d.ts.map +1 -1
- package/dist/languages/index.js +12 -1
- package/dist/languages/index.js.map +1 -1
- package/dist/languages/java.d.ts +67 -0
- package/dist/languages/java.d.ts.map +1 -0
- package/dist/languages/java.js +421 -0
- package/dist/languages/java.js.map +1 -0
- package/dist/languages/kotlin.d.ts +1 -52
- package/dist/languages/kotlin.d.ts.map +1 -1
- package/dist/languages/kotlin.js +16 -286
- package/dist/languages/kotlin.js.map +1 -1
- package/dist/languages/ruby.d.ts +77 -0
- package/dist/languages/ruby.d.ts.map +1 -0
- package/dist/languages/ruby.js +531 -0
- package/dist/languages/ruby.js.map +1 -0
- package/dist/languages/types.d.ts +9 -1
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/tools-cli.d.ts +16 -1
- package/dist/tools-cli.d.ts.map +1 -1
- package/dist/tools-cli.js +69 -6
- package/dist/tools-cli.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/templates/.claude/rules/java.md +11 -0
- package/templates/.claude/rules/ruby.md +11 -0
- package/templates/configs/java/README.md +6 -0
- package/templates/configs/ruby/README.md +6 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.parseJaCoCoXml = parseJaCoCoXml;
|
|
37
|
+
exports.findJaCoCoReport = findJaCoCoReport;
|
|
38
|
+
exports.gatherJaCoCoCoverageResult = gatherJaCoCoCoverageResult;
|
|
39
|
+
/**
|
|
40
|
+
* JaCoCo XML report parser + standard-location finder. Shared across
|
|
41
|
+
* JVM-language packs (kotlin, java) — single source of truth per
|
|
42
|
+
* CLAUDE.md rule #2 ("Each tool has ONE gather function … if another
|
|
43
|
+
* module needs that tool's output, it MUST call the existing
|
|
44
|
+
* function").
|
|
45
|
+
*
|
|
46
|
+
* Extracted from `src/languages/kotlin.ts` in 10k.1.2 (Phase 10k.1
|
|
47
|
+
* SSOT validation) — when the Java pack needed JaCoCo coverage, the
|
|
48
|
+
* parser was already generic (kotlin's own tests cover it against
|
|
49
|
+
* Java source, see test/languages-kotlin.test.ts). Forking would
|
|
50
|
+
* have been the architectural violation; relocating to a shared
|
|
51
|
+
* tool module is the correct outcome.
|
|
52
|
+
*/
|
|
53
|
+
const fs = __importStar(require("fs"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
const coverage_1 = require("./coverage");
|
|
56
|
+
/**
|
|
57
|
+
* Pure parser for JaCoCo's XML report (DTD: `report.dtd`). The structure:
|
|
58
|
+
*
|
|
59
|
+
* <report name="...">
|
|
60
|
+
* <package name="com/example">
|
|
61
|
+
* <class name="com/example/Foo" sourcefilename="Foo.kt">...</class>
|
|
62
|
+
* <sourcefile name="Foo.kt">
|
|
63
|
+
* <line nr="N" mi="..." ci="..." mb="..." cb="..."/>
|
|
64
|
+
* <counter type="LINE" missed="X" covered="Y"/>
|
|
65
|
+
* </sourcefile>
|
|
66
|
+
* <counter type="LINE" missed="X" covered="Y"/>
|
|
67
|
+
* </package>
|
|
68
|
+
* <counter type="LINE" missed="X" covered="Y"/>
|
|
69
|
+
* </report>
|
|
70
|
+
*
|
|
71
|
+
* Per-file coverage comes from `<sourcefile>` blocks: their LINE counter
|
|
72
|
+
* holds the file's missed/covered totals. Project-level total comes from
|
|
73
|
+
* the top-level `<counter type="LINE">` (last in the document).
|
|
74
|
+
*
|
|
75
|
+
* Path attribution joins `<package name>` (forward-slashed, JVM-style)
|
|
76
|
+
* with `<sourcefile name>` to produce the canonical relative path the
|
|
77
|
+
* downstream consumers expect (`com/example/Foo.kt` or
|
|
78
|
+
* `com/example/Foo.java`). JVM bytecode namespacing isn't 1:1 with
|
|
79
|
+
* on-disk source paths in multi-module projects — accepted limitation
|
|
80
|
+
* (matches C#'s cobertura attribution).
|
|
81
|
+
*
|
|
82
|
+
* Returns null when no `<counter type="LINE">` exists at the top level
|
|
83
|
+
* — that's JaCoCo's "no coverage data" signal, distinct from "0%
|
|
84
|
+
* coverage" (where the counter exists with covered=0).
|
|
85
|
+
*
|
|
86
|
+
* Language-agnostic: works for Kotlin (.kt), Java (.java), and any
|
|
87
|
+
* other JVM language whose JaCoCo output respects the `<package>` /
|
|
88
|
+
* `<sourcefile>` shape — `<sourcefile name>` carries the on-disk
|
|
89
|
+
* filename verbatim so the parser doesn't need to know the language.
|
|
90
|
+
*/
|
|
91
|
+
function parseJaCoCoXml(raw, sourceFile, _cwd) {
|
|
92
|
+
const files = new Map();
|
|
93
|
+
// Iterate <package> blocks. Each block contains <sourcefile> children
|
|
94
|
+
// we tally and aggregate counters we can ignore (they're sums of the
|
|
95
|
+
// children, redundant given we sum the children ourselves).
|
|
96
|
+
const packageRe = /<package\s+name="([^"]+)">([\s\S]*?)<\/package>/g;
|
|
97
|
+
let pm;
|
|
98
|
+
while ((pm = packageRe.exec(raw)) !== null) {
|
|
99
|
+
const pkgPath = pm[1].replace(/\\/g, '/'); // JVM uses forward-slashes already; defensive
|
|
100
|
+
const pkgInner = pm[2];
|
|
101
|
+
// Within a <package>, <sourcefile> blocks own per-file counters.
|
|
102
|
+
const sourceFileRe = /<sourcefile\s+name="([^"]+)">([\s\S]*?)<\/sourcefile>/g;
|
|
103
|
+
let sm;
|
|
104
|
+
while ((sm = sourceFileRe.exec(pkgInner)) !== null) {
|
|
105
|
+
const fileName = sm[1];
|
|
106
|
+
const sourceInner = sm[2];
|
|
107
|
+
// The LAST <counter type="LINE"> in <sourcefile> is the
|
|
108
|
+
// file-level aggregate; earlier ones are per-method. We pick the
|
|
109
|
+
// last to get the full-file roll-up.
|
|
110
|
+
const counterRe = /<counter\s+type="LINE"\s+missed="(\d+)"\s+covered="(\d+)"\s*\/>/g;
|
|
111
|
+
let lastMissed = 0;
|
|
112
|
+
let lastCovered = 0;
|
|
113
|
+
let cm;
|
|
114
|
+
while ((cm = counterRe.exec(sourceInner)) !== null) {
|
|
115
|
+
lastMissed = parseInt(cm[1], 10);
|
|
116
|
+
lastCovered = parseInt(cm[2], 10);
|
|
117
|
+
}
|
|
118
|
+
const total = lastMissed + lastCovered;
|
|
119
|
+
const rel = pkgPath ? `${pkgPath}/${fileName}` : fileName;
|
|
120
|
+
files.set(rel, {
|
|
121
|
+
path: rel,
|
|
122
|
+
covered: lastCovered,
|
|
123
|
+
total,
|
|
124
|
+
pct: (0, coverage_1.round1)(total > 0 ? (lastCovered / total) * 100 : 0),
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// Top-level project-wide LINE counter — JaCoCo emits it after the
|
|
129
|
+
// last </package>. Use a non-greedy match against the document tail
|
|
130
|
+
// to avoid grabbing per-package counters as project-level.
|
|
131
|
+
const tailMatch = raw.match(/<\/package>\s*<counter\s+type="LINE"\s+missed="(\d+)"\s+covered="(\d+)"\s*\/>\s*<\/report>/);
|
|
132
|
+
let totalMissed = 0;
|
|
133
|
+
let totalCovered = 0;
|
|
134
|
+
if (tailMatch) {
|
|
135
|
+
totalMissed = parseInt(tailMatch[1], 10);
|
|
136
|
+
totalCovered = parseInt(tailMatch[2], 10);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
// No project-level counter (degenerate report — single package, no
|
|
140
|
+
// explicit roll-up). Sum the per-file totals as the linePercent
|
|
141
|
+
// basis.
|
|
142
|
+
for (const f of files.values()) {
|
|
143
|
+
totalCovered += f.covered;
|
|
144
|
+
totalMissed += f.total - f.covered;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const grandTotal = totalCovered + totalMissed;
|
|
148
|
+
if (grandTotal === 0 && files.size === 0)
|
|
149
|
+
return null;
|
|
150
|
+
return {
|
|
151
|
+
source: 'jacoco',
|
|
152
|
+
sourceFile,
|
|
153
|
+
linePercent: (0, coverage_1.round1)(grandTotal > 0 ? (totalCovered / grandTotal) * 100 : 0),
|
|
154
|
+
files,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Standard JaCoCo report locations across JVM build tools and project
|
|
159
|
+
* layouts:
|
|
160
|
+
*
|
|
161
|
+
* GRADLE (kotlin + java):
|
|
162
|
+
* - app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
|
|
163
|
+
* (Android default — `app` module, `jacocoTestReport` task)
|
|
164
|
+
* - build/reports/jacoco/test/jacocoTestReport.xml
|
|
165
|
+
* (plain JVM via the `jacoco` plugin's default `test` task)
|
|
166
|
+
* - build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
|
|
167
|
+
* (multi-module aggregate; many builds rename the task)
|
|
168
|
+
*
|
|
169
|
+
* MAVEN (java):
|
|
170
|
+
* - target/site/jacoco/jacoco.xml
|
|
171
|
+
* (default Maven JaCoCo plugin — single-module Java)
|
|
172
|
+
* - target/site/jacoco-aggregate/jacoco.xml
|
|
173
|
+
* (Maven multi-module aggregate via jacoco-maven-plugin's
|
|
174
|
+
* `report-aggregate` goal)
|
|
175
|
+
*
|
|
176
|
+
* FALLBACKS:
|
|
177
|
+
* - jacocoTestReport.xml (top-level — fixture / direct path)
|
|
178
|
+
* - jacoco.xml (top-level — Maven fixture / direct path)
|
|
179
|
+
*
|
|
180
|
+
* Returns the first existing path, relative to cwd. Conservative
|
|
181
|
+
* priority: most specific build-tool conventions first, root
|
|
182
|
+
* fallbacks last.
|
|
183
|
+
*/
|
|
184
|
+
function findJaCoCoReport(cwd) {
|
|
185
|
+
const candidates = [
|
|
186
|
+
// Gradle (Android + plain JVM)
|
|
187
|
+
'app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml',
|
|
188
|
+
'build/reports/jacoco/test/jacocoTestReport.xml',
|
|
189
|
+
'build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml',
|
|
190
|
+
// Maven (single-module + multi-module aggregate)
|
|
191
|
+
'target/site/jacoco/jacoco.xml',
|
|
192
|
+
'target/site/jacoco-aggregate/jacoco.xml',
|
|
193
|
+
// Direct-path fallbacks (fixture / hand-placed)
|
|
194
|
+
'jacocoTestReport.xml',
|
|
195
|
+
'jacoco.xml',
|
|
196
|
+
];
|
|
197
|
+
for (const rel of candidates) {
|
|
198
|
+
const abs = path.join(cwd, rel);
|
|
199
|
+
if (fs.existsSync(abs))
|
|
200
|
+
return rel;
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Locate + parse the JaCoCo XML report under cwd, wrap the result in a
|
|
206
|
+
* `CoverageResult` envelope. Identical glue across JVM packs (kotlin,
|
|
207
|
+
* java) — no language-specific dispatch needed because JaCoCo's
|
|
208
|
+
* `<sourcefile name>` carries the on-disk filename verbatim and the
|
|
209
|
+
* file extension survives intact through the parser. Both packs'
|
|
210
|
+
* `coverageProvider.gather` should delegate here.
|
|
211
|
+
*/
|
|
212
|
+
function gatherJaCoCoCoverageResult(cwd) {
|
|
213
|
+
const reportRel = findJaCoCoReport(cwd);
|
|
214
|
+
if (!reportRel)
|
|
215
|
+
return null;
|
|
216
|
+
let raw;
|
|
217
|
+
try {
|
|
218
|
+
raw = fs.readFileSync(path.join(cwd, reportRel), 'utf-8');
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const coverage = parseJaCoCoXml(raw, reportRel, cwd);
|
|
224
|
+
if (!coverage)
|
|
225
|
+
return null;
|
|
226
|
+
return { schemaVersion: 1, tool: `coverage:${coverage.source}`, coverage };
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=jacoco.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jacoco.js","sourceRoot":"","sources":["../../../src/analyzers/tools/jacoco.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { DepVulnFinding, DepVulnGatherOutcome, SeverityCounts } from '../../languages/capabilities/types';
|
|
2
|
+
/**
|
|
3
|
+
* Pure parser for osv-scanner v2.x JSON output, scoped to a single
|
|
4
|
+
* ecosystem. Other ecosystems are filtered out so polyglot repos
|
|
5
|
+
* don't double-count: each pack handles its own ecosystem (typescript
|
|
6
|
+
* → npm, python → PyPI, kotlin/java → Maven, ruby → RubyGems, etc.).
|
|
7
|
+
*
|
|
8
|
+
* The ecosystem parameter is matched against the OSV record's
|
|
9
|
+
* `package.ecosystem` field verbatim — use the exact strings OSV
|
|
10
|
+
* emits (`'Maven'`, `'RubyGems'`, `'PyPI'`, `'npm'`, `'Go'`, etc.).
|
|
11
|
+
*
|
|
12
|
+
* Returns counts + findings + the raw OSV vuln records for downstream
|
|
13
|
+
* CVSS resolution. Exported for unit tests.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseOsvScannerFindings(raw: string, ecosystem: string): {
|
|
16
|
+
counts: SeverityCounts;
|
|
17
|
+
findings: DepVulnFinding[];
|
|
18
|
+
vulnsForCvss: Array<{
|
|
19
|
+
primaryId: string;
|
|
20
|
+
embeddedCvss: number | null;
|
|
21
|
+
aliases: string[];
|
|
22
|
+
}>;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Single source of truth for osv-scanner-driven dep-vuln gathering.
|
|
26
|
+
* Caller supplies:
|
|
27
|
+
* - cwd: project root
|
|
28
|
+
* - source: pack id for envelope attribution (currently reserved —
|
|
29
|
+
* see note at end of function)
|
|
30
|
+
* - ecosystem: OSV ecosystem string (`'Maven'`, `'RubyGems'`, ...)
|
|
31
|
+
* - manifestCandidates: ordered list of manifest filenames to probe.
|
|
32
|
+
* First existing one is passed via `--lockfile`. Lockfiles
|
|
33
|
+
* preferred over higher-level manifests (kotlin: gradle.lockfile
|
|
34
|
+
* before pom.xml; ruby: Gemfile.lock).
|
|
35
|
+
*
|
|
36
|
+
* `scan source --lockfile <path>` is the v2.x form. JSON output to
|
|
37
|
+
* stdout. Exit code is non-zero when findings exist — we ignore the
|
|
38
|
+
* exit code and parse the JSON regardless (run() already swallows
|
|
39
|
+
* non-zero exits cleanly via execSync's catch).
|
|
40
|
+
*
|
|
41
|
+
* CVSS alias-fallback: osv-scanner ships CVSS vectors when present,
|
|
42
|
+
* but advisory data quality varies by ecosystem — some carry only
|
|
43
|
+
* `database_specific.severity` strings. resolveCvssScores looks up
|
|
44
|
+
* via CVE alias when the primary record lacks a vector.
|
|
45
|
+
*/
|
|
46
|
+
export declare function gatherOsvScannerDepVulnsResult(cwd: string, source: string, ecosystem: string, manifestCandidates: string[]): Promise<DepVulnGatherOutcome>;
|
|
47
|
+
//# sourceMappingURL=osv-scanner-deps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osv-scanner-deps.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/osv-scanner-deps.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EACV,cAAc,EACd,oBAAoB,EAEpB,cAAc,EACf,MAAM,oCAAoC,CAAC;AAiB5C;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,GAChB;IACD,MAAM,EAAE,cAAc,CAAC;IACvB,QAAQ,EAAE,cAAc,EAAE,CAAC;IAC3B,YAAY,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC5F,CAiEA;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,kBAAkB,EAAE,MAAM,EAAE,GAC3B,OAAO,CAAC,oBAAoB,CAAC,CA4C/B"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseOsvScannerFindings = parseOsvScannerFindings;
|
|
4
|
+
exports.gatherOsvScannerDepVulnsResult = gatherOsvScannerDepVulnsResult;
|
|
5
|
+
/**
|
|
6
|
+
* osv-scanner against any OSV ecosystem — shared across language packs
|
|
7
|
+
* that use osv-scanner as their canonical depVulns source. CLAUDE.md
|
|
8
|
+
* rule #2 — the gather function lives once.
|
|
9
|
+
*
|
|
10
|
+
* History: extracted from `src/languages/kotlin.ts` in 10k.1.4 (Phase
|
|
11
|
+
* 10k.1 SSOT validation), originally Maven-only. Generalized to all
|
|
12
|
+
* OSV ecosystems in 10k.2.6a (Ruby pack work) — caller passes the
|
|
13
|
+
* ecosystem string + manifest candidate list, parser filters
|
|
14
|
+
* accordingly so polyglot repos don't double-count across packs.
|
|
15
|
+
*
|
|
16
|
+
* Current consumers:
|
|
17
|
+
* - kotlin pack — `Maven` ecosystem, gradle.lockfile + pom.xml + verification-metadata.xml
|
|
18
|
+
* - java pack — `Maven` ecosystem (same manifest set)
|
|
19
|
+
* - ruby pack — `RubyGems` ecosystem, Gemfile.lock
|
|
20
|
+
*
|
|
21
|
+
* osv-scanner is the established multi-ecosystem scanner; no Tier-1
|
|
22
|
+
* native equivalent exists for several of the ecosystems above
|
|
23
|
+
* (CLAUDE.md rule #5 — bundler-audit's JSON is unstable, so Ruby
|
|
24
|
+
* intentionally uses osv-scanner-only rather than dual-source).
|
|
25
|
+
* The typescript pack's `osv-scanner-fix.ts` uses the `fix`
|
|
26
|
+
* subcommand for upgrade planning — different mode, no shared logic.
|
|
27
|
+
*
|
|
28
|
+
* Manifest gating: caller supplies the candidate list. First
|
|
29
|
+
* existing candidate wins. Without any of them, returns
|
|
30
|
+
* `tool-missing` (matches python/csharp's manifest-gating pattern).
|
|
31
|
+
*/
|
|
32
|
+
const osv_1 = require("./osv");
|
|
33
|
+
const runner_1 = require("./runner");
|
|
34
|
+
const tool_registry_1 = require("./tool-registry");
|
|
35
|
+
/**
|
|
36
|
+
* Pure parser for osv-scanner v2.x JSON output, scoped to a single
|
|
37
|
+
* ecosystem. Other ecosystems are filtered out so polyglot repos
|
|
38
|
+
* don't double-count: each pack handles its own ecosystem (typescript
|
|
39
|
+
* → npm, python → PyPI, kotlin/java → Maven, ruby → RubyGems, etc.).
|
|
40
|
+
*
|
|
41
|
+
* The ecosystem parameter is matched against the OSV record's
|
|
42
|
+
* `package.ecosystem` field verbatim — use the exact strings OSV
|
|
43
|
+
* emits (`'Maven'`, `'RubyGems'`, `'PyPI'`, `'npm'`, `'Go'`, etc.).
|
|
44
|
+
*
|
|
45
|
+
* Returns counts + findings + the raw OSV vuln records for downstream
|
|
46
|
+
* CVSS resolution. Exported for unit tests.
|
|
47
|
+
*/
|
|
48
|
+
function parseOsvScannerFindings(raw, ecosystem) {
|
|
49
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
50
|
+
const findings = [];
|
|
51
|
+
const vulnsForCvss = [];
|
|
52
|
+
let data;
|
|
53
|
+
try {
|
|
54
|
+
data = JSON.parse(raw);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return { counts, findings, vulnsForCvss };
|
|
58
|
+
}
|
|
59
|
+
// Dedup at the source: osv-scanner can list the same advisory twice
|
|
60
|
+
// when a transitive dep is reachable through multiple top-level deps.
|
|
61
|
+
// Same (package, version, id) → same fingerprint, so collapse here.
|
|
62
|
+
const seen = new Set();
|
|
63
|
+
for (const result of data.results ?? []) {
|
|
64
|
+
for (const pkg of result.packages ?? []) {
|
|
65
|
+
if (pkg.package?.ecosystem !== ecosystem)
|
|
66
|
+
continue;
|
|
67
|
+
const pkgName = pkg.package.name ?? 'unknown';
|
|
68
|
+
const pkgVersion = pkg.package.version;
|
|
69
|
+
for (const vuln of pkg.vulnerabilities ?? []) {
|
|
70
|
+
if (!vuln.id)
|
|
71
|
+
continue;
|
|
72
|
+
const dedupKey = `${pkgName}\0${pkgVersion ?? ''}\0${vuln.id}`;
|
|
73
|
+
if (seen.has(dedupKey))
|
|
74
|
+
continue;
|
|
75
|
+
seen.add(dedupKey);
|
|
76
|
+
const sev = (0, osv_1.classifyOsvSeverity)(vuln);
|
|
77
|
+
const tier = sev === 'critical' || sev === 'high' || sev === 'medium' || sev === 'low'
|
|
78
|
+
? sev
|
|
79
|
+
: 'medium';
|
|
80
|
+
counts[tier]++;
|
|
81
|
+
const cvss = (0, osv_1.extractOsvCvssScore)(vuln);
|
|
82
|
+
const aliases = (vuln.aliases ?? []).filter((a) => a && a.length > 0);
|
|
83
|
+
const finding = {
|
|
84
|
+
id: vuln.id,
|
|
85
|
+
package: pkgName,
|
|
86
|
+
installedVersion: pkgVersion,
|
|
87
|
+
tool: 'osv-scanner',
|
|
88
|
+
severity: tier,
|
|
89
|
+
};
|
|
90
|
+
if (cvss !== null)
|
|
91
|
+
finding.cvssScore = cvss;
|
|
92
|
+
if (aliases.length > 0)
|
|
93
|
+
finding.aliases = aliases;
|
|
94
|
+
if (vuln.summary)
|
|
95
|
+
finding.summary = vuln.summary;
|
|
96
|
+
// OSV.dev hosts a canonical page per id — synthesize when the
|
|
97
|
+
// record's `references[]` is empty, otherwise keep the
|
|
98
|
+
// tool-supplied URLs.
|
|
99
|
+
const refUrls = (vuln.references ?? []).map((r) => r.url).filter((u) => !!u);
|
|
100
|
+
finding.references =
|
|
101
|
+
refUrls.length > 0 ? refUrls : [`https://osv.dev/vulnerability/${vuln.id}`];
|
|
102
|
+
findings.push(finding);
|
|
103
|
+
vulnsForCvss.push({
|
|
104
|
+
primaryId: vuln.id,
|
|
105
|
+
embeddedCvss: cvss,
|
|
106
|
+
aliases,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return { counts, findings, vulnsForCvss };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Single source of truth for osv-scanner-driven dep-vuln gathering.
|
|
115
|
+
* Caller supplies:
|
|
116
|
+
* - cwd: project root
|
|
117
|
+
* - source: pack id for envelope attribution (currently reserved —
|
|
118
|
+
* see note at end of function)
|
|
119
|
+
* - ecosystem: OSV ecosystem string (`'Maven'`, `'RubyGems'`, ...)
|
|
120
|
+
* - manifestCandidates: ordered list of manifest filenames to probe.
|
|
121
|
+
* First existing one is passed via `--lockfile`. Lockfiles
|
|
122
|
+
* preferred over higher-level manifests (kotlin: gradle.lockfile
|
|
123
|
+
* before pom.xml; ruby: Gemfile.lock).
|
|
124
|
+
*
|
|
125
|
+
* `scan source --lockfile <path>` is the v2.x form. JSON output to
|
|
126
|
+
* stdout. Exit code is non-zero when findings exist — we ignore the
|
|
127
|
+
* exit code and parse the JSON regardless (run() already swallows
|
|
128
|
+
* non-zero exits cleanly via execSync's catch).
|
|
129
|
+
*
|
|
130
|
+
* CVSS alias-fallback: osv-scanner ships CVSS vectors when present,
|
|
131
|
+
* but advisory data quality varies by ecosystem — some carry only
|
|
132
|
+
* `database_specific.severity` strings. resolveCvssScores looks up
|
|
133
|
+
* via CVE alias when the primary record lacks a vector.
|
|
134
|
+
*/
|
|
135
|
+
async function gatherOsvScannerDepVulnsResult(cwd, source, ecosystem, manifestCandidates) {
|
|
136
|
+
let manifest = null;
|
|
137
|
+
for (const rel of manifestCandidates) {
|
|
138
|
+
if ((0, runner_1.fileExists)(cwd, rel)) {
|
|
139
|
+
manifest = rel;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (!manifest)
|
|
144
|
+
return { kind: 'tool-missing' };
|
|
145
|
+
const scanner = (0, tool_registry_1.findTool)(tool_registry_1.TOOL_DEFS['osv-scanner'], cwd);
|
|
146
|
+
if (!scanner.available || !scanner.path)
|
|
147
|
+
return { kind: 'tool-missing' };
|
|
148
|
+
const raw = (0, runner_1.run)(`${scanner.path} scan source --lockfile ${manifest} --format json 2>/dev/null`, cwd, 180000);
|
|
149
|
+
if (!raw)
|
|
150
|
+
return { kind: 'no-output' };
|
|
151
|
+
const { counts, findings, vulnsForCvss } = parseOsvScannerFindings(raw, ecosystem);
|
|
152
|
+
if (findings.length > 0) {
|
|
153
|
+
const resolved = await (0, osv_1.resolveCvssScores)(vulnsForCvss);
|
|
154
|
+
for (const f of findings) {
|
|
155
|
+
const score = resolved.get(f.id);
|
|
156
|
+
if (score !== null && score !== undefined)
|
|
157
|
+
f.cvssScore = score;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const envelope = {
|
|
161
|
+
schemaVersion: 1,
|
|
162
|
+
tool: 'osv-scanner',
|
|
163
|
+
enrichment: 'osv.dev',
|
|
164
|
+
counts,
|
|
165
|
+
findings,
|
|
166
|
+
};
|
|
167
|
+
// Note: `source` is unused at the envelope level today — DepVulnResult
|
|
168
|
+
// carries `tool: 'osv-scanner'` as the producer attribution. Reserved
|
|
169
|
+
// for a future enhancement that distinguishes per-pack provenance
|
|
170
|
+
// (e.g., when both kotlin and java packs run on a mixed monorepo and
|
|
171
|
+
// we want to attribute findings to the originating pack).
|
|
172
|
+
void source;
|
|
173
|
+
return { kind: 'success', envelope };
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=osv-scanner-deps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"osv-scanner-deps.js","sourceRoot":"","sources":["../../../src/analyzers/tools/osv-scanner-deps.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,6 +32,26 @@ import type { DepVulnFinding, DepVulnUpgradePlan } from '../../languages/capabil
|
|
|
32
32
|
*
|
|
33
33
|
* Never throws. `gatherTsDepVulnsResult` calls this unconditionally and
|
|
34
34
|
* treats the empty map as "no enrichment available".
|
|
35
|
+
*
|
|
36
|
+
* **TEMP-DIR ISOLATION (10k.1.5b fix — discovered during 2.4.5 pre-ship
|
|
37
|
+
* regression):** osv-scanner v2's `fix` subcommand invokes `npm install`
|
|
38
|
+
* internally to compute upgrade patches. That `npm install` MUTATES the
|
|
39
|
+
* cwd's node_modules — wipes it then reinstalls (often with
|
|
40
|
+
* `--legacy-peer-deps` fallback if the project's deps don't resolve
|
|
41
|
+
* cleanly). On dxkit's own repo this caused subsequent dxkit subcommand
|
|
42
|
+
* invocations to crash with `Cannot find module 'hosted-git-info'`
|
|
43
|
+
* because osv-scanner's reinstall left an incomplete tree. On
|
|
44
|
+
* vyuhlabs-platform (835MB node_modules) the reinstall happened to
|
|
45
|
+
* succeed but still mutated state silently — a 5-month-old data-mutation
|
|
46
|
+
* bug shipped since 2.4.0 / Phase 10h.6 and only caught now because
|
|
47
|
+
* 2.4.5 pre-ship regression ran reports back-to-back on dxkit's own repo
|
|
48
|
+
* and tripped the chain.
|
|
49
|
+
*
|
|
50
|
+
* Mitigation: we copy `package.json` + `package-lock.json` to a fresh
|
|
51
|
+
* temp dir, run osv-scanner there, and discard the temp dir afterward.
|
|
52
|
+
* Output JSON identifies advisories by `(package, version, id)` tuples
|
|
53
|
+
* which are independent of cwd, so the upgrade-plan map we return is
|
|
54
|
+
* unchanged. The caller's project gets read-only treatment.
|
|
35
55
|
*/
|
|
36
56
|
export declare function gatherOsvScannerFixPlans(cwd: string): Promise<Map<string, DepVulnUpgradePlan>>;
|
|
37
57
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"osv-scanner-fix.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/osv-scanner-fix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;
|
|
1
|
+
{"version":3,"file":"osv-scanner-fix.d.ts","sourceRoot":"","sources":["../../../src/analyzers/tools/osv-scanner-fix.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAsC7F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAwC1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAwCrF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,cAAc,EAAE,EAC1B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,GACrC,MAAM,CAaR;AAED;;;;;;;4BAO4B;AAC5B,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAEhF"}
|
|
@@ -58,9 +58,10 @@ exports.gatherOsvScannerFixPlans = gatherOsvScannerFixPlans;
|
|
|
58
58
|
exports.parseOsvScannerFixOutput = parseOsvScannerFixOutput;
|
|
59
59
|
exports.enrichWithUpgradePlans = enrichWithUpgradePlans;
|
|
60
60
|
exports.planKey = planKey;
|
|
61
|
-
const child_process_1 = require("child_process");
|
|
62
61
|
const fs = __importStar(require("fs"));
|
|
62
|
+
const os = __importStar(require("os"));
|
|
63
63
|
const path = __importStar(require("path"));
|
|
64
|
+
const runner_1 = require("./runner");
|
|
64
65
|
const semver_bump_1 = require("./semver-bump");
|
|
65
66
|
const tool_registry_1 = require("./tool-registry");
|
|
66
67
|
/**
|
|
@@ -75,6 +76,26 @@ const tool_registry_1 = require("./tool-registry");
|
|
|
75
76
|
*
|
|
76
77
|
* Never throws. `gatherTsDepVulnsResult` calls this unconditionally and
|
|
77
78
|
* treats the empty map as "no enrichment available".
|
|
79
|
+
*
|
|
80
|
+
* **TEMP-DIR ISOLATION (10k.1.5b fix — discovered during 2.4.5 pre-ship
|
|
81
|
+
* regression):** osv-scanner v2's `fix` subcommand invokes `npm install`
|
|
82
|
+
* internally to compute upgrade patches. That `npm install` MUTATES the
|
|
83
|
+
* cwd's node_modules — wipes it then reinstalls (often with
|
|
84
|
+
* `--legacy-peer-deps` fallback if the project's deps don't resolve
|
|
85
|
+
* cleanly). On dxkit's own repo this caused subsequent dxkit subcommand
|
|
86
|
+
* invocations to crash with `Cannot find module 'hosted-git-info'`
|
|
87
|
+
* because osv-scanner's reinstall left an incomplete tree. On
|
|
88
|
+
* vyuhlabs-platform (835MB node_modules) the reinstall happened to
|
|
89
|
+
* succeed but still mutated state silently — a 5-month-old data-mutation
|
|
90
|
+
* bug shipped since 2.4.0 / Phase 10h.6 and only caught now because
|
|
91
|
+
* 2.4.5 pre-ship regression ran reports back-to-back on dxkit's own repo
|
|
92
|
+
* and tripped the chain.
|
|
93
|
+
*
|
|
94
|
+
* Mitigation: we copy `package.json` + `package-lock.json` to a fresh
|
|
95
|
+
* temp dir, run osv-scanner there, and discard the temp dir afterward.
|
|
96
|
+
* Output JSON identifies advisories by `(package, version, id)` tuples
|
|
97
|
+
* which are independent of cwd, so the upgrade-plan map we return is
|
|
98
|
+
* unchanged. The caller's project gets read-only treatment.
|
|
78
99
|
*/
|
|
79
100
|
async function gatherOsvScannerFixPlans(cwd) {
|
|
80
101
|
const manifestRel = 'package.json';
|
|
@@ -87,28 +108,32 @@ async function gatherOsvScannerFixPlans(cwd) {
|
|
|
87
108
|
const tool = (0, tool_registry_1.findTool)(tool_registry_1.TOOL_DEFS['osv-scanner'], cwd);
|
|
88
109
|
if (!tool.available || !tool.path)
|
|
89
110
|
return new Map();
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
let raw;
|
|
111
|
+
// Stage manifest + lockfile in a fresh temp dir. osv-scanner mutates
|
|
112
|
+
// node_modules in its working directory; staging here protects the
|
|
113
|
+
// caller's actual project tree from any side effects.
|
|
114
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'dxkit-osv-fix-'));
|
|
95
115
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
const
|
|
107
|
-
|
|
116
|
+
fs.copyFileSync(manifestAbs, path.join(tempDir, manifestRel));
|
|
117
|
+
fs.copyFileSync(lockfileAbs, path.join(tempDir, lockfileRel));
|
|
118
|
+
// Use runDetached (10k.1.5c addition) to put osv-scanner in its own
|
|
119
|
+
// process group. osv-scanner's `fix` subcommand spawns `npm install`
|
|
120
|
+
// grandchildren that orphan if execSync's timeout merely sends
|
|
121
|
+
// SIGTERM to the immediate child (the original 10k.1.5b fix only
|
|
122
|
+
// addressed node_modules mutation; the orphan-process leak was a
|
|
123
|
+
// separate symptom of the same root cause). runDetached SIGKILLs
|
|
124
|
+
// the entire group on timeout — taking npm-install + its
|
|
125
|
+
// subprocesses with it.
|
|
126
|
+
const outcome = await (0, runner_1.runDetached)(tool.path, ['fix', '--format', 'json', '--manifest', manifestRel, '--lockfile', lockfileRel], { cwd: tempDir, timeoutMs: 120_000 });
|
|
127
|
+
// osv-scanner exits non-zero whenever it finds vulns, even though
|
|
128
|
+
// the JSON payload is complete. Always parse stdout regardless of
|
|
129
|
+
// exit code. Empty stdout (timed-out or hard-failed) → empty map.
|
|
130
|
+
if (!outcome.stdout)
|
|
108
131
|
return new Map();
|
|
109
|
-
|
|
132
|
+
return parseOsvScannerFixOutput(outcome.stdout);
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
110
136
|
}
|
|
111
|
-
return parseOsvScannerFixOutput(raw);
|
|
112
137
|
}
|
|
113
138
|
/**
|
|
114
139
|
* Parse raw `osv-scanner fix --format json` stdout into a plan-lookup map.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"osv-scanner-fix.js","sourceRoot":"","sources":["../../../src/analyzers/tools/osv-scanner-fix.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"osv-scanner-fix.js","sourceRoot":"","sources":["../../../src/analyzers/tools/osv-scanner-fix.ts"],"names":[],"mappings":"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"}
|