Skip to content

Commit

Permalink
chore: start implementing OSV mappers
Browse files Browse the repository at this point in the history
  • Loading branch information
fraxken committed Aug 11, 2024
1 parent 7759855 commit ff9adca
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 44 deletions.
42 changes: 3 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,45 +125,9 @@ Where `dependencies` is the dependencies **Map()** object of the NodeSecure Scan
> [!NOTE]
> 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 `useFormat`.

```ts
export interface StandardVulnerability {
/** Unique identifier for the vulnerability **/
id?: string;
/** Vulnerability origin, either Snyk, Sonatype, GitHub or NodeSWG **/
origin: Origin;
/** Package associated with the vulnerability **/
package: string;
/** Vulnerability title **/
title: string;
/** Vulnerability description **/
description?: string;
/** Vulnerability link references on origin's website **/
url?: string;
/** Vulnerability severity levels given the strategy **/
severity?: Severity;
/** Common Vulnerabilities and Exposures dictionary */
cves?: string[];
/**
* Common Vulnerability Scoring System (CVSS) provides a way to capture
* the principal characteristics of a vulnerability,
* and produce a numerical score reflecting its severity,
* as well as a textual representation of that score. **/
cvssVector?: string;
/** CVSS Score **/
cvssScore?: number;
/** The range of vulnerable versions provided when too many versions are vulnerables */
vulnerableRanges: string[];
/** The set of versions that are vulnerable **/
vulnerableVersions: string[];
/** The set of versions that are patched **/
patchedVersions?: string;
/** Overview of available patches to get rid of listed vulnerabilities **/
patches?: Patch[];
}
```
### Formats
- [Standard](./docs/formats/standard.md)
- [OSV](./docs/formats/osv.md)

### Databases
- [OSV](./docs/database/osv.md)
Expand Down
82 changes: 82 additions & 0 deletions docs/formats/osv.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# OSV vulnerability format

