fad-checker 2.4.1 → 2.4.3
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 +77 -0
- package/README.md +3 -2
- package/completions/fad-checker.bash +1 -1
- package/completions/fad-checker.zsh +3 -0
- package/data/cpe-coord-map.json +1 -0
- package/fad-checker.js +103 -13
- package/lib/certs/analyze.js +305 -0
- package/lib/certs/index.js +17 -0
- package/lib/certs/scan.js +67 -0
- package/lib/certs/sniff.js +29 -0
- package/lib/codecs/go/parse.js +50 -8
- package/lib/codecs/go/registry.js +3 -1
- package/lib/codecs/go.codec.js +22 -4
- package/lib/codecs/nuget/parse.js +21 -6
- package/lib/codecs/nuget/registry.js +30 -4
- package/lib/codecs/nuget.codec.js +37 -7
- package/lib/codecs/pypi/parse.js +11 -2
- package/lib/codecs/pypi/registry.js +3 -1
- package/lib/codecs/pypi.codec.js +3 -0
- package/lib/cve-download.js +102 -11
- package/lib/cve-report.js +61 -11
- package/lib/deps-descriptor.js +24 -0
- package/lib/json-export.js +4 -1
- package/lib/maven-bom.js +77 -3
- package/lib/maven-version.js +25 -16
- package/lib/osv.js +8 -4
- package/lib/provenance.js +2 -0
- package/lib/sarif-export.js +48 -2
- package/lib/transitive.js +20 -6
- package/package.json +1 -1
package/lib/maven-bom.js
CHANGED
|
@@ -15,6 +15,76 @@
|
|
|
15
15
|
* skips it offline). Pure except for the injected/real effectivePom fetch.
|
|
16
16
|
*/
|
|
17
17
|
const transitive = require("./transitive");
|
|
18
|
+
const core = require("./core");
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Walk each parsed pom's LOCAL parent chain and collect the distinct EXTERNAL parent
|
|
22
|
+
* coordinates — the parent POMs referenced by a `<parent>` element but NOT present in
|
|
23
|
+
* the source tree (e.g. `spring-boot-starter-parent`, whose own parent
|
|
24
|
+
* `spring-boot-dependencies` holds the managed-version table). Maven inherits their
|
|
25
|
+
* `<dependencyManagement>` into the child, which is how a versionless
|
|
26
|
+
* `spring-boot-starter-actuator` gets its version — but core.js only follows LOCAL
|
|
27
|
+
* parents, so without this those versions stay unresolved and drop out of the scan.
|
|
28
|
+
*
|
|
29
|
+
* Deduped by g:a:v; unresolved (`${…}`) or incomplete coords are skipped. The result
|
|
30
|
+
* is fed to resolveBomManagedVersions/backfillVersions exactly like an import BOM.
|
|
31
|
+
* @param store the metadata store (byPath[pom].parentInfo + byId), as built by core.js
|
|
32
|
+
* @returns [{ groupId, artifactId, version }]
|
|
33
|
+
*/
|
|
34
|
+
function collectExternalParents(store) {
|
|
35
|
+
if (!store || !store.byPath) return [];
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const out = [];
|
|
38
|
+
for (const pomPath of Object.keys(store.byPath)) {
|
|
39
|
+
let cur = pomPath;
|
|
40
|
+
const localSeen = new Set();
|
|
41
|
+
while (cur && !localSeen.has(cur)) {
|
|
42
|
+
localSeen.add(cur);
|
|
43
|
+
const meta = store.byPath[cur];
|
|
44
|
+
if (!meta) break;
|
|
45
|
+
const parentPath = core.resolveParentPath(cur, meta.parentInfo, store);
|
|
46
|
+
if (parentPath) { cur = parentPath; continue; } // local parent → keep climbing
|
|
47
|
+
// No local parent POM: if a <parent> is declared, it's EXTERNAL.
|
|
48
|
+
const p = meta.parentInfo;
|
|
49
|
+
if (p && p.groupId && p.artifactId && p.version && !/\$\{/.test(String(p.version))) {
|
|
50
|
+
const k = `${p.groupId}:${p.artifactId}:${p.version}`;
|
|
51
|
+
if (!seen.has(k)) { seen.add(k); out.push({ groupId: p.groupId, artifactId: p.artifactId, version: p.version }); }
|
|
52
|
+
}
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Collect the version-property OVERRIDES a project declares in its own `<properties>`
|
|
61
|
+
* blocks — the Maven mechanism for patching a coord managed by an external parent
|
|
62
|
+
* (e.g. `<log4j2.version>2.17.1</log4j2.version>` overrides spring-boot-dependencies'
|
|
63
|
+
* default). Fed to effectivePom as `propertyOverrides` so the parent's managed table
|
|
64
|
+
* resolves to the version actually on the classpath, not the framework default.
|
|
65
|
+
*
|
|
66
|
+
* Source is each pom's OWN declared props (store.byPath[pom].properties, raw xml2js
|
|
67
|
+
* `{name:[value]}`) — NOT the merged/inherited map — so we never mistake an inherited
|
|
68
|
+
* builtin for an override. `project.*`/`pom.*` model expressions and unresolved `${…}`
|
|
69
|
+
* values are excluded. Merged across modules (last wins); real-world overrides live in
|
|
70
|
+
* the root and are consistent, so the common case is exact.
|
|
71
|
+
* @returns { [propName]: value }
|
|
72
|
+
*/
|
|
73
|
+
function collectPropertyOverrides(store) {
|
|
74
|
+
const out = {};
|
|
75
|
+
if (!store || !store.byPath) return out;
|
|
76
|
+
for (const meta of Object.values(store.byPath)) {
|
|
77
|
+
const raw = meta && meta.properties;
|
|
78
|
+
if (!raw || typeof raw !== "object") continue;
|
|
79
|
+
for (const [k, val] of Object.entries(raw)) {
|
|
80
|
+
if (k.startsWith("project.") || k.startsWith("pom.")) continue;
|
|
81
|
+
const v = Array.isArray(val) ? val[0] : val;
|
|
82
|
+
if (typeof v !== "string" || /\$\{/.test(v)) continue;
|
|
83
|
+
out[k] = v;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
18
88
|
|
|
19
89
|
/**
|
|
20
90
|
* Extract the distinct external import-BOM coordinates from the parsed poms' merged
|
|
@@ -57,6 +127,10 @@ function collectImportBoms(propsByPom) {
|
|
|
57
127
|
*/
|
|
58
128
|
async function resolveBomManagedVersions(boms, opts = {}) {
|
|
59
129
|
const effectivePom = opts.effectivePom || transitive.effectivePom;
|
|
130
|
+
// `via` labels where the managed version came from: "bom" (import BOM, the default)
|
|
131
|
+
// or "parent" (an external <parent> chain). Only stamped when non-default so the
|
|
132
|
+
// import-BOM entry shape stays byte-identical to before.
|
|
133
|
+
const via = opts.via && opts.via !== "bom" ? opts.via : null;
|
|
60
134
|
const map = new Map();
|
|
61
135
|
for (const bom of boms) {
|
|
62
136
|
let eff = null;
|
|
@@ -68,7 +142,7 @@ async function resolveBomManagedVersions(boms, opts = {}) {
|
|
|
68
142
|
if (!d.groupId || !d.artifactId) continue;
|
|
69
143
|
const k = `${d.groupId}:${d.artifactId}`;
|
|
70
144
|
if (map.has(k)) continue;
|
|
71
|
-
if (d.version && !/\$\{/.test(String(d.version))) map.set(k, { version: String(d.version), bom: bomCoord });
|
|
145
|
+
if (d.version && !/\$\{/.test(String(d.version))) map.set(k, { version: String(d.version), bom: bomCoord, ...(via ? { via } : {}) });
|
|
72
146
|
}
|
|
73
147
|
}
|
|
74
148
|
return map;
|
|
@@ -94,7 +168,7 @@ function backfillVersions(resolvedDeps, mgmtMap) {
|
|
|
94
168
|
dep.version = v;
|
|
95
169
|
if (!Array.isArray(dep.versions)) dep.versions = [];
|
|
96
170
|
if (!dep.versions.includes(v)) dep.versions.push(v);
|
|
97
|
-
dep.versionSource = { via: "bom", bom: entry.bom };
|
|
171
|
+
dep.versionSource = { via: entry.via || "bom", bom: entry.bom };
|
|
98
172
|
filled++;
|
|
99
173
|
}
|
|
100
174
|
return filled;
|
|
@@ -113,4 +187,4 @@ async function resolveAndBackfill(propsByPom, resolvedDeps, opts = {}) {
|
|
|
113
187
|
return { boms: boms.length, filled, mgmtSize: map.size };
|
|
114
188
|
}
|
|
115
189
|
|
|
116
|
-
module.exports = { collectImportBoms, resolveBomManagedVersions, backfillVersions, resolveAndBackfill };
|
|
190
|
+
module.exports = { collectExternalParents, collectPropertyOverrides, collectImportBoms, resolveBomManagedVersions, backfillVersions, resolveAndBackfill };
|
package/lib/maven-version.js
CHANGED
|
@@ -103,6 +103,14 @@ function compareMavenVersions(aStr, bStr) {
|
|
|
103
103
|
* spec shape: { version, status, lessThan, lessThanOrEqual, versionType }
|
|
104
104
|
* Returns true if depVersion is affected.
|
|
105
105
|
*/
|
|
106
|
+
// A bound participates in comparisons only if it looks like a version. CVE 5.x
|
|
107
|
+
// records carry placeholders in these fields ("log4j-core*", "*", "unspecified")
|
|
108
|
+
// — comparing those as Maven versions is garbage (alpha sorts below numeric), so
|
|
109
|
+
// CVE-2021-44228's `lessThan: "log4j-core*"` used to unmatch every real version.
|
|
110
|
+
function versionLikeBound(s) {
|
|
111
|
+
return s != null && /^[0-9]/.test(String(s).trim()) && String(s).trim() !== "0";
|
|
112
|
+
}
|
|
113
|
+
|
|
106
114
|
function isVersionAffected(depVersion, spec) {
|
|
107
115
|
if (!spec) return false;
|
|
108
116
|
if (spec.status && spec.status !== "affected") return false;
|
|
@@ -110,27 +118,27 @@ function isVersionAffected(depVersion, spec) {
|
|
|
110
118
|
const dep = parseMavenVersion(depVersion);
|
|
111
119
|
if (!dep.segments.length) return false;
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
const lower = versionLikeBound(spec.version) && spec.version !== "*" ? spec.version : null;
|
|
122
|
+
const upperExcl = versionLikeBound(spec.lessThan) ? spec.lessThan : null;
|
|
123
|
+
const upperIncl = versionLikeBound(spec.lessThanOrEqual) ? spec.lessThanOrEqual : null;
|
|
124
|
+
|
|
125
|
+
// Fail-closed: a spec with no usable version constraint carries no information.
|
|
114
126
|
// Without this guard the function falls through to `return true` for every input,
|
|
115
|
-
// which was the H1 cascade described in CRITICAL-REVIEW.md.
|
|
116
|
-
|
|
117
|
-
if (!
|
|
127
|
+
// which was the H1 cascade described in CRITICAL-REVIEW.md. A wildcard/placeholder
|
|
128
|
+
// upper on its own does not count ({version:"*", lessThan:"*"} must stay inert).
|
|
129
|
+
if (!lower && !upperExcl && !upperIncl) return false;
|
|
118
130
|
|
|
119
131
|
// Lower bound (inclusive)
|
|
120
|
-
if (
|
|
121
|
-
if (compareMavenVersions(depVersion, spec.version) < 0) return false;
|
|
122
|
-
}
|
|
132
|
+
if (lower && compareMavenVersions(depVersion, lower) < 0) return false;
|
|
123
133
|
// Upper bound exclusive
|
|
124
|
-
if (
|
|
125
|
-
if (compareMavenVersions(depVersion, spec.lessThan) >= 0) return false;
|
|
126
|
-
}
|
|
134
|
+
if (upperExcl && compareMavenVersions(depVersion, upperExcl) >= 0) return false;
|
|
127
135
|
// Upper bound inclusive
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
//
|
|
132
|
-
if (
|
|
133
|
-
if (compareMavenVersions(depVersion,
|
|
136
|
+
if (upperIncl && compareMavenVersions(depVersion, upperIncl) > 0) return false;
|
|
137
|
+
// Exact match with no upper of ANY kind (not even a wildcard) — only affected if
|
|
138
|
+
// equal. A wildcard upper ({version:"2.0", lessThan:"log4j-core*"}) instead means
|
|
139
|
+
// "from 2.0 onward, unbounded", so it must NOT collapse to an exact match.
|
|
140
|
+
if (lower && spec.lessThan == null && spec.lessThanOrEqual == null) {
|
|
141
|
+
if (compareMavenVersions(depVersion, lower) !== 0) return false;
|
|
134
142
|
}
|
|
135
143
|
return true;
|
|
136
144
|
}
|
|
@@ -160,5 +168,6 @@ module.exports = {
|
|
|
160
168
|
parseMavenVersion,
|
|
161
169
|
compareMavenVersions,
|
|
162
170
|
isVersionAffected,
|
|
171
|
+
versionLikeBound,
|
|
163
172
|
parseRange,
|
|
164
173
|
};
|
package/lib/osv.js
CHANGED
|
@@ -31,12 +31,16 @@ function cacheKey(g, a, v, ecosystem = "maven") {
|
|
|
31
31
|
return `${ecosystem}__${safeG}__${safeA}__${v}.json`;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
// OFFLINE bypasses the TTL (same rule as lib/nvd.js): the warmed cache is the
|
|
35
|
+
// ONLY source on an air-gapped box, so a >12h-old entry must still be served —
|
|
36
|
+
// expiring it there silently reported "0 OSV vulns" for every ecosystem.
|
|
37
|
+
// Online keeps enforcing the TTL so entries refresh normally.
|
|
38
|
+
function readCache(name, { ignoreTtl = false } = {}) {
|
|
35
39
|
const p = path.join(OSV_CACHE_DIR, name);
|
|
36
40
|
if (!fs.existsSync(p)) return null;
|
|
37
41
|
try {
|
|
38
42
|
const data = JSON.parse(fs.readFileSync(p, "utf8"));
|
|
39
|
-
if (Date.now() - data._fetchedAt < OSV_CACHE_TTL_MS) return data.body;
|
|
43
|
+
if (ignoreTtl || Date.now() - data._fetchedAt < OSV_CACHE_TTL_MS) return data.body;
|
|
40
44
|
} catch { /* ignore */ }
|
|
41
45
|
return null;
|
|
42
46
|
}
|
|
@@ -223,7 +227,7 @@ async function queryBatch(deps, opts = {}) {
|
|
|
223
227
|
for (let i = 0; i < deps.length; i++) {
|
|
224
228
|
const d = deps[i];
|
|
225
229
|
const ck = cacheKey(d.groupId, d.artifactId, d.version, d.ecosystem || "maven");
|
|
226
|
-
const hit = readCache(ck);
|
|
230
|
+
const hit = readCache(ck, { ignoreTtl: !!offline });
|
|
227
231
|
if (hit !== null) {
|
|
228
232
|
indexMap[i] = { cached: hit };
|
|
229
233
|
} else {
|
|
@@ -297,7 +301,7 @@ async function queryBatch(deps, opts = {}) {
|
|
|
297
301
|
const liveIds = [];
|
|
298
302
|
for (const id of allIds) {
|
|
299
303
|
const detailCacheKey = `vuln_${id}.json`;
|
|
300
|
-
const hit = readCache(detailCacheKey);
|
|
304
|
+
const hit = readCache(detailCacheKey, { ignoreTtl: !!offline });
|
|
301
305
|
if (hit) { detailById.set(id, hit); continue; }
|
|
302
306
|
if (offline) continue;
|
|
303
307
|
liveIds.push(id);
|
package/lib/provenance.js
CHANGED
|
@@ -109,6 +109,8 @@ function runConfiguration(options = {}) {
|
|
|
109
109
|
allLibs: options.allLibs !== false,
|
|
110
110
|
licenses: !!options.licenses,
|
|
111
111
|
typosquat: !!options.typosquat,
|
|
112
|
+
certs: options.certs !== false,
|
|
113
|
+
certExpiryDays: options.certExpiryDays != null ? String(options.certExpiryDays) : null,
|
|
112
114
|
failOn: options.failOn || "none",
|
|
113
115
|
ignoreFile: options.ignore || null,
|
|
114
116
|
vexFile: options.vex || null,
|
package/lib/sarif-export.js
CHANGED
|
@@ -42,12 +42,25 @@ function depCoord(dep) {
|
|
|
42
42
|
return name;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Certificate / key-material finding-type metadata → SARIF rule + level. Keyed by
|
|
46
|
+
// the analyze.js issue `type`. security-severity is a CVSS-like number for GitHub.
|
|
47
|
+
const CERT_RULES = {
|
|
48
|
+
"private-key-committed": { level: "error", score: "9.8", desc: "Committed private key" },
|
|
49
|
+
"keystore-committed": { level: "warning", score: "5.0", desc: "Committed keystore" },
|
|
50
|
+
"public-key-committed": { level: "note", score: "2.0", desc: "Committed public key" },
|
|
51
|
+
"cert-expired": { level: "error", score: "7.5", desc: "Expired certificate" },
|
|
52
|
+
"cert-expiring": { level: "warning", score: "4.0", desc: "Certificate expiring soon" },
|
|
53
|
+
"cert-weak-key": { level: "error", score: "7.5", desc: "Weak certificate key" },
|
|
54
|
+
"cert-weak-signature": { level: "error", score: "7.5", desc: "Weak certificate signature algorithm" },
|
|
55
|
+
"cert-self-signed": { level: "note", score: "2.0", desc: "Self-signed certificate" },
|
|
56
|
+
};
|
|
57
|
+
|
|
45
58
|
/**
|
|
46
59
|
* Build a SARIF 2.1.0 object from matches.
|
|
47
|
-
* opts: { projectInfo, toolVersion }
|
|
60
|
+
* opts: { projectInfo, toolVersion, certFindings }
|
|
48
61
|
*/
|
|
49
62
|
function buildSarif(matches, opts = {}) {
|
|
50
|
-
const { projectInfo = {}, toolVersion = "0" } = opts;
|
|
63
|
+
const { projectInfo = {}, toolVersion = "0", certFindings = [] } = opts;
|
|
51
64
|
const srcRoot = projectInfo.src && projectInfo.src.startsWith("(") ? null : projectInfo.src;
|
|
52
65
|
|
|
53
66
|
const rulesById = new Map();
|
|
@@ -108,6 +121,39 @@ function buildSarif(matches, opts = {}) {
|
|
|
108
121
|
});
|
|
109
122
|
}
|
|
110
123
|
|
|
124
|
+
// Certificate / key-material findings: one rule per finding-type, one result per
|
|
125
|
+
// (file, issue). Located at the file itself (not a manifest).
|
|
126
|
+
for (const c of certFindings || []) {
|
|
127
|
+
for (const issue of c.issues || []) {
|
|
128
|
+
const meta = CERT_RULES[issue.type];
|
|
129
|
+
if (!meta) continue;
|
|
130
|
+
const ruleId = `FAD-${issue.type.toUpperCase()}`;
|
|
131
|
+
if (!rulesById.has(ruleId)) {
|
|
132
|
+
rulesById.set(ruleId, {
|
|
133
|
+
id: ruleId, name: ruleId,
|
|
134
|
+
shortDescription: { text: meta.desc },
|
|
135
|
+
properties: { "security-severity": meta.score, tags: ["security", "cryptography", "key-material"] },
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
const uri = relUri(c.path, srcRoot);
|
|
139
|
+
const vis = c.keyVisibility ? ` [${c.keyVisibility} key]` : "";
|
|
140
|
+
results.push({
|
|
141
|
+
ruleId,
|
|
142
|
+
level: meta.level,
|
|
143
|
+
message: { text: `${meta.desc}${vis}: ${issue.message} — ${uri || c.path}` },
|
|
144
|
+
...(uri ? { locations: [{ physicalLocation: { artifactLocation: { uri } } }] } : {}),
|
|
145
|
+
partialFingerprints: { fadKey: `${c.sha256 || c.path}|${issue.type}` },
|
|
146
|
+
properties: {
|
|
147
|
+
kind: c.kind,
|
|
148
|
+
...(c.keyVisibility ? { keyVisibility: c.keyVisibility } : {}),
|
|
149
|
+
...(c.algorithm ? { algorithm: c.algorithm } : {}),
|
|
150
|
+
...(c.format ? { format: c.format } : {}),
|
|
151
|
+
...(c.sha256 ? { sha256: c.sha256 } : {}),
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
111
157
|
return {
|
|
112
158
|
$schema: "https://json.schemastore.org/sarif-2.1.0.json",
|
|
113
159
|
version: "2.1.0",
|
package/lib/transitive.js
CHANGED
|
@@ -181,7 +181,14 @@ function resolveProps(value, props, builtins, depth = 0) {
|
|
|
181
181
|
* BOM imports inside depMgmt are recursively expanded.
|
|
182
182
|
*/
|
|
183
183
|
async function effectivePom(g, a, v, opts = {}, seen = new Set()) {
|
|
184
|
-
|
|
184
|
+
// A downstream project inheriting this POM as its <parent> can override a version
|
|
185
|
+
// property in its own <properties> (the Spring Boot way to patch a managed coord).
|
|
186
|
+
// opts.propertyOverrides models that: they win over this chain's own property values
|
|
187
|
+
// when resolving depMgmt. They're part of the cache key so an override-free resolution
|
|
188
|
+
// (import-BOM path) and an overridden one (parent path) don't collide in effCache.
|
|
189
|
+
const ovKeys = opts.propertyOverrides ? Object.keys(opts.propertyOverrides) : [];
|
|
190
|
+
const ovSuffix = ovKeys.length ? "|ov:" + ovKeys.sort().map(k => `${k}=${opts.propertyOverrides[k]}`).join(",") : "";
|
|
191
|
+
const key = `${g}:${a}:${v}${ovSuffix}`;
|
|
185
192
|
if (seen.has(key)) return null;
|
|
186
193
|
// Opt-in cross-call memo (used by the per-module overlay, which resolves the
|
|
187
194
|
// same shared parents/BOMs once per module). Immutable POMs → the effective
|
|
@@ -227,21 +234,28 @@ async function effectivePom(g, a, v, opts = {}, seen = new Set()) {
|
|
|
227
234
|
"pom.artifactId": merged.artifactId,
|
|
228
235
|
"pom.version": merged.version,
|
|
229
236
|
};
|
|
237
|
+
// Child overrides win over this chain's own property values (Maven: a child's
|
|
238
|
+
// <properties> override an inherited parent's). Applies to the PARENT chain only —
|
|
239
|
+
// import-BOM managed versions resolve in the BOM's own context (stripped below).
|
|
240
|
+
const effProps = ovKeys.length ? { ...merged.properties, ...opts.propertyOverrides } : merged.properties;
|
|
230
241
|
const resolveDep = d => ({
|
|
231
242
|
...d,
|
|
232
|
-
groupId: resolveProps(d.groupId,
|
|
233
|
-
artifactId: resolveProps(d.artifactId,
|
|
234
|
-
version: resolveProps(d.version,
|
|
243
|
+
groupId: resolveProps(d.groupId, effProps, builtins),
|
|
244
|
+
artifactId: resolveProps(d.artifactId, effProps, builtins),
|
|
245
|
+
version: resolveProps(d.version, effProps, builtins),
|
|
235
246
|
});
|
|
236
247
|
merged.depMgmt = merged.depMgmt.map(resolveDep);
|
|
237
248
|
merged.deps = merged.deps.map(resolveDep);
|
|
238
249
|
|
|
239
250
|
// Expand BOM imports inside depMgmt: any entry with scope=import + type=pom
|
|
240
|
-
// is replaced by the depMgmt entries from that imported POM.
|
|
251
|
+
// is replaced by the depMgmt entries from that imported POM. A <scope>import</scope>
|
|
252
|
+
// BOM resolves its managed versions in ITS OWN property context — the importing
|
|
253
|
+
// project's overrides do NOT reach it — so drop propertyOverrides across this boundary.
|
|
254
|
+
const importOpts = ovKeys.length ? { ...opts, propertyOverrides: undefined } : opts;
|
|
241
255
|
const expanded = [];
|
|
242
256
|
for (const dm of merged.depMgmt) {
|
|
243
257
|
if (dm.scope === "import") {
|
|
244
|
-
const imported = await effectivePom(dm.groupId, dm.artifactId, dm.version,
|
|
258
|
+
const imported = await effectivePom(dm.groupId, dm.artifactId, dm.version, importOpts, new Set(seen));
|
|
245
259
|
if (imported) expanded.push(...imported.depMgmt);
|
|
246
260
|
} else {
|
|
247
261
|
expanded.push(dm);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fad-checker",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.3",
|
|
4
4
|
"description": "Scan ALL Maven, Gradle, npm, Yarn, Composer, Python, C#/.NET, Go & Ruby dependencies — plus embedded JARs (fat-jars/war/ear) — in a source tree ONE SHOT without mvn/python/etc — CVE (EPSS/KEV-prioritised), EOL, obsolete, outdated & licenses, with SBOM/CSAF/SARIF/JSON exports, CI gating and fix recos",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sca",
|