Skip to content

Commit

Permalink
feat: start implementing useFormat including OSV
Browse files Browse the repository at this point in the history
  • Loading branch information
fraxken committed Mar 18, 2024
1 parent 422addd commit 61dc8be
Show file tree
Hide file tree
Showing 17 changed files with 74 additions and 58 deletions.
10 changes: 6 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
```
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down
8 changes: 4 additions & 4 deletions docs/github_advisory.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,17 @@ 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);

const vulnerabilities = isPnpm ?
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
Expand All @@ -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" }
);
```

Expand Down
27 changes: 27 additions & 0 deletions src/formats/index.ts
Original file line number Diff line number Diff line change
@@ -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;
};
}
28 changes: 5 additions & 23 deletions src/formats/standard/index.ts
Original file line number Diff line number Diff line change
@@ -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";
Expand Down Expand Up @@ -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;
};
}


2 changes: 1 addition & 1 deletion src/formats/standard/mappers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import type {
BaseStrategy,
ExtendedStrategy,
BaseStrategyOptions,
BaseStrategyFormat,
HydratePayloadDepsOptions
} from "./strategies/types/api.js";

Expand Down Expand Up @@ -101,6 +102,7 @@ export const defaultStrategyName = VULN_MODE.NONE;
export {
Kind,
BaseStrategyOptions,
BaseStrategyFormat,
BaseStrategy,
ExtendedStrategy,
HydratePayloadDepsOptions,
Expand Down
13 changes: 7 additions & 6 deletions src/strategies/github-advisory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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) === "" ?
Expand All @@ -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
Expand All @@ -102,12 +103,12 @@ async function hydratePayloadDependencies(
dependencies: Dependencies,
options: HydratePayloadDepsOptions
): Promise<void> {
const { path, useStandardFormat } = options;
const { path, useFormat } = options;
if (!path) {
throw new Error("path argument is required for <github-advisory> strategy");
}

const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat);
const formatVulnerabilities = formatVulnsPayload(useFormat);
const registry = getLocalRegistryURL();

try {
Expand Down
6 changes: 3 additions & 3 deletions src/strategies/snyk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions src/strategies/sonatype.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 4 additions & 2 deletions src/strategies/types/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions test/strategies/github_advisory_npm/index.integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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);
Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test/strategies/snyk/index.integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

// await hydratePayloadDependencies(dependencies, {
// path: path.join(kFixturesDir, "snyk"),
// useStandardFormat: true
// useFormat: "Standard"
// });

// assert.strictEqual(
Expand Down
2 changes: 1 addition & 1 deletion test/strategies/snyk/index.unit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions test/strategies/sonatype/index.integration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion test/strategies/sonatype/index.unit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions test/strategies/vuln_payload/standardize.unit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, [{}, {}]);
Expand Down

0 comments on commit 61dc8be

Please sign in to comment.