UNPKG

retire

Version:

Retire is a tool for detecting use of vulnerable libraries

184 lines (183 loc) 6.75 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.scanJsFile = scanJsFile; exports.scanBowerFile = scanBowerFile; exports.on = on; const events_1 = require("events"); const retire = __importStar(require("./retire")); const fs = __importStar(require("fs")); const crypto = __importStar(require("crypto")); const path = __importStar(require("path")); const depsdev_1 = require("./depsdev"); const deepscan_1 = require("./deepscan"); const license_1 = require("./license"); const events = new events_1.EventEmitter(); const hash = { sha1: (data) => { const shasum = crypto.createHash('sha1'); shasum.update(data); return shasum.digest('hex'); }, }; function emitResults(finding, options, repo) { if (options.includeOsv === true) { Promise.all(finding.results.map((r) => (0, depsdev_1.checkOSV)(r.component, r.version, options).then((v) => (r.vulnerabilities = (r.vulnerabilities ?? []).concat(v))))).then(() => filterAndEmitResults(finding, options, repo)); } else { filterAndEmitResults(finding, options, repo); } } function getIdentifiers(v) { return (v.identifiers?.CVE ?? []) .concat(v.identifiers?.bug ?? []) .concat(v.identifiers?.issue ?? []) .concat(v.identifiers?.githubID ?? []); } function uniqueVulnerabilities(vulnerabilities) { if (!vulnerabilities) return undefined; const unique = []; for (const v of vulnerabilities) { if (!unique.some((u) => getIdentifiers(u).some((i) => getIdentifiers(v).includes(i)))) { unique.push(v); } } return unique; } function addLicenses(components, repo) { components.forEach((c) => { const possibleLicenses = repo[c.component]?.licenses; if (possibleLicenses) c.licenses = (0, license_1.evaluateLicense)(possibleLicenses, c.version); }); } function filterAndEmitResults(finding, options, repo) { finding.results.forEach((r) => (r.vulnerabilities = uniqueVulnerabilities(r.vulnerabilities))); if (options.ignore) removeIgnored(finding.results, options.ignore); if (finding.results.length == 0) return; addLicenses(finding.results, repo); if (retire.isVulnerable(finding.results)) { events.emit('vulnerable-dependency-found', finding); } else { events.emit('dependency-found', finding); } } function shouldIgnorePath(fileSpecs, ignores) { return (ignores.paths?.some((i) => { return fileSpecs.some((j) => i.test(j) || i.test(path.resolve(j))); }) ?? false); } function removeIgnored(results, ignores) { if (!('descriptors' in ignores)) return; results.forEach((r) => { if (!('vulnerabilities' in r)) return; ignores.descriptors ?.filter((d) => 'component' in d) .forEach((i) => { if (r.component !== i.component) return; if (i.version && r.version !== i.version) return; if (i.severity) { //Remove vulnerabilities with the severity we want to drop r.vulnerabilities = r.vulnerabilities?.filter((v) => v.severity != i.severity); return; } if (i.identifiers) { removeIgnoredVulnerabilitiesByIdentifier({ ...i.identifiers }, r); return; } r.vulnerabilities = []; }); if (r.vulnerabilities?.length === 0) delete r.vulnerabilities; }); } function removeIgnoredVulnerabilitiesByIdentifier(identifiers, result) { result.vulnerabilities = result.vulnerabilities?.filter((v) => { if (!('identifiers' in v)) return true; return !Object.entries(identifiers || {}).every(([key, value]) => hasIdentifier({ ...v.identifiers }, key, value)); }); } function hasIdentifier(identifiers, key, value) { if (!(key in identifiers)) return false; const identifier = identifiers[key]; return Array.isArray(identifier) ? identifier.some((x) => x === value) : identifier === value; } function scanJsFile(file, repo, options) { if (options.ignore && shouldIgnorePath([file], options.ignore)) { return; } let results = retire.scanFileName(file, repo, true); if (!results || results.length === 0) { const content = fs.readFileSync(file, 'utf-8'); results = retire.scanFileContent(content, repo, hash); if (options.deep) { try { results = results.concat((0, deepscan_1.deepScan)(content, repo)); } catch (e) { options.log.warn(`Failed to scan ${file}: ` + e); } } } emitResults({ file: file, results: results }, options, repo); } function scanBowerFile(file, repo, options) { if (options.ignore && shouldIgnorePath([file], options.ignore)) { return; } try { const bower = JSON.parse(fs.readFileSync(file, 'utf-8')); if (bower.version) { const results = retire.check(bower.name, bower.version, repo); emitResults({ file: file, results: results }, options, repo); } } catch (e) { options.log.warn(`Could not parse file: ${file}`); } } function on(event, handler) { events.on(event, handler); }