Skip to content

Commit 7915664

Browse files
authored
Merge pull request #285 from NodeSecure/use-format-v2
refactor!: prepare API to use multiple formats such as OSV
1 parent b619a67 commit 7915664

18 files changed

Lines changed: 113 additions & 100 deletions

File tree

README.md

Lines changed: 6 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const definition = await vulnera.getStrategy();
4545
console.log(definition.strategy);
4646

4747
const vulnerabilities = await definition.getVulnerabilities(process.cwd(), {
48-
useStandardFormat: true
48+
useFormat: "Standard"
4949
});
5050
console.log(vulnerabilities);
5151
```
@@ -105,11 +105,10 @@ export interface ExtendedStrategy<
105105
) => Promise<(VulnFormat | StandardVulnerability)[]>;
106106
}
107107

108+
export type BaseStrategyFormat = "Standard";
109+
108110
export interface BaseStrategyOptions {
109-
/**
110-
* @default false
111-
*/
112-
useStandardFormat?: boolean;
111+
useFormat?: BaseStrategyFormat;
113112
}
114113

115114
export interface HydratePayloadDepsOptions extends BaseStrategyOptions {
@@ -126,45 +125,8 @@ Where `dependencies` is the dependencies **Map()** object of the NodeSecure Scan
126125
> [!NOTE]
127126
> the option **hydrateDatabase** is only useful for some of the strategy (like Node.js Security WG).
128127
129-
### Standard vulnerability format
130-
We provide an high level format that work for all available strategy. It can be activated with the option `useStandardFormat`.
131-
132-
```ts
133-
export interface StandardVulnerability {
134-
/** Unique identifier for the vulnerability **/
135-
id?: string;
136-
/** Vulnerability origin, either Snyk, Sonatype, GitHub or NodeSWG **/
137-
origin: Origin;
138-
/** Package associated with the vulnerability **/
139-
package: string;
140-
/** Vulnerability title **/
141-
title: string;
142-
/** Vulnerability description **/
143-
description?: string;
144-
/** Vulnerability link references on origin's website **/
145-
url?: string;
146-
/** Vulnerability severity levels given the strategy **/
147-
severity?: Severity;
148-
/** Common Vulnerabilities and Exposures dictionary */
149-
cves?: string[];
150-
/**
151-
* Common Vulnerability Scoring System (CVSS) provides a way to capture
152-
* the principal characteristics of a vulnerability,
153-
* and produce a numerical score reflecting its severity,
154-
* as well as a textual representation of that score. **/
155-
cvssVector?: string;
156-
/** CVSS Score **/
157-
cvssScore?: number;
158-
/** The range of vulnerable versions provided when too many versions are vulnerables */
159-
vulnerableRanges: string[];
160-
/** The set of versions that are vulnerable **/
161-
vulnerableVersions: string[];
162-
/** The set of versions that are patched **/
163-
patchedVersions?: string;
164-
/** Overview of available patches to get rid of listed vulnerabilities **/
165-
patches?: Patch[];
166-
}
167-
```
128+
### Formats
129+
- [Standard](./docs/formats/standard.md)
168130

169131
### Databases
170132
- [OSV](./docs/database/osv.md)

docs/formats/standard.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Standard vulnerability format
2+
3+
We provide a high-level format that works for all available strategies. It can be activated with the option `useFormat` equal `Standard`.
4+
5+
```ts
6+
export interface StandardVulnerability {
7+
/** Unique identifier for the vulnerability **/
8+
id?: string;
9+
/** Vulnerability origin, either Snyk, Sonatype, GitHub or NodeSWG **/
10+
origin: Origin;
11+
/** Package associated with the vulnerability **/
12+
package: string;
13+
/** Vulnerability title **/
14+
title: string;
15+
/** Vulnerability description **/
16+
description?: string;
17+
/** Vulnerability link references on origin's website **/
18+
url?: string;
19+
/** Vulnerability severity levels given the strategy **/
20+
severity?: Severity;
21+
/** Common Vulnerabilities and Exposures dictionary */
22+
cves?: string[];
23+
/**
24+
* Common Vulnerability Scoring System (CVSS) provides a way to capture
25+
* the principal characteristics of a vulnerability,
26+
* and produce a numerical score reflecting its severity,
27+
* as well as a textual representation of that score. **/
28+
cvssVector?: string;
29+
/** CVSS Score **/
30+
cvssScore?: number;
31+
/** The range of vulnerable versions provided when too many versions are vulnerables */
32+
vulnerableRanges: string[];
33+
/** The set of versions that are vulnerable **/
34+
vulnerableVersions: string[];
35+
/** The set of versions that are patched **/
36+
patchedVersions?: string;
37+
/** Overview of available patches to get rid of listed vulnerabilities **/
38+
patches?: Patch[];
39+
}
40+
```

docs/github_advisory.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,17 +41,17 @@ For audit a specific manifest (package.json, lock-file or nodes_modules), there
4141

4242
```js
4343
async function getVulnerabilities(path, options = {}) {
44-
const { useStandardFormat } = options;
44+
const { useFormat } = options;
4545

46-
const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat);
46+
const formatVulnerabilities = formatVulnsPayload(useFormat);
4747
const registry = getLocalRegistryURL();
4848
const isPnpm = await hasPnpmLockFile(path);
4949

