From 09cd3451bf6a25d72bbad4c5b68b83a24402a602 Mon Sep 17 00:00:00 2001 From: fraxken Date: Fri, 26 Jan 2024 22:19:10 +0100 Subject: [PATCH] feat: start implementing useFormat including OSV --- README.md | 10 ++++--- docs/github_advisory.md | 8 +++--- src/formats/index.ts | 27 ++++++++++++++++++ src/formats/standard/index.ts | 28 ++++--------------- src/formats/standard/mappers.ts | 2 +- src/index.ts | 2 ++ src/strategies/github-advisory.ts | 13 +++++---- src/strategies/snyk.ts | 6 ++-- src/strategies/sonatype.ts | 6 ++-- src/strategies/types/api.ts | 6 ++-- .../index.integration.spec.ts | 4 +-- .../index.integration.spec.ts | 6 ++-- .../strategies/snyk/index.integration.spec.ts | 2 +- test/strategies/snyk/index.unit.spec.ts | 2 +- .../sonatype/index.integration.spec.ts | 4 +-- test/strategies/sonatype/index.unit.spec.ts | 2 +- .../vuln_payload/standardize.unit.spec.ts | 4 +-- 17 files changed, 74 insertions(+), 58 deletions(-) create mode 100644 src/formats/index.ts diff --git a/README.md b/README.md index 03d83f0..854750f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ const definition = await vulnera.getStrategy(); console.log(definition.strategy); const vulnerabilities = await definition.getVulnerabilities(process.cwd(), { - useStandardFormat: true + useFormat: "Standard" }); console.log(vulnerabilities); ``` @@ -105,11 +105,13 @@ export interface ExtendedStrategy< ) => Promise<(VulnFormat | StandardVulnerability)[]>; } +export type BaseStrategyFormat = "Preserve" | "Standard" | "OSV"; + export interface BaseStrategyOptions { /** - * @default false + * @default "Preserve" */ - useStandardFormat?: boolean; + useFormat?: BaseStrategyFormat; } export interface HydratePayloadDepsOptions extends BaseStrategyOptions { @@ -127,7 +129,7 @@ Where `dependencies` is the dependencies **Map()** object of the NodeSecure Scan > the option **hydrateDatabase** is only useful for some of the strategy (like Node.js Security WG). ### Standard vulnerability format -We provide an high level format that work for all available strategy. It can be activated with the option `useStandardFormat`. +We provide an high level format that work for all available strategy. It can be activated with the option `useFormat`. ```ts export interface StandardVulnerability { diff --git a/docs/github_advisory.md b/docs/github_advisory.md index 0781501..3ffce1a 100644 --- a/docs/github_advisory.md +++ b/docs/github_advisory.md @@ -41,9 +41,9 @@ For audit a specific manifest (package.json, lock-file or nodes_modules), there ```js async function getVulnerabilities(path, options = {}) { - const { useStandardFormat } = options; + const { useFormat } = options; - const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat); + const formatVulnerabilities = formatVulnsPayload(useFormat); const registry = getLocalRegistryURL(); const isPnpm = await hasPnpmLockFile(path); @@ -51,7 +51,7 @@ async function getVulnerabilities(path, options = {}) { await pnpmAudit(path, registry) : await npmAudit(path, registry); - if (useStandardFormat) { + if (useFormat) { return formatVulnerabilities( isPnpm ? VULN_MODE.GITHUB_ADVISORY + "_pnpm" : VULN_MODE.GITHUB_ADVISORY, vulnerabilities @@ -69,7 +69,7 @@ import * as vulnera from "@nodesecure/vulnera"; const definition = await vulnera.setStrategy(vulnera.strategies.GITHUB_ADVISORY); const vulnerabilites = await definition.getVulnerabilities( './package.json', - { useStandardFormat: true } + { useFormat: "Standard" } ); ``` diff --git a/src/formats/index.ts b/src/formats/index.ts new file mode 100644 index 0000000..33b7c5f --- /dev/null +++ b/src/formats/index.ts @@ -0,0 +1,27 @@ +// Import Internal Dependencies +import { BaseStrategyFormat } from "../strategies/types/api.js"; + +import { + standardVulnerabilityMapper, + StandardizeKind +} from "./standard/index.js"; + +export function formatVulnsPayload(format: BaseStrategyFormat = "Preserve") { + return function formatVulnerabilities( + strategy: StandardizeKind, + vulnerabilities: any[] + ) { + if (format === "Standard") { + return standardVulnerabilityMapper( + strategy, + vulnerabilities + ); + } + if (format === "OSV") { + throw new Error("Not Implemented Yet"); + } + + // identity function + return vulnerabilities; + }; +} diff --git a/src/formats/standard/index.ts b/src/formats/standard/index.ts index 06392df..278a402 100644 --- a/src/formats/standard/index.ts +++ b/src/formats/standard/index.ts @@ -1,5 +1,5 @@ // Import Internal Dependencies -import { VULN_MAPPERS } from "./mappers.js"; +import { STANDARD_VULN_MAPPERS } from "./mappers.js"; import { Kind } from "../../constants.js"; export type Severity = "info" | "low" | "medium" | "high" | "critical"; @@ -47,33 +47,15 @@ export interface StandardVulnerability { patches?: StandardPatch[]; } -export type StandardizeKind = keyof typeof VULN_MAPPERS; +export type StandardizeKind = keyof typeof STANDARD_VULN_MAPPERS; -function useStrategyVulnerabilityMapper( +export function standardVulnerabilityMapper( strategy: StandardizeKind, vulnerabilities: any[] ): StandardVulnerability[] { - if (!(strategy in VULN_MAPPERS)) { + if (!(strategy in STANDARD_VULN_MAPPERS)) { return []; } - return vulnerabilities.map(VULN_MAPPERS[strategy]); + return vulnerabilities.map(STANDARD_VULN_MAPPERS[strategy]); } - -export function standardizeVulnsPayload(useStandardFormat = false) { - return function formatVulnerabilities( - strategy: StandardizeKind, - vulnerabilities: any[] - ) { - if (useStandardFormat) { - return useStrategyVulnerabilityMapper( - strategy, vulnerabilities - ); - } - - // identity function - return vulnerabilities; - }; -} - - diff --git a/src/formats/standard/mappers.ts b/src/formats/standard/mappers.ts index 25de79a..57dc484 100644 --- a/src/formats/standard/mappers.ts +++ b/src/formats/standard/mappers.ts @@ -91,7 +91,7 @@ function mapFromSonatype(vuln: SonatypeVulnerability): StandardVulnerability { }; } -export const VULN_MAPPERS = Object.freeze({ +export const STANDARD_VULN_MAPPERS = Object.freeze({ [VULN_MODE.GITHUB_ADVISORY]: mapFromNPM, "github-advisory_pnpm": mapFromPnpm, [VULN_MODE.SNYK]: mapFromSnyk, diff --git a/src/index.ts b/src/index.ts index 5d75c36..4b07564 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,6 +43,7 @@ import type { BaseStrategy, ExtendedStrategy, BaseStrategyOptions, + BaseStrategyFormat, HydratePayloadDepsOptions } from "./strategies/types/api.js"; @@ -101,6 +102,7 @@ export const defaultStrategyName = VULN_MODE.NONE; export { Kind, BaseStrategyOptions, + BaseStrategyFormat, BaseStrategy, ExtendedStrategy, HydratePayloadDepsOptions, diff --git a/src/strategies/github-advisory.ts b/src/strategies/github-advisory.ts index c857042..164dda1 100644 --- a/src/strategies/github-advisory.ts +++ b/src/strategies/github-advisory.ts @@ -10,7 +10,8 @@ import { readWantedLockfile } from "@pnpm/lockfile-file"; // Import Internal Dependencies import { VULN_MODE, NPM_TOKEN } from "../constants.js"; -import { StandardVulnerability, standardizeVulnsPayload } from "../formats/standard/index.js"; +import { StandardVulnerability } from "../formats/standard/index.js"; +import { formatVulnsPayload } from "../formats/index.js"; import type { Dependencies } from "./types/scanner.js"; import type { BaseStrategyOptions, @@ -71,9 +72,9 @@ async function getVulnerabilities( lockDirOrManifestPath: string, options: BaseStrategyOptions = {} ): Promise<(GithubVulnerability | StandardVulnerability)[]> { - const { useStandardFormat } = options; + const { useFormat } = options; - const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat); + const formatVulnerabilities = formatVulnsPayload(useFormat); const registry = getLocalRegistryURL(); const lockfileDir = path.extname(lockDirOrManifestPath) === "" ? @@ -88,7 +89,7 @@ async function getVulnerabilities( await pnpmAudit(lockfileDir, registry) : await npmAudit(lockDirOrManifestPath, registry); - if (useStandardFormat) { + if (useFormat) { return formatVulnerabilities( isPnpm ? "github-advisory_pnpm" : VULN_MODE.GITHUB_ADVISORY, vulnerabilities @@ -102,12 +103,12 @@ async function hydratePayloadDependencies( dependencies: Dependencies, options: HydratePayloadDepsOptions ): Promise { - const { path, useStandardFormat } = options; + const { path, useFormat } = options; if (!path) { throw new Error("path argument is required for strategy"); } - const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat); + const formatVulnerabilities = formatVulnsPayload(useFormat); const registry = getLocalRegistryURL(); try { diff --git a/src/strategies/snyk.ts b/src/strategies/snyk.ts index f6281ba..aa7b215 100644 --- a/src/strategies/snyk.ts +++ b/src/strategies/snyk.ts @@ -7,7 +7,7 @@ import * as httpie from "@myunisoft/httpie"; // Import Internal Dependencies import { VULN_MODE, SNYK_ORG, SNYK_TOKEN } from "../constants.js"; -import { standardizeVulnsPayload } from "../formats/standard/index.js"; +import { formatVulnsPayload } from "../formats/index.js"; import type { Dependencies } from "./types/scanner.js"; import type { HydratePayloadDepsOptions, @@ -180,8 +180,8 @@ function extractSnykVulnerabilities( options: HydratePayloadDepsOptions ) { const { ok, issues } = snykAudit; - const { useStandardFormat } = options; - const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat); + const { useFormat } = options; + const formatVulnerabilities = formatVulnsPayload(useFormat); if (!ok) { const vulnerabilities = formatVulnerabilities(VULN_MODE.SNYK, issues.vulnerabilities); diff --git a/src/strategies/sonatype.ts b/src/strategies/sonatype.ts index e1d6bda..a7b1c72 100644 --- a/src/strategies/sonatype.ts +++ b/src/strategies/sonatype.ts @@ -4,7 +4,7 @@ import * as httpie from "@myunisoft/httpie"; // Import Internal Dependencies import * as utils from "../utils.js"; import { VULN_MODE } from "../constants.js"; -import { standardizeVulnsPayload } from "../formats/standard/index.js"; +import { formatVulnsPayload } from "../formats/index.js"; import type { Dependencies, ScannerVersionDescriptor } from "./types/scanner.js"; import type { BaseStrategyOptions, @@ -135,8 +135,8 @@ async function hydratePayloadDependencies( Array.from(dependencies).flatMap(createPackageURLCoordinates) ); - const formatVulnerabilities = standardizeVulnsPayload( - options.useStandardFormat + const formatVulnerabilities = formatVulnsPayload( + options.useFormat ); for (const sonatypeResponse of packageURLsData) { const packageName = extractNameFromPackageURL(sonatypeResponse.coordinates); diff --git a/src/strategies/types/api.ts b/src/strategies/types/api.ts index bb06518..7733628 100644 --- a/src/strategies/types/api.ts +++ b/src/strategies/types/api.ts @@ -3,11 +3,13 @@ import type { Dependencies } from "./scanner.js"; import type { StandardVulnerability } from "../../formats/standard/index.js"; import type { Kind } from "../../constants.js"; +export type BaseStrategyFormat = "Preserve" | "Standard" | "OSV"; + export interface BaseStrategyOptions { /** - * @default false + * @default "Preserve" */ - useStandardFormat?: boolean; + useFormat?: BaseStrategyFormat; } export interface HydratePayloadDepsOptions extends BaseStrategyOptions { diff --git a/test/strategies/github_advisory_npm/index.integration.spec.ts b/test/strategies/github_advisory_npm/index.integration.spec.ts index d1137b1..5608663 100644 --- a/test/strategies/github_advisory_npm/index.integration.spec.ts +++ b/test/strategies/github_advisory_npm/index.integration.spec.ts @@ -52,7 +52,7 @@ test("GitHubAdvisoryStrategy (npm): hydratePayloadDependencies using NodeSecure await hydratePayloadDependencies(dependencies, { path: path.join(kFixturesDir, "audit"), - useStandardFormat: true + useFormat: "Standard" }); assert.strictEqual(dependencies.size, 1, "hydratePayloadDependencies must not add new dependencies by itself"); @@ -75,7 +75,7 @@ test("GitHubAdvisoryStrategy (npm): getVulnerabilities in the standard NodeSecur const { getVulnerabilities } = GitHubAdvisoryStrategy(); const vulnerabilities = await getVulnerabilities( path.join(kFixturesDir, "audit"), - { useStandardFormat: true } + { useFormat: "Standard" } ); assert.equal(vulnerabilities.length > 0, true); diff --git a/test/strategies/github_advisory_pnpm/index.integration.spec.ts b/test/strategies/github_advisory_pnpm/index.integration.spec.ts index 0327311..ef4e140 100644 --- a/test/strategies/github_advisory_pnpm/index.integration.spec.ts +++ b/test/strategies/github_advisory_pnpm/index.integration.spec.ts @@ -46,7 +46,7 @@ test("GitHubAdvisoryStrategy (pnpm): hydratePayloadDependencies using NodeSecure await hydratePayloadDependencies(dependencies, { path: path.join(kFixturesDir, "audit_pnpm"), - useStandardFormat: true + useFormat: "Standard" }); assert.strictEqual(dependencies.size, 1, "hydratePayloadDependencies must not add new dependencies by itself"); @@ -71,7 +71,7 @@ test("GitHubAdvisoryStrategy (pnpm): getVulnerabilities in the standard NodeSecu const { getVulnerabilities } = GitHubAdvisoryStrategy(); const vulnerabilities = await getVulnerabilities( path.join(kFixturesDir, "audit_pnpm"), - { useStandardFormat: true } + { useFormat: "Standard" } ); assert.equal(vulnerabilities.length > 0, true); @@ -82,7 +82,7 @@ test("GitHubAdvisoryStrategy (pnpm): getVulnerabilities should work even if we p const { getVulnerabilities } = GitHubAdvisoryStrategy(); const vulnerabilities = await getVulnerabilities( path.join(kFixturesDir, "audit_pnpm", "package.json"), - { useStandardFormat: true } + { useFormat: "Standard" } ); assert.equal(vulnerabilities.length > 0, true); diff --git a/test/strategies/snyk/index.integration.spec.ts b/test/strategies/snyk/index.integration.spec.ts index 1f004f6..fc31e8b 100644 --- a/test/strategies/snyk/index.integration.spec.ts +++ b/test/strategies/snyk/index.integration.spec.ts @@ -21,7 +21,7 @@ // await hydratePayloadDependencies(dependencies, { // path: path.join(kFixturesDir, "snyk"), -// useStandardFormat: true +// useFormat: "Standard" // }); // assert.strictEqual( diff --git a/test/strategies/snyk/index.unit.spec.ts b/test/strategies/snyk/index.unit.spec.ts index be9b933..13ff425 100644 --- a/test/strategies/snyk/index.unit.spec.ts +++ b/test/strategies/snyk/index.unit.spec.ts @@ -114,7 +114,7 @@ test("snyk strategy: hydratePayloadDependencies using NodeSecure standard format await hydratePayloadDependencies(dependencies, { path: path.join(kFixturesDir, "snyk"), - useStandardFormat: true + useFormat: "Standard" }); assert.strictEqual( diff --git a/test/strategies/sonatype/index.integration.spec.ts b/test/strategies/sonatype/index.integration.spec.ts index c14976a..7f48590 100644 --- a/test/strategies/sonatype/index.integration.spec.ts +++ b/test/strategies/sonatype/index.integration.spec.ts @@ -27,7 +27,7 @@ test("sonatype strategy: fetching a package with a vulnerability using the API", }); await hydratePayloadDependencies(dependencies, { - useStandardFormat: true + useFormat: "Standard" }); assert.strictEqual( @@ -70,7 +70,7 @@ test("sonatype strategy: fetching a package with a name that should be percent-e } ); - await hydratePayloadDependencies(dependencies, { useStandardFormat: true }); + await hydratePayloadDependencies(dependencies, { useFormat: "Standard" }); assert.strictEqual( dependencies.size, diff --git a/test/strategies/sonatype/index.unit.spec.ts b/test/strategies/sonatype/index.unit.spec.ts index 106a45c..17fb5fb 100644 --- a/test/strategies/sonatype/index.unit.spec.ts +++ b/test/strategies/sonatype/index.unit.spec.ts @@ -104,7 +104,7 @@ test("sonatype strategy: hydratePayloadDependencies when using NodeSecure standa } }); - await hydratePayloadDependencies(dependencies, { useStandardFormat: true }); + await hydratePayloadDependencies(dependencies, { useFormat: "Standard" }); assert.strictEqual( dependencies.size, diff --git a/test/strategies/vuln_payload/standardize.unit.spec.ts b/test/strategies/vuln_payload/standardize.unit.spec.ts index eb7c8d8..85ea0b6 100644 --- a/test/strategies/vuln_payload/standardize.unit.spec.ts +++ b/test/strategies/vuln_payload/standardize.unit.spec.ts @@ -4,14 +4,14 @@ import assert from "node:assert"; // Import Internal Dependencies import { VULN_MODE } from "../../../src/constants.js"; -import { standardizeVulnsPayload } from "../../../src/formats/standard/index.js"; +import { formatVulnsPayload } from "../../../src/formats/index.js"; import { NPM_VULNS_PAYLOADS, SNYK_VULNS_PAYLOADS, SONATYPE_VULNS_PAYLOADS } from "../../fixtures/vuln_payload/payloads.js"; -const formatVulnerabilities = standardizeVulnsPayload(true); +const formatVulnerabilities = formatVulnsPayload("Standard"); test("should convert NONE or unknown strategy into blank payload", () => { let notStandardized = formatVulnerabilities(VULN_MODE.NONE as any, [{}, {}]);