diff --git a/.gitignore b/.gitignore
index 220bed7..85bbcb7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -108,3 +108,4 @@ dist
.vscode/
temp/
+.claude
diff --git a/docs/database/nvd.md b/docs/database/nvd.md
index e535da2..339af72 100644
--- a/docs/database/nvd.md
+++ b/docs/database/nvd.md
@@ -4,7 +4,7 @@ NVD stand for National Vulnerability Database, which is the U.S. gove
## Implementation Notes
-The NVD integration uses the REST API (v2.0) available at [services.nvd.nist.gov](https://services.nvd.nist.gov/rest/json/cves/2.0).
+The NVD integration uses the REST API (v2.0) available at [services.nvd.nist.gov](https://services.nvd.nist.gov/rest/json/cves/2.0).
### Search Parameters
@@ -30,7 +30,27 @@ export interface NVD {
## API
-### findOne(parameters: NVDApiParameter): Promise< NVD[] >
+### Constructor
+
+```ts
+import * as vulnera from "@nodesecure/vulnera";
+
+const db = new vulnera.Database.NVD({
+ credential: new vulnera.ApiCredential({
+ type: "querystring",
+ name: "apiKey",
+ value: "your-api-key"
+ })
+});
+```
+
+```ts
+export interface NVDOptions {
+ credential?: ApiCredential;
+}
+```
+
+### `findOne(parameters: NVDApiParameter): Promise`
Find the vulnerabilities of a given package using available NVD API parameters.
```ts
@@ -43,19 +63,20 @@ export type NVDApiParameter = {
};
```
-### findOneBySpec(spec: string): Promise< NVD[] >
+### `findOneBySpec(spec: string): Promise`
Find the vulnerabilities of a given package using the NPM spec format like `packageName@version`.
```ts
-import * as vulnera from "@nodesecure/vulnera";
-
-const vulns = await vulnera.Database.nvd.findOneBySpec(
- "express@4.0.0"
-);
+const vulns = await db.findOneBySpec("express@4.0.0");
console.log(vulns);
```
-### findMany< T extends string >(specs: T[]): Promise< Record< T, NVD[] > >
+### `findMany(specs: T[]): Promise>`
Find the vulnerabilities of many packages using the spec format.
-Returns a Record where keys are equals to the provided specs.
\ No newline at end of file
+Returns a Record where keys are equals to the provided specs.
+
+```ts
+const vulns = await db.findMany(["express@4.0.0", "lodash@4.17.0"]);
+console.log(vulns);
+```
diff --git a/docs/database/osv.md b/docs/database/osv.md
index fe169b0..c534d95 100644
--- a/docs/database/osv.md
+++ b/docs/database/osv.md
@@ -38,7 +38,21 @@ export interface OSV {
## API
-### findOne(parameters: OSVApiParameter): Promise< OSV[] >
+### Constructor
+
+```ts
+import * as vulnera from "@nodesecure/vulnera";
+
+const db = new vulnera.Database.OSV();
+```
+
+```ts
+export interface OSVOptions {
+ credential?: ApiCredential;
+}
+```
+
+### `findOne(parameters: OSVApiParameter): Promise`
Find the vulnerabilities of a given package using available OSV API parameters.
```ts
@@ -54,19 +68,20 @@ export type OSVApiParameter = {
}
```
-### findOneBySpec(spec: string): Promise< OSV[] >
+### `findOneBySpec(spec: string): Promise`
Find the vulnerabilities of a given package using the NPM spec format like `packageName@version`.
```ts
-import * as vulnera from "@nodesecure/vulnera";
-
-const vulns = await vulnera.Database.osv.findOneBySpec(
- "01template1"
-);
+const vulns = await db.findOneBySpec("01template1");
console.log(vulns);
```
-### findMany< T extends string >(specs: T[]): Promise< Record< T, OSV[] > >
+### `findMany(specs: T[]): Promise>`
Find the vulnerabilities of many packages using the spec format.
Return a Record where keys are equals to the provided specs.
+
+```ts
+const vulns = await db.findMany(["express@4.0.0", "lodash@4.17.0"]);
+console.log(vulns);
+```
diff --git a/docs/database/snyk.md b/docs/database/snyk.md
new file mode 100644
index 0000000..7285f69
--- /dev/null
+++ b/docs/database/snyk.md
@@ -0,0 +1,95 @@
+# Snyk
+
+[Snyk](https://snyk.io/fr) Snyk Limited is a developer-oriented cybersecurity company, specializing in securing custom developed code, open-source dependencies and cloud infrastructure.
+
+## Implementation Notes
+
+The Snyk integration uses the REST API (v1) available at [snyk.io](https://snyk.io/api/v1/test/npm) to perform security audit.
+
+### Authentication
+
+The `Snyk` constructor requires an `org` and a `credential`. These are generated when you create an organization on Snyk.
+
+- `org`: Your Snyk organization ID
+- `credential`: An `ApiCredential` instance using the `token` type (passed as `Authorization: token ` header)
+
+### Format
+
+The Snyk interface is exported as root like `SnykAuditResponse`.
+
+```ts
+export interface SnykAuditResponse {
+ /** Does this package have one or more issues? **/
+ ok: boolean;
+ /** The issues found. **/
+ issues: {
+ vulnerabilities: SnykVulnerability[];
+ licenses: SnykVulnerability[];
+ };
+ /** The number of dependencies the package has. **/
+ dependencyCount: number;
+ /** The organization this test was carried out for. **/
+ org: {
+ id: string;
+ name: string;
+ };
+ /** The organization's licenses policy used for this test **/
+ licensesPolicy: null | object;
+ /** The package manager for this package **/
+ packageManager: string;
+}
+```
+
+## API
+
+### Constructor
+
+```ts
+import * as vulnera from "@nodesecure/vulnera";
+
+const db = new vulnera.Database.Snyk({
+ org: process.env.SNYK_ORG,
+ credential: new vulnera.ApiCredential(process.env.SNYK_TOKEN)
+});
+```
+
+```ts
+export interface SnykOptions {
+ org: string;
+ credential: ApiCredential;
+}
+```
+
+### `findOne(parameters: SnykFindOneParameters): Promise`
+
+Find the vulnerabilities of a given package using available SnykFindOneParameters API parameters.
+
+```ts
+export type SnykFindOneParameters = {
+ files: {
+ target: {
+ contents: string;
+ };
+ additional?: {
+ contents: string;
+ }[];
+ };
+};
+```
+
+```ts
+import * as vulnera from "@nodesecure/vulnera";
+
+const db = new vulnera.Database.Snyk({
+ org: process.env.SNYK_ORG,
+ credential: new vulnera.ApiCredential({
+ type: "token",
+ token: process.env.SNYK_TOKEN
+ })
+});
+const result = await db.findOne({
+ files: {
+ target: { contents: packageJsonBase64 }
+ }
+});
+```
diff --git a/docs/database/sonatype.md b/docs/database/sonatype.md
index 9928e4c..aa0c730 100644
--- a/docs/database/sonatype.md
+++ b/docs/database/sonatype.md
@@ -2,22 +2,50 @@
Sonatype provides software supply chain security and repository management tools to help organizations manage risks in their open source dependencies.
-### Implementation Notes
+## Implementation Notes
The Sonatype integration uses the REST API (v3) available at [ossindex.sonatype.org](https://ossindex.sonatype.org/api/v3/component-report).
+### Authentication
+
+`Sonatype` supports optional basic auth credentials for higher rate limits. Without credentials, the API is still accessible at reduced rate limits.
+
### Format
-the Sonatype interface is exported as root like `SonatypeResponse`.
+The Sonatype interface is exported as root like `SonatypeResponse`.
+
+```ts
+export type SonatypeResponse = {
+ coordinates: string;
+ vulnerabilities: SonatypeVulnerability[];
+};
+```
+
+## API
+
+### Constructor
+
+```ts
+import * as vulnera from "@nodesecure/vulnera";
+
+const db = new vulnera.Database.Sonatype({
+ credential: new vulnera.ApiCredential({
+ type: "basic",
+ username: process.env.SONATYPE_USERNAME,
+ password: process.env.SONATYPE_PASSWORD
+ })
+});
+```
```ts
-export type SonatypeResponse = {
- coordinates: string; vulnerabilities: SonatypeVulnerability[];
- };
+export interface SonatypeOptions {
+ credential?: ApiCredential;
+}
```
-### API
-### findOne(parameters: SonaTypeFindOneParameters): Promise< SonatypeResponse[] >
+### `findOne(parameters: SonaTypeFindOneParameters): Promise`
+
+Find the vulnerabilities of a given package using available Sonatype API parameters.
```ts
export type SonaTypeFindOneParameters = {
@@ -25,9 +53,17 @@ export type SonaTypeFindOneParameters = {
};
```
-Find the vulnerabilities of a given package using available Sonatype API parameters.
+```ts
+import * as vulnera from "@nodesecure/vulnera";
-### findMany(parameters: SonaTypeFindManyParameters): Promise< SonatypeResponse[] > >
+const db = new vulnera.Database.Sonatype();
+const vulns = await db.findOne({ coordinates: ["pkg:npm/express@4.0.0"] });
+console.log(vulns);
+```
+
+### `findMany(parameters: SonaTypeFindManyParameters): Promise`
+
+Find the vulnerabilities of many packages.
```ts
export type SonaTypeFindManyParameters = {
@@ -35,4 +71,15 @@ export type SonaTypeFindManyParameters = {
};
```
-Find the vulnerabilities of many packages.
\ No newline at end of file
+```ts
+import * as vulnera from "@nodesecure/vulnera";
+
+const db = new vulnera.Database.Sonatype();
+const vulns = await db.findMany({
+ coordinates: [
+ ["pkg:npm/express@4.0.0"],
+ ["pkg:npm/lodash@4.17.0"]
+ ]
+});
+console.log(vulns);
+```
diff --git a/docs/database/synk.md b/docs/database/synk.md
deleted file mode 100644
index 2412c26..0000000
--- a/docs/database/synk.md
+++ /dev/null
@@ -1,62 +0,0 @@
-# Synk
-
-[Snyk](https://snyk.io/fr) Snyk Limited is a developer-oriented cybersecurity company, specializing in securing custom developed code, open-source dependencies and cloud infrastructure.
-
-## Implementation Notes
-
-the Snyk integration uses the REST API (v1) available at [snyk.io](https://snyk.io/api/v1/test/npm) to perfom security audit.
-
-
-### Search Parameters
-
-We have to pass the `org` search paraeter who is generated when you create an organization on Synk.
-
-### Headers
-
-we also need to pass the `Authorization` header with the token generated by Synk.
-
-### Format
-
-The Synk interface is exported as root like `SnykAuditResponse`.
-
-```ts
-export interface SnykAuditResponse {
- /** Does this package have one or more issues? **/
- ok: boolean;
- /** The issues found. **/
- issues: {
- vulnerabilities: SnykVulnerability[];
- licenses: SnykVulnerability[];
- };
- /** The number of dependencies the package has. **/
- dependencyCount: number;
- /** The organization this test was carried out for. **/
- org: {
- id: string;
- name: string;
- };
- /** The organization's licenses policy used for this test **/
- licensesPolicy: null | object;
- /** The package manager for this package **/
- packageManager: string;
-}
-```
-
-## API
-
-### findOne(parameters: SnykFindOneParameters): Promise< SnykAuditResponse>
-
-Find the vulnerabilities of a given package using available SnykFindOneParameters API parameters.
-
-```ts
-export type SnykFindOneParameters = {
- files: {
- target: {
- contents: string;
- };
- additional?: {
- contents: string;
- }[];
- };
-};
-```
\ No newline at end of file
diff --git a/src/constants.ts b/src/constants.ts
index 988891f..04d492b 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -1,7 +1,5 @@
export const NPM_TOKEN = typeof process.env.NODE_SECURE_TOKEN === "string" ?
{ token: process.env.NODE_SECURE_TOKEN } : {};
-export const SNYK_ORG = process.env.SNYK_ORG;
-export const SNYK_TOKEN = process.env.SNYK_TOKEN;
export const VULN_MODE = Object.freeze({
GITHUB_ADVISORY: "github-advisory",
diff --git a/src/credential.ts b/src/credential.ts
new file mode 100644
index 0000000..c4040f5
--- /dev/null
+++ b/src/credential.ts
@@ -0,0 +1,56 @@
+export type ApiCredentialOptions =
+ | { type: "bearer"; token: string; }
+ | { type: "token"; token: string; }
+ | { type: "basic"; username: string; password: string; }
+ | { type: "querystring"; name: string; value: string; }
+ | { type: "custom"; authorization: string; };
+
+export class ApiCredential {
+ readonly #options: ApiCredentialOptions | undefined;
+
+ constructor(
+ optionsOrToken?: ApiCredentialOptions | string
+ ) {
+ this.#options = typeof optionsOrToken === "string"
+ ? { type: "bearer", token: optionsOrToken }
+ : optionsOrToken;
+ }
+
+ get headers(): Record {
+ if (!this.#options) {
+ return {};
+ }
+
+ const options = this.#options;
+
+ if (options.type === "bearer") {
+ return { Authorization: `Bearer ${options.token}` };
+ }
+ if (options.type === "token") {
+ return { Authorization: `token ${options.token}` };
+ }
+ if (options.type === "basic") {
+ const encoded = Buffer.from(`${options.username}:${options.password}`).toString("base64");
+
+ return { Authorization: `Basic ${encoded}` };
+ }
+ if (options.type === "querystring") {
+ return {};
+ }
+ if (options.type === "custom") {
+ return { Authorization: options.authorization };
+ }
+
+ return {};
+ }
+
+ get queryParams(): Record {
+ if (this.#options?.type === "querystring") {
+ return {
+ [this.#options.name]: this.#options.value
+ };
+ }
+
+ return {};
+ }
+}
diff --git a/src/database/index.ts b/src/database/index.ts
index e9e022a..26e5272 100644
--- a/src/database/index.ts
+++ b/src/database/index.ts
@@ -1,4 +1,29 @@
-export * as osv from "./osv.ts";
-export * as snyk from "./snyk.ts";
-export * as nvd from "./nvd.ts";
-export * as sonatype from "./sonatype.ts";
+export { ApiCredential } from "../credential.ts";
+export type { ApiCredentialOptions } from "../credential.ts";
+
+export {
+ NVD
+} from "./nvd.ts";
+export type {
+ NVDOptions,
+ NVDApiParameter
+} from "./nvd.ts";
+
+export { OSV } from "./osv.ts";
+export type {
+ OSVOptions,
+ OSVApiParameter
+} from "./osv.ts";
+
+export { Snyk } from "./snyk.ts";
+export type {
+ SnykOptions,
+ SnykFindOneParameters
+} from "./snyk.ts";
+
+export { Sonatype } from "./sonatype.ts";
+export type {
+ SonatypeOptions,
+ SonaTypeFindOneParameters,
+ SonaTypeFindManyParameters
+} from "./sonatype.ts";
diff --git a/src/database/nvd.ts b/src/database/nvd.ts
index 769a2d8..5dc317f 100644
--- a/src/database/nvd.ts
+++ b/src/database/nvd.ts
@@ -3,10 +3,8 @@ import * as httpie from "@openally/httpie";
// Import Internal Dependencies
import * as utils from "../utils.ts";
-import type { NVD } from "../formats/nvd/index.ts";
-
-// CONSTANTS
-export const ROOT_API = "https://services.nvd.nist.gov/rest/json/cves/2.0";
+import type { NVD as NVDFormat } from "../formats/nvd/index.ts";
+import type { ApiCredential } from "../credential.ts";
/**
* @description Parameters for querying the NVD API
@@ -29,63 +27,76 @@ export type NVDApiParameter = {
ecosystem?: string;
};
-export async function findOne(
- parameters: NVDApiParameter
-): Promise {
- const queryParams = new URLSearchParams();
-
- if (parameters.packageName) {
- queryParams.append("keywordSearch", parameters.packageName);
- /**
- * NVD doesn't support cpeMatchString
- * We will only search by keyword for now
- */
- }
- if (parameters.cvssV3Severity) {
- queryParams.append("cvssV3Severity", parameters.cvssV3Severity);
- }
+export interface NVDOptions {
+ credential: ApiCredential;
+}
- if (parameters.cweId) {
- queryParams.append("cweId", parameters.cweId);
+export class NVD {
+ static readonly ROOT_API = "https://services.nvd.nist.gov/rest/json/cves/2.0";
+
+ readonly #credential: ApiCredential;
+
+ constructor(
+ options: NVDOptions
+ ) {
+ this.#credential = options.credential;
}
- const url = new URL(ROOT_API);
- url.search = queryParams.toString();
+ async findOne(
+ parameters: NVDApiParameter
+ ): Promise {
+ const queryParams = new URLSearchParams();
- try {
- const { data } = await httpie.get<{ vulnerabilities: NVD[]; }>(url.toString());
+ if (parameters.packageName) {
+ queryParams.append("keywordSearch", parameters.packageName);
+ /**
+ * NVD doesn't support cpeMatchString
+ * We will only search by keyword for now
+ */
+ }
+ if (parameters.cvssV3Severity) {
+ queryParams.append("cvssV3Severity", parameters.cvssV3Severity);
+ }
+ if (parameters.cweId) {
+ queryParams.append("cweId", parameters.cweId);
+ }
+ for (const [name, value] of Object.entries(this.#credential.queryParams)) {
+ queryParams.append(name, value);
+ }
- return data.vulnerabilities || [];
- }
- catch (error: any) {
- console.error("NVD API Error:", error.message || error);
+ const url = new URL(NVD.ROOT_API);
+ url.search = queryParams.toString();
- return [];
+ try {
+ const { data } = await httpie.get<{ vulnerabilities: NVDFormat[]; }>(url.toString());
+
+ return data.vulnerabilities || [];
+ }
+ catch (error: any) {
+ console.error("NVD API Error:", error.message || error);
+
+ return [];
+ }
}
-}
-export function findOneBySpec(
- spec: string
-) {
- const { name } = utils.parseNpmSpec(spec);
+ findOneBySpec(
+ spec: string
+ ): Promise {
+ const { name } = utils.parseNpmSpec(spec);
- return findOne({
- packageName: name,
- ecosystem: "npm"
- });
-}
+ return this.findOne({
+ packageName: name,
+ ecosystem: "npm"
+ });
+ }
+
+ async findMany(
+ specs: T[]
+ ): Promise> {
+ const entries = await Promise.all(
+ specs.map(async(spec) => [spec, await this.findOneBySpec(spec)] as [T, NVDFormat[]])
+ );
-export async function findMany(
- specs: T[]
-): Promise> {
- const packagesVulns = await Promise.all(
- specs.map(async(spec) => {
- return {
- [spec]: await findOneBySpec(spec)
- };
- })
- );
-
- // @ts-ignore
- return Object.assign(...packagesVulns);
+ return Object.fromEntries(entries) as Record;
+ }
}
diff --git a/src/database/osv.ts b/src/database/osv.ts
index 9978f0e..2621c18 100644
--- a/src/database/osv.ts
+++ b/src/database/osv.ts
@@ -2,11 +2,9 @@
import * as httpie from "@openally/httpie";
// Import Internal Dependencies
-import type { OSV } from "../formats/osv/index.ts";
+import type { OSV as OSVFormat } from "../formats/osv/index.ts";
import * as utils from "../utils.ts";
-
-// CONSTANTS
-export const ROOT_API = "https://api.osv.dev";
+import type { ApiCredential } from "../credential.ts";
export type OSVApiParameter = {
version?: string;
@@ -19,47 +17,59 @@ export type OSVApiParameter = {
};
};
-export async function findOne(
- parameters: OSVApiParameter
-): Promise {
- if (!parameters.package.ecosystem) {
- parameters.package.ecosystem = "npm";
+export interface OSVOptions {
+ credential?: ApiCredential;
+}
+
+export class OSV {
+ static readonly ROOT_API = "https://api.osv.dev";
+
+ readonly #credential: ApiCredential | undefined;
+
+ constructor(
+ options: OSVOptions = {}
+ ) {
+ this.#credential = options.credential;
}
- const { data } = await httpie.post<{ vulns: OSV[]; }>(
- new URL("v1/query", ROOT_API),
- {
- body: parameters
+ async findOne(
+ parameters: OSVApiParameter
+ ): Promise {
+ if (!parameters.package.ecosystem) {
+ parameters.package.ecosystem = "npm";
}
- );
- return data.vulns;
-}
+ const { data } = await httpie.post<{ vulns: OSVFormat[]; }>(
+ new URL("v1/query", OSV.ROOT_API),
+ {
+ headers: this.#credential?.headers,
+ body: parameters
+ }
+ );
-export function findOneBySpec(
- spec: string
-) {
- const { name, version } = utils.parseNpmSpec(spec);
+ return data.vulns;
+ }
- return findOne({
- version,
- package: {
- name
- }
- });
-}
+ findOneBySpec(
+ spec: string
+ ): Promise {
+ const { name, version } = utils.parseNpmSpec(spec);
-export async function findMany(
- specs: T[]
-): Promise> {
- const packagesVulns = await Promise.all(
- specs.map(async(spec) => {
- return {
- [spec]: await findOneBySpec(spec)
- };
- })
- );
+ return this.findOne({
+ version,
+ package: {
+ name
+ }
+ });
+ }
+
+ async findMany(
+ specs: T[]
+ ): Promise> {
+ const entries = await Promise.all(
+ specs.map(async(spec) => [spec, await this.findOneBySpec(spec)] as [T, OSVFormat[]])
+ );
- // @ts-ignore
- return Object.assign(...packagesVulns);
+ return Object.fromEntries(entries) as Record;
+ }
}
diff --git a/src/database/snyk.ts b/src/database/snyk.ts
index feb6776..4139f92 100644
--- a/src/database/snyk.ts
+++ b/src/database/snyk.ts
@@ -2,11 +2,8 @@
import * as httpie from "@openally/httpie";
// Import Internal Dependencies
-import { SNYK_ORG, SNYK_TOKEN } from "../constants.ts";
import type { SnykAuditResponse } from "../formats/snyk/index.ts";
-
-// CONSTANTS
-export const ROOT_API = "https://snyk.io";
+import type { ApiCredential } from "../credential.ts";
export type SnykFindOneParameters = {
files: {
@@ -19,18 +16,35 @@ export type SnykFindOneParameters = {
};
};
-export async function findOne(
- parameters: SnykFindOneParameters
-): Promise {
- const { data } = await httpie.post(
- new URL(`/api/v1/test/npm?org=${SNYK_ORG}`, ROOT_API),
- {
- headers: {
- Authorization: `token ${SNYK_TOKEN}`
- },
- body: parameters
- }
- );
-
- return data;
+export interface SnykOptions {
+ org: string;
+ credential: ApiCredential;
+}
+
+export class Snyk {
+ static readonly ROOT_API = "https://snyk.io";
+
+ readonly #org: string;
+ readonly #credential: ApiCredential;
+
+ constructor(
+ options: SnykOptions
+ ) {
+ this.#org = options.org;
+ this.#credential = options.credential;
+ }
+
+ async findOne(
+ parameters: SnykFindOneParameters
+ ): Promise {
+ const { data } = await httpie.post(
+ new URL(`/api/v1/test/npm?org=${this.#org}`, Snyk.ROOT_API),
+ {
+ headers: this.#credential.headers,
+ body: parameters
+ }
+ );
+
+ return data;
+ }
}
diff --git a/src/database/sonatype.ts b/src/database/sonatype.ts
index 187d8e1..ebeb316 100644
--- a/src/database/sonatype.ts
+++ b/src/database/sonatype.ts
@@ -3,6 +3,7 @@ import * as httpie from "@openally/httpie";
// Import Internal Dependencies
import type { SonatypeResponse } from "../formats/sonatype/index.ts";
+import type { ApiCredential } from "../credential.ts";
export type SonaTypeFindOneParameters = {
coordinates: string[];
@@ -12,29 +13,47 @@ export type SonaTypeFindManyParameters = {
coordinates: string[][];
};
-// CONSTANTS
-export const ROOT_API = "https://ossindex.sonatype.org";
-
-export async function findOne(
- parameters: SonaTypeFindOneParameters
-): Promise {
- const { data } = await httpie.post(
- new URL("/api/v3/component-report", ROOT_API),
- {
- headers: {
- accept: "application/json"
- },
- body: parameters
- }
- );
-
- return data;
+export interface SonatypeOptions {
+ credential: ApiCredential;
}
-export async function findMany(parameters: SonaTypeFindManyParameters): Promise {
- const data = await Promise.all(
- parameters.coordinates.map((coordinates) => findOne({ coordinates }))
- );
-
- return data.flat();
+export class Sonatype {
+ static readonly ROOT_API = "https://ossindex.sonatype.org";
+
+ readonly #credential: ApiCredential;
+
+ constructor(
+ options: SonatypeOptions
+ ) {
+ this.#credential = options.credential;
+ }
+
+ async findOne(
+ parameters: SonaTypeFindOneParameters
+ ): Promise {
+ const headers: Record = {
+ accept: "application/json",
+ ...this.#credential.headers
+ };
+
+ const { data } = await httpie.post(
+ new URL("/api/v3/component-report", Sonatype.ROOT_API),
+ {
+ headers,
+ body: parameters
+ }
+ );
+
+ return data;
+ }
+
+ async findMany(
+ parameters: SonaTypeFindManyParameters
+ ): Promise {
+ const data = await Promise.all(
+ parameters.coordinates.map((coordinates) => this.findOne({ coordinates }))
+ );
+
+ return data.flat();
+ }
}
diff --git a/src/index.ts b/src/index.ts
index 83f7c65..f6bde63 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -9,12 +9,14 @@ import {
import {
SnykStrategy,
- type SnykStrategyDefinition
+ type SnykStrategyDefinition,
+ type SnykStrategyOptions
} from "./strategies/snyk.ts";
import {
SonatypeStrategy,
type SonatypeStrategyDefinition,
+ type SonatypeStrategyOptions,
type SonatypeVulnerability
} from "./strategies/sonatype.ts";
@@ -28,6 +30,8 @@ import {
type Kind
} from "./constants.ts";
+import { ApiCredential, type ApiCredentialOptions } from "./credential.ts";
+
import type {
SnykVulnerability
} from "./formats/snyk/index.ts";
@@ -51,6 +55,8 @@ import type {
} from "./strategies/types/api.ts";
export * as Database from "./database/index.ts";
+export { ApiCredential };
+export type { ApiCredentialOptions };
export type AllStrategy = {
none: NoneStrategyDefinition;
@@ -60,6 +66,13 @@ export type AllStrategy = {
};
export type AnyStrategy = AllStrategy[keyof AllStrategy];
+type StrategyOptions = {
+ none: undefined;
+ "github-advisory": undefined;
+ snyk: SnykStrategyOptions;
+ sonatype: SonatypeStrategyOptions;
+};
+
// CONSTANTS
const kAvailableStrategy = new Set(Object.values(VULN_MODE));
@@ -67,7 +80,8 @@ const kAvailableStrategy = new Set(Object.values(VULN_MODE));
let localVulnerabilityStrategy: AnyStrategy;
export function setStrategy(
- name: T
+ name: T,
+ options?: StrategyOptions[T]
): AllStrategy[T] {
if (!kAvailableStrategy.has(name)) {
throw new Error(
@@ -79,10 +93,10 @@ export function setStrategy(
localVulnerabilityStrategy = Object.seal(GitHubAdvisoryStrategy());
}
else if (name === VULN_MODE.SNYK) {
- localVulnerabilityStrategy = Object.seal(SnykStrategy());
+ localVulnerabilityStrategy = Object.seal(SnykStrategy(options as SnykStrategyOptions));
}
else if (name === VULN_MODE.SONATYPE) {
- localVulnerabilityStrategy = Object.seal(SonatypeStrategy());
+ localVulnerabilityStrategy = Object.seal(SonatypeStrategy(options as SonatypeStrategyOptions));
}
else {
localVulnerabilityStrategy = Object.seal(NoneStrategy());
diff --git a/src/strategies/snyk.ts b/src/strategies/snyk.ts
index 08fd899..f79b666 100644
--- a/src/strategies/snyk.ts
+++ b/src/strategies/snyk.ts
@@ -11,19 +11,33 @@ import type {
BaseStrategy
} from "./types/api.ts";
import { type SnykAuditResponse } from "../formats/snyk/index.ts";
-import { snyk } from "../database/index.ts";
+import { Snyk } from "../database/index.ts";
import { formatVulnsPayload } from "../formats/index.ts";
+import type { ApiCredential } from "../credential.ts";
+
+export interface SnykStrategyOptions {
+ org: string;
+ credential: ApiCredential;
+}
export type SnykStrategyDefinition = BaseStrategy<"snyk">;
-export function SnykStrategy(): SnykStrategyDefinition {
+export function SnykStrategy(
+ options: SnykStrategyOptions
+): SnykStrategyDefinition {
+ const snyk = new Snyk({
+ org: options.org,
+ credential: options.credential
+ });
+
return {
strategy: VULN_MODE.SNYK,
- hydratePayloadDependencies
+ hydratePayloadDependencies: hydratePayloadDependencies.bind(null, snyk)
};
}
async function hydratePayloadDependencies(
+ snyk: Snyk,
dependencies: Dependencies,
options: HydratePayloadDepsOptions
) {
diff --git a/src/strategies/sonatype.ts b/src/strategies/sonatype.ts
index 72f7a55..192e758 100644
--- a/src/strategies/sonatype.ts
+++ b/src/strategies/sonatype.ts
@@ -7,11 +7,16 @@ import type {
BaseStrategy
} from "./types/api.ts";
import { formatVulnsPayload } from "../formats/index.ts";
-import { sonatype } from "../database/index.ts";
+import { Sonatype } from "../database/index.ts";
+import type { ApiCredential } from "../credential.ts";
// CONSTANTS
const kRatelimitChunkSize = 128;
+export interface SonatypeStrategyOptions {
+ credential: ApiCredential;
+}
+
export interface SonatypeVulnerability {
id: string;
displayName: string;
@@ -29,10 +34,15 @@ export interface SonatypeVulnerability {
export type SonatypeStrategyDefinition = BaseStrategy<"sonatype">;
-export function SonatypeStrategy(): SonatypeStrategyDefinition {
+export function SonatypeStrategy(
+ options: SonatypeStrategyOptions
+): SonatypeStrategyDefinition {
+ const { credential } = options;
+ const sonatype = new Sonatype({ credential });
+
return {
strategy: VULN_MODE.SONATYPE,
- hydratePayloadDependencies
+ hydratePayloadDependencies: hydratePayloadDependencies.bind(null, sonatype)
};
}
@@ -70,9 +80,13 @@ function createPackageURLCoordinates(
return Object.keys(versions).map((version) => toPackageURL(dependencyName, version));
}
-type SonatypeHttpResponse = { coordinates: string; vulnerabilities: SonatypeVulnerability[]; };
+type SonatypeHttpResponse = {
+ coordinates: string;
+ vulnerabilities: SonatypeVulnerability[];
+};
async function fetchDataForPackageURLs(
+ sonatype: Sonatype,
unchunkedCoordinates: string[]
): Promise {
try {
@@ -112,10 +126,12 @@ function vulnWithPackageName(packageName: string) {
}
async function hydratePayloadDependencies(
+ sonatype: Sonatype,
dependencies: Dependencies,
options: BaseStrategyOptions = {}
): Promise {
const packageURLsData = await fetchDataForPackageURLs(
+ sonatype,
Array.from(dependencies).flatMap(createPackageURLCoordinates)
);
diff --git a/test/database/credential.unit.spec.ts b/test/database/credential.unit.spec.ts
new file mode 100644
index 0000000..50e6ca8
--- /dev/null
+++ b/test/database/credential.unit.spec.ts
@@ -0,0 +1,58 @@
+// Import Node.js Dependencies
+import { describe, test } from "node:test";
+import assert from "node:assert";
+
+// Import Internal Dependencies
+import { ApiCredential } from "../../src/credential.ts";
+
+describe("ApiCredential", () => {
+ test("string token produces Authorization: Bearer header", () => {
+ const cred = new ApiCredential("mytoken");
+
+ assert.deepStrictEqual(cred.headers, { Authorization: "Bearer mytoken" });
+ assert.deepStrictEqual(cred.queryParams, {});
+ });
+
+ test("bearer type produces Authorization: Bearer header", () => {
+ const cred = new ApiCredential({ type: "bearer", token: "mytoken" });
+
+ assert.deepStrictEqual(cred.headers, { Authorization: "Bearer mytoken" });
+ assert.deepStrictEqual(cred.queryParams, {});
+ });
+
+ test("token type produces Authorization: token header", () => {
+ const cred = new ApiCredential({ type: "token", token: "mytoken" });
+
+ assert.deepStrictEqual(cred.headers, { Authorization: "token mytoken" });
+ assert.deepStrictEqual(cred.queryParams, {});
+ });
+
+ test("basic type produces Authorization: Basic header with base64-encoded credentials", () => {
+ const cred = new ApiCredential({ type: "basic", username: "user", password: "pass" });
+ const expected = Buffer.from("user:pass").toString("base64");
+
+ assert.deepStrictEqual(cred.headers, { Authorization: `Basic ${expected}` });
+ assert.deepStrictEqual(cred.queryParams, {});
+ });
+
+ test("querystring type produces a query param and no headers", () => {
+ const cred = new ApiCredential({ type: "querystring", name: "apiKey", value: "secret" });
+
+ assert.deepStrictEqual(cred.headers, {});
+ assert.deepStrictEqual(cred.queryParams, { apiKey: "secret" });
+ });
+
+ test("custom type produces Authorization header with the raw value", () => {
+ const cred = new ApiCredential({ type: "custom", authorization: "SharedAccessSignature sv=..." });
+
+ assert.deepStrictEqual(cred.headers, { Authorization: "SharedAccessSignature sv=..." });
+ assert.deepStrictEqual(cred.queryParams, {});
+ });
+
+ test("no options produces empty headers and queryParams", () => {
+ const cred = new ApiCredential();
+
+ assert.deepStrictEqual(cred.headers, {});
+ assert.deepStrictEqual(cred.queryParams, {});
+ });
+});
diff --git a/test/database/nvd.unit.spec.ts b/test/database/nvd.unit.spec.ts
index 04c42e5..6d937a3 100644
--- a/test/database/nvd.unit.spec.ts
+++ b/test/database/nvd.unit.spec.ts
@@ -7,11 +7,17 @@ import {
HTTP_CLIENT_HEADERS,
setupHttpAgentMock
} from "../strategies/utils.ts";
-import { nvd } from "../../src/database/index.ts";
+import { NVD, ApiCredential } from "../../src/database/index.ts";
-describe("nvd", () => {
+// CONSTANTS
+const kTestApiKey = "test-api-key";
+const kTestCredential = new ApiCredential({ type: "querystring", name: "apiKey", value: kTestApiKey });
+const kNvdPathname = new URL(NVD.ROOT_API).pathname;
+
+describe("Database.NVD", () => {
+ const db = new NVD({ credential: kTestCredential });
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
- const mockedHttpClient = mockedHttpAgent.get(new URL(nvd.ROOT_API).origin);
+ const mockedHttpClient = mockedHttpAgent.get(new URL(NVD.ROOT_API).origin);
after(() => {
restoreHttpAgent();
@@ -22,16 +28,17 @@ describe("nvd", () => {
const expectedResponse = { vulnerabilities: ["cve-data-1", "cve-data-2"] };
const params = new URLSearchParams();
params.append("keywordSearch", "express");
+ params.append("apiKey", kTestApiKey);
const queryString = params.toString();
mockedHttpClient
.intercept({
- path: `${new URL(nvd.ROOT_API).pathname}?${queryString}`,
+ path: `${kNvdPathname}?${queryString}`,
method: "GET"
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const vulns = await nvd.findOne({
+ const vulns = await db.findOne({
packageName: "express",
ecosystem: "npm"
});
@@ -44,16 +51,17 @@ describe("nvd", () => {
const params = new URLSearchParams();
params.append("keywordSearch", "express");
params.append("cvssV3Severity", "HIGH");
+ params.append("apiKey", kTestApiKey);
const queryString = params.toString();
mockedHttpClient
.intercept({
- path: `${new URL(nvd.ROOT_API).pathname}?${queryString}`,
+ path: `${kNvdPathname}?${queryString}`,
method: "GET"
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const vulns = await nvd.findOne({
+ const vulns = await db.findOne({
packageName: "express",
ecosystem: "npm",
cvssV3Severity: "HIGH"
@@ -67,16 +75,17 @@ describe("nvd", () => {
const packageName = "express";
const params = new URLSearchParams();
params.append("keywordSearch", packageName);
+ params.append("apiKey", kTestApiKey);
const queryString = params.toString();
mockedHttpClient
.intercept({
- path: `${new URL(nvd.ROOT_API).pathname}?${queryString}`,
+ path: `${kNvdPathname}?${queryString}`,
method: "GET"
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const vulns = await nvd.findOneBySpec(`${packageName}@1.0.0`);
+ const vulns = await db.findOneBySpec(`${packageName}@1.0.0`);
assert.deepStrictEqual(vulns, expectedResponse.vulnerabilities);
});
@@ -85,27 +94,29 @@ describe("nvd", () => {
const paramsFirst = new URLSearchParams();
paramsFirst.append("keywordSearch", "foobar");
+ paramsFirst.append("apiKey", kTestApiKey);
const queryStringFirst = paramsFirst.toString();
const paramsSecond = new URLSearchParams();
paramsSecond.append("keywordSearch", "yoobar");
+ paramsSecond.append("apiKey", kTestApiKey);
const queryStringSecond = paramsSecond.toString();
mockedHttpClient
.intercept({
- path: `${new URL(nvd.ROOT_API).pathname}?${queryStringFirst}`,
+ path: `${kNvdPathname}?${queryStringFirst}`,
method: "GET"
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
mockedHttpClient
.intercept({
- path: `${new URL(nvd.ROOT_API).pathname}?${queryStringSecond}`,
+ path: `${kNvdPathname}?${queryStringSecond}`,
method: "GET"
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const result = await nvd.findMany(
+ const result = await db.findMany(
["foobar", "yoobar"]
);
@@ -120,16 +131,17 @@ describe("nvd", () => {
const params = new URLSearchParams();
params.append("keywordSearch", "nonexistent");
+ params.append("apiKey", kTestApiKey);
const queryString = params.toString();
mockedHttpClient
.intercept({
- path: `${new URL(nvd.ROOT_API).pathname}?${queryString}`,
+ path: `${kNvdPathname}?${queryString}`,
method: "GET"
})
.reply(200, emptyResponse, HTTP_CLIENT_HEADERS);
- const vulns = await nvd.findOne({
+ const vulns = await db.findOne({
packageName: "nonexistent",
ecosystem: "npm"
});
diff --git a/test/database/osv.unit.spec.ts b/test/database/osv.unit.spec.ts
index 3eba4ff..ef2e0af 100644
--- a/test/database/osv.unit.spec.ts
+++ b/test/database/osv.unit.spec.ts
@@ -7,11 +7,12 @@ import {
HTTP_CLIENT_HEADERS,
setupHttpAgentMock
} from "../strategies/utils.ts";
-import { osv } from "../../src/database/index.ts";
+import { OSV } from "../../src/database/index.ts";
-describe("osv", () => {
+describe("Database.OSV", () => {
+ const db = new OSV();
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
- const mockedHttpClient = mockedHttpAgent.get(osv.ROOT_API);
+ const mockedHttpClient = mockedHttpAgent.get(OSV.ROOT_API);
after(() => {
restoreHttpAgent();
@@ -22,13 +23,13 @@ describe("osv", () => {
const expectedResponse = { vulns: "hello world" };
mockedHttpClient
.intercept({
- path: new URL("/v1/query", osv.ROOT_API).href,
+ path: new URL("/v1/query", OSV.ROOT_API).href,
method: "POST",
body: JSON.stringify({ package: { name: "foobar", ecosystem: "npm" } })
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const vulns = await osv.findOne({
+ const vulns = await db.findOne({
package: {
name: "foobar",
ecosystem: "npm"
@@ -44,7 +45,7 @@ describe("osv", () => {
mockedHttpClient
.intercept({
- path: new URL("/v1/query", osv.ROOT_API).href,
+ path: new URL("/v1/query", OSV.ROOT_API).href,
method: "POST",
body: JSON.stringify({
version: "2.0.0",
@@ -53,7 +54,7 @@ describe("osv", () => {
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const vulns = await osv.findOneBySpec(`${packageName}@2.0.0`);
+ const vulns = await db.findOneBySpec(`${packageName}@2.0.0`);
assert.strictEqual(vulns, expectedResponse.vulns);
});
@@ -62,13 +63,13 @@ describe("osv", () => {
mockedHttpClient
.intercept({
- path: new URL("/v1/query", osv.ROOT_API).href,
+ path: new URL("/v1/query", OSV.ROOT_API).href,
method: "POST"
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS)
.times(2);
- const result = await osv.findMany(
+ const result = await db.findMany(
["foobar", "yoobar"]
);
assert.deepEqual(result, {
diff --git a/test/database/snyk.unit.spec.ts b/test/database/snyk.unit.spec.ts
index 770e074..177a79f 100644
--- a/test/database/snyk.unit.spec.ts
+++ b/test/database/snyk.unit.spec.ts
@@ -4,12 +4,17 @@ import assert from "node:assert";
// Import Internal Dependencies
import { HTTP_CLIENT_HEADERS, setupHttpAgentMock } from "../strategies/utils.ts";
-import { snyk } from "../../src/database/index.ts";
-import { SNYK_ORG } from "../../src/constants.ts";
+import { Snyk, ApiCredential } from "../../src/database/index.ts";
-describe("snyk", () => {
+describe("Database.Snyk", () => {
+ const org = process.env.SNYK_ORG ?? "test-org";
+ const token = process.env.SNYK_TOKEN ?? "test-token";
+ const db = new Snyk({
+ org,
+ credential: new ApiCredential({ type: "token", token })
+ });
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
- const mockedHttpClient = mockedHttpAgent.get(snyk.ROOT_API);
+ const mockedHttpClient = mockedHttpAgent.get(Snyk.ROOT_API);
after(() => {
restoreHttpAgent();
@@ -22,7 +27,7 @@ describe("snyk", () => {
mockedHttpClient
.intercept({
- path: new URL(`/api/v1/test/npm?org=${SNYK_ORG}`, snyk.ROOT_API).href,
+ path: new URL(`/api/v1/test/npm?org=${org}`, Snyk.ROOT_API).href,
method: "POST",
body: JSON.stringify({
files: {
@@ -33,7 +38,7 @@ describe("snyk", () => {
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const data = await snyk.findOne({
+ const data = await db.findOne({
files: {
target: { contents: targetFile },
additional: [{ contents: additionalFile }]
@@ -49,7 +54,7 @@ describe("snyk", () => {
mockedHttpClient
.intercept({
- path: new URL(`/api/v1/test/npm?org=${SNYK_ORG}`, snyk.ROOT_API).href,
+ path: new URL(`/api/v1/test/npm?org=${org}`, Snyk.ROOT_API).href,
method: "POST",
body: JSON.stringify({
files: {
@@ -59,7 +64,7 @@ describe("snyk", () => {
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const data = await snyk.findOne({
+ const data = await db.findOne({
files: { target: { contents: targetFile } }
});
diff --git a/test/database/sonatype.unit.spec.ts b/test/database/sonatype.unit.spec.ts
index c3f59b3..c36762d 100644
--- a/test/database/sonatype.unit.spec.ts
+++ b/test/database/sonatype.unit.spec.ts
@@ -7,11 +7,16 @@ import {
HTTP_CLIENT_HEADERS,
setupHttpAgentMock
} from "../strategies/utils.ts";
-import { sonatype } from "../../src/database/index.ts";
+import { Sonatype, ApiCredential } from "../../src/database/index.ts";
-describe("sonatype", () => {
+// CONSTANTS
+const kTestCredential = new ApiCredential({ type: "basic", username: "user", password: "pass" });
+const kExpectedAuth = `Basic ${Buffer.from("user:pass").toString("base64")}`;
+
+describe("Database.Sonatype", () => {
+ const db = new Sonatype({ credential: kTestCredential });
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
- const mockedHttpClient = mockedHttpAgent.get(sonatype.ROOT_API);
+ const mockedHttpClient = mockedHttpAgent.get(Sonatype.ROOT_API);
after(() => {
restoreHttpAgent();
@@ -28,37 +33,62 @@ describe("sonatype", () => {
const coordinates = ["coord1", "coord2"];
mockedHttpClient
.intercept({
- path: new URL("/api/v3/component-report", sonatype.ROOT_API).href,
+ path: new URL("/api/v3/component-report", Sonatype.ROOT_API).href,
method: "POST",
body: JSON.stringify({ coordinates }),
headers: {
- accept: "application/json"
+ accept: "application/json",
+ Authorization: kExpectedAuth
}
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
- const vulns = await sonatype.findOne({
+ const vulns = await db.findOne({
coordinates
});
assert.deepStrictEqual(vulns, expectedResponse);
});
+ test("should send a POST http request with Basic auth header when credential is provided", async() => {
+ const credential = new ApiCredential({ type: "basic", username: "user", password: "pass" });
+ const dbWithAuth = new Sonatype({ credential });
+ const expectedResponse = [kSonatypeVulnComponent];
+ const coordinates = ["coord1"];
+ const expectedAuth = `Basic ${Buffer.from("user:pass").toString("base64")}`;
+
+ mockedHttpClient
+ .intercept({
+ path: new URL("/api/v3/component-report", Sonatype.ROOT_API).href,
+ method: "POST",
+ body: JSON.stringify({ coordinates }),
+ headers: {
+ accept: "application/json",
+ Authorization: expectedAuth
+ }
+ })
+ .reply(200, expectedResponse, HTTP_CLIENT_HEADERS);
+
+ const vulns = await dbWithAuth.findOne({ coordinates });
+ assert.deepStrictEqual(vulns, expectedResponse);
+ });
+
test("should send multiple POST http requests to the SONATYPE API using findMany", async() => {
const expectedResponse = [kSonatypeVulnComponent];
mockedHttpClient
.intercept({
- path: new URL("/api/v3/component-report", sonatype.ROOT_API).href,
+ path: new URL("/api/v3/component-report", Sonatype.ROOT_API).href,
method: "POST",
headers: {
- accept: "application/json"
+ accept: "application/json",
+ Authorization: kExpectedAuth
},
body: JSON.stringify({ coordinates: ["coord1", "coord2"] })
})
.reply(200, expectedResponse, HTTP_CLIENT_HEADERS)
.times(2);
- const result = await sonatype.findMany(
+ const result = await db.findMany(
{
coordinates: [["coord1", "coord2"], ["coord1", "coord2"]]
}
diff --git a/test/strategies/snyk/index.unit.spec.ts b/test/strategies/snyk/index.unit.spec.ts
index 0a5b7f3..25e6e85 100644
--- a/test/strategies/snyk/index.unit.spec.ts
+++ b/test/strategies/snyk/index.unit.spec.ts
@@ -7,6 +7,7 @@ import fs from "node:fs/promises";
// Import Internal Dependencies
import { SnykStrategy } from "../../../src/strategies/snyk.ts";
+import { ApiCredential } from "../../../src/credential.ts";
import {
expectVulnToBeNodeSecureStandardCompliant,
HTTP_CLIENT_HEADERS,
@@ -17,7 +18,8 @@ import {
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const kFixturesDir = path.join(__dirname, "..", "..", "fixtures");
const kSnykOrigin = "https://snyk.io";
-const kSnykApiPath = "/api/v1/test/npm?org=undefined";
+const kSnykApiPath = "/api/v1/test/npm?org=";
+const kTestCredential = new ApiCredential({ type: "token", token: "test-token" });
async function readFileJSON(location: string): Promise {
const rawText = await fs.readFile(location, "utf-8");
@@ -44,7 +46,7 @@ function isAdvisory(data: any) {
}
test("SnykStrategy definition must return only two keys.", () => {
- const definition = SnykStrategy();
+ const definition = SnykStrategy({ org: "", credential: kTestCredential });
assert.strictEqual(
definition.strategy,
@@ -58,7 +60,7 @@ test("SnykStrategy definition must return only two keys.", () => {
});
test("snyk strategy: hydratePayloadDependencies", async() => {
- const { hydratePayloadDependencies } = SnykStrategy();
+ const { hydratePayloadDependencies } = SnykStrategy({ org: "", credential: kTestCredential });
const dependencies = new Map();
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
const mockedHttpClient = mockedHttpAgent.get(kSnykOrigin);
@@ -95,7 +97,7 @@ test("snyk strategy: hydratePayloadDependencies", async() => {
});
test("snyk strategy: hydratePayloadDependencies using NodeSecure standard format", async() => {
- const { hydratePayloadDependencies } = SnykStrategy();
+ const { hydratePayloadDependencies } = SnykStrategy({ org: "", credential: kTestCredential });
const dependencies = new Map();
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
const mockedHttpClient = mockedHttpAgent.get(kSnykOrigin);
diff --git a/test/strategies/sonatype/index.integration.spec.ts b/test/strategies/sonatype/index.integration.spec.ts
index d4c402a..45007c3 100644
--- a/test/strategies/sonatype/index.integration.spec.ts
+++ b/test/strategies/sonatype/index.integration.spec.ts
@@ -4,10 +4,14 @@ import assert from "node:assert";
// Import Internal Dependencies
import { SonatypeStrategy } from "../../../src/strategies/sonatype.ts";
+import { ApiCredential } from "../../../src/credential.ts";
import { expectVulnToBeNodeSecureStandardCompliant } from "../utils.ts";
+// CONSTANTS
+const kTestCredential = new ApiCredential({ type: "basic", username: "user", password: "pass" });
+
test.skip("sonatype strategy: fetching a package with a vulnerability using the API", async() => {
- const { hydratePayloadDependencies } = SonatypeStrategy();
+ const { hydratePayloadDependencies } = SonatypeStrategy({ credential: kTestCredential });
const dependencies = new Map();
/**
* This package is arbitrary chosen and hardcoded as there is no way to fetch
@@ -51,7 +55,7 @@ test.skip("sonatype strategy: fetching a package with a vulnerability using the
});
test.skip("sonatype strategy: fetching a package with a name that should be percent-encoded/decoded, using the API", async() => {
- const { hydratePayloadDependencies } = SonatypeStrategy();
+ const { hydratePayloadDependencies } = SonatypeStrategy({ credential: kTestCredential });
const dependencies = new Map();
const packageWithScopeThatShouldBePercentEncoded = {
/**
diff --git a/test/strategies/sonatype/index.unit.spec.ts b/test/strategies/sonatype/index.unit.spec.ts
index 57b0f38..e2b6ee5 100644
--- a/test/strategies/sonatype/index.unit.spec.ts
+++ b/test/strategies/sonatype/index.unit.spec.ts
@@ -6,6 +6,7 @@ import assert from "node:assert";
import {
SonatypeStrategy
} from "../../../src/strategies/sonatype.ts";
+import { ApiCredential } from "../../../src/credential.ts";
import {
expectVulnToBeNodeSecureStandardCompliant,
HTTP_CLIENT_HEADERS,
@@ -13,6 +14,7 @@ import {
} from "../utils.ts";
// CONSTANTS
+const kTestCredential = new ApiCredential({ type: "basic", username: "user", password: "pass" });
const kSonatypeOrigin = "https://ossindex.sonatype.org";
const kSonatypeApiPath = "/api/v3/component-report";
const kSonatypeVulnComponent = {
@@ -22,7 +24,7 @@ const kSonatypeVulnComponent = {
const kFakePackageURL = "pkg:npm/fake-npm-package@3.0.1";
test("SonatypeStrategy definition must return only two keys.", () => {
- const definition = SonatypeStrategy();
+ const definition = SonatypeStrategy({ credential: kTestCredential });
assert.strictEqual(
definition.strategy,
@@ -36,7 +38,7 @@ test("SonatypeStrategy definition must return only two keys.", () => {
});
test("sonatype strategy: hydratePayloadDependencies", async() => {
- const { hydratePayloadDependencies } = SonatypeStrategy();
+ const { hydratePayloadDependencies } = SonatypeStrategy({ credential: kTestCredential });
const dependencies = new Map();
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
const mockedHttpClient = mockedHttpAgent.get(kSonatypeOrigin);
@@ -81,7 +83,7 @@ test("sonatype strategy: hydratePayloadDependencies", async() => {
});
test("sonatype strategy: hydratePayloadDependencies when using NodeSecure standard format", async() => {
- const { hydratePayloadDependencies } = SonatypeStrategy();
+ const { hydratePayloadDependencies } = SonatypeStrategy({ credential: kTestCredential });
const dependencies = new Map();
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
const mockedHttpClient = mockedHttpAgent.get(kSonatypeOrigin);
@@ -136,7 +138,7 @@ test("sonatype strategy: hydratePayloadDependencies when using NodeSecure standa
});
test("sonatype strategy: fetchDataForPackageURLs with coordinates exceeding the ratelimit", async() => {
- const { hydratePayloadDependencies } = SonatypeStrategy();
+ const { hydratePayloadDependencies } = SonatypeStrategy({ credential: kTestCredential });
const chunkSizeApiLimit = 128;
const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock();
const mockedHttpClient = mockedHttpAgent.get(kSonatypeOrigin);
diff --git a/test/tsconfig.json b/test/tsconfig.json
new file mode 100644
index 0000000..93b98a1
--- /dev/null
+++ b/test/tsconfig.json
@@ -0,0 +1,11 @@
+{
+ "extends": "../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "..",
+ "types": ["node"],
+ "lib": ["DOM", "ES2022", "ES2023", "ES2024", "ESNext"]
+ },
+ "include": [
+ "."
+ ]
+}