From 8d0e663e1cda8b56506e0bb593a65556f3d1cc74 Mon Sep 17 00:00:00 2001 From: "Thomas.G" Date: Sat, 4 Jan 2025 16:03:49 +0100 Subject: [PATCH] chore: update eslint & ts configs (#273) --- .eslintrc | 10 -- .github/workflows/main.yml | 2 +- eslint.config.mjs | 3 + package.json | 5 +- src/database/osv.ts | 6 +- src/database/snyk.ts | 16 +-- src/formats/osv/index.ts | 2 +- src/formats/snyk/index.ts | 154 ++++++++++++++-------------- src/formats/standard/index.ts | 3 +- src/index.ts | 14 +-- src/strategies/github-advisory.ts | 13 +-- src/strategies/none.ts | 1 - src/strategies/snyk.ts | 4 +- src/strategies/sonatype.ts | 2 +- src/strategies/types/api.ts | 2 +- src/utils.ts | 2 +- test/database/snyk.unit.spec.ts | 94 ++++++++--------- test/fixtures/vuln_payload/vulns.js | 2 +- tsconfig.json | 14 +-- 19 files changed, 167 insertions(+), 182 deletions(-) delete mode 100644 .eslintrc create mode 100644 eslint.config.mjs diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 36ace3e..0000000 --- a/.eslintrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "@nodesecure/eslint-config", - "parserOptions": { - "sourceType": "module", - "requireConfigFile": false - }, - "rules": { - "no-empty": "off" - } -} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7a43058..10537d5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v2.6.0 - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af # v4.1.0 with: - node-version: 16.x + node-version: 22.x - name: Install dependencies run: npm install - name: Run ESLint diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..8a1e9d0 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,3 @@ +import { typescriptConfig } from "@openally/config.eslint"; + +export default typescriptConfig(); diff --git a/package.json b/package.json index a27ca7f..2acb767 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "scripts": { "build": "tsc", "prepublishOnly": "npm run build", - "lint": "cross-env eslint src/**/*.ts", + "lint": "eslint src test", "test-only": "glob -c \"tsx --test\" \"./test/**/*.spec.ts\"", "unit-test-only": "glob -c \"tsx --test\" \"./test/**/*.unit.spec.ts\"", "integration-test-only": "glob -c \"tsx --test\" \"./test/**/*.integration.spec.ts\"", @@ -46,7 +46,8 @@ }, "homepage": "https://github.com/NodeSecure/vulnera#readme", "devDependencies": { - "@nodesecure/eslint-config": "^1.8.0", + "@openally/config.eslint": "^1.1.0", + "@openally/config.typescript": "^1.0.3", "@slimio/is": "^2.0.0", "@types/node": "^22.1.0", "c8": "^10.1.2", diff --git a/src/database/osv.ts b/src/database/osv.ts index 0e4a790..1ff8063 100644 --- a/src/database/osv.ts +++ b/src/database/osv.ts @@ -2,7 +2,7 @@ import * as httpie from "@myunisoft/httpie"; // Import Internal Dependencies -import { OSV } from "../formats/osv"; +import type { OSV } from "../formats/osv/index.js"; import * as utils from "../utils.js"; // CONSTANTS @@ -17,7 +17,7 @@ export type OSVApiParameter = { */ ecosystem?: string; }; -} +}; export async function findOne( parameters: OSVApiParameter @@ -26,7 +26,7 @@ export async function findOne( parameters.package.ecosystem = "npm"; } - const { data } = await httpie.post<{ vulns: OSV[] }>( + const { data } = await httpie.post<{ vulns: OSV[]; }>( new URL("v1/query", ROOT_API), { body: parameters diff --git a/src/database/snyk.ts b/src/database/snyk.ts index eaa8386..ebbe727 100644 --- a/src/database/snyk.ts +++ b/src/database/snyk.ts @@ -3,20 +3,20 @@ import * as httpie from "@myunisoft/httpie"; // Import Internal Dependencies import { SNYK_ORG, SNYK_TOKEN } from "../constants.js"; -import { SnykAuditResponse } from "../formats/snyk/index.js"; +import type { SnykAuditResponse } from "../formats/snyk/index.js"; // CONSTANTS export const ROOT_API = "https://snyk.io"; export type SnykFindOneParameters = { - files: { - target: { - contents: string; - }; - additional?: { - contents: string; - }[]; + files: { + target: { + contents: string; }; + additional?: { + contents: string; + }[]; + }; }; export async function findOne( diff --git a/src/formats/osv/index.ts b/src/formats/osv/index.ts index 78bd28b..6b6aed9 100644 --- a/src/formats/osv/index.ts +++ b/src/formats/osv/index.ts @@ -51,7 +51,7 @@ export type OSVCreditType = "FINDER" | export interface OSVAffected { package: { - ecosystem: "npm", + ecosystem: "npm"; name: string; purl: string; }; diff --git a/src/formats/snyk/index.ts b/src/formats/snyk/index.ts index 6e03516..d39261f 100644 --- a/src/formats/snyk/index.ts +++ b/src/formats/snyk/index.ts @@ -1,89 +1,89 @@ export interface SnykPatch { - id: string; - urls: string[]; - version: string; - modificationTime: string; - comments: string[]; + id: string; + urls: string[]; + version: string; + modificationTime: string; + comments: string[]; } export interface SnykVulnerability { - /** The issue ID **/ - id: string; - /** A link to the issue details on snyk.io **/ - url: string; - /** The issue title **/ - title: string; - /** The issue type **/ - type: "vulnerability" | "license"; - /** The paths to the dependencies which have an issue, and their corresponding upgrade path (if an upgrade is available) **/ - paths?: Array<{ - "from": Array, - "upgrade": Array - }>; - /** The package identifier according to its package manager **/ - package: string; - /** The package version this issue is applicable to. **/ - version: string; - /** The Snyk defined severity level **/ - severity: "critical" | "high" | "medium" | "low"; - /** The package's programming language **/ - language: string; - /** The package manager **/ - packageManager: string; - /** One or more semver ranges this issue is applicable to. **/ - semver: Record; - /** The vulnerability publication time **/ - publicationTime: string; - /** The time this vulnerability was originally disclosed to the package maintainers **/ - disclosureTime: string; - /** Is this vulnerability fixable by upgrading a dependency? **/ - isUpgradable: boolean; - /** The detailed description of the vulnerability, why and how it is exploitable. **/ - description: string; - /** Is this vulnerability fixable by using a Snyk supplied patch? **/ - isPatchable: boolean; - /** Is this vulnerability fixable by pinning a transitive dependency **/ - isPinnable: boolean; - /** Additional vulnerability identifiers **/ - identifiers: Record; - /** The reporter of the vulnerability **/ - credit: string; - /** + /** The issue ID **/ + id: string; + /** A link to the issue details on snyk.io **/ + url: string; + /** The issue title **/ + title: string; + /** The issue type **/ + type: "vulnerability" | "license"; + /** The paths to the dependencies which have an issue, and their corresponding upgrade path (if an upgrade is available) **/ + paths?: Array<{ + from: Array; + upgrade: Array; + }>; + /** The package identifier according to its package manager **/ + package: string; + /** The package version this issue is applicable to. **/ + version: string; + /** The Snyk defined severity level **/ + severity: "critical" | "high" | "medium" | "low"; + /** The package's programming language **/ + language: string; + /** The package manager **/ + packageManager: string; + /** One or more semver ranges this issue is applicable to. **/ + semver: Record; + /** The vulnerability publication time **/ + publicationTime: string; + /** The time this vulnerability was originally disclosed to the package maintainers **/ + disclosureTime: string; + /** Is this vulnerability fixable by upgrading a dependency? **/ + isUpgradable: boolean; + /** The detailed description of the vulnerability, why and how it is exploitable. **/ + description: string; + /** Is this vulnerability fixable by using a Snyk supplied patch? **/ + isPatchable: boolean; + /** Is this vulnerability fixable by pinning a transitive dependency **/ + isPinnable: boolean; + /** Additional vulnerability identifiers **/ + identifiers: Record; + /** The reporter of the vulnerability **/ + credit: 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. * **/ - CVSSv3: string; - /** CVSS Score **/ - cvssScore: number; - /** Patches to fix this issue, by snyk **/ - patches: SnykPatch[]; - /** The path to upgrade this issue, if applicable **/ - upgradePath: string[]; - /** Is this vulnerability patched? **/ - isPatched: boolean; - /** The snyk exploit maturity level **/ - exploitMaturity: string; - functions: any; + CVSSv3: string; + /** CVSS Score **/ + cvssScore: number; + /** Patches to fix this issue, by snyk **/ + patches: SnykPatch[]; + /** The path to upgrade this issue, if applicable **/ + upgradePath: string[]; + /** Is this vulnerability patched? **/ + isPatched: boolean; + /** The snyk exploit maturity level **/ + exploitMaturity: string; + functions: any; } 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; + /** 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; } diff --git a/src/formats/standard/index.ts b/src/formats/standard/index.ts index 06392df..76e1c1c 100644 --- a/src/formats/standard/index.ts +++ b/src/formats/standard/index.ts @@ -1,6 +1,6 @@ // Import Internal Dependencies import { VULN_MAPPERS } from "./mappers.js"; -import { Kind } from "../../constants.js"; +import type { Kind } from "../../constants.js"; export type Severity = "info" | "low" | "medium" | "high" | "critical"; @@ -76,4 +76,3 @@ export function standardizeVulnsPayload(useStandardFormat = false) { }; } - diff --git a/src/index.ts b/src/index.ts index 2b0274f..552a926 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,8 +8,7 @@ import { import { SnykStrategy, - type SnykStrategyDefinition, - type SnykVulnerability + type SnykStrategyDefinition } from "./strategies/snyk.js"; import { @@ -28,6 +27,9 @@ import { type Kind } from "./constants.js"; +import type { + SnykVulnerability +} from "./formats/snyk/index.js"; import type { StandardVulnerability, Severity, StandardPatch } from "./formats/standard/index.js"; @@ -49,10 +51,10 @@ import type { export * as Database from "./database/index.js"; export type AllStrategy = { - "none": NoneStrategyDefinition; + none: NoneStrategyDefinition; "github-advisory": GithubAdvisoryStrategyDefinition; - "snyk": SnykStrategyDefinition; - "sonatype": SonatypeStrategyDefinition; + snyk: SnykStrategyDefinition; + sonatype: SonatypeStrategyDefinition; }; export type AnyStrategy = AllStrategy[keyof AllStrategy]; @@ -98,7 +100,7 @@ export function getStrategy(): AnyStrategy { export const strategies = VULN_MODE; export const defaultStrategyName = VULN_MODE.NONE; -export { +export type { Kind, BaseStrategyOptions, BaseStrategy, diff --git a/src/strategies/github-advisory.ts b/src/strategies/github-advisory.ts index 368f582..aa7548e 100644 --- a/src/strategies/github-advisory.ts +++ b/src/strategies/github-advisory.ts @@ -1,16 +1,17 @@ +/* eslint-disable no-empty */ // Import Node.js Dependencies import fs from "node:fs/promises"; import path from "node:path"; // Import Third-party Dependencies import Arborist from "@npmcli/arborist"; -import { audit, AuditAdvisory } from "@pnpm/audit"; +import { audit, type AuditAdvisory } from "@pnpm/audit"; import { getLocalRegistryURL } from "@nodesecure/npm-registry-sdk"; 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 { type StandardVulnerability, standardizeVulnsPayload } from "../formats/standard/index.js"; import type { Dependencies } from "./types/scanner.js"; import type { BaseStrategyOptions, @@ -44,7 +45,7 @@ export type NpmAuditAdvisory = { range: string; /** The set of versions that are vulnerable **/ vulnerableVersions?: string[]; -} +}; export type PnpmAuditAdvisory = Exclude & { github_advisory_id: string; @@ -53,11 +54,11 @@ export type PnpmAuditAdvisory = Exclude & { cvss: { score: number; vectorString: string; - } + }; }; export type GithubVulnerability = PnpmAuditAdvisory | NpmAuditAdvisory; -export type GithubAdvisoryStrategyDefinition = ExtendedStrategy<"github-advisory", GithubVulnerability> +export type GithubAdvisoryStrategyDefinition = ExtendedStrategy<"github-advisory", GithubVulnerability>; export function GitHubAdvisoryStrategy(): GithubAdvisoryStrategyDefinition { return { @@ -140,7 +141,7 @@ async function npmAudit( registry: string ): Promise { const arborist = new Arborist({ ...NPM_TOKEN, registry, path }); - const { vulnerabilities } = (await arborist.audit()).toJSON() as { vulnerabilities: any[] }; + const { vulnerabilities } = (await arborist.audit()).toJSON() as { vulnerabilities: any[]; }; // TODO: remove Symbols? return Object.values(vulnerabilities) diff --git a/src/strategies/none.ts b/src/strategies/none.ts index f2e2ae2..6181611 100644 --- a/src/strategies/none.ts +++ b/src/strategies/none.ts @@ -11,7 +11,6 @@ export function NoneStrategy(): NoneStrategyDefinition { }; } -// eslint-disable-next-line @typescript-eslint/no-unused-vars async function hydratePayloadDependencies(dependencies: any) { // Do nothing } diff --git a/src/strategies/snyk.ts b/src/strategies/snyk.ts index 9e4b412..928e837 100644 --- a/src/strategies/snyk.ts +++ b/src/strategies/snyk.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-empty */ // Import Node.js Dependencies import path from "node:path"; import { readFile } from "node:fs/promises"; @@ -10,10 +11,9 @@ import type { HydratePayloadDepsOptions, BaseStrategy } from "./types/api.js"; -import { SnykAuditResponse } from "../formats/snyk/index.js"; +import { type SnykAuditResponse } from "../formats/snyk/index.js"; import { snyk } from "../database/index.js"; - export type SnykStrategyDefinition = BaseStrategy<"snyk">; export function SnykStrategy(): SnykStrategyDefinition { diff --git a/src/strategies/sonatype.ts b/src/strategies/sonatype.ts index e2fde5a..0a9b044 100644 --- a/src/strategies/sonatype.ts +++ b/src/strategies/sonatype.ts @@ -73,7 +73,7 @@ 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( unchunkedCoordinates: string[] diff --git a/src/strategies/types/api.ts b/src/strategies/types/api.ts index bb06518..3f36106 100644 --- a/src/strategies/types/api.ts +++ b/src/strategies/types/api.ts @@ -24,7 +24,7 @@ export interface BaseStrategy { /** Method to hydrate (insert/push) vulnerabilities in the dependencies retrieved by the Scanner **/ hydratePayloadDependencies: ( dependencies: Dependencies, - options?: HydratePayloadDepsOptions + options: HydratePayloadDepsOptions ) => Promise; } diff --git a/src/utils.ts b/src/utils.ts index 3a14bda..046a53e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ // Import Internal Dependencies -import { Severity } from "./formats/standard/index.js"; +import type { Severity } from "./formats/standard/index.js"; export function fromMaybeStringToArray( value: undefined | null | string | string[] diff --git a/test/database/snyk.unit.spec.ts b/test/database/snyk.unit.spec.ts index 44fa394..643a15b 100644 --- a/test/database/snyk.unit.spec.ts +++ b/test/database/snyk.unit.spec.ts @@ -8,61 +8,61 @@ import { snyk } from "../../src/database"; import { SNYK_ORG } from "../../src/constants"; describe("snyk", () => { - const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock(); - const mockedHttpClient = mockedHttpAgent.get(snyk.ROOT_API); + const [mockedHttpAgent, restoreHttpAgent] = setupHttpAgentMock(); + const mockedHttpClient = mockedHttpAgent.get(snyk.ROOT_API); - after(() => { - restoreHttpAgent(); - }); - - test(`should send a POST http request to the Snyk API using findOne and then return the SnykAuditResponse`, async() => { - const expectedResponse = { issues: "some issues data" }; - const targetFile = "some target file content"; - const additionalFile = "some additional file content"; + after(() => { + restoreHttpAgent(); + }); - mockedHttpClient - .intercept({ - path: new URL(`/api/v1/test/npm?org=${SNYK_ORG}`, snyk.ROOT_API).href, - method: "POST", - body: JSON.stringify({ - files: { - target: { contents: targetFile }, - additional: [{ contents: additionalFile }] - } - }) - }) - .reply(200, expectedResponse, kHttpClientHeaders); + test(`should send a POST http request to the Snyk API using findOne and then return the SnykAuditResponse`, async() => { + const expectedResponse = { issues: "some issues data" }; + const targetFile = "some target file content"; + const additionalFile = "some additional file content"; - const data = await snyk.findOne({ - files: { - target: { contents: targetFile }, - additional: [{ contents: additionalFile }] - } - }); + mockedHttpClient + .intercept({ + path: new URL(`/api/v1/test/npm?org=${SNYK_ORG}`, snyk.ROOT_API).href, + method: "POST", + body: JSON.stringify({ + files: { + target: { contents: targetFile }, + additional: [{ contents: additionalFile }] + } + }) + }) + .reply(200, expectedResponse, kHttpClientHeaders); - assert.deepStrictEqual(data, expectedResponse); + const data = await snyk.findOne({ + files: { + target: { contents: targetFile }, + additional: [{ contents: additionalFile }] + } }); - test(`should send a POST http request to the Snyk API using findOne without additional files`, async() => { - const expectedResponse = { issues: "some issues data" }; - const targetFile = "some target file content"; + assert.deepStrictEqual(data, expectedResponse); + }); - mockedHttpClient - .intercept({ - path: new URL(`/api/v1/test/npm?org=${SNYK_ORG}`, snyk.ROOT_API).href, - method: "POST", - body: JSON.stringify({ - files: { - target: { contents: targetFile } - } - }) - }) - .reply(200, expectedResponse, kHttpClientHeaders); + test(`should send a POST http request to the Snyk API using findOne without additional files`, async() => { + const expectedResponse = { issues: "some issues data" }; + const targetFile = "some target file content"; - const data = await snyk.findOne({ - files: { target: { contents: targetFile } } - }); + mockedHttpClient + .intercept({ + path: new URL(`/api/v1/test/npm?org=${SNYK_ORG}`, snyk.ROOT_API).href, + method: "POST", + body: JSON.stringify({ + files: { + target: { contents: targetFile } + } + }) + }) + .reply(200, expectedResponse, kHttpClientHeaders); - assert.deepStrictEqual(data, expectedResponse); + const data = await snyk.findOne({ + files: { target: { contents: targetFile } } }); + + assert.deepStrictEqual(data, expectedResponse); + }); }); diff --git a/test/fixtures/vuln_payload/vulns.js b/test/fixtures/vuln_payload/vulns.js index 5314f7b..5bd37cd 100644 --- a/test/fixtures/vuln_payload/vulns.js +++ b/test/fixtures/vuln_payload/vulns.js @@ -1,4 +1,4 @@ -/* eslint-disable max-len */ + export const NPM_VULNERABILITY = { title: "Arbitrary Command Injection due to Improper Command Sanitization", name: "@npmcli/git", diff --git a/tsconfig.json b/tsconfig.json index 55d82f0..74a87f6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,19 +1,9 @@ { + "extends": "@openally/config.typescript", "compilerOptions": { - "declaration": true, - "strictNullChecks": true, - "target": "ES2022", "outDir": "dist", - "module": "ES2022", - "moduleResolution": "node", - "esModuleInterop": true, - "resolveJsonModule": true, - "skipDefaultLibCheck": true, - "forceConsistentCasingInFileNames": true, - "sourceMap": true, "rootDir": "./src", - "types": ["node"] }, - "include": ["src", "index.d.ts", "types"], + "include": ["src"], "exclude": ["node_modules", "dist"] }