@nodesecure/vulnera
Version:
NodeSecure vulnerabilities strategies
90 lines • 3.7 kB
JavaScript
// 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 } 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 { standardizeVulnsPayload } from "../formats/standard/index.js";
export function GitHubAdvisoryStrategy() {
return {
strategy: VULN_MODE.GITHUB_ADVISORY,
hydratePayloadDependencies,
getVulnerabilities
};
}
async function getVulnerabilities(lockDirOrManifestPath, options = {}) {
const { useStandardFormat } = options;
const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat);
const registry = getLocalRegistryURL();
const lockfileDir = path.extname(lockDirOrManifestPath) === "" ?
lockDirOrManifestPath :
path.dirname(lockDirOrManifestPath);
const isPnpm = await hasPnpmLockFile(lockfileDir);
const vulnerabilities = isPnpm ?
await pnpmAudit(lockfileDir, registry) :
await npmAudit(lockDirOrManifestPath, registry);
if (useStandardFormat) {
return formatVulnerabilities(isPnpm ? "github-advisory_pnpm" : VULN_MODE.GITHUB_ADVISORY, vulnerabilities);
}
return vulnerabilities;
}
async function hydratePayloadDependencies(dependencies, options) {
const { path, useStandardFormat } = options;
if (!path) {
throw new Error("path argument is required for <github-advisory> strategy");
}
const formatVulnerabilities = standardizeVulnsPayload(useStandardFormat);
const registry = getLocalRegistryURL();
try {
const isPnpm = await hasPnpmLockFile(path);
const vulnerabilities = isPnpm ?
await pnpmAudit(path, registry) :
await npmAudit(path, registry);
for (const packageVulns of vulnerabilities) {
const packageName = packageVulns.name || packageVulns.module_name;
if (!dependencies.has(packageName)) {
continue;
}
const dependenciesVulnerabilities = dependencies.get(packageName).vulnerabilities;
dependenciesVulnerabilities.push(...formatVulnerabilities(isPnpm ? "github-advisory_pnpm" : VULN_MODE.GITHUB_ADVISORY, [packageVulns]));
}
}
catch { }
}
async function npmAudit(path, registry) {
const arborist = new Arborist({ ...NPM_TOKEN, registry, path });
const { vulnerabilities } = (await arborist.audit()).toJSON();
// TODO: remove Symbols?
return Object.values(vulnerabilities)
.flatMap((vuln) => (Array.isArray(vuln.via) && typeof vuln.via[0] === "object" ? vuln.via : []));
}
async function pnpmAudit(lockfileDir, registry) {
const auditOptions = {
include: { dependencies: true, devDependencies: true, optionalDependencies: false },
lockfileDir,
registry,
virtualStoreDirMaxLength: 120
};
const lockfile = await readWantedLockfile(lockfileDir, {
ignoreIncompatible: false
});
// eslint-disable-next-line
const getAuthHeader = () => (void 0);
const { advisories } = await audit(lockfile, getAuthHeader, auditOptions);
// Note: we need to cast because original interface is incomplete
return Object.values(advisories);
}
async function hasPnpmLockFile(lockfileDir) {
try {
await fs.access(path.join(lockfileDir, "pnpm-lock.yaml"), fs.constants.F_OK);
return true;
}
catch {
return false;
}
}
//# sourceMappingURL=github-advisory.js.map