Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/api/advisories-bulk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 📂 Api `advisories-bulk`

The `advisories-bulk` api retrieves vulnerabilities informations about packages and their specific versions.


## Syntax

```ts
advisoriesBulk(packageVersions:PackageVersions,options?:AdvisoriesBulkApiOptions): Promise<Advisories>
```

## Types

```ts
/**
* Record of package names to their versions to query advisories for.
* Key: package name (e.g., "lodash")
* Value: array of versions (e.g., ["4.17.21", "4.17.20"])
*/
export type PackageVersions = Record<string, string[]>;
/**
* Record of package names to their Advisory.
* Key: package name (e.g., "lodash")
* Value: Advisory
*/

export type AdvisoriesBulkApiOptions = DefaultRegistryApiOptions & { registry?: string; };

export type Advisories = Record<string, Advisory[]>;

export interface Cvss {
score: number;
vectorString: string | null;
}

export interface Advisory {
id: number;
url: string;
title: string;
severity: string;
vulnerable_versions: string;
cwe: string[];
cvss: Cvss;
}
```
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"homepage": "https://github.com/NodeSecure/npm-registry-sdk#readme",
"dependencies": {
"@nodesecure/npm-types": "^1.3.0",
"@openally/httpie": "^1.0.0"
"@openally/httpie": "1.1.0"
},
"devDependencies": {
"@openally/config.eslint": "^2.0.0",
Expand Down
51 changes: 51 additions & 0 deletions src/api/advisories-bulk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Import Third-party Dependencies
import * as httpie from "@openally/httpie";

// Import Internal Dependencies
import { getHttpAgent } from "../http.ts";
import { getLocalRegistryURL } from "../registry.ts";
import type { DefaultRegistryApiOptions } from "./common/types.ts";

/**
* Record of package names to their versions to query advisories for.
* Key: package name (e.g., "lodash")
* Value: array of versions (e.g., ["4.17.21", "4.17.20"])
*/
export type PackageVersions = Record<string, string[]>;

export type AdvisoriesBulkApiOptions = DefaultRegistryApiOptions & { registry?: string; };

export interface Cvss {
score: number;
vectorString: string | null;
}

export interface Advisory {
id: number;
url: string;
title: string;
severity: string;
vulnerable_versions: string;
cwe: string[];
cvss: Cvss;
}

/**
* Record of package names to their Advisory.
* Key: package name (e.g., "lodash")
* Value: Advisory
*/
export type Advisories = Record<string, Advisory[]>;

export async function advisoriesBulk(packageVersions: PackageVersions, options?: AdvisoriesBulkApiOptions): Promise<Advisories> {
const query = new URL("/-/npm/v1/security/advisories/bulk", options?.registry ?? getLocalRegistryURL());

const { data } = await httpie.post<string>(query, {
authorization: options?.token,
body: packageVersions,
agent: getHttpAgent()
});

return JSON.parse(data);
}

1 change: 1 addition & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from "./keys.ts";
export * from "./package-dist-tags.ts";
export * from "./tarball-download.ts";
export * from "./org.ts";
export * from "./advisories-bulk.ts";
119 changes: 119 additions & 0 deletions test/advisories-bulk.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Import Node.js Dependencies
import { describe, it } from "node:test";
import assert from "node:assert";

// Import Internal Dependencies
import {
advisoriesBulk,
setHttpAgent,
restoreHttpAgent,
setLocalRegistryURL,
getLocalRegistryURL
} from "../src/index.ts";
import { setupHttpAgentMock } from "./httpie-mock.ts";

const kDefaultAuditReports = {
lodash: [
{
id: 1106900,
url: "https://github.com/advisories/GHSA-fvqr-27wr-82fm",
title: "Prototype Pollution in lodash",
severity: "moderate",
vulnerable_versions: "<4.17.5",
cwe: ["CWE-471", "CWE-1321"],
cvss: {
score: 6.5,
vectorString: "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N"
}
},
{
id: 1106913,
url: "https://github.com/advisories/GHSA-35jh-r3h4-6jhm",
title: "Command Injection in lodash",
severity: "high",
vulnerable_versions: "<4.17.21",
cwe: ["CWE-77", "CWE-94"],
cvss: {
score: 7.2,
vectorString: "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H"
}
},
{
id: 1106914,
url: "https://github.com/advisories/GHSA-4xc9-xhrj-v574",
title: "Prototype Pollution in lodash",
severity: "high",
vulnerable_versions: "<4.17.11",
cwe: ["CWE-400"],
cvss: {
score: 0,
vectorString: null
}
},
{
id: 1106918,
url: "https://github.com/advisories/GHSA-jf85-cpcp-j695",
title: "Prototype Pollution in lodash",
severity: "critical",
vulnerable_versions: "<4.17.12",
cwe: ["CWE-20", "CWE-1321"],
cvss: {
score: 9.1,
vectorString: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H"
}
}
]
};

describe("advisories-bulk", () => {
it("should retrieve the audit reports based on the provided body", async() => {
const auditReports = await advisoriesBulk({ lodash: ["1.0.1"] });
assert.deepEqual(auditReports, kDefaultAuditReports);
});

it("should be able to force the registry and pass an authentication token", async() => {
const packageVersions = { lodash: ["1.0.1"] };
const registry = "https://some.private.registry";
const [dispatcher, close, agent] = setupHttpAgentMock(new URL(registry).origin);
setHttpAgent(agent);
dispatcher
.intercept({
path: "/-/npm/v1/security/advisories/bulk",
method: "POST",
headers: { "user-agent": "httpie", Authorization: "Bearer npmToken" },
body: JSON.stringify(packageVersions)
})
.reply(200, kDefaultAuditReports);
const auditReports = await advisoriesBulk(packageVersions, {
registry,
token: "npmToken"
});
assert.deepEqual(auditReports, kDefaultAuditReports);
close();
restoreHttpAgent();
});

it("should query with the local registry", async() => {
const packageVersions = { lodash: ["1.0.1"] };
const orignalLocalRegistry = getLocalRegistryURL();
const registry = "https://some.private.registry";
setLocalRegistryURL(registry);
const [dispatcher, close, agent] = setupHttpAgentMock(new URL(registry).origin);
setHttpAgent(agent);
dispatcher
.intercept({
path: "/-/npm/v1/security/advisories/bulk",
method: "POST",
headers: { "user-agent": "httpie", Authorization: "Bearer npmToken" },
body: JSON.stringify(packageVersions)
})
.reply(200, kDefaultAuditReports);
const auditReports = await advisoriesBulk(packageVersions, {
token: "npmToken"
});
assert.deepEqual(auditReports, kDefaultAuditReports);
close();
setLocalRegistryURL(orignalLocalRegistry);
restoreHttpAgent();
});
});
Loading