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/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 ((_k = data.supplyChain) === null || _k === void 0 ? void 0 : _k.signals) || []) {
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 = (_l = data.supplyChain) === null || _l === void 0 ? void 0 : _l.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',