diff --git a/.changeset/pump-it-up.md b/.changeset/pump-it-up.md new file mode 100644 index 00000000..48c01d31 --- /dev/null +++ b/.changeset/pump-it-up.md @@ -0,0 +1,5 @@ +--- +"@nodesecure/scanner": minor +--- + +Add possibility to highlight all packageas under a scope diff --git a/workspaces/scanner/src/depWalker.ts b/workspaces/scanner/src/depWalker.ts index 366aa9a3..61d42dc5 100644 --- a/workspaces/scanner/src/depWalker.ts +++ b/workspaces/scanner/src/depWalker.ts @@ -386,8 +386,14 @@ export async function depWalker( const semverRanges = parseSemverRange(options.highlight?.packages ?? {}); for (const version of Object.entries(dependency.versions)) { const [verStr, verDescriptor] = version as [string, DependencyVersion]; - const range = semverRanges?.[packageName]; - if (range && semver.satisfies(verStr, range)) { + const packageRange = semverRanges?.[packageName]; + const org = parseNpmSpec(packageName)?.org; + const isScopeHighlighted = org !== null && `@${org}` in semverRanges; + + if ( + (packageRange && semver.satisfies(verStr, packageRange)) || + isScopeHighlighted + ) { highlightedPackages.add(`${packageName}@${verStr}`); } verDescriptor.flags.push( diff --git a/workspaces/scanner/src/utils/parseSemverRange.ts b/workspaces/scanner/src/utils/parseSemverRange.ts index 808d2c1e..e8f234f6 100644 --- a/workspaces/scanner/src/utils/parseSemverRange.ts +++ b/workspaces/scanner/src/utils/parseSemverRange.ts @@ -21,6 +21,13 @@ export function parseSemverRange(packages: HighlightPackages) { function parseSpecs(specs: string[]) { return specs.reduce((acc, spec) => { + // Handle scope-only entries like "@fastify", matching all packages under that scope + if (/^@[^/@]+$/.test(spec)) { + acc[spec] = ["*"]; + + return acc; + } + const parsedSpec = parseNpmSpec(spec); if (!parsedSpec) { return acc; diff --git a/workspaces/scanner/test/depWalker.spec.ts b/workspaces/scanner/test/depWalker.spec.ts index e236cca7..fc04e49a 100644 --- a/workspaces/scanner/test/depWalker.spec.ts +++ b/workspaces/scanner/test/depWalker.spec.ts @@ -316,6 +316,48 @@ describe("depWalker", { concurrency: 2 }, () => { ] ); }); + + it("should highlight all packages of a scope using a semver range map", { skip }, async(t) => { + const { logger } = buildLogger(); + t.after(() => logger.removeAllListeners()); + + const { highlighted } = await depWalker( + new ManifestManager(config), + structuredClone({ + ...kDefaultWalkerOptions, + highlight: { + packages: { "@slimio": "*" }, + contacts: [] + } + }), + logger + ); + + assert.ok(highlighted.packages.every((pkg) => pkg.startsWith("@slimio/"))); + assert.ok(highlighted.packages.some((pkg) => pkg.startsWith("@slimio/is"))); + assert.ok(highlighted.packages.some((pkg) => pkg.startsWith("@slimio/config"))); + }); + + it("should highlight all packages of a scope from an array of specs", { skip }, async(t) => { + const { logger } = buildLogger(); + t.after(() => logger.removeAllListeners()); + + const { highlighted } = await depWalker( + new ManifestManager(config), + structuredClone({ + ...kDefaultWalkerOptions, + highlight: { + packages: ["@slimio"], + contacts: [] + } + }), + logger + ); + + assert.ok(highlighted.packages.every((pkg) => pkg.startsWith("@slimio/"))); + assert.ok(highlighted.packages.some((pkg) => pkg.startsWith("@slimio/is"))); + assert.ok(highlighted.packages.some((pkg) => pkg.startsWith("@slimio/config"))); + }); }); }); diff --git a/workspaces/scanner/test/utils/parseSemverRange.spec.ts b/workspaces/scanner/test/utils/parseSemverRange.spec.ts index 17bfe94c..72d7ff79 100644 --- a/workspaces/scanner/test/utils/parseSemverRange.spec.ts +++ b/workspaces/scanner/test/utils/parseSemverRange.spec.ts @@ -52,5 +52,18 @@ describe("utils.parseSemverRange", () => { it("should not parse invalid specs", () => { assert.deepEqual(parseSemverRange([""]), {}); }); + + it("should parse scope-only entries as wildcards", () => { + assert.deepEqual(parseSemverRange(["@nodesecure"]), { + "@nodesecure": "*" + }); + }); + + it("should parse scope-only entries alongside regular specs", () => { + assert.deepEqual(parseSemverRange(["@nodesecure", "foo@1.0.0"]), { + "@nodesecure": "*", + foo: "1.0.0" + }); + }); }); });