diff --git a/.c8rc.json b/.c8rc.json index 5bc11a97..71957531 100644 --- a/.c8rc.json +++ b/.c8rc.json @@ -1,6 +1,6 @@ { "statements": "78", - "branches": "80", + "branches": "78", "functions": "82", "lines": "78" } diff --git a/lib/allowlist.ts b/lib/allowlist.ts index 885144f9..933f0c58 100644 --- a/lib/allowlist.ts +++ b/lib/allowlist.ts @@ -1,6 +1,6 @@ import type { GitHubAdvisoryId } from "audit-types"; import { isGitHubAdvisoryId } from "./common"; -import { AuditCiPreprocessedConfig } from "./config"; +import type { AuditCiPreprocessedConfig } from "./config"; class Allowlist { modules: string[]; @@ -9,7 +9,7 @@ class Allowlist { /** * @param input the allowlisted module names, advisories, and module paths */ - constructor(input: string[]) { + constructor(input?: string[]) { this.modules = []; this.advisories = []; this.paths = []; diff --git a/lib/audit-ci-version.ts b/lib/audit-ci-version.ts index e5904260..937d2215 100644 --- a/lib/audit-ci-version.ts +++ b/lib/audit-ci-version.ts @@ -1,4 +1,4 @@ -import { AuditCiConfig } from "./config"; +import type { AuditCiConfig } from "./config"; // Ignoring importing package.json because that changes the package's build // eslint-disable-next-line @typescript-eslint/no-var-requires const { bugs, version } = require("../package.json"); diff --git a/lib/audit.ts b/lib/audit.ts index b498600f..8b834c18 100644 --- a/lib/audit.ts +++ b/lib/audit.ts @@ -1,6 +1,6 @@ import { yellow } from "./colors"; -import { AuditCiConfig } from "./config"; -import { Summary } from "./model"; +import type { AuditCiConfig } from "./config"; +import type { Summary } from "./model"; import * as npmAuditer from "./npm-auditer"; import * as pnpmAuditer from "./pnpm-auditer"; import * as yarnAuditer from "./yarn-auditer"; diff --git a/lib/common.ts b/lib/common.ts index 7756ca9f..c7f087ca 100644 --- a/lib/common.ts +++ b/lib/common.ts @@ -113,6 +113,18 @@ export function reportAudit(summary: Summary, config: AuditCiConfig) { return summary; } +function hasMessage(value: unknown): value is { message: unknown } { + return typeof value === "object" && value != undefined && "message" in value; +} + +function hasStatusCode( + value: unknown +): value is { statusCode: unknown; message: unknown } { + return ( + typeof value === "object" && value != undefined && "statusCode" in value + ); +} + export function runProgram( command: string, arguments_: readonly string[], @@ -130,15 +142,20 @@ export function runProgram( // @ts-ignore .pipe(JSONStream.parse()) .pipe( - eventStream.mapSync((data) => { + eventStream.mapSync((data: unknown) => { if (!data) return; try { // due to response without error - if (data.message && data.message.includes("ENOTFOUND")) { + if ( + hasMessage(data) && + typeof data.message === "string" && + data.message.includes("ENOTFOUND") + ) { stderrListener(data.message); return; } - if (data.statusCode === 404) { + // TODO: There are no tests that cover this case, not sure when this happens. + if (hasStatusCode(data) && data.statusCode === 404) { stderrListener(data.message); return; } diff --git a/lib/model.ts b/lib/model.ts index 5f32dac8..3b6ade41 100644 --- a/lib/model.ts +++ b/lib/model.ts @@ -11,8 +11,8 @@ import { matchString, partition, } from "./common"; -import { AuditCiConfig } from "./config"; -import { VulnerabilityLevels } from "./map-vulnerability"; +import type { AuditCiConfig } from "./config"; +import type { VulnerabilityLevels } from "./map-vulnerability"; import type { DeepWriteable } from "./types"; const SUPPORTED_SEVERITY_LEVELS = new Set([ @@ -22,8 +22,10 @@ const SUPPORTED_SEVERITY_LEVELS = new Set([ "low", ]); -const prependPath = (newItem: string, currentPath: string) => - `${newItem}>${currentPath}`; +const prependPath = ( + newItem: N, + currentPath: C +): `${N}>${C}` => `${newItem}>${currentPath}`; export interface Summary { advisoriesFound: GitHubAdvisoryId[]; @@ -57,7 +59,7 @@ class Model { advisoriesFound: ProcessedAdvisory[]; advisoryPathsFound: string[]; - constructor(config: AuditCiConfig) { + constructor(config: Pick) { const unsupported = Object.keys(config.levels).filter( (level) => !SUPPORTED_SEVERITY_LEVELS.has(level) ); diff --git a/lib/npm-auditer.ts b/lib/npm-auditer.ts index 37b1af5f..3bc00b27 100644 --- a/lib/npm-auditer.ts +++ b/lib/npm-auditer.ts @@ -1,7 +1,7 @@ import type { NPMAuditReportV1, NPMAuditReportV2 } from "audit-types"; import { blue } from "./colors"; import { reportAudit, runProgram } from "./common"; -import { AuditCiConfig } from "./config"; +import type { AuditCiConfig } from "./config"; import Model from "./model"; async function runNpmAudit( diff --git a/lib/pnpm-auditer.ts b/lib/pnpm-auditer.ts index 065a6968..68446f34 100644 --- a/lib/pnpm-auditer.ts +++ b/lib/pnpm-auditer.ts @@ -1,8 +1,8 @@ import type { PNPMAuditReport } from "audit-types"; import { blue, yellow } from "./colors"; import { reportAudit, runProgram } from "./common"; -import { AuditCiConfig } from "./config"; -import Model, { Summary } from "./model"; +import type { AuditCiConfig } from "./config"; +import Model, { type Summary } from "./model"; async function runPnpmAudit( config: AuditCiConfig diff --git a/lib/yarn-auditer.ts b/lib/yarn-auditer.ts index 483dc842..7370c676 100644 --- a/lib/yarn-auditer.ts +++ b/lib/yarn-auditer.ts @@ -1,10 +1,10 @@ import type { YarnAudit, YarnBerryAuditReport } from "audit-types"; -import * as childProcess from "child_process"; +import { execSync } from "child_process"; import * as semver from "semver"; import { blue, red, yellow } from "./colors"; import { reportAudit, runProgram } from "./common"; -import { AuditCiConfig } from "./config"; -import Model, { Summary } from "./model"; +import type { AuditCiConfig } from "./config"; +import Model, { type Summary } from "./model"; const MINIMUM_YARN_CLASSIC_VERSION = "1.12.3"; const MINIMUM_YARN_BERRY_VERSION = "2.4.0"; @@ -16,10 +16,7 @@ const MINIMUM_YARN_BERRY_VERSION = "2.4.0"; const MINIMUM_YARN_AUDIT_REGISTRY_VERSION = "99.99.99"; function getYarnVersion(cwd?: string) { - const version = childProcess - .execSync("yarn -v", { cwd }) - .toString() - .replace("\n", ""); + const version = execSync("yarn -v", { cwd }).toString().replace("\n", ""); return version; } diff --git a/package-lock.json b/package-lock.json index 6a481983..5b560f2e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "@types/yargs": "^17.0.9", "@typescript-eslint/eslint-plugin": "^5.14.0", "@typescript-eslint/parser": "^5.14.0", - "audit-types": "^0.5.2", + "audit-types": "^0.5.3", "c8": "^7.11.3", "chai": "^4.3.6", "eslint": "^8.11.0", @@ -735,9 +735,9 @@ } }, "node_modules/audit-types": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/audit-types/-/audit-types-0.5.2.tgz", - "integrity": "sha512-fEeeHXi8aLYjXn5NRzAAQVe0o9GCHtQcPs/A05zyA+j95ueMg2TQ50mnCnoG1sR6kjrspqitpG0iEVOe5qa3lg==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/audit-types/-/audit-types-0.5.3.tgz", + "integrity": "sha512-6lUi2Kv/28mDP8ooBkCuSWzI3qkMd3K8pSRd4wGn+JR0D10slKSLM5D8FH5lQPHCpVYlGarzh6v3gAt7Ha4Z8Q==", "dev": true }, "node_modules/balanced-match": { @@ -4807,9 +4807,9 @@ "dev": true }, "audit-types": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/audit-types/-/audit-types-0.5.2.tgz", - "integrity": "sha512-fEeeHXi8aLYjXn5NRzAAQVe0o9GCHtQcPs/A05zyA+j95ueMg2TQ50mnCnoG1sR6kjrspqitpG0iEVOe5qa3lg==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/audit-types/-/audit-types-0.5.3.tgz", + "integrity": "sha512-6lUi2Kv/28mDP8ooBkCuSWzI3qkMd3K8pSRd4wGn+JR0D10slKSLM5D8FH5lQPHCpVYlGarzh6v3gAt7Ha4Z8Q==", "dev": true }, "balanced-match": { diff --git a/package.json b/package.json index 2b279316..15c32730 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,7 @@ "@types/yargs": "^17.0.9", "@typescript-eslint/eslint-plugin": "^5.14.0", "@typescript-eslint/parser": "^5.14.0", - "audit-types": "^0.5.2", + "audit-types": "^0.5.3", "c8": "^7.11.3", "chai": "^4.3.6", "eslint": "^8.11.0", diff --git a/test/allowlist.spec.js b/test/allowlist.spec.js new file mode 100644 index 00000000..aa70e115 --- /dev/null +++ b/test/allowlist.spec.js @@ -0,0 +1,75 @@ +// @ts-check +const { expect } = require("chai"); +const { default: Allowlist } = require("../dist/allowlist"); + +describe("Allowlist", () => { + it("can map config to empty Allowlist", () => { + const { advisories, modules, paths } = Allowlist.mapConfigToAllowlist({ + allowlist: [], + }); + expect(advisories).to.deep.equal([]); + expect(modules).to.deep.equal([]); + expect(paths).to.deep.equal([]); + }); + it("can map config to modules Allowlist", () => { + const { advisories, modules, paths } = Allowlist.mapConfigToAllowlist({ + allowlist: ["axios", "mocha"], + }); + expect(advisories).to.deep.equal([]); + expect(modules).to.deep.equal(["axios", "mocha"]); + expect(paths).to.deep.equal([]); + }); + it("can map config to advisories Allowlist", () => { + const { advisories, modules, paths } = Allowlist.mapConfigToAllowlist({ + allowlist: [ + "GHSA-pw2r-vq6v-hr8c", + "GHSA-74fj-2j2h-c42q", + "GHSA-cph5-m8f7-6c5x", + "GHSA-4w2v-q235-vp99", + "GHSA-42xw-2xvc-qx8m", + ], + }); + expect(advisories).to.deep.equal([ + "GHSA-pw2r-vq6v-hr8c", + "GHSA-74fj-2j2h-c42q", + "GHSA-cph5-m8f7-6c5x", + "GHSA-4w2v-q235-vp99", + "GHSA-42xw-2xvc-qx8m", + ]); + expect(modules).to.deep.equal([]); + expect(paths).to.deep.equal([]); + }); + + it("can map config to paths Allowlist", () => { + const { advisories, modules, paths } = Allowlist.mapConfigToAllowlist({ + allowlist: [ + "GHSA-pw2r-vq6v-hr8c|axios>follow-redirects", + "GHSA-74fj-2j2h-c42q|axios>follow-redirects", + "GHSA-cph5-m8f7-6c5x|axios", + "GHSA-4w2v-q235-vp99|axios", + "GHSA-42xw-2xvc-qx8m|axios", + ], + }); + expect(advisories).to.deep.equal([]); + expect(modules).to.deep.equal([]); + expect(paths).to.deep.equal([ + "GHSA-pw2r-vq6v-hr8c|axios>follow-redirects", + "GHSA-74fj-2j2h-c42q|axios>follow-redirects", + "GHSA-cph5-m8f7-6c5x|axios", + "GHSA-4w2v-q235-vp99|axios", + "GHSA-42xw-2xvc-qx8m|axios", + ]); + }); + + it("can remove duplicates", () => { + const { advisories, modules, paths } = Allowlist.mapConfigToAllowlist({ + allowlist: [ + "GHSA-74fj-2j2h-c42q|axios>follow-redirects", + "GHSA-74fj-2j2h-c42q|axios>follow-redirects", + ], + }); + expect(advisories).to.deep.equal([]); + expect(modules).to.deep.equal([]); + expect(paths).to.deep.equal(["GHSA-74fj-2j2h-c42q|axios>follow-redirects"]); + }); +}); diff --git a/test/common.js b/test/common.js index 60de5780..1eb26b4a 100644 --- a/test/common.js +++ b/test/common.js @@ -1,3 +1,4 @@ +// @ts-check const path = require("path"); const { default: Allowlist } = require("../dist/allowlist"); const { mapVulnerabilityLevelInput } = require("../dist/map-vulnerability"); diff --git a/test/common.spec.js b/test/common.spec.js index e8f52180..e68c4ffb 100644 --- a/test/common.spec.js +++ b/test/common.spec.js @@ -1,3 +1,4 @@ +// @ts-check const { expect } = require("chai"); const { matchString, diff --git a/test/npm-auditer.spec.js b/test/npm-auditer.spec.js index 7567a98a..7b58fd3d 100644 --- a/test/npm-auditer.spec.js +++ b/test/npm-auditer.spec.js @@ -1,3 +1,4 @@ +// @ts-check const { expect } = require("chai"); const { audit, report } = require("../dist/npm-auditer"); const { default: Allowlist } = require("../dist/allowlist"); diff --git a/test/npm7-auditer.spec.js b/test/npm7-auditer.spec.js index 4594cdfe..9ec40c58 100644 --- a/test/npm7-auditer.spec.js +++ b/test/npm7-auditer.spec.js @@ -1,3 +1,4 @@ +// @ts-check const { expect } = require("chai"); const { audit, report } = require("../dist/npm-auditer"); const { default: Allowlist } = require("../dist/allowlist"); diff --git a/test/pnpm-auditer.spec.js b/test/pnpm-auditer.spec.js index a2b7b735..3cfaa519 100644 --- a/test/pnpm-auditer.spec.js +++ b/test/pnpm-auditer.spec.js @@ -1,3 +1,4 @@ +// @ts-check const { expect } = require("chai"); const { report } = require("../dist/pnpm-auditer"); const { default: Allowlist } = require("../dist/allowlist"); diff --git a/test/yarn-auditer.spec.js b/test/yarn-auditer.spec.js index d7a67fe7..d78a0ef5 100644 --- a/test/yarn-auditer.spec.js +++ b/test/yarn-auditer.spec.js @@ -1,3 +1,4 @@ +// @ts-check const { expect } = require("chai"); const childProcess = require("child_process"); const semver = require("semver");