Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shaggy-carpets-cough.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@nodesecure/scanner": minor
---

feat(scanner): support more formats for highlight.packages
4 changes: 3 additions & 1 deletion workspaces/scanner/src/depWalker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import type {
Options,
Payload
} from "./types.ts";
import { parseSemverRange } from "./utils/parseSemverRange.ts";

// CONSTANTS
const kDefaultDependencyVersionFields = {
Expand Down Expand Up @@ -300,9 +301,10 @@ 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 = options.highlight?.packages?.[packageName];
const range = semverRanges?.[packageName];
if (range && semver.satisfies(verStr, range)) {
highlightedPackages.add(`${packageName}@${verStr}`);
}
Expand Down
2 changes: 1 addition & 1 deletion workspaces/scanner/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export interface Payload {

export type SemverRange = string | "*";

export type HighlightPackages = Record<string, SemverRange>;
export type HighlightPackages = string[] | Record<string, string[] | SemverRange>;

export interface Options {
/**
Expand Down
40 changes: 40 additions & 0 deletions workspaces/scanner/src/utils/parseSemverRange.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Import Third-party Dependencies
import { parseNpmSpec } from "@nodesecure/mama";

// Import Internal Dependencies
import { type HighlightPackages } from "../types.ts";

export function parseSemverRange(packages: HighlightPackages) {
const pkgs = Array.isArray(packages) ? parseSpecs(packages) : packages;

return Object.entries(pkgs).reduce((acc, [name, semverRange]) => {
if (Array.isArray(semverRange)) {
acc[name] = semverRange.join(" || ");
}
else {
acc[name] = semverRange;
}

return acc;
}, {});
}

function parseSpecs(specs: string[]) {
return specs.reduce((acc, spec) => {
const parsedSpec = parseNpmSpec(spec);
if (!parsedSpec) {
return acc;
}
const { name, semver } = parsedSpec;
const version = semver || "*";
if (name in acc) {
acc[name].push(version);
}
else {
acc[name] = [version];
}

return acc;
}, {});
}

27 changes: 27 additions & 0 deletions workspaces/scanner/test/depWalker.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,33 @@ test("should highlight the given packages", async() => {
);
});

test("should support multiple formats for packages highlighted", async() => {
const { logger } = errorLogger();
test.after(() => logger.removeAllListeners());

const hightlightPackages = ["zen-observable@0.8.14 || 0.8.15", "nanoid"];

const result = await depWalker(
pkgHighlightedPackages,
structuredClone({
...kDefaultWalkerOptions,
highlight: {
packages: hightlightPackages,
contacts: []
}
}),
logger
);

assert.deepStrictEqual(
result.highlighted.packages.sort(),
[
"nanoid@5.1.6",
"zen-observable@0.8.15"
]
);
});

test("fetch payload of pacote on the npm registry", async() => {
const result = await from(
"pacote",
Expand Down
54 changes: 54 additions & 0 deletions workspaces/scanner/test/utils/parseSemverRange.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Import Node.js Dependencies
import { test, describe } from "node:test";
import assert from "node:assert";

// Import Internal Dependencies
import { parseSemverRange } from "../../src/utils/parseSemverRange.ts";

describe("parseSemverRange", () => {
test("should do nothing when the semver ranges are already well formatted", () => {
assert.deepEqual(parseSemverRange({
foo: "1.2.3",
bar: "1.2.3 || 1.2.4"
}), {
foo: "1.2.3",
bar: "1.2.3 || 1.2.4"
});
});

test("should parse to semver range string when getting an array", () => {
assert.deepEqual(parseSemverRange({
foo: ["1.2.3"],
bar: ["1.2.3", "1.2.4"]
}), {
foo: "1.2.3",
bar: "1.2.3 || 1.2.4"
});
});

describe("specs", () => {
test("should parse specs to name semver range", () => {
assert.deepEqual(parseSemverRange(["foo@1.2.3", "bar@1.2.3", "bar@1.2.4"]), {
foo: "1.2.3",
bar: "1.2.3 || 1.2.4"
});
});

test("should parse to wildcard when there is no version", () => {
assert.deepEqual(parseSemverRange(["mocha", "jest@1.2.1", "jest"]), {
mocha: "*",
jest: "1.2.1 || *"
});
});

test("should include the org in the name", () => {
assert.deepEqual(parseSemverRange(["@nodesecure/js-x-ray@1.0.0", "@nodesecure/js-x-ray@1.0.1"]), {
"@nodesecure/js-x-ray": "1.0.0 || 1.0.1"
});
});

test("should should not parse invalid specs", () => {
assert.deepEqual(parseSemverRange([""]), {});
});
});
});