dependency-radar 0.8.1 → 0.9.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/README.md +176 -15
- package/dist/aggregator.js +458 -15
- package/dist/cli.js +60 -6
- package/dist/explain.js +83 -1
- package/dist/failOn.js +370 -1
- package/dist/findings.js +81 -3
- package/dist/report-assets.js +3 -4
- package/dist/reportDetailRules.js +162 -0
- package/dist/runners/npmRegistryMetadata.js +390 -0
- package/package.json +4 -4
package/dist/findings.js
CHANGED
|
@@ -17,6 +17,14 @@ function supportsTargetNode(dep, targetNodeMajor) {
|
|
|
17
17
|
return undefined;
|
|
18
18
|
return (0, nodeEngine_1.isNodeEngineTargetCompatible)(dep.upgrade.nodeEngine, targetNodeMajor);
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Constructs a DependencyFinding by combining package identifiers derived from a dependency with the provided finding fields.
|
|
22
|
+
*
|
|
23
|
+
* @param dep - Dependency record used to derive `packageId`, `packageName`, `packageVersion`, and to generate the finding `id`.
|
|
24
|
+
* @param suffix - Suffix appended to the package-based id to produce the finding `id`.
|
|
25
|
+
* @param fields - Remaining `DependencyFinding` properties to include; must not contain `id`, `packageId`, `packageName`, or `packageVersion`.
|
|
26
|
+
* @returns The assembled `DependencyFinding` with `id`, `packageId`, `packageName`, `packageVersion`, and the supplied fields.
|
|
27
|
+
*/
|
|
20
28
|
function baseFinding(dep, suffix, fields) {
|
|
21
29
|
return {
|
|
22
30
|
id: findingId(dep, suffix),
|
|
@@ -26,8 +34,41 @@ function baseFinding(dep, suffix, fields) {
|
|
|
26
34
|
...fields
|
|
27
35
|
};
|
|
28
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Produce the execution signals for a dependency after removing any signals that originate from install-only scripts.
|
|
39
|
+
*
|
|
40
|
+
* @param dep - Dependency record to inspect for execution signals and script-specific signals
|
|
41
|
+
* @returns A sorted array of execution signal keys that are not associated with install-only scripts
|
|
42
|
+
*/
|
|
43
|
+
function withoutInstallOnlySignals(dep) {
|
|
44
|
+
var _a, _b, _c;
|
|
45
|
+
const all = new Set(((_a = dep.execution) === null || _a === void 0 ? void 0 : _a.signals) || []);
|
|
46
|
+
for (const signal of ((_c = (_b = dep.execution) === null || _b === void 0 ? void 0 : _b.scripts) === null || _c === void 0 ? void 0 : _c.signals) || []) {
|
|
47
|
+
all.delete(signal);
|
|
48
|
+
}
|
|
49
|
+
return Array.from(all).sort();
|
|
50
|
+
}
|
|
51
|
+
const REGISTRY_SIGNAL_TITLES = {
|
|
52
|
+
'recent-package': 'Recently created package',
|
|
53
|
+
'recent-version': 'Recently published installed version',
|
|
54
|
+
'low-release-history': 'Low release history',
|
|
55
|
+
'reactivated-package': 'Package reactivated after dormancy',
|
|
56
|
+
'old-major-new-patch': 'Recent patch on older major line'
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Convert aggregated dependency and supply-chain data into a sorted list of dependency findings.
|
|
60
|
+
*
|
|
61
|
+
* Processes each dependency and the supply-chain signals to emit findings for security, license/compliance,
|
|
62
|
+
* execution and packaging signals, registry metadata, provenance/signature verification, and Node engine
|
|
63
|
+
* compatibility relative to an optional target Node major version.
|
|
64
|
+
*
|
|
65
|
+
* @param data - Aggregated input containing `dependencies` and `supplyChain` information.
|
|
66
|
+
* @param options.targetNodeMajor - If provided, emits findings when a dependency's declared Node engine
|
|
67
|
+
* does not appear to include the given major Node version.
|
|
68
|
+
* @returns A list of DependencyFinding objects sorted by severity (error, warning, info), then by `packageId`, then by `id`.
|
|
69
|
+
*/
|
|
29
70
|
function buildDependencyFindings(data, options = {}) {
|
|
30
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
71
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
|
|
31
72
|
const findings = [];
|
|
32
73
|
for (const dep of Object.values(data.dependencies || {})) {
|
|
33
74
|
const vulnCount = vulnerabilityTotal(dep);
|
|
@@ -82,6 +123,17 @@ function buildDependencyFindings(data, options = {}) {
|
|
|
82
123
|
recommendation: 'Review install-time behavior, especially in CI and release environments.'
|
|
83
124
|
}));
|
|
84
125
|
}
|
|
126
|
+
const executionSignals = withoutInstallOnlySignals(dep);
|
|
127
|
+
if (executionSignals.length > 0) {
|
|
128
|
+
findings.push(baseFinding(dep, 'local-execution-signals', {
|
|
129
|
+
category: 'execution',
|
|
130
|
+
severity: 'warning',
|
|
131
|
+
title: 'Local execution capability signal',
|
|
132
|
+
message: `${dep.package.id} contains local execution capability signals that may warrant review.`,
|
|
133
|
+
evidence: executionSignals.join(', '),
|
|
134
|
+
recommendation: 'Review the referenced package entrypoints or executables and confirm this behavior is expected.'
|
|
135
|
+
}));
|
|
136
|
+
}
|
|
85
137
|
if ((_j = dep.execution) === null || _j === void 0 ? void 0 : _j.native) {
|
|
86
138
|
findings.push(baseFinding(dep, 'native-bindings', {
|
|
87
139
|
category: 'upgrade',
|
|
@@ -100,6 +152,32 @@ function buildDependencyFindings(data, options = {}) {
|
|
|
100
152
|
recommendation: 'Plan migration to a maintained replacement.'
|
|
101
153
|
}));
|
|
102
154
|
}
|
|
155
|
+
if ((_l = (_k = dep.packaging) === null || _k === void 0 ? void 0 : _k.signals) === null || _l === void 0 ? void 0 : _l.length) {
|
|
156
|
+
findings.push(baseFinding(dep, 'packaging-signals', {
|
|
157
|
+
category: 'supply-chain',
|
|
158
|
+
severity: 'info',
|
|
159
|
+
title: 'Package packaging review signal',
|
|
160
|
+
message: `${dep.package.id} has packaging signals that may warrant review.`,
|
|
161
|
+
evidence: dep.packaging.signals.join(', '),
|
|
162
|
+
recommendation: 'Review package contents and confirm the packaging pattern is expected.'
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
for (const signal of ((_o = (_m = dep.supplyChain) === null || _m === void 0 ? void 0 : _m.registry) === null || _o === void 0 ? void 0 : _o.signals) || []) {
|
|
166
|
+
const registry = (_p = dep.supplyChain) === null || _p === void 0 ? void 0 : _p.registry;
|
|
167
|
+
findings.push(baseFinding(dep, `registry-${signal}`, {
|
|
168
|
+
category: 'supply-chain',
|
|
169
|
+
severity: 'info',
|
|
170
|
+
title: REGISTRY_SIGNAL_TITLES[signal] || 'Registry metadata review signal',
|
|
171
|
+
message: `${dep.package.id} has npm registry metadata that may warrant review: ${signal}.`,
|
|
172
|
+
evidence: [
|
|
173
|
+
(registry === null || registry === void 0 ? void 0 : registry.installedVersionPublishedAt) ? `installedVersionPublishedAt=${registry.installedVersionPublishedAt}` : undefined,
|
|
174
|
+
(registry === null || registry === void 0 ? void 0 : registry.packageCreatedAt) ? `packageCreatedAt=${registry.packageCreatedAt}` : undefined,
|
|
175
|
+
typeof (registry === null || registry === void 0 ? void 0 : registry.versionCount) === 'number' ? `versionCount=${registry.versionCount}` : undefined,
|
|
176
|
+
(registry === null || registry === void 0 ? void 0 : registry.latestVersion) ? `latest=${registry.latestVersion}` : undefined
|
|
177
|
+
].filter(Boolean).join('; '),
|
|
178
|
+
recommendation: 'Review the package release history and confirm this registry activity is expected.'
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
103
181
|
const targetSupport = supportsTargetNode(dep, options.targetNodeMajor);
|
|
104
182
|
if (targetSupport === false && options.targetNodeMajor) {
|
|
105
183
|
findings.push(baseFinding(dep, `target-node-${options.targetNodeMajor}`, {
|
|
@@ -111,10 +189,10 @@ function buildDependencyFindings(data, options = {}) {
|
|
|
111
189
|
}));
|
|
112
190
|
}
|
|
113
191
|
}
|
|
114
|
-
for (const signal of ((
|
|
192
|
+
for (const signal of ((_q = data.supplyChain) === null || _q === void 0 ? void 0 : _q.signals) || []) {
|
|
115
193
|
findings.push(buildSupplyChainFinding(signal));
|
|
116
194
|
}
|
|
117
|
-
const signatureAudit = (
|
|
195
|
+
const signatureAudit = (_r = data.supplyChain) === null || _r === void 0 ? void 0 : _r.signatureAudit;
|
|
118
196
|
if ((signatureAudit === null || signatureAudit === void 0 ? void 0 : signatureAudit.attempted) && !signatureAudit.ok) {
|
|
119
197
|
findings.push({
|
|
120
198
|
id: 'supply-chain:signature-verification-failed',
|