Skip to content

Commit 63df914

Browse files
authored
feat(extractScannerData): refactor extract scanner data (#457)
* refactor(extractScannerData) use new Extractors api * --amend * update deps * refactor(extractScannerData): further refacto with extensions and node deps
1 parent 6fb6a59 commit 63df914

File tree

2 files changed

+72
-87
lines changed

2 files changed

+72
-87
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"@nodesecure/flags": "^3.0.3",
5050
"@nodesecure/ossf-scorecard-sdk": "^3.2.1",
5151
"@nodesecure/rc": "^4.1.0",
52-
"@nodesecure/scanner": "^6.5.0",
52+
"@nodesecure/scanner": "^6.8.0",
5353
"@nodesecure/utils": "^2.2.0",
5454
"@openally/mutex": "^1.0.0",
5555
"@topcli/spinner": "^3.0.0",

src/analysis/extractScannerData.ts

Lines changed: 71 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
/* eslint-disable max-depth */
21
// Import Node.js Dependencies
32
import fs from "node:fs";
43

54
// Import Third-party Dependencies
6-
import { formatBytes, getScoreColor, getVCSRepositoryPathAndPlatform } from "@nodesecure/utils";
5+
import { getScoreColor, getVCSRepositoryPathAndPlatform } from "@nodesecure/utils";
76
import { getManifest, getFlags } from "@nodesecure/flags/web";
87
import * as scorecard from "@nodesecure/ossf-scorecard-sdk";
9-
import type { Payload } from "@nodesecure/scanner";
8+
import { Extractors, type Payload, type Dependency, type DependencyVersion, type DependencyLinks } from "@nodesecure/scanner";
109
import type { RC } from "@nodesecure/rc";
1110

1211
// Import Internal Dependencies
@@ -62,11 +61,6 @@ export async function buildStatsFromScannerDependencies(
6261
const { reportConfig } = options;
6362

6463
const config = reportConfig ?? localStorage.getConfig().report!;
65-
const sizeStats = {
66-
all: 0,
67-
internal: 0,
68-
external: 0
69-
};
7064

7165
const stats: ReportStat = {
7266
size: {
@@ -111,86 +105,80 @@ export async function buildStatsFromScannerDependencies(
111105

112106
const payloads = Array.isArray(payloadFiles) ? payloadFiles : [payloadFiles];
113107
const npmConfig = config.npm!;
114-
for (const fileOrJson of payloads) {
115-
const dependencies = getPayloadDependencies(fileOrJson);
116-
117-
for (const [name, descriptor] of Object.entries(dependencies)) {
118-
const { versions, metadata } = descriptor;
119-
const isThird = npmConfig.organizationPrefix === null ?
120-
true :
121-
!name.startsWith(`${npmConfig.organizationPrefix}/`);
122-
123-
for (const human of metadata.maintainers) {
124-
if (human.email) {
125-
stats.authors[human.email] = human.email in stats.authors ?
126-
++stats.authors[human.email] : 1;
127-
}
128-
}
129108

130-
if (!(name in stats.packages)) {
131-
const { orgPrefix, name: splitName } = splitPackageWithOrg(name);
132-
const isGiven = config.npm?.packages.includes(splitName) && orgPrefix === config.npm?.organizationPrefix;
133-
if (isThird) {
134-
stats.packages_count.external++;
135-
}
136-
stats.packages[name] = { isThird, versions: new Set(), fullName: name, isGiven, flags: {} };
109+
const dependencies = payloads.reduce<Payload["dependencies"]>((acc, curr) => {
110+
const dep = getPayloadDependencies(curr);
111+
Object.assign(acc, dep);
112+
113+
return acc;
114+
}, {});
115+
116+
const extractor = new Extractors.Payload(dependencies, [
117+
new Extractors.Probes.Contacts(),
118+
new Extractors.Probes.Flags(),
119+
new Extractors.Probes.Licenses(),
120+
new Extractors.Probes.Warnings(),
121+
new Extractors.Probes.Size({ organizationPrefix: npmConfig.organizationPrefix }),
122+
new Extractors.Probes.Extensions(),
123+
new Extractors.Probes.NodeDependencies()
124+
]);
125+
126+
extractor.on("manifest", (spec: string,
127+
{ flags, links = [] }: Omit<DependencyVersion, "links"> & {
128+
links: DependencyLinks | never[];
129+
},
130+
{ name }: { name: string; dependency: Dependency; }) => {
131+
const isThird = npmConfig.organizationPrefix === null ?
132+
true :
133+
!name.startsWith(`${npmConfig.organizationPrefix}/`);
134+
if (!(name in stats.packages)) {
135+
const { orgPrefix, name: splitName } = splitPackageWithOrg(name);
136+
const isGiven = config.npm?.packages.includes(splitName) && orgPrefix === config.npm?.organizationPrefix;
137+
if (isThird) {
138+
stats.packages_count.external++;
137139
}
140+
stats.packages[name] = { isThird, versions: new Set(), fullName: name, isGiven, flags: {} };
141+
}
142+
const curr = stats.packages[name];
143+
if (curr.versions.has(spec)) {
144+
return;
145+
}
138146

139-
const curr = stats.packages[name];
140-
for (const [localVersion, localDescriptor] of Object.entries(versions)) {
141-
if (curr.versions.has(localVersion)) {
142-
continue;
143-
}
144-
const { flags, size, composition, uniqueLicenseIds, author, warnings = [], links = [] } = localDescriptor;
145-
146-
sizeStats.all += size;
147-
sizeStats[isThird ? "external" : "internal"] += size;
148-
149-
for (const { kind } of warnings) {
150-
stats.warnings[kind] = kind in stats.warnings ? ++stats.warnings[kind] : 1;
151-
}
152-
153-
for (const flag of flags) {
154-
if (!(flag in kWantedFlags)) {
155-
continue;
156-
}
157-
stats.flags[flag] = flag in stats.flags ? ++stats.flags[flag] : 1;
158-
stats.packages[name].flags[flag] = { ...stats.flagsList[flag] };
159-
}
160-
161-
(composition.required_nodejs)
162-
.forEach((dep) => (stats.deps.node[dep] = { visualizerUrl: `${kNodeVisualizerUrl}/${dep.replace("node:", "")}.html` }));
163-
for (const extName of composition.extensions.filter((extName) => extName !== "")) {
164-
stats.extensions[extName] = extName in stats.extensions ? ++stats.extensions[extName] : 1;
165-
}
166-
167-
for (const licenseName of uniqueLicenseIds) {
168-
stats.licenses[licenseName] = licenseName in stats.licenses ?
169-
++stats.licenses[licenseName] : 1;
170-
}
171-
172-
if (author?.email) {
173-
stats.authors[author.email] = author.email in stats.authors ?
174-
++stats.authors[author.email] : 1;
175-
}
176-
177-
curr.versions.add(localVersion);
178-
const hasIndirectDependencies = flags.includes("hasIndirectDependencies");
179-
id: if (hasIndirectDependencies) {
180-
if (!config.includeTransitiveInternal && name.startsWith(npmConfig.organizationPrefix)) {
181-
break id;
182-
}
183-
184-
stats.deps.transitive[`${name}@${localVersion}`] = { links };
185-
}
186-
curr[localVersion] = { hasIndirectDependencies };
187-
188-
if (!curr.links) {
189-
Object.assign(curr, { links });
190-
}
147+
for (const flag of flags) {
148+
if (!(kWantedFlags.has(flag))) {
149+
continue;
191150
}
151+
stats.packages[name].flags[flag] = { ...stats.flagsList[flag] };
192152
}
193-
}
153+
curr.versions.add(spec);
154+
const hasIndirectDependencies = flags.includes("hasIndirectDependencies");
155+
id: if (hasIndirectDependencies) {
156+
if (!config.includeTransitiveInternal && name.startsWith(npmConfig.organizationPrefix)) {
157+
break id;
158+
}
159+
160+
stats.deps.transitive[`${name}@${spec}`] = { links };
161+
}
162+
curr[spec] = { hasIndirectDependencies };
163+
164+
if (!curr.links) {
165+
Object.assign(curr, { links });
166+
}
167+
});
168+
169+
const { contacts, licenses, flags, warnings, size, extensions, nodeDeps } = extractor.extractAndMerge();
170+
171+
stats.authors = contacts;
172+
stats.licenses = { ...stats.licenses, ...licenses };
173+
stats.size = size;
174+
stats.flags = flags;
175+
stats.warnings = warnings.uniqueKinds;
176+
stats.extensions = extensions;
177+
stats.deps.node = nodeDeps.reduce((acc: ReportStat["deps"]["node"], curr) => {
178+
Object.assign(acc, { [curr]: { visualizerUrl: `${kNodeVisualizerUrl}/${curr.replace("node:", "")}.html` } });
179+
180+
return acc;
181+
}, {});
194182

195183
const givenPackages = Object.values(stats.packages).filter((pkg) => pkg.isGiven);
196184

@@ -207,9 +195,6 @@ export async function buildStatsFromScannerDependencies(
207195

208196
stats.packages_count.all = Object.keys(stats.packages).length;
209197
stats.packages_count.internal = stats.packages_count.all - stats.packages_count.external;
210-
stats.size.all = formatBytes(sizeStats.all);
211-
stats.size.internal = formatBytes(sizeStats.internal);
212-
stats.size.external = formatBytes(sizeStats.external);
213198

214199
return stats;
215200
}

0 commit comments

Comments
 (0)