See [Open Source Vulnerability format](https://ossf.github.io/osv-schema/)

```ts
export interface OSV {
schema_version: string;
id: string;
modified: string;
published: string;
withdraw: string;
aliases: string[];
related: string[];
summary: string;
details: string;
severity: OSVSeverity[];
affected: OSVAffected[];
references: {
type: OSVReferenceType;
url: string;
}[];
credits: {
name: string;
contact: string[];
type: OSVCreditType;
}[];
database_specific: Record<string, any>;
}

export type OSVReferenceType = "ADVISORY" |
"ARTICLE" |
"DETECTION" |
"DISCUSSION" |
"REPORT" |
"FIX" |
"GIT" |
"INTRODUCED" |
"PACKAGE" |
"EVIDENCE" |
"WEB";

export type OSVCreditType = "FINDER" |
"REPORTER" |
"ANALYST" |
"COORDINATOR" |
"REMEDIATION_DEVELOPER" |
"REMEDIATION_REVIEWER" |
"REMEDIATION_VERIFIER" |
"TOOL" |
"SPONSOR" |
"OTHER";

export interface OSVAffected {
package: {
ecosystem: "npm",
name: string;
purl: string;
};
severity: OSVSeverity[];
ranges: OSVRange[];
versions: string[];
ecosystem_specific: Record<string, any>;
database_specific: Record<string, any>;
}

export interface OSVRange {
type: string;
repo: string;
events: {
introduced?: string;
fixed?: string;
last_affected?: string;
limit?: string;
}[];
database_specific: Record<string, any>;
}

export interface OSVSeverity {
type: string;
score: string;
}
```
40 changes: 40 additions & 0 deletions docs/formats/standard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Standard vulnerability format

We provide an high level format that work for all available strategy. It can be activated with the option `useFormat` equal `Standard`.

```ts
export interface StandardVulnerability {
/** Unique identifier for the vulnerability **/
id?: string;
/** Vulnerability origin, either Snyk, Sonatype, GitHub or NodeSWG **/
origin: Origin;
/** Package associated with the vulnerability **/
package: string;
/** Vulnerability title **/
title: string;
/** Vulnerability description **/
description?: string;
/** Vulnerability link references on origin's website **/
url?: string;
/** Vulnerability severity levels given the strategy **/
severity?: Severity;
/** Common Vulnerabilities and Exposures dictionary */
cves?: string[];
/**
* Common Vulnerability Scoring System (CVSS) provides a way to capture
* the principal characteristics of a vulnerability,
* and produce a numerical score reflecting its severity,
* as well as a textual representation of that score. **/
cvssVector?: string;
/** CVSS Score **/
cvssScore?: number;
/** The range of vulnerable versions provided when too many versions are vulnerables */
vulnerableRanges: string[];
/** The set of versions that are vulnerable **/
vulnerableVersions: string[];
/** The set of versions that are patched **/
patchedVersions?: string;
/** Overview of available patches to get rid of listed vulnerabilities **/
patches?: Patch[];
}
```
12 changes: 10 additions & 2 deletions src/formats/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import {
StandardizeKind
} from "./standard/index.js";

import {
osvVulnerabilityMapper,
OSVKind
} from "./osv/index.js";

export function formatVulnsPayload(
format: BaseStrategyFormat | null = null
) {
return function formatVulnerabilities(
strategy: StandardizeKind,
strategy: StandardizeKind | OSVKind,
vulnerabilities: any[]
) {
if (format === "Standard") {
Expand All @@ -20,7 +25,10 @@ export function formatVulnsPayload(
);
}
if (format === "OSV") {
throw new Error("Not Implemented Yet");
return osvVulnerabilityMapper(
strategy,
vulnerabilities
);
}

// identity function
Expand Down
21 changes: 18 additions & 3 deletions src/formats/osv/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
// Import Internal Dependencies
import { OSV_VULN_MAPPERS } from "./mappers.js";

/**
* @see https://ossf.github.io/osv-schema/
*/
export interface OSV {
schema_version: string;
schema_version?: string;
id: string;
modified: string;
published: string;
withdraw: string;
withdraw?: string;
aliases: string[];
related: string[];
related?: string[];
summary: string;
details: string;
severity: OSVSeverity[];
Expand Down Expand Up @@ -78,3 +80,16 @@ export interface OSVSeverity {
type: string;
score: string;
}

export type OSVKind = keyof typeof OSV_VULN_MAPPERS;

export function osvVulnerabilityMapper(
strategy: OSVKind,
vulnerabilities: any[]
): OSV[] {
if (!(strategy in OSV_VULN_MAPPERS)) {
return [];
}

return vulnerabilities.map(OSV_VULN_MAPPERS[strategy]);
}
111 changes: 111 additions & 0 deletions src/formats/osv/mappers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Import Internal Dependencies
import { VULN_MODE } from "../../constants.js";
import * as utils from "../../utils.js";

import type { OSV } from "./index.js";
import type {
SonatypeVulnerability,
SnykVulnerability,
NpmAuditAdvisory,
PnpmAuditAdvisory
} from "../../index.js";

function mapFromNPM(
vuln: NpmAuditAdvisory
): OSV {
const hasCVSS = typeof vuln.cvss !== "undefined";

return {
id: String(vuln.source),
references: [
{
type: "ADVISORY",
url: vuln.url
}
],
package: vuln.name,
title: vuln.title,
severity: utils.standardizeNpmSeverity(vuln.severity),
vulnerableRanges: utils.fromMaybeStringToArray(vuln.range),
vulnerableVersions: utils.fromMaybeStringToArray(vuln.vulnerableVersions),
...(hasCVSS ?
{ cvssScore: vuln.cvss!.score, cvssVector: vuln.cvss!.vectorString } :
{}
)
};
}

function mapFromPnpm(
vuln: PnpmAuditAdvisory
): OSV {
const hasCVSS = typeof vuln.cvss !== "undefined";

return {
id: String(vuln.id),
origin: VULN_MODE.GITHUB_ADVISORY,
package: vuln.module_name,
title: vuln.title,
description: vuln.overview,
url: vuln.url,
severity: utils.standardizeNpmSeverity(vuln.severity),
cves: vuln.cves,
patchedVersions: vuln.patched_versions,
vulnerableRanges: [],
vulnerableVersions: utils.fromMaybeStringToArray(vuln.vulnerable_versions),
...(hasCVSS ?
{ cvssScore: vuln.cvss.score, cvssVector: vuln.cvss.vectorString } :
{}
)
};
}

function mapFromSnyk(
vuln: SnykVulnerability
): OSV {
function concatVulnerableVersions(vulnFunctions) {
return vulnFunctions
.reduce((ranges, functions) => [...ranges, ...functions.version], []);
}

return {
id: vuln.id,
origin: VULN_MODE.SNYK,
package: vuln.package,
title: vuln.title,
url: vuln.url,
description: vuln.description,
severity: vuln.severity,
vulnerableVersions: concatVulnerableVersions(vuln.functions),
vulnerableRanges: vuln.semver.vulnerable,
cves: vuln.identifiers.CVE,
cvssVector: vuln.CVSSv3,
cvssScore: vuln.cvssScore,
patches: vuln.patches
};
}

function mapFromSonatype(
vuln: SonatypeVulnerability
): OSV {
return {
id: vuln.id,
origin: VULN_MODE.SONATYPE,
package: vuln.package,
title: vuln.title,
url: vuln.reference,
description: vuln.description,
vulnerableRanges: vuln.versionRanges ?? [],
vulnerableVersions: vuln.versionRanges ?? [],
cves: utils.fromMaybeStringToArray(vuln.cve),
cvssVector: vuln.cvssVector,
cvssScore: vuln.cvssScore
};
}

export const OSV_VULN_MAPPERS = Object.freeze({
[VULN_MODE.GITHUB_ADVISORY]: mapFromNPM,
"github-advisory_pnpm": mapFromPnpm,
[VULN_MODE.SNYK]: mapFromSnyk,
[VULN_MODE.SONATYPE]: mapFromSonatype
});

0 comments on commit ff9adca

Please sign in to comment.