UNPKG

worm-sign

Version:

A prescient scanner to detect and banish Shai Hulud malware from your dependencies.

133 lines (132 loc) 5.17 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 }); const fs = __importStar(require("fs")); const path = __importStar(require("path")); // @ts-expect-error: No types available for @yarnpkg/lockfile const lockfile = __importStar(require("@yarnpkg/lockfile")); function detectFromPackageManagerField(fieldValue) { if (!fieldValue) return false; return fieldValue.startsWith('yarn'); } function parseYarnLock(content) { const packages = new Map(); const integrity = new Map(); let parsed; try { const result = lockfile.parse(content); if (result.type === 'success' || result.type === 'merge') { parsed = result.object; } else { parsed = result.object; } } catch (e) { const msg = e instanceof Error ? e.message : String(e); throw new Error(`Yarn lockfile parse error: ${msg}`); } if (!parsed) { return { packages, integrity }; } for (const [key, info] of Object.entries(parsed)) { const lastAt = key.lastIndexOf('@'); if (lastAt === -1) continue; const name = key.slice(0, lastAt); const version = info.version; if (name && version) { const descriptors = key.split(',').map((k) => k.trim()); for (const descriptor of descriptors) { const lastAtDesc = descriptor.lastIndexOf('@'); if (lastAtDesc !== -1) { const pkgName = descriptor.slice(0, lastAtDesc); const set = packages.get(pkgName) ?? new Set(); set.add(version); packages.set(pkgName, set); if (info.integrity || info.resolved) { // Yarn 1 often puts hash in 'resolved' if 'integrity' is missing, or 'integrity' itself. // We prioritize integrity, then try to extract hash from resolved url if possible (e.g. #sha1-...) let hash = info.integrity; if (!hash && info.resolved && info.resolved.includes('#')) { hash = info.resolved.split('#')[1]; } if (hash) { const pkgIntegrity = integrity.get(pkgName) ?? new Map(); pkgIntegrity.set(version, hash); integrity.set(pkgName, pkgIntegrity); } } } } } } return { packages, integrity }; } function loadLockPackages(lockPath) { const warnings = []; let packages = new Map(); if (!fs.existsSync(lockPath)) { warnings.push(`Lockfile not found at ${lockPath}`); return { packages, warnings, success: false }; } try { const content = fs.readFileSync(lockPath, 'utf8'); const result = parseYarnLock(content); packages = result.packages; if (packages.size === 0) { warnings.push(`No packages parsed from ${path.basename(lockPath)}; check format.`); } return { packages, packageIntegrity: result.integrity, warnings, success: true }; } catch (err) { const msg = err instanceof Error ? err.message : String(err); warnings.push(`Unable to read ${path.basename(lockPath)}: ${msg}`); return { packages, warnings, success: false }; } } const yarnHandler = { id: 'yarn', label: 'Yarn', lockFiles: ['yarn.lock'], detectFromPackageManagerField, findLockFile: (repoRoot) => { const candidate = path.join(repoRoot, 'yarn.lock'); return fs.existsSync(candidate) ? candidate : null; }, loadLockPackages, }; exports.default = yarnHandler;