5050
const vulnerabilities = isPnpm ?
5151
await pnpmAudit(path, registry) :
5252
await npmAudit(path, registry);
5353

54-
if (useStandardFormat) {
54+
if (useFormat) {
5555
return formatVulnerabilities(
5656
isPnpm ? VULN_MODE.GITHUB_ADVISORY + "_pnpm" : VULN_MODE.GITHUB_ADVISORY,
5757
vulnerabilities
@@ -69,7 +69,7 @@ import * as vulnera from "@nodesecure/vulnera";
6969
const definition = await vulnera.setStrategy(vulnera.strategies.GITHUB_ADVISORY);
7070
const vulnerabilites = await definition.getVulnerabilities(
7171
'./package.json',
72-
{ useStandardFormat: true }
72+
{ useFormat: "Standard" }
7373
);
7474
```
7575

src/formats/index.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Import Internal Dependencies
2+
import type { BaseStrategyFormat } from "../strategies/types/api.js";
3+
4+
import {
5+
standardVulnerabilityMapper,
6+
type StandardizeKind
7+
} from "./standard/index.js";
8+
9+
export function formatVulnsPayload(
10+
format: BaseStrategyFormat | null = null
11+
) {
12+
return function formatVulnerabilities(
13+
strategy: StandardizeKind,
14+
vulnerabilities: any[]
15+
) {
16+
if (format === "Standard") {
17+
return standardVulnerabilityMapper(
18+
strategy,
19+
vulnerabilities
20+
);
21+
}
22+
23+
// identity function
24+
return vulnerabilities;
25+
};
26+
}

src/formats/standard/index.ts

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Import Internal Dependencies
2-
import { VULN_MAPPERS } from "./mappers.js";
2+
import { STANDARD_VULN_MAPPERS } from "./mappers.js";
33
import type { Kind } from "../../constants.js";
44

55
export type Severity = "info" | "low" | "medium" | "high" | "critical";
@@ -47,32 +47,15 @@ export interface StandardVulnerability {
4747
patches?: StandardPatch[];
4848
}
4949

50-
export type StandardizeKind = keyof typeof VULN_MAPPERS;
50+
export type StandardizeKind = keyof typeof STANDARD_VULN_MAPPERS;
5151

52-
function useStrategyVulnerabilityMapper(
52+
export function standardVulnerabilityMapper(
5353
strategy: StandardizeKind,
5454
vulnerabilities: any[]
5555
): StandardVulnerability[] {
56-
if (!(strategy in VULN_MAPPERS)) {
56+
if (!(strategy in STANDARD_VULN_MAPPERS)) {
5757
return [];
5858
}
5959

60-
return vulnerabilities.map(VULN_MAPPERS[strategy]);
60+
return vulnerabilities.map(STANDARD_VULN_MAPPERS[strategy]);
6161
}
62-
63-
export function standardizeVulnsPayload(useStandardFormat = false) {
64-
return function formatVulnerabilities(
65-
strategy: StandardizeKind,
66-
vulnerabilities: any[]
67-
) {
68-
if (useStandardFormat) {
69-
return useStrategyVulnerabilityMapper(
70-
strategy, vulnerabilities
71-
);
72-
}
73-
74-
// identity function
75-
return vulnerabilities;
76-
};
77-
}
78-

src/formats/standard/mappers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ function mapFromSonatype(vuln: SonatypeVulnerability): StandardVulnerability {
9191
};
9292
}
9393

94-
export const VULN_MAPPERS = Object.freeze({
94+
export const STANDARD_VULN_MAPPERS = Object.freeze({
9595
[VULN_MODE.GITHUB_ADVISORY]: mapFromNPM,
9696
"github-advisory_pnpm": mapFromPnpm,
9797
[VULN_MODE.SNYK]: mapFromSnyk,

src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,9 @@ import type {
4444

4545
import type {
4646
BaseStrategy,
47-
ExtendedStrategy,
4847
BaseStrategyOptions,
48+
BaseStrategyFormat,
49+
ExtendedStrategy,
4950
HydratePayloadDepsOptions
5051
} from "./strategies/types/api.js";
5152

@@ -104,6 +105,7 @@ export const defaultStrategyName = VULN_MODE.NONE;
104105
export type {
105106
Kind,
106107
BaseStrategyOptions,
108+
BaseStrategyFormat,
107109
BaseStrategy,
108110
ExtendedStrategy,
109111
HydratePayloadDepsOptions,

src/strategies/github-advisory.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import { readWantedLockfile } from "@pnpm/lockfile-file";
1111

1212
// Import Internal Dependencies
1313
import { VULN_MODE, NPM_TOKEN } from "../constants.js";
14-
import { type StandardVulnerability, standardizeVulnsPayload } from "../formats/standard/index.js";
14+
import type { StandardVulnerability } from "../formats/standard/index.js";
15+
import { formatVulnsPayload } from "../formats/index.js";
1516
import type { Dependencies } from "./types/scanner.js";
1617
import type {
1718
BaseStrategyOptions,
@@ -72,9 +73,9 @@ async function getVulnerabilities(
7273
lockDirOrManifestPath: string,
7374
options: BaseStrategyOptions = {}
7475
): Promise<(GithubVulnerability | StandardVulnerability)[]> {
75-
const { useStandardFormat } = options;
76+
const { useFormat } = options;
7677

77-
const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat);
78+
const formatVulnerabilities = formatVulnsPayload(useFormat);
7879
const registry = getLocalRegistryURL();
7980

8081
const lockfileDir = path.extname(lockDirOrManifestPath) === "" ?
@@ -89,7 +90,7 @@ async function getVulnerabilities(
8990
await pnpmAudit(lockfileDir, registry) :
9091
await npmAudit(lockDirOrManifestPath, registry);
9192

92-
if (useStandardFormat) {
93+
if (useFormat) {
9394
return formatVulnerabilities(
9495
isPnpm ? "github-advisory_pnpm" : VULN_MODE.GITHUB_ADVISORY,
9596
vulnerabilities
@@ -103,12 +104,12 @@ async function hydratePayloadDependencies(
103104
dependencies: Dependencies,
104105
options: HydratePayloadDepsOptions
105106
): Promise<void> {
106-
const { path, useStandardFormat } = options;
107+
const { path, useFormat } = options;
107108
if (!path) {
108109
throw new Error("path argument is required for <github-advisory> strategy");
109110
}
110111

111-
const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat);
112+
const formatVulnerabilities = formatVulnsPayload(useFormat);
112113
const registry = getLocalRegistryURL();
113114

114115
try {

src/strategies/snyk.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ import { readFile } from "node:fs/promises";
55

66
// Import Internal Dependencies
77
import { VULN_MODE } from "../constants.js";
8-
import { standardizeVulnsPayload } from "../formats/standard/index.js";
98
import type { Dependencies } from "./types/scanner.js";
109
import type {
1110
HydratePayloadDepsOptions,
1211
BaseStrategy
1312
} from "./types/api.js";
1413
import { type SnykAuditResponse } from "../formats/snyk/index.js";
1514
import { snyk } from "../database/index.js";
15+
import { formatVulnsPayload } from "../formats/index.js";
1616

1717
export type SnykStrategyDefinition = BaseStrategy<"snyk">;
1818

@@ -78,8 +78,8 @@ function extractSnykVulnerabilities(
7878
options: HydratePayloadDepsOptions
7979
) {
8080
const { ok, issues } = snykAudit;
81-
const { useStandardFormat } = options;
82-
const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat);
81+
const { useFormat } = options;
82+
const formatVulnerabilities = formatVulnsPayload(useFormat);
8383

8484
if (!ok) {
8585
const vulnerabilities = formatVulnerabilities(VULN_MODE.SNYK, issues.vulnerabilities);

src/strategies/sonatype.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import * as httpie from "@myunisoft/httpie";
44
// Import Internal Dependencies
55
import * as utils from "../utils.js";
66
import { VULN_MODE } from "../constants.js";
7-
import { standardizeVulnsPayload } from "../formats/standard/index.js";
87
import type { Dependencies, Dependency } from "./types/scanner.js";
98
import type {
109
BaseStrategyOptions,
1110
BaseStrategy
1211
} from "./types/api.js";
12+
import { formatVulnsPayload } from "../formats/index.js";
1313

1414
// CONSTANTS
1515
const kSonatypeApiURL = "https://ossindex.sonatype.org/api/v3/component-report";
@@ -135,8 +135,8 @@ async function hydratePayloadDependencies(
135135
Array.from(dependencies).flatMap(createPackageURLCoordinates)
136136
);
137137

138-
const formatVulnerabilities = standardizeVulnsPayload(
139-
options.useStandardFormat
138+
const formatVulnerabilities = formatVulnsPayload(
139+
options.useFormat
140140
);
141141
for (const sonatypeResponse of packageURLsData) {
142142
const packageName = extractNameFromPackageURL(sonatypeResponse.coordinates);

0 commit comments

Comments
 (0)