verimu 0.0.9 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -6
- package/dist/{cli.mjs → cli.js} +1295 -65
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +1503 -182
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +415 -24
- package/dist/index.d.ts +415 -24
- package/dist/index.mjs +1495 -182
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -3
- package/dist/cli.mjs.map +0 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/generate-sbom.ts","../src/scan.ts","../src/scanners/npm/npm-scanner.ts","../src/core/errors.ts","../src/scanners/nuget/nuget-scanner.ts","../src/scanners/cargo/cargo-scanner.ts","../src/scanners/pip/pip-scanner.ts","../src/scanners/maven/maven-scanner.ts","../src/scanners/go/go-scanner.ts","../src/scanners/ruby/ruby-scanner.ts","../src/scanners/composer/composer-scanner.ts","../src/scanners/registry.ts","../src/sbom/cyclonedx.ts","../src/cve/osv.ts","../src/cve/aggregator.ts","../src/reporters/console.ts","../src/api/client.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport type {\n GenerateSbomInput,\n GenerateSbomResult,\n SbomDependency,\n Ecosystem,\n} from './core/types.js';\n\n/**\n * Generates an NTIA-compliant CycloneDX 1.7 SBOM from structured dependency data.\n *\n * This is a **pure function** — no filesystem access, no network calls, no side effects.\n * It takes a project name, version, and list of dependencies, and returns a complete\n * CycloneDX 1.7 JSON SBOM that passes NTIA minimum-element validation.\n *\n * @example\n * ```ts\n * import { generateSbom } from 'verimu';\n *\n * const result = generateSbom({\n * projectName: 'my-app',\n * projectVersion: '1.0.0',\n * dependencies: [\n * { name: 'express', version: '4.18.2', ecosystem: 'npm' },\n * { name: '@types/node', version: '20.11.5', ecosystem: 'npm', direct: false },\n * ],\n * });\n *\n * console.log(result.componentCount); // 2\n * console.log(result.content); // formatted JSON string\n * ```\n */\nexport function generateSbom(input: GenerateSbomInput): GenerateSbomResult {\n const {\n projectName,\n projectVersion = '0.0.0',\n dependencies,\n } = input;\n\n const timestamp = new Date().toISOString();\n\n // Resolve PURLs for any deps that don't have one\n const resolvedDeps = dependencies.map((dep) => ({\n ...dep,\n direct: dep.direct ?? true,\n purl: dep.purl ?? buildPurl(dep.name, dep.version, dep.ecosystem),\n }));\n\n const rootPurl = buildPurl(projectName, projectVersion, 'npm');\n\n const sbom = {\n $schema: 'http://cyclonedx.org/schema/bom-1.7.schema.json',\n bomFormat: 'CycloneDX',\n specVersion: '1.7',\n serialNumber: `urn:uuid:${randomUUID()}`,\n version: 1,\n metadata: {\n timestamp,\n tools: {\n components: [\n {\n type: 'application',\n name: 'verimu',\n version: '0.0.1',\n description: 'Verimu CRA Compliance Scanner',\n supplier: { name: 'Verimu' },\n externalReferences: [\n { type: 'website', url: 'https://verimu.com' },\n ],\n },\n ],\n },\n supplier: { name: projectName },\n component: {\n type: 'application',\n name: projectName,\n version: projectVersion,\n 'bom-ref': rootPurl,\n supplier: { name: projectName },\n },\n },\n components: resolvedDeps.map((dep) => ({\n type: 'library',\n name: dep.name,\n version: dep.version,\n purl: dep.purl,\n 'bom-ref': dep.purl,\n scope: dep.direct ? 'required' : 'optional',\n supplier: { name: deriveSupplierName(dep.name) },\n })),\n dependencies: [\n {\n ref: rootPurl,\n dependsOn: resolvedDeps.map((d) => d.purl),\n },\n ],\n };\n\n const content = JSON.stringify(sbom, null, 2);\n\n return {\n sbom,\n content,\n componentCount: resolvedDeps.length,\n specVersion: '1.7',\n generatedAt: timestamp,\n };\n}\n\n// ─── Internal helpers ───────────────────────────────────────────\n\nconst PURL_TYPE_MAP: Record<Ecosystem, string> = {\n npm: 'npm',\n nuget: 'nuget',\n cargo: 'cargo',\n maven: 'maven',\n pip: 'pypi',\n go: 'golang',\n ruby: 'gem',\n composer: 'composer',\n};\n\n/**\n * Builds a Package URL (purl) per the purl spec.\n *\n * For npm scoped packages, the @ prefix is percent-encoded as %40:\n * @types/node@20.11.5 → pkg:npm/%40types/node@20.11.5\n *\n * See: https://github.com/package-url/purl-spec/blob/main/types-doc/npm-definition.md\n */\nfunction buildPurl(name: string, version: string, ecosystem: Ecosystem): string {\n const type = PURL_TYPE_MAP[ecosystem] || ecosystem;\n\n if (ecosystem === 'npm' && name.startsWith('@')) {\n return `pkg:${type}/%40${name.slice(1)}@${version}`;\n }\n\n return `pkg:${type}/${name}@${version}`;\n}\n\n/**\n * Derives supplier name from a package name.\n * Scoped packages: \"@vue/reactivity\" → \"@vue\"\n * Unscoped packages: \"express\" → \"express\"\n */\nfunction deriveSupplierName(packageName: string): string {\n if (packageName.startsWith('@')) {\n return packageName.split('/')[0];\n }\n return packageName;\n}\n","import { writeFile } from 'fs/promises';\nimport { basename } from 'path';\nimport { ScannerRegistry } from './scanners/registry.js';\nimport { CycloneDxGenerator } from './sbom/cyclonedx.js';\nimport { CveAggregator } from './cve/aggregator.js';\nimport { ConsoleReporter } from './reporters/console.js';\nimport { VerimuApiClient } from './api/client.js';\nimport type { ScanResponse } from './api/client.js';\nimport type { VerimuConfig, VerimuReport, Severity } from './core/types.js';\n\n/** Result of uploading scan results to the Verimu platform */\nexport interface UploadResult {\n projectId: string;\n projectCreated: boolean;\n totalDependencies: number;\n vulnerableDependencies: number;\n dashboardUrl: string;\n scanResponse: ScanResponse;\n}\n\n/**\n * Main scan pipeline — orchestrates the full Verimu workflow:\n * 1. Detect ecosystem & parse lockfile\n * 2. Generate CycloneDX SBOM\n * 3. Check dependencies for CVEs (via OSV)\n * 4. Produce report\n * 5. Upload to Verimu platform (if API key provided)\n */\nexport async function scan(config: VerimuConfig): Promise<VerimuReport> {\n const {\n projectPath,\n sbomOutput = './sbom.cdx.json',\n skipCveCheck = false,\n } = config;\n\n // 1. Scan dependencies\n const registry = new ScannerRegistry();\n const scanResult = await registry.detectAndScan(projectPath);\n\n // 2. Generate SBOM\n const sbomGenerator = new CycloneDxGenerator();\n const sbom = sbomGenerator.generate(scanResult);\n\n // 3. Write SBOM to disk\n await writeFile(sbomOutput, sbom.content, 'utf-8');\n\n // 4. Check CVEs (unless skipped)\n let cveCheck;\n if (skipCveCheck) {\n cveCheck = {\n vulnerabilities: [],\n sourcesQueried: [],\n sourceErrors: [],\n checkDurationMs: 0,\n };\n } else {\n const aggregator = new CveAggregator();\n cveCheck = await aggregator.check(scanResult.dependencies);\n }\n\n // 5. Build report\n const summary = {\n totalDependencies: scanResult.dependencies.length,\n totalVulnerabilities: cveCheck.vulnerabilities.length,\n critical: cveCheck.vulnerabilities.filter((v) => v.severity === 'CRITICAL').length,\n high: cveCheck.vulnerabilities.filter((v) => v.severity === 'HIGH').length,\n medium: cveCheck.vulnerabilities.filter((v) => v.severity === 'MEDIUM').length,\n low: cveCheck.vulnerabilities.filter((v) => v.severity === 'LOW').length,\n exploitedInWild: cveCheck.vulnerabilities.filter((v) => v.exploitedInWild).length,\n };\n\n const report: VerimuReport = {\n project: {\n path: projectPath,\n ecosystem: scanResult.ecosystem,\n dependencyCount: scanResult.dependencies.length,\n },\n sbom,\n cveCheck,\n summary,\n generatedAt: new Date().toISOString(),\n };\n\n // 6. Upload to Verimu platform (if API key provided)\n if (config.apiKey) {\n try {\n const uploadResult = await uploadToVerimu(report, config);\n (report as VerimuReport & { upload?: UploadResult }).upload = uploadResult;\n } catch {\n // Upload failure should not break the scan — log but continue\n // The CLI will handle displaying the error\n }\n }\n\n return report;\n}\n\n/**\n * Uploads scan results to the Verimu platform.\n *\n * 1. Upserts the project (create-if-not-exists by name)\n * 2. POSTs the SBOM for dependency tracking + CVE scanning\n */\nexport async function uploadToVerimu(\n report: VerimuReport,\n config: VerimuConfig\n): Promise<UploadResult> {\n if (!config.apiKey) {\n throw new Error('API key required for upload');\n }\n\n const client = new VerimuApiClient(config.apiKey, config.apiBaseUrl);\n\n // Derive project name from the directory\n const projectName = basename(config.projectPath);\n\n // 1. Upsert project\n const upsertRes = await client.upsertProject({\n name: projectName,\n ecosystem: report.project.ecosystem,\n });\n\n const projectId = upsertRes.project.id;\n\n // 2. Upload SBOM\n const scanRes = await client.uploadSbom(projectId, report.sbom.content);\n\n return {\n projectId,\n projectCreated: upsertRes.created,\n totalDependencies: scanRes.summary.total_dependencies,\n vulnerableDependencies: scanRes.summary.vulnerable_dependencies,\n dashboardUrl: `https://app.verimu.com/dashboard/projects/${projectId}`,\n scanResponse: scanRes,\n };\n}\n\n/**\n * Determines if the scan should fail CI based on severity threshold.\n */\nexport function shouldFailCi(report: VerimuReport, threshold: Severity): boolean {\n const severityOrder: Record<Severity, number> = {\n CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3, UNKNOWN: 4,\n };\n const thresholdLevel = severityOrder[threshold] ?? 4;\n\n return report.cveCheck.vulnerabilities.some(\n (v) => severityOrder[v.severity] <= thresholdLevel\n );\n}\n\n/**\n * Prints a console report to stdout.\n */\nexport function printReport(report: VerimuReport): void {\n const reporter = new ConsoleReporter();\n console.log(reporter.report(report));\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * npm / Node.js dependency scanner.\n *\n * Parses package-lock.json (v2/v3 format) to extract the full\n * resolved dependency tree. Also reads package.json to determine\n * which dependencies are direct vs transitive.\n */\nexport class NpmScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'npm';\n readonly lockfileNames = ['package-lock.json'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'package-lock.json');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockfileRaw, packageJsonRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'package.json'), 'utf-8').catch(() => null),\n ]);\n\n let lockfile: NpmLockfile;\n try {\n lockfile = JSON.parse(lockfileRaw);\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON');\n }\n\n // Determine direct dependency names from package.json\n const directNames = new Set<string>();\n if (packageJsonRaw) {\n try {\n const pkg = JSON.parse(packageJsonRaw);\n for (const name of Object.keys(pkg.dependencies ?? {})) {\n directNames.add(name);\n }\n for (const name of Object.keys(pkg.devDependencies ?? {})) {\n directNames.add(name);\n }\n } catch {\n // If package.json can't be parsed, all deps are \"unknown\" direct status\n }\n }\n\n const dependencies = this.parseLockfile(lockfile, directNames);\n\n return {\n projectPath,\n ecosystem: 'npm',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses package-lock.json and extracts dependencies.\n * Supports lockfile v2 and v3 (uses the `packages` field).\n * Falls back to `dependencies` field for lockfile v1.\n */\n private parseLockfile(lockfile: NpmLockfile, directNames: Set<string>): Dependency[] {\n const deps: Dependency[] = [];\n\n if (lockfile.packages) {\n // Lockfile v2/v3: `packages` is a flat map of \"node_modules/name\" → info\n for (const [pkgPath, pkgInfo] of Object.entries(lockfile.packages)) {\n // Skip the root package (empty string key)\n if (pkgPath === '') continue;\n\n // Extract package name from the path\n // e.g., \"node_modules/express\" → \"express\"\n // e.g., \"node_modules/@types/node\" → \"@types/node\"\n const name = this.extractPackageName(pkgPath);\n if (!name || !pkgInfo.version) continue;\n\n // Skip link: true entries (workspace references)\n if (pkgInfo.link) continue;\n\n deps.push({\n name,\n version: pkgInfo.version,\n direct: directNames.has(name),\n ecosystem: 'npm',\n purl: this.buildPurl(name, pkgInfo.version),\n });\n }\n } else if (lockfile.dependencies) {\n // Lockfile v1 fallback: `dependencies` is a nested tree\n this.parseDependenciesV1(lockfile.dependencies, directNames, deps);\n }\n\n return deps;\n }\n\n /**\n * Builds a purl (Package URL) for an npm package.\n *\n * Per the purl spec (https://github.com/package-url/purl-spec/blob/main/types-doc/npm-definition.md):\n * \"The npm scope @ sign prefix is always percent encoded.\"\n *\n * So @types/node@20.11.5 → pkg:npm/%40types/node@20.11.5\n * And express@4.18.2 → pkg:npm/express@4.18.2\n */\n private buildPurl(name: string, version: string): string {\n if (name.startsWith('@')) {\n // Scoped: encode the @ as %40 per purl spec\n return `pkg:npm/%40${name.slice(1)}@${version}`;\n }\n return `pkg:npm/${name}@${version}`;\n }\n\n /** Extracts the package name from a node_modules path */\n private extractPackageName(pkgPath: string): string | null {\n // \"node_modules/@scope/name\" → \"@scope/name\"\n // \"node_modules/name\" → \"name\"\n // \"node_modules/a/node_modules/b\" → \"b\" (nested)\n const parts = pkgPath.split('node_modules/');\n const last = parts[parts.length - 1];\n return last || null;\n }\n\n /** Recursively parses lockfile v1 `dependencies` tree */\n private parseDependenciesV1(\n depsObj: Record<string, NpmLockfileV1Dep>,\n directNames: Set<string>,\n result: Dependency[]\n ): void {\n for (const [name, info] of Object.entries(depsObj)) {\n if (info.version) {\n result.push({\n name,\n version: info.version,\n direct: directNames.has(name),\n ecosystem: 'npm',\n purl: this.buildPurl(name, info.version),\n });\n }\n // Recurse into nested dependencies\n if (info.dependencies) {\n this.parseDependenciesV1(info.dependencies, directNames, result);\n }\n }\n }\n}\n\n// ─── Types for package-lock.json parsing ─────────────────────────\n\ninterface NpmLockfile {\n name?: string;\n version?: string;\n lockfileVersion?: number;\n packages?: Record<string, NpmLockfilePackage>;\n dependencies?: Record<string, NpmLockfileV1Dep>;\n}\n\ninterface NpmLockfilePackage {\n version?: string;\n resolved?: string;\n integrity?: string;\n dev?: boolean;\n optional?: boolean;\n link?: boolean;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\ninterface NpmLockfileV1Dep {\n version?: string;\n resolved?: string;\n integrity?: string;\n requires?: Record<string, string>;\n dependencies?: Record<string, NpmLockfileV1Dep>;\n}\n","/** Base error for all Verimu errors */\nexport class VerimuError extends Error {\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = 'VerimuError';\n }\n}\n\n/** Thrown when no supported lockfile is found */\nexport class NoLockfileError extends VerimuError {\n constructor(projectPath: string) {\n super(\n `No supported lockfile found in ${projectPath}. ` +\n `Supported: package-lock.json (npm), packages.lock.json (NuGet), ` +\n `Cargo.lock (Rust), requirements.txt / Pipfile.lock (Python), pom.xml (Maven), go.sum (Go), Gemfile.lock (Ruby), composer.lock (Composer)`,\n 'NO_LOCKFILE'\n );\n this.name = 'NoLockfileError';\n }\n}\n\n/** Thrown when lockfile parsing fails */\nexport class LockfileParseError extends VerimuError {\n constructor(lockfilePath: string, reason: string) {\n super(`Failed to parse ${lockfilePath}: ${reason}`, 'LOCKFILE_PARSE_ERROR');\n this.name = 'LockfileParseError';\n }\n}\n\n/** Thrown when a CVE source query fails */\nexport class CveSourceError extends VerimuError {\n constructor(source: string, reason: string) {\n super(`CVE source \"${source}\" failed: ${reason}`, 'CVE_SOURCE_ERROR');\n this.name = 'CveSourceError';\n }\n}\n\n/** Thrown when API key is required but missing */\nexport class ApiKeyRequiredError extends VerimuError {\n constructor(feature: string) {\n super(\n `API key required for \"${feature}\". Get one at https://verimu.com/dashboard`,\n 'API_KEY_REQUIRED'\n );\n this.name = 'ApiKeyRequiredError';\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * C# / NuGet dependency scanner.\n *\n * Parses `packages.lock.json` (NuGet lock file, generated by\n * `dotnet restore --use-lock-file`) to extract the full resolved\n * dependency tree. The lock file itself tracks Direct vs Transitive.\n *\n * Lock file format (NuGet v1):\n * ```json\n * {\n * \"version\": 1,\n * \"dependencies\": {\n * \"net8.0\": {\n * \"PackageName\": {\n * \"type\": \"Direct\" | \"Transitive\",\n * \"resolved\": \"13.0.3\",\n * \"contentHash\": \"...\"\n * }\n * }\n * }\n * }\n * ```\n */\nexport class NugetScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'nuget';\n readonly lockfileNames = ['packages.lock.json'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'packages.lock.json');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const lockfileRaw = await readFile(lockfilePath, 'utf-8');\n\n let lockfile: NugetLockfile;\n try {\n lockfile = JSON.parse(lockfileRaw);\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON');\n }\n\n if (!lockfile.dependencies) {\n throw new LockfileParseError(lockfilePath, 'Missing \"dependencies\" field');\n }\n\n const dependencies = this.parseLockfile(lockfile);\n\n return {\n projectPath,\n ecosystem: 'nuget',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses packages.lock.json and extracts dependencies across all\n * target frameworks. Deduplicates by package name (keeps highest version\n * if the same package appears under multiple frameworks).\n */\n private parseLockfile(lockfile: NugetLockfile): Dependency[] {\n const depMap = new Map<string, Dependency>();\n\n for (const [_framework, packages] of Object.entries(lockfile.dependencies)) {\n for (const [name, info] of Object.entries(packages)) {\n if (!info.resolved) continue;\n\n // \"Direct\" in the lock file means it's a PackageReference in .csproj\n const isDirect = info.type === 'Direct';\n\n const existing = depMap.get(name);\n if (!existing) {\n depMap.set(name, {\n name,\n version: info.resolved,\n direct: isDirect,\n ecosystem: 'nuget',\n purl: this.buildPurl(name, info.resolved),\n });\n }\n // If already seen, keep the direct flag if either occurrence is direct\n else if (isDirect && !existing.direct) {\n existing.direct = true;\n }\n }\n }\n\n return Array.from(depMap.values());\n }\n\n /**\n * Builds a purl for a NuGet package.\n * NuGet purls are straightforward: pkg:nuget/Name@Version\n */\n private buildPurl(name: string, version: string): string {\n return `pkg:nuget/${name}@${version}`;\n }\n}\n\n// ─── Types for packages.lock.json parsing ────────────────────────\n\ninterface NugetLockfile {\n version?: number;\n dependencies: Record<string, Record<string, NugetLockfileEntry>>;\n}\n\ninterface NugetLockfileEntry {\n type?: 'Direct' | 'Transitive';\n resolved?: string;\n contentHash?: string;\n dependencies?: Record<string, string>;\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Rust / Cargo dependency scanner.\n *\n * Parses `Cargo.lock` (TOML format) to extract the full resolved\n * dependency tree. Reads `Cargo.toml` to determine which packages\n * are direct dependencies vs transitive.\n *\n * Cargo.lock format (v3):\n * ```toml\n * [[package]]\n * name = \"serde\"\n * version = \"1.0.195\"\n * source = \"registry+https://github.com/rust-lang/crates.io-index\"\n * checksum = \"abc123...\"\n * dependencies = [\n * \"serde_derive\",\n * ]\n * ```\n *\n * Note: We use a simple TOML parser since Cargo.lock has a very\n * regular structure (just [[package]] entries). No need for a full\n * TOML library.\n */\nexport class CargoScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'cargo';\n readonly lockfileNames = ['Cargo.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'Cargo.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockfileRaw, cargoTomlRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'Cargo.toml'), 'utf-8').catch(() => null),\n ]);\n\n const packages = this.parseLockfile(lockfileRaw, lockfilePath);\n const directNames = cargoTomlRaw ? this.parseCargoToml(cargoTomlRaw) : new Set<string>();\n\n // The first [[package]] is typically the root project — skip it\n const rootName = packages.length > 0 ? packages[0].name : null;\n\n const dependencies: Dependency[] = [];\n for (const pkg of packages) {\n // Skip the root project itself\n if (pkg.name === rootName && pkg.source === undefined) continue;\n\n dependencies.push({\n name: pkg.name,\n version: pkg.version,\n direct: directNames.has(pkg.name),\n ecosystem: 'cargo',\n purl: this.buildPurl(pkg.name, pkg.version),\n });\n }\n\n return {\n projectPath,\n ecosystem: 'cargo',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses Cargo.lock by splitting on [[package]] blocks.\n * This is a lightweight parser that handles the regular structure\n * of Cargo.lock without needing a full TOML parser.\n */\n private parseLockfile(content: string, lockfilePath: string): CargoPackage[] {\n const packages: CargoPackage[] = [];\n const blocks = content.split(/^\\[\\[package\\]\\]$/m);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const name = this.extractField(block, 'name');\n const version = this.extractField(block, 'version');\n const source = this.extractField(block, 'source');\n\n if (name && version) {\n packages.push({ name, version, source: source || undefined });\n }\n }\n\n if (packages.length === 0 && content.includes('[[package]]')) {\n throw new LockfileParseError(lockfilePath, 'Failed to parse any packages from Cargo.lock');\n }\n\n return packages;\n }\n\n /**\n * Extracts a string field value from a TOML block.\n * Handles: `name = \"value\"` format.\n */\n private extractField(block: string, fieldName: string): string | null {\n const regex = new RegExp(`^${fieldName}\\\\s*=\\\\s*\"([^\"]*)\"`, 'm');\n const match = block.match(regex);\n return match ? match[1] : null;\n }\n\n /**\n * Parses Cargo.toml to extract direct dependency names.\n * Looks for [dependencies] and [dev-dependencies] sections.\n */\n private parseCargoToml(content: string): Set<string> {\n const directNames = new Set<string>();\n let inDepsSection = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n // Detect section headers\n if (line.startsWith('[')) {\n inDepsSection =\n line === '[dependencies]' ||\n line === '[dev-dependencies]' ||\n line === '[build-dependencies]';\n continue;\n }\n\n if (inDepsSection && line && !line.startsWith('#')) {\n // Extract package name from \"name = ...\" or \"name = { version = ... }\"\n const match = line.match(/^([a-zA-Z0-9_-]+)\\s*=/);\n if (match) {\n directNames.add(match[1]);\n }\n }\n }\n\n return directNames;\n }\n\n /**\n * Builds a purl for a Cargo (crates.io) package.\n */\n private buildPurl(name: string, version: string): string {\n return `pkg:cargo/${name}@${version}`;\n }\n}\n\n// ─── Internal types ──────────────────────────────────────────────\n\ninterface CargoPackage {\n name: string;\n version: string;\n source?: string;\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Python / pip dependency scanner.\n *\n * Supports multiple Python dependency file formats (in priority order):\n * 1. `requirements.txt` — flat list of pinned dependencies\n * 2. `Pipfile.lock` — Pipenv lock file with exact versions\n *\n * For `requirements.txt`, all listed packages are treated as direct\n * dependencies (the file doesn't distinguish direct vs transitive).\n * For `Pipfile.lock`, `default` packages are direct and `develop`\n * packages are dev dependencies.\n *\n * Limitation: `requirements.txt` doesn't capture transitive deps unless\n * generated with `pip freeze`. If using `pip freeze` output, all deps\n * are listed but we can't distinguish direct vs transitive.\n */\nexport class PipScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'pip';\n readonly lockfileNames = ['Pipfile.lock', 'requirements.txt'];\n\n async detect(projectPath: string): Promise<string | null> {\n // Check in priority order\n for (const lockfile of this.lockfileNames) {\n const fullPath = path.join(projectPath, lockfile);\n if (existsSync(fullPath)) return fullPath;\n }\n return null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const raw = await readFile(lockfilePath, 'utf-8');\n const filename = path.basename(lockfilePath);\n\n let dependencies: Dependency[];\n\n if (filename === 'Pipfile.lock') {\n dependencies = this.parsePipfileLock(raw, lockfilePath);\n } else {\n dependencies = await this.parseRequirementsTxt(raw, lockfilePath);\n }\n\n return {\n projectPath,\n ecosystem: 'pip',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses `requirements.txt` format.\n *\n * Supports:\n * - `package==1.2.3` (pinned) — REQUIRED\n * - Comments (`#`) and blank lines are skipped\n * - `-r other-file.txt` (include directive) — recursively parsed\n * - `--index-url` and other pip flags — skipped\n *\n * Throws if any dependency is not strictly pinned with ==.\n * Use `pip freeze` to generate a properly pinned requirements.txt.\n */\n private async parseRequirementsTxt(\n content: string,\n lockfilePath: string,\n visited: Set<string> = new Set()\n ): Promise<Dependency[]> {\n const deps: Dependency[] = [];\n const currentDir = path.dirname(lockfilePath);\n const normalizedPath = path.resolve(lockfilePath);\n\n // Prevent circular includes\n if (visited.has(normalizedPath)) {\n return deps;\n }\n visited.add(normalizedPath);\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n // Skip comments and blank lines\n if (!line || line.startsWith('#')) {\n continue;\n }\n\n // Handle -r / --requirement includes\n const includeMatch = line.match(/^-r\\s+(.+)$/) || line.match(/^--requirement\\s+(.+)$/);\n if (includeMatch) {\n const includePath = path.resolve(currentDir, includeMatch[1].trim());\n if (existsSync(includePath)) {\n const includeContent = await readFile(includePath, 'utf-8');\n const includedDeps = await this.parseRequirementsTxt(includeContent, includePath, visited);\n deps.push(...includedDeps);\n }\n continue;\n }\n\n // Skip other pip flags (--index-url, -e, etc.)\n if (line.startsWith('-') || line.startsWith('--')) {\n continue;\n }\n\n // Parse strictly pinned \"package==version\" only\n const pinnedMatch = line.match(/^([a-zA-Z0-9_][a-zA-Z0-9._-]*)\\s*==\\s*([^,\\s]+)$/);\n if (pinnedMatch) {\n const [, name, version] = pinnedMatch;\n if (name && version) {\n deps.push({\n name: this.normalizePipName(name),\n version,\n direct: true, // requirements.txt doesn't distinguish\n ecosystem: 'pip',\n purl: this.buildPurl(name, version),\n });\n }\n continue;\n }\n\n // Check if it's a dependency line with non-pinned version specifier\n const depMatch = line.match(/^([a-zA-Z0-9_][a-zA-Z0-9._-]*)\\s*([~=!<>].*)$/);\n if (depMatch) {\n throw new LockfileParseError(\n lockfilePath,\n `Non-pinned dependency detected: \"${line}\". Use pip freeze or Pipfile.lock.`\n );\n }\n }\n\n return deps;\n }\n\n /**\n * Parses `Pipfile.lock` (JSON format from Pipenv).\n *\n * Structure:\n * ```json\n * {\n * \"_meta\": { ... },\n * \"default\": {\n * \"requests\": { \"version\": \"==2.31.0\", ... }\n * },\n * \"develop\": {\n * \"pytest\": { \"version\": \"==7.4.0\", ... }\n * }\n * }\n * ```\n */\n private parsePipfileLock(content: string, lockfilePath: string): Dependency[] {\n let lockfile: PipfileLock;\n try {\n lockfile = JSON.parse(content);\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON in Pipfile.lock');\n }\n\n const deps: Dependency[] = [];\n\n // Parse \"default\" (production) dependencies\n if (lockfile.default) {\n for (const [name, info] of Object.entries(lockfile.default)) {\n const version = info.version?.replace(/^==/, '') ?? '';\n if (version) {\n deps.push({\n name: this.normalizePipName(name),\n version,\n direct: true,\n ecosystem: 'pip',\n purl: this.buildPurl(name, version),\n });\n }\n }\n }\n\n // Parse \"develop\" dependencies\n if (lockfile.develop) {\n for (const [name, info] of Object.entries(lockfile.develop)) {\n const version = info.version?.replace(/^==/, '') ?? '';\n if (version) {\n deps.push({\n name: this.normalizePipName(name),\n version,\n direct: true,\n ecosystem: 'pip',\n purl: this.buildPurl(name, version),\n });\n }\n }\n }\n\n return deps;\n }\n\n /**\n * Normalizes a pip package name per PEP 503.\n * Converts to lowercase and replaces any run of [-_.] with a single hyphen.\n */\n private normalizePipName(name: string): string {\n return name.toLowerCase().replace(/[-_.]+/g, '-');\n }\n\n /**\n * Builds a purl for a PyPI package.\n * Per purl spec, the type is \"pypi\" (not \"pip\").\n */\n private buildPurl(name: string, version: string): string {\n return `pkg:pypi/${this.normalizePipName(name)}@${version}`;\n }\n}\n\n// ─── Types for Pipfile.lock parsing ──────────────────────────────\n\ninterface PipfileLock {\n _meta?: Record<string, unknown>;\n default?: Record<string, PipfileLockEntry>;\n develop?: Record<string, PipfileLockEntry>;\n}\n\ninterface PipfileLockEntry {\n version?: string;\n hashes?: string[];\n markers?: string;\n index?: string;\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Java / Maven dependency scanner.\n *\n * Maven doesn't have a lockfile. This scanner uses two strategies:\n *\n * 1. **Primary (auto)**: If `mvn` is on `$PATH`, runs\n * `mvn dependency:list -DoutputType=text` to get the resolved\n * dependency tree including transitive dependencies.\n *\n * 2. **Fallback (pre-generated)**: Looks for a `dependency-tree.txt`\n * file in the project root. Users can generate this with:\n * ```\n * mvn dependency:list -DoutputFile=dependency-tree.txt -DoutputType=text\n * ```\n *\n * The scanner detects a Maven project by the presence of `pom.xml`.\n *\n * Maven dependency:list output format (one per line):\n * ```\n * com.google.guava:guava:jar:32.1.3-jre:compile\n * org.slf4j:slf4j-api:jar:2.0.9:compile\n * junit:junit:jar:4.13.2:test\n * ```\n * Fields: groupId:artifactId:type:version:scope\n * With classifier: groupId:artifactId:type:classifier:version:scope\n */\nexport class MavenScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'maven';\n readonly lockfileNames = ['pom.xml'];\n\n /** Allow injection for testing */\n private execSyncFn: typeof execSync;\n\n constructor(execSyncImpl?: typeof execSync) {\n this.execSyncFn = execSyncImpl ?? execSync;\n }\n\n async detect(projectPath: string): Promise<string | null> {\n const pomPath = path.join(projectPath, 'pom.xml');\n return existsSync(pomPath) ? pomPath : null;\n }\n\n async scan(projectPath: string, _lockfilePath: string): Promise<ScanResult> {\n // Read pom.xml to determine direct dependencies\n const pomPath = path.join(projectPath, 'pom.xml');\n const pomContent = await readFile(pomPath, 'utf-8').catch(() => null);\n const directDeps = pomContent ? this.parsePomDependencies(pomContent) : new Set<string>();\n\n // Strategy 1: Try pre-generated dependency-tree.txt\n const depTreePath = path.join(projectPath, 'dependency-tree.txt');\n if (existsSync(depTreePath)) {\n const content = await readFile(depTreePath, 'utf-8');\n const dependencies = this.parseDependencyList(content, directDeps);\n return this.buildResult(projectPath, depTreePath, dependencies);\n }\n\n // Strategy 2: Try running `mvn dependency:list`\n if (this.isMavenAvailable()) {\n const output = this.runMavenDependencyList(projectPath);\n const dependencies = this.parseDependencyList(output, directDeps);\n return this.buildResult(projectPath, pomPath, dependencies);\n }\n\n throw new LockfileParseError(\n pomPath,\n 'Maven project detected (pom.xml found) but could not resolve dependencies. ' +\n 'Either install Maven (`mvn` must be on $PATH) or pre-generate a dependency list:\\n' +\n ' mvn dependency:list -DoutputFile=dependency-tree.txt -DappendOutput=true'\n );\n }\n\n /**\n * Parses pom.xml to extract direct dependency coordinates (groupId:artifactId).\n * This is a simple regex-based parser that handles standard dependency declarations.\n */\n private parsePomDependencies(pomContent: string): Set<string> {\n const directDeps = new Set<string>();\n\n // Match <dependency> blocks and extract groupId + artifactId\n // This regex handles multi-line dependency declarations\n const depBlockRegex = /<dependency>\\s*([\\s\\S]*?)<\\/dependency>/g;\n const groupIdRegex = /<groupId>\\s*([^<]+)\\s*<\\/groupId>/;\n const artifactIdRegex = /<artifactId>\\s*([^<]+)\\s*<\\/artifactId>/;\n\n let match;\n while ((match = depBlockRegex.exec(pomContent)) !== null) {\n const block = match[1];\n const groupMatch = block.match(groupIdRegex);\n const artifactMatch = block.match(artifactIdRegex);\n\n if (groupMatch && artifactMatch) {\n const groupId = groupMatch[1].trim();\n const artifactId = artifactMatch[1].trim();\n directDeps.add(`${groupId}:${artifactId}`);\n }\n }\n\n return directDeps;\n }\n\n /**\n * Parses Maven `dependency:list` output.\n *\n * Each dependency line has the format:\n * groupId:artifactId:type:version:scope (5 parts)\n * groupId:artifactId:type:classifier:version:scope (6 parts)\n *\n * Lines are typically indented with leading whitespace.\n */\n private parseDependencyList(content: string, directDeps: Set<string>): Dependency[] {\n const deps: Dependency[] = [];\n const seen = new Set<string>();\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n if (!line) continue;\n\n // Split by colon - Maven coords use : as separator\n const parts = line.split(':');\n\n // Need at least 5 parts: groupId:artifactId:type:version:scope\n if (parts.length < 5) continue;\n\n const groupId = parts[0];\n const artifactId = parts[1];\n // parts[2] = type (jar, war, pom, etc.)\n\n // Handle optional classifier (6 parts) vs no classifier (5 parts)\n let version: string;\n let scope: string;\n\n if (parts.length >= 6) {\n // With classifier: groupId:artifactId:type:classifier:version:scope\n version = parts[4];\n scope = parts[5];\n } else {\n // Without classifier: groupId:artifactId:type:version:scope\n version = parts[3];\n scope = parts[4];\n }\n\n // Skip invalid entries\n if (!groupId || !artifactId || !version) continue;\n\n // Skip test-scoped dependencies\n if (scope === 'test') continue;\n\n const name = `${groupId}:${artifactId}`;\n\n // Deduplicate (same dep might appear multiple times)\n if (seen.has(name)) continue;\n seen.add(name);\n\n // Determine if direct by checking against pom.xml dependencies\n const isDirect = directDeps.has(name);\n\n deps.push({\n name,\n version,\n direct: isDirect,\n ecosystem: 'maven',\n purl: this.buildPurl(groupId, artifactId, version),\n });\n }\n\n return deps;\n }\n\n /** Checks if `mvn` is available on PATH */\n private isMavenAvailable(): boolean {\n try {\n this.execSyncFn('mvn --version', { stdio: 'pipe', timeout: 10_000 });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Runs `mvn dependency:list` and returns the output.\n */\n private runMavenDependencyList(projectPath: string): string {\n try {\n const output = this.execSyncFn(\n 'mvn dependency:list -DoutputType=text -DincludeScope=compile',\n {\n cwd: projectPath,\n stdio: 'pipe',\n timeout: 120_000, // 2 minute timeout\n encoding: 'utf-8',\n }\n );\n return output.toString();\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n throw new LockfileParseError(\n path.join(projectPath, 'pom.xml'),\n `Failed to run 'mvn dependency:list': ${message}`\n );\n }\n }\n\n /**\n * Builds a purl for a Maven package.\n * Format: pkg:maven/groupId/artifactId@version\n */\n private buildPurl(groupId: string, artifactId: string, version: string): string {\n return `pkg:maven/${groupId}/${artifactId}@${version}`;\n }\n\n private buildResult(\n projectPath: string,\n lockfilePath: string,\n dependencies: Dependency[]\n ): ScanResult {\n return {\n projectPath,\n ecosystem: 'maven',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Go module dependency scanner.\n *\n * Parses `go.sum` to extract the full resolved dependency list, and\n * cross-references `go.mod` to distinguish direct vs indirect (transitive)\n * dependencies.\n *\n * go.sum format (one or two lines per module):\n * ```\n * github.com/gin-gonic/gin v1.9.1 h1:abc123...=\n * github.com/gin-gonic/gin v1.9.1/go.mod h1:def456...=\n * ```\n *\n * Lines ending in `/go.mod` are checksums of the module's go.mod file —\n * we skip those and only keep the `h1:` lines (source archive checksums).\n *\n * go.mod `require` block format:\n * ```\n * require (\n * github.com/gin-gonic/gin v1.9.1\n * golang.org/x/text v0.14.0 // indirect\n * )\n * ```\n *\n * Dependencies marked `// indirect` are transitive.\n */\nexport class GoScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'go';\n readonly lockfileNames = ['go.sum'];\n\n async detect(projectPath: string): Promise<string | null> {\n const goSumPath = path.join(projectPath, 'go.sum');\n return existsSync(goSumPath) ? goSumPath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [goSumRaw, goModRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'go.mod'), 'utf-8').catch(() => null),\n ]);\n\n const { directNames, indirectNames } = goModRaw\n ? this.parseGoMod(goModRaw)\n : { directNames: new Set<string>(), indirectNames: new Set<string>() };\n\n const dependencies = this.parseGoSum(goSumRaw, lockfilePath, directNames, indirectNames);\n\n return {\n projectPath,\n ecosystem: 'go',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses go.sum and extracts unique module dependencies.\n *\n * Each module may appear twice in go.sum (once for the source archive,\n * once for go.mod). We deduplicate by module path + version, keeping\n * only the `h1:` entry (not the `/go.mod` entry).\n */\n private parseGoSum(\n content: string,\n lockfilePath: string,\n directNames: Set<string>,\n indirectNames: Set<string>,\n ): Dependency[] {\n const depMap = new Map<string, Dependency>();\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n if (!line) continue;\n\n // Format: \"module version hash\"\n const parts = line.split(/\\s+/);\n if (parts.length < 3) continue;\n\n const modulePath = parts[0];\n let version = parts[1];\n\n // Skip /go.mod checksum lines\n if (version.endsWith('/go.mod')) continue;\n\n // Strip any +incompatible suffix for cleaner versions\n version = version.replace(/\\+incompatible$/, '');\n\n const key = `${modulePath}@${version}`;\n if (depMap.has(key)) continue;\n\n // Determine direct/indirect from go.mod data\n // If go.mod is available: explicit direct or not marked indirect = direct\n // If go.mod is not available: default to direct (conservative)\n const isDirect = directNames.size > 0 || indirectNames.size > 0\n ? directNames.has(modulePath) || (!indirectNames.has(modulePath) && !directNames.has(modulePath) ? false : directNames.has(modulePath))\n : true;\n\n depMap.set(key, {\n name: modulePath,\n version,\n direct: isDirect,\n ecosystem: 'go',\n purl: this.buildPurl(modulePath, version),\n });\n }\n\n return Array.from(depMap.values());\n }\n\n /**\n * Parses go.mod to extract direct and indirect dependency names.\n *\n * Handles both single-line and block `require` directives:\n * ```\n * require github.com/pkg/errors v0.9.1\n *\n * require (\n * github.com/gin-gonic/gin v1.9.1\n * golang.org/x/text v0.14.0 // indirect\n * )\n * ```\n */\n private parseGoMod(content: string): { directNames: Set<string>; indirectNames: Set<string> } {\n const directNames = new Set<string>();\n const indirectNames = new Set<string>();\n\n let inRequireBlock = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n // Single-line require: `require github.com/pkg/errors v0.9.1`\n if (line.startsWith('require ') && !line.includes('(')) {\n const match = line.match(/^require\\s+(\\S+)\\s+\\S+(.*)$/);\n if (match) {\n const modulePath = match[1];\n const rest = match[2];\n if (rest.includes('// indirect')) {\n indirectNames.add(modulePath);\n } else {\n directNames.add(modulePath);\n }\n }\n continue;\n }\n\n // Start of require block\n if (line === 'require (' || line.startsWith('require (')) {\n inRequireBlock = true;\n continue;\n }\n\n // End of require block\n if (inRequireBlock && line === ')') {\n inRequireBlock = false;\n continue;\n }\n\n // Inside require block\n if (inRequireBlock && line && !line.startsWith('//')) {\n const match = line.match(/^(\\S+)\\s+\\S+(.*)$/);\n if (match) {\n const modulePath = match[1];\n const rest = match[2];\n if (rest.includes('// indirect')) {\n indirectNames.add(modulePath);\n } else {\n directNames.add(modulePath);\n }\n }\n }\n }\n\n return { directNames, indirectNames };\n }\n\n /**\n * Builds a purl for a Go module.\n *\n * Per purl spec, the type is \"golang\" and the module path\n * uses `/` separators (no encoding needed for path segments).\n *\n * Example: `pkg:golang/github.com/gin-gonic/gin@v1.9.1`\n */\n private buildPurl(modulePath: string, version: string): string {\n return `pkg:golang/${modulePath}@${version}`;\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Ruby dependency scanner (Bundler).\n *\n * Parses `Gemfile.lock` to extract the full resolved dependency list,\n * and cross-references the `DEPENDENCIES` section to distinguish\n * direct vs transitive gems.\n *\n * Gemfile.lock format:\n * ```\n * GEM\n * remote: https://rubygems.org/\n * specs:\n * actioncable (7.1.2)\n * actionpack (= 7.1.2)\n * activesupport (= 7.1.2)\n * rack (3.0.8)\n *\n * PLATFORMS\n * ruby\n *\n * DEPENDENCIES\n * puma (>= 5.0)\n * rails (~> 7.1.2)\n *\n * BUNDLED WITH\n * 2.5.3\n * ```\n *\n * The `GEM > specs:` section lists all resolved gems with exact versions.\n * The `DEPENDENCIES` section lists direct gems (from the Gemfile).\n */\nexport class RubyScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'ruby';\n readonly lockfileNames = ['Gemfile.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'Gemfile.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const content = await readFile(lockfilePath, 'utf-8');\n\n const specs = this.parseSpecs(content, lockfilePath);\n const directNames = this.parseDependencies(content);\n\n const dependencies: Dependency[] = specs.map(({ name, version }) => ({\n name,\n version,\n direct: directNames.has(name),\n ecosystem: 'ruby' as Ecosystem,\n purl: `pkg:gem/${name}@${version}`,\n }));\n\n return {\n projectPath,\n ecosystem: 'ruby',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses the GEM > specs section to extract all resolved gems.\n *\n * Gems at the top level of the specs section (indented 4 spaces) are\n * resolved packages. Their sub-dependencies (indented 6+ spaces) are\n * constraints, not separate entries — those sub-deps appear as their\n * own top-level spec entries elsewhere.\n *\n * Format: ` gem-name (1.2.3)`\n */\n private parseSpecs(\n content: string,\n lockfilePath: string,\n ): Array<{ name: string; version: string }> {\n const gems: Array<{ name: string; version: string }> = [];\n\n let inGemSection = false;\n let inSpecs = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine;\n\n // Detect section boundaries (lines with no leading whitespace)\n if (line.length > 0 && line[0] !== ' ') {\n if (line.startsWith('GEM')) {\n inGemSection = true;\n inSpecs = false;\n continue;\n }\n // Any other top-level section ends GEM\n inGemSection = false;\n inSpecs = false;\n continue;\n }\n\n if (inGemSection && line.trimStart().startsWith('specs:')) {\n inSpecs = true;\n continue;\n }\n\n if (!inSpecs) continue;\n\n // Top-level gems are indented exactly 4 spaces: \" gem-name (1.2.3)\"\n // Sub-dependencies are indented 6+ spaces: \" dep-name (>= 1.0)\"\n // We only want the 4-space entries (actual resolved packages)\n const match = line.match(/^ {4}(\\S+)\\s+\\(([^)]+)\\)$/);\n if (match) {\n const [, name, version] = match;\n gems.push({ name, version });\n }\n }\n\n if (gems.length === 0) {\n throw new LockfileParseError(\n lockfilePath,\n 'No gems found in GEM specs section',\n );\n }\n\n return gems;\n }\n\n /**\n * Parses the DEPENDENCIES section to get direct dependency names.\n *\n * Format: ` gem-name (>= 1.0)` or ` gem-name`\n * The version constraint is optional and we only need the name.\n */\n private parseDependencies(content: string): Set<string> {\n const directNames = new Set<string>();\n let inDependencies = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine;\n\n // Section header (no leading space)\n if (line.length > 0 && line[0] !== ' ') {\n if (line.startsWith('DEPENDENCIES')) {\n inDependencies = true;\n continue;\n }\n if (inDependencies) break; // Next section → stop\n continue;\n }\n\n if (!inDependencies) continue;\n\n // Dependencies are indented 2 spaces: \" gem-name\" or \" gem-name (>= 1.0)\"\n // The ! suffix indicates a gem loaded from a specific source/path\n const match = line.match(/^ {2}(\\S+?)!?\\s*(?:\\(|$)/);\n if (match) {\n directNames.add(match[1]);\n }\n }\n\n return directNames;\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * PHP / Composer dependency scanner.\n *\n * Parses `composer.lock` for resolved packages and uses `composer.json`\n * (if present) to identify direct dependencies.\n */\nexport class ComposerScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'composer';\n readonly lockfileNames = ['composer.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'composer.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockRaw, manifestRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'composer.json'), 'utf-8').catch(() => null),\n ]);\n\n const directNames = manifestRaw ? this.parseComposerManifest(manifestRaw) : null;\n const dependencies = this.parseComposerLock(lockRaw, lockfilePath, directNames);\n\n return {\n projectPath,\n ecosystem: 'composer',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n private parseComposerLock(\n content: string,\n lockfilePath: string,\n directNames: Set<string> | null\n ): Dependency[] {\n let lock: ComposerLock;\n try {\n lock = JSON.parse(content) as ComposerLock;\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON in composer.lock');\n }\n\n const allPackages = [...(lock.packages ?? []), ...(lock['packages-dev'] ?? [])];\n if (allPackages.length === 0) {\n throw new LockfileParseError(lockfilePath, 'No packages found in composer.lock');\n }\n\n return allPackages\n .filter((pkg) => pkg.name && pkg.version)\n .map((pkg) => ({\n name: pkg.name,\n version: this.normalizeVersion(pkg.version),\n direct: directNames ? directNames.has(pkg.name) : true,\n ecosystem: 'composer' as Ecosystem,\n purl: this.buildPurl(pkg.name, this.normalizeVersion(pkg.version)),\n }));\n }\n\n private parseComposerManifest(content: string): Set<string> {\n let manifest: ComposerManifest;\n try {\n manifest = JSON.parse(content) as ComposerManifest;\n } catch {\n return new Set<string>();\n }\n\n const names = new Set<string>();\n for (const section of [manifest.require ?? {}, manifest['require-dev'] ?? {}]) {\n for (const name of Object.keys(section)) {\n // Exclude platform constraints.\n if (name === 'php' || name.startsWith('ext-') || name.startsWith('lib-')) continue;\n names.add(name);\n }\n }\n\n return names;\n }\n\n private normalizeVersion(version: string): string {\n return version.trim();\n }\n\n private buildPurl(name: string, version: string): string {\n return `pkg:composer/${name}@${version}`;\n }\n}\n\ninterface ComposerLock {\n packages?: ComposerPackage[];\n 'packages-dev'?: ComposerPackage[];\n}\n\ninterface ComposerPackage {\n name: string;\n version: string;\n}\n\ninterface ComposerManifest {\n require?: Record<string, string>;\n 'require-dev'?: Record<string, string>;\n}\n\n","import type { DependencyScanner } from './scanner.interface.js';\nimport type { ScanResult } from '../core/types.js';\nimport { NpmScanner } from './npm/npm-scanner.js';\nimport { NugetScanner } from './nuget/nuget-scanner.js';\nimport { CargoScanner } from './cargo/cargo-scanner.js';\nimport { PipScanner } from './pip/pip-scanner.js';\nimport { MavenScanner } from './maven/maven-scanner.js';\nimport { GoScanner } from './go/go-scanner.js';\nimport { RubyScanner } from './ruby/ruby-scanner.js';\nimport { ComposerScanner } from './composer/composer-scanner.js';\nimport { NoLockfileError } from '../core/errors.js';\n\n/**\n * Registry of all available dependency scanners.\n * Auto-detects the correct scanner for a given project.\n */\nexport class ScannerRegistry {\n private scanners: DependencyScanner[];\n\n constructor() {\n this.scanners = [\n new NpmScanner(),\n new NugetScanner(),\n new CargoScanner(),\n new PipScanner(),\n new MavenScanner(),\n new GoScanner(),\n new RubyScanner(),\n new ComposerScanner(),\n ];\n }\n\n /**\n * Auto-detects the project's ecosystem and scans dependencies.\n * Tries each registered scanner in order until one matches.\n */\n async detectAndScan(projectPath: string): Promise<ScanResult> {\n for (const scanner of this.scanners) {\n const lockfilePath = await scanner.detect(projectPath);\n if (lockfilePath) {\n return scanner.scan(projectPath, lockfilePath);\n }\n }\n throw new NoLockfileError(projectPath);\n }\n\n /** Returns a specific scanner by ecosystem name */\n getScanner(ecosystem: string): DependencyScanner | undefined {\n return this.scanners.find((s) => s.ecosystem === ecosystem);\n }\n\n /** Lists all registered ecosystems */\n listEcosystems(): string[] {\n return this.scanners.map((s) => s.ecosystem);\n }\n}\n","import { randomUUID } from 'crypto';\nimport type { SbomGenerator } from './generator.interface.js';\nimport type { ScanResult, Sbom, SbomFormat, Dependency } from '../core/types.js';\n\n/**\n * Generates CycloneDX 1.7 JSON SBOMs.\n *\n * CycloneDX is the preferred SBOM format for CRA compliance.\n * Spec: https://cyclonedx.org/docs/1.7/json/\n *\n * NTIA minimum elements are satisfied:\n * - metadata.supplier (supplier of the root software)\n * - components[].supplier (supplier of each dependency)\n * - components[].name, version, purl, bom-ref\n * - dependencies[] graph\n */\nexport class CycloneDxGenerator implements SbomGenerator {\n readonly format: SbomFormat = 'cyclonedx-json';\n\n generate(scanResult: ScanResult, toolVersion: string = '0.1.0'): Sbom {\n const bom = this.buildBom(scanResult, toolVersion);\n const content = JSON.stringify(bom, null, 2);\n\n return {\n format: 'cyclonedx-json',\n specVersion: '1.7',\n content,\n componentCount: scanResult.dependencies.length,\n generatedAt: new Date().toISOString(),\n };\n }\n\n private buildBom(scanResult: ScanResult, toolVersion: string): CycloneDxBom {\n const projectName = this.extractProjectName(scanResult.projectPath);\n\n return {\n $schema: 'http://cyclonedx.org/schema/bom-1.7.schema.json',\n bomFormat: 'CycloneDX',\n specVersion: '1.7',\n serialNumber: `urn:uuid:${randomUUID()}`,\n version: 1,\n metadata: {\n timestamp: new Date().toISOString(),\n tools: {\n components: [\n {\n type: 'application',\n name: 'verimu',\n version: toolVersion,\n description: 'Verimu CRA Compliance Scanner',\n supplier: { name: 'Verimu' },\n externalReferences: [\n {\n type: 'website',\n url: 'https://verimu.com',\n },\n ],\n },\n ],\n },\n // NTIA: metadata.supplier — the org supplying the root software\n supplier: {\n name: projectName,\n },\n component: {\n type: 'application',\n name: projectName,\n 'bom-ref': 'root-component',\n supplier: { name: projectName },\n },\n },\n components: scanResult.dependencies.map((dep) => this.toComponent(dep)),\n dependencies: this.buildDependencyGraph(scanResult),\n };\n }\n\n /** Converts a Verimu Dependency to a CycloneDX component */\n private toComponent(dep: Dependency): CycloneDxComponent {\n return {\n type: 'library',\n name: dep.name,\n version: dep.version,\n purl: dep.purl,\n 'bom-ref': dep.purl,\n scope: dep.direct ? 'required' : 'optional',\n // NTIA: component.supplier — derived from npm scope or package name\n supplier: {\n name: this.deriveSupplierName(dep.name),\n },\n };\n }\n\n /**\n * Derives a supplier name from a package name.\n *\n * For scoped packages like \"@vue/reactivity\" → \"@vue\"\n * For unscoped packages like \"express\" → \"express\"\n *\n * This is the same heuristic used by Syft, Trivy, and other SBOM tools\n * when registry metadata (author/publisher) isn't available from the lockfile.\n */\n private deriveSupplierName(packageName: string): string {\n if (packageName.startsWith('@')) {\n // Scoped package: \"@scope/name\" → \"@scope\"\n const scope = packageName.split('/')[0];\n return scope;\n }\n return packageName;\n }\n\n /**\n * Builds the dependency graph section of the SBOM.\n *\n * The root component depends on all dependencies (direct + transitive).\n * This ensures a single root node in the graph, which NTIA validators expect.\n *\n * We include ALL deps under root (not just direct) because from a flat lockfile\n * we can't reliably reconstruct which transitive dep belongs to which direct dep.\n * This is still valid per the CycloneDX spec — it represents a complete but flat\n * dependency relationship.\n */\n private buildDependencyGraph(scanResult: ScanResult): CycloneDxDependencyEntry[] {\n const allDepPurls = scanResult.dependencies.map((d) => d.purl);\n\n return [\n {\n ref: 'root-component',\n dependsOn: allDepPurls,\n },\n ];\n }\n\n /** Extracts project name from path */\n private extractProjectName(projectPath: string): string {\n const parts = projectPath.replace(/\\\\/g, '/').split('/');\n return parts[parts.length - 1] || 'unknown-project';\n }\n}\n\n// ─── CycloneDX 1.7 JSON Types ──────────────────────────────────\n\ninterface OrganizationalEntity {\n name: string;\n url?: string[];\n contact?: Array<{ name?: string; email?: string; phone?: string }>;\n}\n\ninterface CycloneDxBom {\n $schema: string;\n bomFormat: string;\n specVersion: string;\n serialNumber: string;\n version: number;\n metadata: {\n timestamp: string;\n tools: {\n components: Array<{\n type: string;\n name: string;\n version: string;\n description?: string;\n supplier?: OrganizationalEntity;\n externalReferences?: Array<{ type: string; url: string }>;\n }>;\n };\n supplier: OrganizationalEntity;\n component: {\n type: string;\n name: string;\n 'bom-ref': string;\n supplier: OrganizationalEntity;\n };\n };\n components: CycloneDxComponent[];\n dependencies: CycloneDxDependencyEntry[];\n}\n\ninterface CycloneDxComponent {\n type: string;\n name: string;\n version: string;\n purl: string;\n 'bom-ref': string;\n scope?: string;\n supplier: OrganizationalEntity;\n}\n\ninterface CycloneDxDependencyEntry {\n ref: string;\n dependsOn: string[];\n}\n","import type { CveSource } from './source.interface.js';\nimport type { Dependency, Vulnerability, VulnerabilitySource, Severity } from '../core/types.js';\n\nconst OSV_API_BASE = 'https://api.osv.dev/v1';\nconst BATCH_SIZE = 1000; // OSV querybatch supports up to 1000\n\n/**\n * OSV.dev (Google Open Source Vulnerabilities) CVE source.\n *\n * Primary CVE source for Verimu because:\n * - Supports direct package name + ecosystem + version queries\n * - Has batch query endpoint for efficiency\n * - No authentication required\n * - Covers npm, PyPI, Go, Rust, Maven, NuGet, etc.\n * - Aggregates data from GitHub Advisory, NVD, and others\n *\n * API docs: https://google.github.io/osv.dev/api/\n *\n * Note: /v1/querybatch only returns minimal data (id, modified).\n * Full vulnerability details must be fetched via /v1/vulns/{id}.\n */\nexport class OsvSource implements CveSource {\n readonly sourceId: VulnerabilitySource = 'osv';\n readonly name = 'OSV.dev (Google Open Source Vulnerabilities)';\n\n private fetchFn: typeof fetch;\n\n constructor(fetchImpl?: typeof fetch) {\n // Allow injecting fetch for testing\n this.fetchFn = fetchImpl ?? globalThis.fetch;\n }\n\n async checkDependencies(dependencies: Dependency[]): Promise<Vulnerability[]> {\n if (dependencies.length === 0) return [];\n\n const allVulns: Vulnerability[] = [];\n\n // Process in batches of BATCH_SIZE\n for (let i = 0; i < dependencies.length; i += BATCH_SIZE) {\n const batch = dependencies.slice(i, i + BATCH_SIZE);\n const batchVulns = await this.queryBatch(batch);\n allVulns.push(...batchVulns);\n }\n\n return allVulns;\n }\n\n /**\n * Uses OSV's /querybatch endpoint to get vulnerability IDs,\n * then fetches full details for each unique vulnerability.\n */\n private async queryBatch(dependencies: Dependency[]): Promise<Vulnerability[]> {\n const queries = dependencies.map((dep) => ({\n version: dep.version,\n package: {\n name: dep.name,\n ecosystem: this.mapEcosystem(dep.ecosystem),\n },\n }));\n\n const response = await this.fetchFn(`${OSV_API_BASE}/querybatch`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ queries }),\n });\n\n if (!response.ok) {\n throw new Error(`OSV API error: ${response.status} ${response.statusText}`);\n }\n\n const data = (await response.json()) as OsvBatchResponse;\n\n // Collect unique vuln IDs and track which dependencies they affect\n const vulnIdToDeps = new Map<string, Dependency[]>();\n\n for (let i = 0; i < data.results.length; i++) {\n const result = data.results[i];\n const dep = dependencies[i];\n\n if (result.vulns && result.vulns.length > 0) {\n for (const vuln of result.vulns) {\n const existing = vulnIdToDeps.get(vuln.id);\n if (existing) {\n existing.push(dep);\n } else {\n vulnIdToDeps.set(vuln.id, [dep]);\n }\n }\n }\n }\n\n if (vulnIdToDeps.size === 0) {\n return [];\n }\n\n // Fetch full details for each unique vulnerability\n const vulnIds = Array.from(vulnIdToDeps.keys());\n const fullVulns = await this.fetchVulnerabilityDetails(vulnIds);\n\n // Map full vulnerability data to our format, linking to affected deps\n const vulnerabilities: Vulnerability[] = [];\n\n for (const osvVuln of fullVulns) {\n const affectedDeps = vulnIdToDeps.get(osvVuln.id) ?? [];\n for (const dep of affectedDeps) {\n vulnerabilities.push(this.mapVulnerability(osvVuln, dep));\n }\n }\n\n return vulnerabilities;\n }\n\n /**\n * Fetches full vulnerability details from /v1/vulns/{id} for each ID.\n * Makes parallel requests for efficiency.\n */\n private async fetchVulnerabilityDetails(vulnIds: string[]): Promise<OsvVulnerability[]> {\n const results: OsvVulnerability[] = [];\n\n // Fetch in parallel with a reasonable concurrency limit\n const CONCURRENCY = 10;\n for (let i = 0; i < vulnIds.length; i += CONCURRENCY) {\n const batch = vulnIds.slice(i, i + CONCURRENCY);\n const promises = batch.map(async (id) => {\n try {\n const response = await this.fetchFn(`${OSV_API_BASE}/vulns/${encodeURIComponent(id)}`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n // Log but don't fail the entire scan for individual vuln fetch errors\n console.warn(`Failed to fetch vulnerability ${id}: ${response.status}`);\n return null;\n }\n\n return (await response.json()) as OsvVulnerability;\n } catch (err) {\n console.warn(`Error fetching vulnerability ${id}:`, err);\n return null;\n }\n });\n\n const batchResults = await Promise.all(promises);\n results.push(...batchResults.filter((v): v is OsvVulnerability => v !== null));\n }\n\n return results;\n }\n\n /** Maps an OSV vulnerability record to our Vulnerability type */\n private mapVulnerability(osvVuln: OsvVulnerability, dep: Dependency): Vulnerability {\n const cveId = this.extractCveId(osvVuln);\n const severity = this.extractSeverity(osvVuln);\n\n return {\n id: cveId || osvVuln.id,\n aliases: Array.from(new Set([osvVuln.id, ...(osvVuln.aliases ?? [])])),\n summary: osvVuln.summary ?? osvVuln.details?.slice(0, 200) ?? 'No description available',\n severity: severity.level,\n cvssScore: severity.score,\n packageName: dep.name,\n ecosystem: dep.ecosystem,\n affectedVersionRange: this.extractAffectedRange(osvVuln, dep.name),\n fixedVersion: this.extractFixedVersion(osvVuln, dep.name),\n exploitedInWild: false, // OSV doesn't track this — CISA KEV does\n source: 'osv',\n referenceUrl: `https://osv.dev/vulnerability/${osvVuln.id}`,\n publishedAt: osvVuln.published,\n };\n }\n\n /** Extracts CVE ID from aliases (prefers CVE-xxxx over GHSA-xxxx) */\n private extractCveId(vuln: OsvVulnerability): string | null {\n // Check the main ID first\n if (vuln.id.startsWith('CVE-')) return vuln.id;\n\n // Check aliases\n if (vuln.aliases) {\n const cve = vuln.aliases.find((a) => a.startsWith('CVE-'));\n if (cve) return cve;\n }\n\n return null;\n }\n\n /** Extracts severity from CVSS scores in the OSV record */\n private extractSeverity(vuln: OsvVulnerability): { level: Severity; score?: number } {\n // Try database_specific first (often has CVSS)\n if (vuln.severity && vuln.severity.length > 0) {\n for (const sev of vuln.severity) {\n if (sev.type === 'CVSS_V3') {\n const score = this.parseCvssScore(sev.score);\n if (score !== null) {\n return { level: this.scoreToSeverity(score), score };\n }\n }\n }\n }\n\n // Try to extract from database_specific\n if (vuln.database_specific?.severity) {\n const s = vuln.database_specific.severity.toUpperCase();\n if (['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].includes(s)) {\n return { level: s as Severity };\n }\n }\n\n return { level: 'UNKNOWN' };\n }\n\n /** Parses CVSS v3 vector string to extract/calculate the base score */\n private parseCvssScore(vectorOrScore: string): number | null {\n // Could be a raw score like \"7.5\"\n const num = parseFloat(vectorOrScore);\n if (!isNaN(num) && num >= 0 && num <= 10) return num;\n\n // Parse CVSS v3.x vector string like \"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H\"\n if (vectorOrScore.startsWith('CVSS:3')) {\n return this.calculateCvss3Score(vectorOrScore);\n }\n\n return null;\n }\n\n /** Calculate CVSS v3.x base score from vector string */\n private calculateCvss3Score(vector: string): number | null {\n // CVSS v3 metric values\n const metricValues: Record<string, Record<string, number>> = {\n AV: { N: 0.85, A: 0.62, L: 0.55, P: 0.2 }, // Attack Vector\n AC: { L: 0.77, H: 0.44 }, // Attack Complexity\n PR: { // Privileges Required (varies by Scope)\n N_U: 0.85, L_U: 0.62, H_U: 0.27,\n N_C: 0.85, L_C: 0.68, H_C: 0.5,\n },\n UI: { N: 0.85, R: 0.62 }, // User Interaction\n C: { H: 0.56, L: 0.22, N: 0 }, // Confidentiality Impact\n I: { H: 0.56, L: 0.22, N: 0 }, // Integrity Impact\n A: { H: 0.56, L: 0.22, N: 0 }, // Availability Impact\n };\n\n // Parse vector string\n const parts = vector.split('/');\n const metrics: Record<string, string> = {};\n for (const part of parts) {\n const [key, value] = part.split(':');\n if (key && value) metrics[key] = value;\n }\n\n // Extract required metrics\n const av = metricValues.AV[metrics.AV];\n const ac = metricValues.AC[metrics.AC];\n const ui = metricValues.UI[metrics.UI];\n const scope = metrics.S; // 'U' = Unchanged, 'C' = Changed\n const c = metricValues.C[metrics.C];\n const i = metricValues.I[metrics.I];\n const a = metricValues.A[metrics.A];\n\n // PR depends on Scope\n const prKey = `${metrics.PR}_${scope}`;\n const pr = metricValues.PR[prKey];\n\n if ([av, ac, pr, ui, c, i, a].some((v) => v === undefined)) {\n return null; // Missing required metrics\n }\n\n // Calculate Impact Sub Score (ISS)\n const iss = 1 - (1 - c) * (1 - i) * (1 - a);\n\n // Calculate Impact\n let impact: number;\n if (scope === 'U') {\n impact = 6.42 * iss;\n } else {\n impact = 7.52 * (iss - 0.029) - 3.25 * Math.pow(iss - 0.02, 15);\n }\n\n // Calculate Exploitability\n const exploitability = 8.22 * av * ac * pr * ui;\n\n // Calculate Base Score\n if (impact <= 0) return 0;\n\n let baseScore: number;\n if (scope === 'U') {\n baseScore = Math.min(impact + exploitability, 10);\n } else {\n baseScore = Math.min(1.08 * (impact + exploitability), 10);\n }\n\n // Round up to 1 decimal place (CVSS spec)\n return Math.ceil(baseScore * 10) / 10;\n }\n\n /** Converts a CVSS score (0-10) to a severity level */\n private scoreToSeverity(score: number): Severity {\n if (score >= 9.0) return 'CRITICAL';\n if (score >= 7.0) return 'HIGH';\n if (score >= 4.0) return 'MEDIUM';\n if (score > 0.0) return 'LOW';\n return 'UNKNOWN';\n }\n\n /** Extracts affected version range for a specific package */\n private extractAffectedRange(vuln: OsvVulnerability, packageName: string): string | undefined {\n if (!vuln.affected) return undefined;\n\n for (const affected of vuln.affected) {\n if (affected.package?.name === packageName && affected.ranges) {\n for (const range of affected.ranges) {\n if (range.events) {\n const introduced = range.events.find((e) => e.introduced)?.introduced;\n const fixed = range.events.find((e) => e.fixed)?.fixed;\n if (introduced && fixed) return `>=${introduced}, <${fixed}`;\n if (introduced) return `>=${introduced}`;\n }\n }\n }\n }\n return undefined;\n }\n\n /** Extracts the fixed version for a specific package */\n private extractFixedVersion(vuln: OsvVulnerability, packageName: string): string | undefined {\n if (!vuln.affected) return undefined;\n\n for (const affected of vuln.affected) {\n if (affected.package?.name === packageName && affected.ranges) {\n for (const range of affected.ranges) {\n if (range.events) {\n const fixed = range.events.find((e) => e.fixed)?.fixed;\n if (fixed) return fixed;\n }\n }\n }\n }\n return undefined;\n }\n\n /** Maps our ecosystem names to OSV ecosystem names */\n private mapEcosystem(ecosystem: string): string {\n const map: Record<string, string> = {\n npm: 'npm',\n nuget: 'NuGet',\n cargo: 'crates.io',\n maven: 'Maven',\n pip: 'PyPI',\n go: 'Go',\n ruby: 'RubyGems',\n composer: 'Packagist',\n };\n return map[ecosystem] ?? ecosystem;\n }\n}\n\n// ─── OSV API Response Types ─────────────────────────────────────\n\n/** Response from /v1/querybatch - returns minimal vuln info (just id and modified) */\ninterface OsvBatchResponse {\n results: Array<{\n vulns?: OsvBatchVuln[];\n }>;\n}\n\n/** Minimal vulnerability info returned by /v1/querybatch */\ninterface OsvBatchVuln {\n id: string;\n modified?: string;\n}\n\n/** Full vulnerability details from /v1/vulns/{id} */\ninterface OsvVulnerability {\n id: string;\n summary?: string;\n details?: string;\n aliases?: string[];\n published?: string;\n modified?: string;\n severity?: Array<{\n type: string;\n score: string;\n }>;\n affected?: Array<{\n package?: {\n name: string;\n ecosystem: string;\n };\n ranges?: Array<{\n type: string;\n events: Array<{\n introduced?: string;\n fixed?: string;\n last_affected?: string;\n }>;\n }>;\n versions?: string[];\n }>;\n database_specific?: {\n severity?: string;\n [key: string]: unknown;\n };\n references?: Array<{\n type: string;\n url: string;\n }>;\n}\n","import type { CveSource } from './source.interface.js';\nimport type { Dependency, CveCheckResult, Vulnerability, VulnerabilitySource } from '../core/types.js';\nimport { OsvSource } from './osv.js';\n\n/**\n * Aggregates vulnerability data from multiple CVE sources.\n * Deduplicates results by CVE ID across sources.\n */\nexport class CveAggregator {\n private sources: CveSource[];\n\n constructor(sources?: CveSource[]) {\n this.sources = sources ?? [\n new OsvSource(),\n // Future: new NvdSource(), new EuvdSource(), new CisaKevSource()\n ];\n }\n\n /**\n * Checks dependencies against all registered CVE sources.\n * Runs sources in parallel and merges/deduplicates results.\n */\n async check(dependencies: Dependency[]): Promise<CveCheckResult> {\n const startTime = Date.now();\n const sourcesQueried: VulnerabilitySource[] = [];\n const sourceErrors: { source: VulnerabilitySource; error: string }[] = [];\n const allVulns: Vulnerability[] = [];\n\n // Run all sources in parallel\n const results = await Promise.allSettled(\n this.sources.map(async (source) => {\n const vulns = await source.checkDependencies(dependencies);\n return { sourceId: source.sourceId, vulns };\n })\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled') {\n sourcesQueried.push(result.value.sourceId);\n allVulns.push(...result.value.vulns);\n } else {\n // Extract the source ID from the error context\n const sourceIndex = results.indexOf(result);\n const sourceId = this.sources[sourceIndex].sourceId;\n sourceErrors.push({\n source: sourceId,\n error: result.reason instanceof Error ? result.reason.message : String(result.reason),\n });\n }\n }\n\n // Deduplicate by CVE ID (prefer the entry with more data)\n const deduplicated = this.deduplicateVulnerabilities(allVulns);\n\n return {\n vulnerabilities: deduplicated,\n sourcesQueried,\n sourceErrors,\n checkDurationMs: Date.now() - startTime,\n };\n }\n\n /**\n * Deduplicates vulnerabilities by ID.\n * When the same CVE appears from multiple sources,\n * keeps the one with more complete data (has CVSS score, has fix version, etc.)\n */\n private deduplicateVulnerabilities(vulns: Vulnerability[]): Vulnerability[] {\n const byKey = new Map<string, Vulnerability>();\n\n for (const vuln of vulns) {\n // Key by (vulnerability ID + package name) to handle the same CVE\n // affecting multiple packages\n const key = `${vuln.id}::${vuln.packageName}`;\n const existing = byKey.get(key);\n\n if (!existing) {\n byKey.set(key, vuln);\n } else {\n // Keep the one with more data\n byKey.set(key, this.pickBetterEntry(existing, vuln));\n }\n }\n\n return Array.from(byKey.values());\n }\n\n /** Picks the vulnerability entry with more complete data */\n private pickBetterEntry(a: Vulnerability, b: Vulnerability): Vulnerability {\n let scoreA = 0;\n let scoreB = 0;\n\n if (a.cvssScore !== undefined) scoreA++;\n if (b.cvssScore !== undefined) scoreB++;\n if (a.fixedVersion) scoreA++;\n if (b.fixedVersion) scoreB++;\n if (a.affectedVersionRange) scoreA++;\n if (b.affectedVersionRange) scoreB++;\n if (a.severity !== 'UNKNOWN') scoreA++;\n if (b.severity !== 'UNKNOWN') scoreB++;\n\n // Merge: start with the lesser entry, overlay with the better one.\n // Strip undefined/null values so they don't overwrite real data.\n const strip = (obj: Record<string, unknown>) =>\n Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined && v !== null));\n\n const winner = scoreB > scoreA\n ? { ...strip(a as unknown as Record<string, unknown>), ...strip(b as unknown as Record<string, unknown>) } as unknown as Vulnerability\n : { ...strip(b as unknown as Record<string, unknown>), ...strip(a as unknown as Record<string, unknown>) } as unknown as Vulnerability;\n\n // Merge aliases\n const allAliases = new Set([...a.aliases, ...b.aliases]);\n winner.aliases = Array.from(allAliases);\n\n // If either says exploited, it's exploited\n winner.exploitedInWild = a.exploitedInWild || b.exploitedInWild;\n\n return winner;\n }\n}\n","import type { Reporter } from './reporter.interface.js';\nimport type { VerimuReport, Vulnerability, Severity } from '../core/types.js';\n\n/** Outputs a human-readable console report */\nexport class ConsoleReporter implements Reporter {\n readonly name = 'console';\n\n report(result: VerimuReport): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push('┌─────────────────────────────────────────────┐');\n lines.push('│ VERIMU CRA COMPLIANCE SCAN │');\n lines.push('└─────────────────────────────────────────────┘');\n lines.push('');\n\n // Project info\n lines.push(` Project: ${result.project.path}`);\n lines.push(` Ecosystem: ${result.project.ecosystem}`);\n lines.push(` Dependencies: ${result.project.dependencyCount}`);\n lines.push(` Scanned at: ${result.generatedAt}`);\n lines.push('');\n\n // SBOM info\n lines.push(` ✓ SBOM generated (${result.sbom.format}, ${result.sbom.specVersion})`);\n lines.push(` Components: ${result.sbom.componentCount}`);\n lines.push('');\n\n // CVE results\n const vulns = result.cveCheck.vulnerabilities;\n if (vulns.length === 0) {\n lines.push(' ✓ No known vulnerabilities found');\n } else {\n lines.push(` ⚠ ${vulns.length} vulnerabilit${vulns.length === 1 ? 'y' : 'ies'} found:`);\n lines.push('');\n\n // Sort by severity: CRITICAL → HIGH → MEDIUM → LOW → UNKNOWN\n const sorted = [...vulns].sort((a, b) => severityOrder(a.severity) - severityOrder(b.severity));\n\n for (const vuln of sorted) {\n const badge = severityBadge(vuln.severity);\n const fix = vuln.fixedVersion ? ` → fix: ${vuln.fixedVersion}` : '';\n lines.push(` ${badge} ${vuln.id}`);\n lines.push(` ${vuln.packageName}@${vuln.affectedVersionRange ?? '?'}${fix}`);\n lines.push(` ${vuln.summary.slice(0, 100)}`);\n if (vuln.exploitedInWild) {\n lines.push(` 🔴 ACTIVELY EXPLOITED — 24h CRA reporting required`);\n }\n lines.push('');\n }\n }\n\n // Sources\n const sources = result.cveCheck.sourcesQueried.join(', ');\n lines.push(` Sources queried: ${sources} (${result.cveCheck.checkDurationMs}ms)`);\n\n if (result.cveCheck.sourceErrors.length > 0) {\n for (const err of result.cveCheck.sourceErrors) {\n lines.push(` ⚠ ${err.source}: ${err.error}`);\n }\n }\n\n // Summary\n lines.push('');\n lines.push(' ─── Summary ───');\n lines.push(` Total: ${result.summary.totalVulnerabilities} | ` +\n `Critical: ${result.summary.critical} | ` +\n `High: ${result.summary.high} | ` +\n `Medium: ${result.summary.medium} | ` +\n `Low: ${result.summary.low}`);\n\n if (result.summary.exploitedInWild > 0) {\n lines.push(` 🔴 ${result.summary.exploitedInWild} actively exploited — immediate action required`);\n }\n\n lines.push('');\n return lines.join('\\n');\n }\n}\n\nfunction severityOrder(s: Severity): number {\n const order: Record<Severity, number> = {\n CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3, UNKNOWN: 4,\n };\n return order[s] ?? 5;\n}\n\nfunction severityBadge(s: Severity): string {\n const badges: Record<Severity, string> = {\n CRITICAL: '[CRIT]',\n HIGH: '[HIGH]',\n MEDIUM: '[MED] ',\n LOW: '[LOW] ',\n UNKNOWN: '[???] ',\n };\n return badges[s] ?? '[???] ';\n}\n","/**\n * Verimu API client — communicates with the Verimu backend.\n *\n * Used by the CLI and scan pipeline to:\n * 1. Upsert a project (create-if-not-exists)\n * 2. Upload SBOM + trigger CVE scan\n */\n\nimport type { Ecosystem } from '../core/types.js';\n\nconst DEFAULT_API_BASE = 'https://api.verimu.com';\n\nexport interface UpsertProjectResponse {\n project: {\n id: string;\n name: string;\n ecosystem: string;\n repository_url: string | null;\n platform: string | null;\n };\n created: boolean;\n}\n\nexport interface ScanResponse {\n project: {\n id: string;\n name: string;\n };\n scan_results: Array<{\n dependency_id: string;\n dependency_name: string;\n version: string;\n vulnerabilities: Array<{\n cve_id: string;\n severity?: string | null;\n summary?: string | null;\n description?: string | null;\n fixed_version?: string | null;\n sources?: Array<{\n name?: string;\n url?: string;\n data?: {\n fixed_version?: string | null;\n } | null;\n }> | null;\n }> | null;\n }>;\n summary: {\n total_dependencies: number;\n vulnerable_dependencies: number;\n };\n}\n\nexport class VerimuApiClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiKey: string, baseUrl?: string) {\n this.apiKey = apiKey;\n this.baseUrl = (baseUrl ?? DEFAULT_API_BASE).replace(/\\/+$/, '');\n }\n\n /**\n * Upsert a project — finds by name or creates it.\n * Used so `npx verimu` auto-registers projects without manual dashboard setup.\n */\n async upsertProject(opts: {\n name: string;\n ecosystem: Ecosystem;\n repositoryUrl?: string;\n platform?: string;\n }): Promise<UpsertProjectResponse> {\n const res = await fetch(`${this.baseUrl}/api/projects/upsert`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify({\n name: opts.name,\n ecosystem: this.mapEcosystem(opts.ecosystem),\n repository_url: opts.repositoryUrl ?? null,\n platform: opts.platform ?? null,\n }),\n });\n\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Verimu API: upsert project failed (${res.status}): ${body}`);\n }\n\n return res.json() as Promise<UpsertProjectResponse>;\n }\n\n /**\n * Upload a CycloneDX SBOM to a project and trigger CVE scanning.\n */\n async uploadSbom(projectId: string, sbomContent: string): Promise<ScanResponse> {\n const sbomJson = JSON.parse(sbomContent);\n\n const res = await fetch(`${this.baseUrl}/api/projects/${projectId}/scan`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify(sbomJson),\n });\n\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Verimu API: upload SBOM failed (${res.status}): ${body}`);\n }\n\n return res.json() as Promise<ScanResponse>;\n }\n\n private headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n };\n }\n\n /**\n * Maps internal ecosystem names to what the backend expects.\n * Currently 1:1, but keeps the mapping explicit.\n */\n private mapEcosystem(eco: Ecosystem): string {\n const map: Record<Ecosystem, string> = {\n npm: 'npm',\n pip: 'pip',\n maven: 'maven',\n nuget: 'nuget',\n go: 'gomod',\n cargo: 'cargo',\n ruby: 'bundler',\n composer: 'composer',\n };\n return map[eco] ?? eco;\n }\n}\n"],"mappings":"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","names":["readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","randomUUID","severityOrder"]}
|
|
1
|
+
{"version":3,"sources":["../src/generate-sbom.ts","../src/sbom/shared.ts","../src/generate-spdx.ts","../src/generate-swid.ts","../src/scan.ts","../src/scanners/npm/npm-scanner.ts","../src/core/errors.ts","../src/scanners/nuget/nuget-scanner.ts","../src/scanners/cargo/cargo-scanner.ts","../src/scanners/pip/pip-scanner.ts","../src/scanners/maven/maven-scanner.ts","../src/scanners/go/go-scanner.ts","../src/scanners/ruby/ruby-scanner.ts","../src/scanners/composer/composer-scanner.ts","../src/scanners/yarn/yarn-scanner.ts","../src/scanners/pnpm/pnpm-scanner.ts","../src/scanners/deno/deno-scanner.ts","../src/scanners/poetry/poetry-scanner.ts","../src/scanners/uv/uv-scanner.ts","../src/scanners/registry.ts","../src/sbom/cyclonedx.ts","../src/sbom/spdx.ts","../src/sbom/swid.ts","../src/sbom/artifacts.ts","../src/cve/osv.ts","../src/cve/aggregator.ts","../src/reporters/console.ts","../src/api/client.ts"],"sourcesContent":["import { randomUUID } from 'crypto';\nimport type {\n GenerateSbomInput,\n GenerateSbomResult,\n} from './core/types.js';\nimport {\n DEFAULT_PROJECT_VERSION,\n VERIMU_TOOL_DESCRIPTION,\n VERIMU_TOOL_NAME,\n VERIMU_TOOL_WEBSITE,\n buildPurl,\n normalizeDependencies,\n} from './sbom/shared.js';\n\n/**\n * Generates an NTIA-compliant CycloneDX 1.7 SBOM from structured dependency data.\n *\n * This is a **pure function** — no filesystem access, no network calls, no side effects.\n * It takes a project name, version, and list of dependencies, and returns a complete\n * CycloneDX 1.7 JSON SBOM that passes NTIA minimum-element validation.\n *\n * @example\n * ```ts\n * import { generateSbom } from 'verimu';\n *\n * const result = generateSbom({\n * projectName: 'my-app',\n * projectVersion: '1.0.0',\n * dependencies: [\n * { name: 'express', version: '4.18.2', ecosystem: 'npm' },\n * { name: '@types/node', version: '20.11.5', ecosystem: 'npm', direct: false },\n * ],\n * });\n *\n * console.log(result.componentCount); // 2\n * console.log(result.content); // formatted JSON string\n * ```\n */\nexport function generateSbom(input: GenerateSbomInput): GenerateSbomResult {\n const {\n projectName,\n projectVersion = DEFAULT_PROJECT_VERSION,\n dependencies,\n } = input;\n\n const timestamp = new Date().toISOString();\n const resolvedDeps = normalizeDependencies(dependencies);\n\n const rootPurl = buildPurl(projectName, projectVersion, 'npm');\n\n const sbom = {\n $schema: 'http://cyclonedx.org/schema/bom-1.7.schema.json',\n bomFormat: 'CycloneDX',\n specVersion: '1.7',\n serialNumber: `urn:uuid:${randomUUID()}`,\n version: 1,\n metadata: {\n timestamp,\n tools: {\n components: [\n {\n type: 'application',\n name: VERIMU_TOOL_NAME,\n version: '0.0.1',\n description: VERIMU_TOOL_DESCRIPTION,\n supplier: { name: 'Verimu' },\n externalReferences: [\n { type: 'website', url: VERIMU_TOOL_WEBSITE },\n ],\n },\n ],\n },\n supplier: { name: projectName },\n component: {\n type: 'application',\n name: projectName,\n version: projectVersion,\n 'bom-ref': rootPurl,\n supplier: { name: projectName },\n },\n },\n components: resolvedDeps.map((dep) => ({\n type: 'library',\n name: dep.name,\n version: dep.version,\n purl: dep.purl,\n 'bom-ref': dep.purl,\n scope: dep.direct ? 'required' : 'optional',\n supplier: { name: dep.supplierName },\n })),\n dependencies: [\n {\n ref: rootPurl,\n dependsOn: resolvedDeps.map((d) => d.purl),\n },\n ],\n };\n\n const content = JSON.stringify(sbom, null, 2);\n\n return {\n sbom,\n content,\n componentCount: resolvedDeps.length,\n specVersion: '1.7',\n generatedAt: timestamp,\n };\n}\n","import type { Ecosystem } from '../core/types.js';\n\nexport const VERIMU_TOOL_NAME = 'verimu';\nexport const VERIMU_TOOL_WEBSITE = 'https://verimu.com';\nexport const VERIMU_TOOL_DESCRIPTION = 'Verimu CRA Compliance Scanner';\nexport const DEFAULT_TOOL_VERSION = '0.1.0';\nexport const DEFAULT_PROJECT_VERSION = '0.0.0';\nexport const DEFAULT_SWID_VERSION = '0.0.0';\n\ntype DependencyLike = {\n name: string;\n version: string;\n ecosystem: Ecosystem;\n direct?: boolean;\n purl?: string;\n};\n\nexport interface NormalizedDependency {\n name: string;\n version: string;\n ecosystem: Ecosystem;\n direct: boolean;\n purl: string;\n supplierName: string;\n}\n\nconst PURL_TYPE_MAP: Record<Ecosystem, string> = {\n npm: 'npm',\n nuget: 'nuget',\n cargo: 'cargo',\n maven: 'maven',\n pip: 'pypi',\n poetry: 'pypi',\n uv: 'pypi',\n go: 'golang',\n ruby: 'gem',\n composer: 'composer',\n deno: 'deno',\n};\n\n/**\n * Builds a Package URL (purl) per the purl spec.\n *\n * For npm scoped packages, the @ prefix is percent-encoded as %40:\n * @types/node@20.11.5 → pkg:npm/%40types/node@20.11.5\n */\nexport function buildPurl(name: string, version: string, ecosystem: Ecosystem): string {\n const type = PURL_TYPE_MAP[ecosystem] || ecosystem;\n\n if (ecosystem === 'npm' && name.startsWith('@')) {\n return `pkg:${type}/%40${name.slice(1)}@${version}`;\n }\n\n return `pkg:${type}/${name}@${version}`;\n}\n\n/**\n * Derives supplier name from a package name.\n * Scoped packages: \"@vue/reactivity\" → \"@vue\"\n * Unscoped packages: \"express\" → \"express\"\n */\nexport function deriveSupplierName(packageName: string): string {\n if (packageName.startsWith('@')) {\n return packageName.split('/')[0];\n }\n return packageName;\n}\n\n/** Extracts project name from a file system path */\nexport function extractProjectName(projectPath: string): string {\n const parts = projectPath.replace(/\\\\/g, '/').split('/');\n return parts[parts.length - 1] || 'unknown-project';\n}\n\n/** Normalizes dependencies so all generators work from the same shape */\nexport function normalizeDependencies(dependencies: DependencyLike[]): NormalizedDependency[] {\n return dependencies.map((dep) => ({\n name: dep.name,\n version: dep.version,\n ecosystem: dep.ecosystem,\n direct: dep.direct ?? true,\n purl: dep.purl ?? buildPurl(dep.name, dep.version, dep.ecosystem),\n supplierName: deriveSupplierName(dep.name),\n }));\n}\n\n","import { randomUUID } from 'crypto';\nimport type {\n GenerateSbomInput,\n GenerateSpdxSbomResult,\n} from './core/types.js';\nimport {\n DEFAULT_PROJECT_VERSION,\n VERIMU_TOOL_NAME,\n normalizeDependencies,\n} from './sbom/shared.js';\n\nconst SPDX_VERSION = '2.3';\n\n/**\n * Generates an SPDX 2.3 JSON document from structured dependency data.\n *\n * This is a pure function with no filesystem or network access.\n */\nexport function generateSpdxSbom(input: GenerateSbomInput): GenerateSpdxSbomResult {\n const {\n projectName,\n projectVersion = DEFAULT_PROJECT_VERSION,\n dependencies,\n } = input;\n\n const timestamp = new Date().toISOString();\n const resolvedDeps = normalizeDependencies(dependencies);\n const rootPackageId = 'SPDXRef-Package-root';\n\n const spdx = {\n spdxVersion: `SPDX-${SPDX_VERSION}`,\n dataLicense: 'CC0-1.0',\n SPDXID: 'SPDXRef-DOCUMENT',\n name: `${projectName}-sbom`,\n documentNamespace: `https://verimu.com/spdxdocs/${projectName}-${randomUUID()}`,\n creationInfo: {\n created: timestamp,\n creators: [`Tool: ${VERIMU_TOOL_NAME}`],\n },\n documentDescribes: [rootPackageId],\n packages: [\n {\n name: projectName,\n SPDXID: rootPackageId,\n versionInfo: projectVersion,\n supplier: `Organization: ${projectName}`,\n downloadLocation: 'NOASSERTION',\n filesAnalyzed: false,\n licenseConcluded: 'NOASSERTION',\n licenseDeclared: 'NOASSERTION',\n primaryPackagePurpose: 'APPLICATION',\n },\n ...resolvedDeps.map((dep, index) => ({\n name: dep.name,\n SPDXID: `SPDXRef-Package-${index + 1}`,\n versionInfo: dep.version,\n supplier: `Organization: ${dep.supplierName}`,\n downloadLocation: 'NOASSERTION',\n filesAnalyzed: false,\n licenseConcluded: 'NOASSERTION',\n licenseDeclared: 'NOASSERTION',\n primaryPackagePurpose: 'LIBRARY',\n externalRefs: [\n {\n referenceCategory: 'PACKAGE-MANAGER',\n referenceType: 'purl',\n referenceLocator: dep.purl,\n },\n ],\n })),\n ],\n relationships: [\n {\n spdxElementId: 'SPDXRef-DOCUMENT',\n relationshipType: 'DESCRIBES',\n relatedSpdxElement: rootPackageId,\n },\n ...resolvedDeps.map((_dep, index) => ({\n spdxElementId: rootPackageId,\n relationshipType: 'DEPENDS_ON',\n relatedSpdxElement: `SPDXRef-Package-${index + 1}`,\n })),\n ],\n };\n\n const content = JSON.stringify(spdx, null, 2);\n\n return {\n sbom: spdx,\n content,\n componentCount: resolvedDeps.length,\n specVersion: SPDX_VERSION,\n generatedAt: timestamp,\n };\n}\n\n","import { randomUUID } from 'crypto';\nimport type {\n GenerateSbomInput,\n GenerateSwidTagResult,\n} from './core/types.js';\nimport {\n DEFAULT_PROJECT_VERSION,\n DEFAULT_SWID_VERSION,\n VERIMU_TOOL_NAME,\n} from './sbom/shared.js';\n\nconst SWID_SPEC_VERSION = 'ISO/IEC 19770-2:2015';\n\n/**\n * Generates a minimal SWID XML tag for the root software product.\n *\n * This is intentionally minimal for v1. The current tag describes the root\n * product identity and generator metadata only.\n */\nexport function generateSwidTag(input: GenerateSbomInput): GenerateSwidTagResult {\n const {\n projectName,\n projectVersion = DEFAULT_PROJECT_VERSION,\n } = input;\n\n const timestamp = new Date().toISOString();\n const tagVersion = 1;\n const normalizedVersion = projectVersion || DEFAULT_SWID_VERSION;\n const tagId = `com.verimu:${sanitizeTagId(projectName)}:${normalizedVersion}:${randomUUID()}`;\n\n const tag = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n '<SoftwareIdentity',\n ' xmlns=\"http://standards.iso.org/iso/19770/-2/2015/schema.xsd\"',\n ` name=\"${escapeXml(projectName)}\"`,\n ` tagId=\"${escapeXml(tagId)}\"`,\n ` tagVersion=\"${tagVersion}\"`,\n ` version=\"${escapeXml(normalizedVersion)}\"`,\n ' versionScheme=\"semver\">',\n ` <Entity name=\"${escapeXml(projectName)}\" role=\"softwareCreator\" />`,\n ' <Entity name=\"Verimu\" role=\"tagCreator\" />',\n ` <Meta product=\"${escapeXml(projectName)}\" generator=\"${VERIMU_TOOL_NAME}\" generated=\"${timestamp}\" />`,\n ' <!-- TODO: Consider adding dependency/package evidence if we need richer SWID coverage. -->',\n ' <Link rel=\"describedby\" href=\"https://verimu.com\" />',\n '</SoftwareIdentity>',\n ].join('\\n');\n\n return {\n tag,\n content: tag,\n componentCount: 1,\n specVersion: SWID_SPEC_VERSION,\n generatedAt: timestamp,\n };\n}\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nfunction sanitizeTagId(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n","import { writeFile } from 'fs/promises';\nimport { basename, join, parse } from 'path';\nimport { ScannerRegistry } from './scanners/registry.js';\nimport { generateSbomArtifacts } from './sbom/artifacts.js';\nimport { CveAggregator } from './cve/aggregator.js';\nimport { ConsoleReporter } from './reporters/console.js';\nimport { VerimuApiClient } from './api/client.js';\nimport type { ScanResponse } from './api/client.js';\nimport type { VerimuConfig, VerimuReport, Severity } from './core/types.js';\n\n/** Result of uploading scan results to the Verimu platform */\nexport interface UploadResult {\n projectId: string;\n projectCreated: boolean;\n totalDependencies: number;\n vulnerableDependencies: number;\n dashboardUrl: string;\n scanResponse: ScanResponse;\n}\n\n/**\n * Main scan pipeline — orchestrates the full Verimu workflow:\n * 1. Detect ecosystem & parse lockfile\n * 2. Generate software inventory artifacts (CycloneDX + SPDX + SWID)\n * 3. Check dependencies for CVEs (via OSV)\n * 4. Produce report\n * 5. Upload to Verimu platform (if API key provided)\n */\nexport async function scan(config: VerimuConfig): Promise<VerimuReport> {\n const {\n projectPath,\n sbomOutput = './sbom.cdx.json',\n skipCveCheck = false,\n cyclonedxVersion = '1.7',\n } = config;\n\n // 1. Scan dependencies\n const registry = new ScannerRegistry();\n const scanResult = await registry.detectAndScan(projectPath);\n\n // 2. Generate all supported artifacts\n const artifacts = generateSbomArtifacts(scanResult, undefined, cyclonedxVersion);\n const sbom = artifacts.cyclonedx;\n\n // 3. Write artifacts to disk\n const outputPaths = deriveArtifactOutputPaths(sbomOutput);\n await Promise.all([\n writeFile(outputPaths.cyclonedx, artifacts.cyclonedx.content, 'utf-8'),\n writeFile(outputPaths.spdx, artifacts.spdx.content, 'utf-8'),\n writeFile(outputPaths.swid, artifacts.swid.content, 'utf-8'),\n ]);\n\n // 4. Check CVEs (unless skipped)\n let cveCheck;\n if (skipCveCheck) {\n cveCheck = {\n vulnerabilities: [],\n sourcesQueried: [],\n sourceErrors: [],\n checkDurationMs: 0,\n };\n } else {\n const aggregator = new CveAggregator();\n cveCheck = await aggregator.check(scanResult.dependencies);\n }\n\n // 5. Build report\n const summary = {\n totalDependencies: scanResult.dependencies.length,\n totalVulnerabilities: cveCheck.vulnerabilities.length,\n critical: cveCheck.vulnerabilities.filter((v) => v.severity === 'CRITICAL').length,\n high: cveCheck.vulnerabilities.filter((v) => v.severity === 'HIGH').length,\n medium: cveCheck.vulnerabilities.filter((v) => v.severity === 'MEDIUM').length,\n low: cveCheck.vulnerabilities.filter((v) => v.severity === 'LOW').length,\n exploitedInWild: cveCheck.vulnerabilities.filter((v) => v.exploitedInWild).length,\n };\n\n const report: VerimuReport = {\n project: {\n path: projectPath,\n ecosystem: scanResult.ecosystem,\n dependencyCount: scanResult.dependencies.length,\n },\n sbom,\n artifacts,\n cveCheck,\n summary,\n generatedAt: new Date().toISOString(),\n };\n\n // 6. Upload to Verimu platform (if API key provided)\n if (config.apiKey) {\n try {\n const uploadResult = await uploadToVerimu(report, config);\n (report as VerimuReport & { upload?: UploadResult }).upload = uploadResult;\n } catch {\n // Upload failure should not break the scan — log but continue\n // The CLI will handle displaying the error\n }\n }\n\n return report;\n}\n\n/**\n * Uploads scan results to the Verimu platform.\n *\n * 1. Upserts the project (create-if-not-exists by name)\n * 2. POSTs the artifact bundle for dependency tracking + CVE scanning\n */\nexport async function uploadToVerimu(\n report: VerimuReport,\n config: VerimuConfig\n): Promise<UploadResult> {\n if (!config.apiKey) {\n throw new Error('API key required for upload');\n }\n\n const client = new VerimuApiClient(config.apiKey, config.apiBaseUrl);\n\n // Derive project name from the directory\n const projectName = basename(config.projectPath);\n\n // 1. Upsert project\n const upsertRes = await client.upsertProject({\n name: projectName,\n ecosystem: report.project.ecosystem,\n });\n\n const projectId = upsertRes.project.id;\n\n // 2. Upload software inventory artifacts\n const scanRes = await client.uploadSbom(projectId, buildUploadPayload(report));\n\n return {\n projectId,\n projectCreated: upsertRes.created,\n totalDependencies: scanRes.summary.total_dependencies,\n vulnerableDependencies: scanRes.summary.vulnerable_dependencies,\n dashboardUrl: `https://app.verimu.com/dashboard/projects/${projectId}`,\n scanResponse: scanRes,\n };\n}\n\n/**\n * Determines if the scan should fail CI based on severity threshold.\n */\nexport function shouldFailCi(report: VerimuReport, threshold: Severity): boolean {\n const severityOrder: Record<Severity, number> = {\n CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3, UNKNOWN: 4,\n };\n const thresholdLevel = severityOrder[threshold] ?? 4;\n\n return report.cveCheck.vulnerabilities.some(\n (v) => severityOrder[v.severity] <= thresholdLevel\n );\n}\n\n/**\n * Prints a console report to stdout.\n */\nexport function printReport(report: VerimuReport): void {\n const reporter = new ConsoleReporter();\n console.log(reporter.report(report));\n}\n\nfunction deriveArtifactOutputPaths(cycloneDxOutput: string): {\n cyclonedx: string;\n spdx: string;\n swid: string;\n} {\n const parsed = parse(cycloneDxOutput);\n let baseName = parsed.name;\n\n if (parsed.ext === '.json' && baseName.endsWith('.cdx')) {\n baseName = baseName.slice(0, -4);\n }\n\n return {\n cyclonedx: cycloneDxOutput,\n spdx: join(parsed.dir, `${baseName}.spdx.json`),\n swid: join(parsed.dir, `${baseName}.swid.xml`),\n };\n}\n\nfunction buildUploadPayload(report: VerimuReport): string | {\n cyclonedx: Record<string, unknown>;\n spdx: Record<string, unknown>;\n swid: string;\n} {\n if (!report.artifacts) {\n return report.sbom.content;\n }\n\n return {\n cyclonedx: JSON.parse(report.artifacts.cyclonedx.content) as Record<string, unknown>,\n spdx: JSON.parse(report.artifacts.spdx.content) as Record<string, unknown>,\n swid: report.artifacts.swid.content,\n };\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * npm / Node.js dependency scanner.\n *\n * Parses package-lock.json (v2/v3 format) to extract the full\n * resolved dependency tree. Also reads package.json to determine\n * which dependencies are direct vs transitive.\n */\nexport class NpmScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'npm';\n readonly lockfileNames = ['package-lock.json'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'package-lock.json');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockfileRaw, packageJsonRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'package.json'), 'utf-8').catch(() => null),\n ]);\n\n let lockfile: NpmLockfile;\n try {\n lockfile = JSON.parse(lockfileRaw);\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON');\n }\n\n // Determine direct dependency names from package.json\n const directNames = new Set<string>();\n if (packageJsonRaw) {\n try {\n const pkg = JSON.parse(packageJsonRaw);\n for (const name of Object.keys(pkg.dependencies ?? {})) {\n directNames.add(name);\n }\n for (const name of Object.keys(pkg.devDependencies ?? {})) {\n directNames.add(name);\n }\n } catch {\n // If package.json can't be parsed, all deps are \"unknown\" direct status\n }\n }\n\n const dependencies = this.parseLockfile(lockfile, directNames);\n\n return {\n projectPath,\n ecosystem: 'npm',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses package-lock.json and extracts dependencies.\n * Supports lockfile v2 and v3 (uses the `packages` field).\n * Falls back to `dependencies` field for lockfile v1.\n */\n private parseLockfile(lockfile: NpmLockfile, directNames: Set<string>): Dependency[] {\n const deps: Dependency[] = [];\n\n if (lockfile.packages) {\n // Lockfile v2/v3: `packages` is a flat map of \"node_modules/name\" → info\n for (const [pkgPath, pkgInfo] of Object.entries(lockfile.packages)) {\n // Skip the root package (empty string key)\n if (pkgPath === '') continue;\n\n // Extract package name from the path\n // e.g., \"node_modules/express\" → \"express\"\n // e.g., \"node_modules/@types/node\" → \"@types/node\"\n const name = this.extractPackageName(pkgPath);\n if (!name || !pkgInfo.version) continue;\n\n // Skip link: true entries (workspace references)\n if (pkgInfo.link) continue;\n\n deps.push({\n name,\n version: pkgInfo.version,\n direct: directNames.has(name),\n ecosystem: 'npm',\n purl: this.buildPurl(name, pkgInfo.version),\n });\n }\n } else if (lockfile.dependencies) {\n // Lockfile v1 fallback: `dependencies` is a nested tree\n this.parseDependenciesV1(lockfile.dependencies, directNames, deps);\n }\n\n return deps;\n }\n\n /**\n * Builds a purl (Package URL) for an npm package.\n *\n * Per the purl spec (https://github.com/package-url/purl-spec/blob/main/types-doc/npm-definition.md):\n * \"The npm scope @ sign prefix is always percent encoded.\"\n *\n * So @types/node@20.11.5 → pkg:npm/%40types/node@20.11.5\n * And express@4.18.2 → pkg:npm/express@4.18.2\n */\n private buildPurl(name: string, version: string): string {\n if (name.startsWith('@')) {\n // Scoped: encode the @ as %40 per purl spec\n return `pkg:npm/%40${name.slice(1)}@${version}`;\n }\n return `pkg:npm/${name}@${version}`;\n }\n\n /** Extracts the package name from a node_modules path */\n private extractPackageName(pkgPath: string): string | null {\n // \"node_modules/@scope/name\" → \"@scope/name\"\n // \"node_modules/name\" → \"name\"\n // \"node_modules/a/node_modules/b\" → \"b\" (nested)\n const parts = pkgPath.split('node_modules/');\n const last = parts[parts.length - 1];\n return last || null;\n }\n\n /** Recursively parses lockfile v1 `dependencies` tree */\n private parseDependenciesV1(\n depsObj: Record<string, NpmLockfileV1Dep>,\n directNames: Set<string>,\n result: Dependency[]\n ): void {\n for (const [name, info] of Object.entries(depsObj)) {\n if (info.version) {\n result.push({\n name,\n version: info.version,\n direct: directNames.has(name),\n ecosystem: 'npm',\n purl: this.buildPurl(name, info.version),\n });\n }\n // Recurse into nested dependencies\n if (info.dependencies) {\n this.parseDependenciesV1(info.dependencies, directNames, result);\n }\n }\n }\n}\n\n// ─── Types for package-lock.json parsing ─────────────────────────\n\ninterface NpmLockfile {\n name?: string;\n version?: string;\n lockfileVersion?: number;\n packages?: Record<string, NpmLockfilePackage>;\n dependencies?: Record<string, NpmLockfileV1Dep>;\n}\n\ninterface NpmLockfilePackage {\n version?: string;\n resolved?: string;\n integrity?: string;\n dev?: boolean;\n optional?: boolean;\n link?: boolean;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n}\n\ninterface NpmLockfileV1Dep {\n version?: string;\n resolved?: string;\n integrity?: string;\n requires?: Record<string, string>;\n dependencies?: Record<string, NpmLockfileV1Dep>;\n}\n","/** Base error for all Verimu errors */\nexport class VerimuError extends Error {\n constructor(message: string, public readonly code: string) {\n super(message);\n this.name = 'VerimuError';\n }\n}\n\n/** Thrown when no supported lockfile is found */\nexport class NoLockfileError extends VerimuError {\n constructor(projectPath: string) {\n super(\n `No supported lockfile found in ${projectPath}. ` +\n `Supported: package-lock.json (npm), packages.lock.json (NuGet), ` +\n `Cargo.lock (Rust), requirements.txt / Pipfile.lock (pip), poetry.lock (Poetry), uv.lock (uv), pom.xml (Maven), go.sum (Go), Gemfile.lock (Ruby), composer.lock (Composer), yarn.lock (Yarn), pnpm-lock.yaml (pnpm)`,\n 'NO_LOCKFILE'\n );\n this.name = 'NoLockfileError';\n }\n}\n\n/** Thrown when lockfile parsing fails */\nexport class LockfileParseError extends VerimuError {\n constructor(lockfilePath: string, reason: string) {\n super(`Failed to parse ${lockfilePath}: ${reason}`, 'LOCKFILE_PARSE_ERROR');\n this.name = 'LockfileParseError';\n }\n}\n\n/** Thrown when a CVE source query fails */\nexport class CveSourceError extends VerimuError {\n constructor(source: string, reason: string) {\n super(`CVE source \"${source}\" failed: ${reason}`, 'CVE_SOURCE_ERROR');\n this.name = 'CveSourceError';\n }\n}\n\n/** Thrown when API key is required but missing */\nexport class ApiKeyRequiredError extends VerimuError {\n constructor(feature: string) {\n super(\n `API key required for \"${feature}\". Get one at https://verimu.com/dashboard`,\n 'API_KEY_REQUIRED'\n );\n this.name = 'ApiKeyRequiredError';\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * C# / NuGet dependency scanner.\n *\n * Parses `packages.lock.json` (NuGet lock file, generated by\n * `dotnet restore --use-lock-file`) to extract the full resolved\n * dependency tree. The lock file itself tracks Direct vs Transitive.\n *\n * Lock file format (NuGet v1):\n * ```json\n * {\n * \"version\": 1,\n * \"dependencies\": {\n * \"net8.0\": {\n * \"PackageName\": {\n * \"type\": \"Direct\" | \"Transitive\",\n * \"resolved\": \"13.0.3\",\n * \"contentHash\": \"...\"\n * }\n * }\n * }\n * }\n * ```\n */\nexport class NugetScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'nuget';\n readonly lockfileNames = ['packages.lock.json'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'packages.lock.json');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const lockfileRaw = await readFile(lockfilePath, 'utf-8');\n\n let lockfile: NugetLockfile;\n try {\n lockfile = JSON.parse(lockfileRaw);\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON');\n }\n\n if (!lockfile.dependencies) {\n throw new LockfileParseError(lockfilePath, 'Missing \"dependencies\" field');\n }\n\n const dependencies = this.parseLockfile(lockfile);\n\n return {\n projectPath,\n ecosystem: 'nuget',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses packages.lock.json and extracts dependencies across all\n * target frameworks. Deduplicates by package name (keeps highest version\n * if the same package appears under multiple frameworks).\n */\n private parseLockfile(lockfile: NugetLockfile): Dependency[] {\n const depMap = new Map<string, Dependency>();\n\n for (const [_framework, packages] of Object.entries(lockfile.dependencies)) {\n for (const [name, info] of Object.entries(packages)) {\n if (!info.resolved) continue;\n\n // \"Direct\" in the lock file means it's a PackageReference in .csproj\n const isDirect = info.type === 'Direct';\n\n const existing = depMap.get(name);\n if (!existing) {\n depMap.set(name, {\n name,\n version: info.resolved,\n direct: isDirect,\n ecosystem: 'nuget',\n purl: this.buildPurl(name, info.resolved),\n });\n }\n // If already seen, keep the direct flag if either occurrence is direct\n else if (isDirect && !existing.direct) {\n existing.direct = true;\n }\n }\n }\n\n return Array.from(depMap.values());\n }\n\n /**\n * Builds a purl for a NuGet package.\n * NuGet purls are straightforward: pkg:nuget/Name@Version\n */\n private buildPurl(name: string, version: string): string {\n return `pkg:nuget/${name}@${version}`;\n }\n}\n\n// ─── Types for packages.lock.json parsing ────────────────────────\n\ninterface NugetLockfile {\n version?: number;\n dependencies: Record<string, Record<string, NugetLockfileEntry>>;\n}\n\ninterface NugetLockfileEntry {\n type?: 'Direct' | 'Transitive';\n resolved?: string;\n contentHash?: string;\n dependencies?: Record<string, string>;\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Rust / Cargo dependency scanner.\n *\n * Parses `Cargo.lock` (TOML format) to extract the full resolved\n * dependency tree. Reads `Cargo.toml` to determine which packages\n * are direct dependencies vs transitive.\n *\n * Cargo.lock format (v3):\n * ```toml\n * [[package]]\n * name = \"serde\"\n * version = \"1.0.195\"\n * source = \"registry+https://github.com/rust-lang/crates.io-index\"\n * checksum = \"abc123...\"\n * dependencies = [\n * \"serde_derive\",\n * ]\n * ```\n *\n * Note: We use a simple TOML parser since Cargo.lock has a very\n * regular structure (just [[package]] entries). No need for a full\n * TOML library.\n */\nexport class CargoScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'cargo';\n readonly lockfileNames = ['Cargo.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'Cargo.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockfileRaw, cargoTomlRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'Cargo.toml'), 'utf-8').catch(() => null),\n ]);\n\n const packages = this.parseLockfile(lockfileRaw, lockfilePath);\n const directNames = cargoTomlRaw ? this.parseCargoToml(cargoTomlRaw) : new Set<string>();\n\n // The first [[package]] is typically the root project — skip it\n const rootName = packages.length > 0 ? packages[0].name : null;\n\n const dependencies: Dependency[] = [];\n for (const pkg of packages) {\n // Skip the root project itself\n if (pkg.name === rootName && pkg.source === undefined) continue;\n\n dependencies.push({\n name: pkg.name,\n version: pkg.version,\n direct: directNames.has(pkg.name),\n ecosystem: 'cargo',\n purl: this.buildPurl(pkg.name, pkg.version),\n });\n }\n\n return {\n projectPath,\n ecosystem: 'cargo',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses Cargo.lock by splitting on [[package]] blocks.\n * This is a lightweight parser that handles the regular structure\n * of Cargo.lock without needing a full TOML parser.\n */\n private parseLockfile(content: string, lockfilePath: string): CargoPackage[] {\n const packages: CargoPackage[] = [];\n const blocks = content.split(/^\\[\\[package\\]\\]$/m);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const name = this.extractField(block, 'name');\n const version = this.extractField(block, 'version');\n const source = this.extractField(block, 'source');\n\n if (name && version) {\n packages.push({ name, version, source: source || undefined });\n }\n }\n\n if (packages.length === 0 && content.includes('[[package]]')) {\n throw new LockfileParseError(lockfilePath, 'Failed to parse any packages from Cargo.lock');\n }\n\n return packages;\n }\n\n /**\n * Extracts a string field value from a TOML block.\n * Handles: `name = \"value\"` format.\n */\n private extractField(block: string, fieldName: string): string | null {\n const regex = new RegExp(`^${fieldName}\\\\s*=\\\\s*\"([^\"]*)\"`, 'm');\n const match = block.match(regex);\n return match ? match[1] : null;\n }\n\n /**\n * Parses Cargo.toml to extract direct dependency names.\n * Looks for [dependencies] and [dev-dependencies] sections.\n */\n private parseCargoToml(content: string): Set<string> {\n const directNames = new Set<string>();\n let inDepsSection = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n // Detect section headers\n if (line.startsWith('[')) {\n inDepsSection =\n line === '[dependencies]' ||\n line === '[dev-dependencies]' ||\n line === '[build-dependencies]';\n continue;\n }\n\n if (inDepsSection && line && !line.startsWith('#')) {\n // Extract package name from \"name = ...\" or \"name = { version = ... }\"\n const match = line.match(/^([a-zA-Z0-9_-]+)\\s*=/);\n if (match) {\n directNames.add(match[1]);\n }\n }\n }\n\n return directNames;\n }\n\n /**\n * Builds a purl for a Cargo (crates.io) package.\n */\n private buildPurl(name: string, version: string): string {\n return `pkg:cargo/${name}@${version}`;\n }\n}\n\n// ─── Internal types ──────────────────────────────────────────────\n\ninterface CargoPackage {\n name: string;\n version: string;\n source?: string;\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Python / pip dependency scanner.\n *\n * Supports multiple Python dependency file formats (in priority order):\n * 1. `requirements.txt` — flat list of pinned dependencies\n * 2. `Pipfile.lock` — Pipenv lock file with exact versions\n *\n * For `requirements.txt`, all listed packages are treated as direct\n * dependencies (the file doesn't distinguish direct vs transitive).\n * For `Pipfile.lock`, `default` packages are direct and `develop`\n * packages are dev dependencies.\n *\n * Limitation: `requirements.txt` doesn't capture transitive deps unless\n * generated with `pip freeze`. If using `pip freeze` output, all deps\n * are listed but we can't distinguish direct vs transitive.\n */\nexport class PipScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'pip';\n readonly lockfileNames = ['Pipfile.lock', 'requirements.txt'];\n\n async detect(projectPath: string): Promise<string | null> {\n // Check in priority order\n for (const lockfile of this.lockfileNames) {\n const fullPath = path.join(projectPath, lockfile);\n if (existsSync(fullPath)) return fullPath;\n }\n return null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const raw = await readFile(lockfilePath, 'utf-8');\n const filename = path.basename(lockfilePath);\n\n let dependencies: Dependency[];\n\n if (filename === 'Pipfile.lock') {\n dependencies = this.parsePipfileLock(raw, lockfilePath);\n } else {\n dependencies = await this.parseRequirementsTxt(raw, lockfilePath);\n }\n\n return {\n projectPath,\n ecosystem: 'pip',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses `requirements.txt` format.\n *\n * Supports:\n * - `package==1.2.3` (pinned) — REQUIRED\n * - Comments (`#`) and blank lines are skipped\n * - `-r other-file.txt` (include directive) — recursively parsed\n * - `--index-url` and other pip flags — skipped\n *\n * Throws if any dependency is not strictly pinned with ==.\n * Use `pip freeze` to generate a properly pinned requirements.txt.\n */\n private async parseRequirementsTxt(\n content: string,\n lockfilePath: string,\n visited: Set<string> = new Set()\n ): Promise<Dependency[]> {\n const deps: Dependency[] = [];\n const currentDir = path.dirname(lockfilePath);\n const normalizedPath = path.resolve(lockfilePath);\n\n // Prevent circular includes\n if (visited.has(normalizedPath)) {\n return deps;\n }\n visited.add(normalizedPath);\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n // Skip comments and blank lines\n if (!line || line.startsWith('#')) {\n continue;\n }\n\n // Handle -r / --requirement includes\n const includeMatch = line.match(/^-r\\s+(.+)$/) || line.match(/^--requirement\\s+(.+)$/);\n if (includeMatch) {\n const includePath = path.resolve(currentDir, includeMatch[1].trim());\n if (existsSync(includePath)) {\n const includeContent = await readFile(includePath, 'utf-8');\n const includedDeps = await this.parseRequirementsTxt(includeContent, includePath, visited);\n deps.push(...includedDeps);\n }\n continue;\n }\n\n // Skip other pip flags (--index-url, -e, etc.)\n if (line.startsWith('-') || line.startsWith('--')) {\n continue;\n }\n\n // Parse strictly pinned \"package==version\" only\n const pinnedMatch = line.match(/^([a-zA-Z0-9_][a-zA-Z0-9._-]*)\\s*==\\s*([^,\\s]+)$/);\n if (pinnedMatch) {\n const [, name, version] = pinnedMatch;\n if (name && version) {\n deps.push({\n name: this.normalizePipName(name),\n version,\n direct: true, // requirements.txt doesn't distinguish\n ecosystem: 'pip',\n purl: this.buildPurl(name, version),\n });\n }\n continue;\n }\n\n // Check if it's a dependency line with non-pinned version specifier\n const depMatch = line.match(/^([a-zA-Z0-9_][a-zA-Z0-9._-]*)\\s*([~=!<>].*)$/);\n if (depMatch) {\n throw new LockfileParseError(\n lockfilePath,\n `Non-pinned dependency detected: \"${line}\". Use pip freeze or Pipfile.lock.`\n );\n }\n }\n\n return deps;\n }\n\n /**\n * Parses `Pipfile.lock` (JSON format from Pipenv).\n *\n * Structure:\n * ```json\n * {\n * \"_meta\": { ... },\n * \"default\": {\n * \"requests\": { \"version\": \"==2.31.0\", ... }\n * },\n * \"develop\": {\n * \"pytest\": { \"version\": \"==7.4.0\", ... }\n * }\n * }\n * ```\n */\n private parsePipfileLock(content: string, lockfilePath: string): Dependency[] {\n let lockfile: PipfileLock;\n try {\n lockfile = JSON.parse(content);\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON in Pipfile.lock');\n }\n\n const deps: Dependency[] = [];\n\n // Parse \"default\" (production) dependencies\n if (lockfile.default) {\n for (const [name, info] of Object.entries(lockfile.default)) {\n const version = info.version?.replace(/^==/, '') ?? '';\n if (version) {\n deps.push({\n name: this.normalizePipName(name),\n version,\n direct: true,\n ecosystem: 'pip',\n purl: this.buildPurl(name, version),\n });\n }\n }\n }\n\n // Parse \"develop\" dependencies\n if (lockfile.develop) {\n for (const [name, info] of Object.entries(lockfile.develop)) {\n const version = info.version?.replace(/^==/, '') ?? '';\n if (version) {\n deps.push({\n name: this.normalizePipName(name),\n version,\n direct: true,\n ecosystem: 'pip',\n purl: this.buildPurl(name, version),\n });\n }\n }\n }\n\n return deps;\n }\n\n /**\n * Normalizes a pip package name per PEP 503.\n * Converts to lowercase and replaces any run of [-_.] with a single hyphen.\n */\n private normalizePipName(name: string): string {\n return name.toLowerCase().replace(/[-_.]+/g, '-');\n }\n\n /**\n * Builds a purl for a PyPI package.\n * Per purl spec, the type is \"pypi\" (not \"pip\").\n */\n private buildPurl(name: string, version: string): string {\n return `pkg:pypi/${this.normalizePipName(name)}@${version}`;\n }\n}\n\n// ─── Types for Pipfile.lock parsing ──────────────────────────────\n\ninterface PipfileLock {\n _meta?: Record<string, unknown>;\n default?: Record<string, PipfileLockEntry>;\n develop?: Record<string, PipfileLockEntry>;\n}\n\ninterface PipfileLockEntry {\n version?: string;\n hashes?: string[];\n markers?: string;\n index?: string;\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { execSync } from 'child_process';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Java / Maven dependency scanner.\n *\n * Maven doesn't have a lockfile. This scanner uses two strategies:\n *\n * 1. **Primary (auto)**: If `mvn` is on `$PATH`, runs\n * `mvn dependency:list -DoutputType=text` to get the resolved\n * dependency tree including transitive dependencies.\n *\n * 2. **Fallback (pre-generated)**: Looks for a `dependency-tree.txt`\n * file in the project root. Users can generate this with:\n * ```\n * mvn dependency:list -DoutputFile=dependency-tree.txt -DoutputType=text\n * ```\n *\n * The scanner detects a Maven project by the presence of `pom.xml`.\n *\n * Maven dependency:list output format (one per line):\n * ```\n * com.google.guava:guava:jar:32.1.3-jre:compile\n * org.slf4j:slf4j-api:jar:2.0.9:compile\n * junit:junit:jar:4.13.2:test\n * ```\n * Fields: groupId:artifactId:type:version:scope\n * With classifier: groupId:artifactId:type:classifier:version:scope\n */\nexport class MavenScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'maven';\n readonly lockfileNames = ['pom.xml'];\n\n /** Allow injection for testing */\n private execSyncFn: typeof execSync;\n\n constructor(execSyncImpl?: typeof execSync) {\n this.execSyncFn = execSyncImpl ?? execSync;\n }\n\n async detect(projectPath: string): Promise<string | null> {\n const pomPath = path.join(projectPath, 'pom.xml');\n return existsSync(pomPath) ? pomPath : null;\n }\n\n async scan(projectPath: string, _lockfilePath: string): Promise<ScanResult> {\n // Read pom.xml to determine direct dependencies\n const pomPath = path.join(projectPath, 'pom.xml');\n const pomContent = await readFile(pomPath, 'utf-8').catch(() => null);\n const directDeps = pomContent ? this.parsePomDependencies(pomContent) : new Set<string>();\n\n // Strategy 1: Try pre-generated dependency-tree.txt\n const depTreePath = path.join(projectPath, 'dependency-tree.txt');\n if (existsSync(depTreePath)) {\n const content = await readFile(depTreePath, 'utf-8');\n const dependencies = this.parseDependencyList(content, directDeps);\n return this.buildResult(projectPath, depTreePath, dependencies);\n }\n\n // Strategy 2: Try running `mvn dependency:list`\n if (this.isMavenAvailable()) {\n const output = this.runMavenDependencyList(projectPath);\n const dependencies = this.parseDependencyList(output, directDeps);\n return this.buildResult(projectPath, pomPath, dependencies);\n }\n\n throw new LockfileParseError(\n pomPath,\n 'Maven project detected (pom.xml found) but could not resolve dependencies. ' +\n 'Either install Maven (`mvn` must be on $PATH) or pre-generate a dependency list:\\n' +\n ' mvn dependency:list -DoutputFile=dependency-tree.txt -DappendOutput=true'\n );\n }\n\n /**\n * Parses pom.xml to extract direct dependency coordinates (groupId:artifactId).\n * This is a simple regex-based parser that handles standard dependency declarations.\n */\n private parsePomDependencies(pomContent: string): Set<string> {\n const directDeps = new Set<string>();\n\n // Match <dependency> blocks and extract groupId + artifactId\n // This regex handles multi-line dependency declarations\n const depBlockRegex = /<dependency>\\s*([\\s\\S]*?)<\\/dependency>/g;\n const groupIdRegex = /<groupId>\\s*([^<]+)\\s*<\\/groupId>/;\n const artifactIdRegex = /<artifactId>\\s*([^<]+)\\s*<\\/artifactId>/;\n\n let match;\n while ((match = depBlockRegex.exec(pomContent)) !== null) {\n const block = match[1];\n const groupMatch = block.match(groupIdRegex);\n const artifactMatch = block.match(artifactIdRegex);\n\n if (groupMatch && artifactMatch) {\n const groupId = groupMatch[1].trim();\n const artifactId = artifactMatch[1].trim();\n directDeps.add(`${groupId}:${artifactId}`);\n }\n }\n\n return directDeps;\n }\n\n /**\n * Parses Maven `dependency:list` output.\n *\n * Each dependency line has the format:\n * groupId:artifactId:type:version:scope (5 parts)\n * groupId:artifactId:type:classifier:version:scope (6 parts)\n *\n * Lines are typically indented with leading whitespace.\n */\n private parseDependencyList(content: string, directDeps: Set<string>): Dependency[] {\n const deps: Dependency[] = [];\n const seen = new Set<string>();\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n if (!line) continue;\n\n // Split by colon - Maven coords use : as separator\n const parts = line.split(':');\n\n // Need at least 5 parts: groupId:artifactId:type:version:scope\n if (parts.length < 5) continue;\n\n const groupId = parts[0];\n const artifactId = parts[1];\n // parts[2] = type (jar, war, pom, etc.)\n\n // Handle optional classifier (6 parts) vs no classifier (5 parts)\n let version: string;\n let scope: string;\n\n if (parts.length >= 6) {\n // With classifier: groupId:artifactId:type:classifier:version:scope\n version = parts[4];\n scope = parts[5];\n } else {\n // Without classifier: groupId:artifactId:type:version:scope\n version = parts[3];\n scope = parts[4];\n }\n\n // Skip invalid entries\n if (!groupId || !artifactId || !version) continue;\n\n // Skip test-scoped dependencies\n if (scope === 'test') continue;\n\n const name = `${groupId}:${artifactId}`;\n\n // Deduplicate (same dep might appear multiple times)\n if (seen.has(name)) continue;\n seen.add(name);\n\n // Determine if direct by checking against pom.xml dependencies\n const isDirect = directDeps.has(name);\n\n deps.push({\n name,\n version,\n direct: isDirect,\n ecosystem: 'maven',\n purl: this.buildPurl(groupId, artifactId, version),\n });\n }\n\n return deps;\n }\n\n /** Checks if `mvn` is available on PATH */\n private isMavenAvailable(): boolean {\n try {\n this.execSyncFn('mvn --version', { stdio: 'pipe', timeout: 10_000 });\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Runs `mvn dependency:list` and returns the output.\n */\n private runMavenDependencyList(projectPath: string): string {\n try {\n const output = this.execSyncFn(\n 'mvn dependency:list -DoutputType=text -DincludeScope=compile',\n {\n cwd: projectPath,\n stdio: 'pipe',\n timeout: 120_000, // 2 minute timeout\n encoding: 'utf-8',\n }\n );\n return output.toString();\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n throw new LockfileParseError(\n path.join(projectPath, 'pom.xml'),\n `Failed to run 'mvn dependency:list': ${message}`\n );\n }\n }\n\n /**\n * Builds a purl for a Maven package.\n * Format: pkg:maven/groupId/artifactId@version\n */\n private buildPurl(groupId: string, artifactId: string, version: string): string {\n return `pkg:maven/${groupId}/${artifactId}@${version}`;\n }\n\n private buildResult(\n projectPath: string,\n lockfilePath: string,\n dependencies: Dependency[]\n ): ScanResult {\n return {\n projectPath,\n ecosystem: 'maven',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Go module dependency scanner.\n *\n * Parses `go.sum` to extract the full resolved dependency list, and\n * cross-references `go.mod` to distinguish direct vs indirect (transitive)\n * dependencies.\n *\n * go.sum format (one or two lines per module):\n * ```\n * github.com/gin-gonic/gin v1.9.1 h1:abc123...=\n * github.com/gin-gonic/gin v1.9.1/go.mod h1:def456...=\n * ```\n *\n * Lines ending in `/go.mod` are checksums of the module's go.mod file —\n * we skip those and only keep the `h1:` lines (source archive checksums).\n *\n * go.mod `require` block format:\n * ```\n * require (\n * github.com/gin-gonic/gin v1.9.1\n * golang.org/x/text v0.14.0 // indirect\n * )\n * ```\n *\n * Dependencies marked `// indirect` are transitive.\n */\nexport class GoScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'go';\n readonly lockfileNames = ['go.sum'];\n\n async detect(projectPath: string): Promise<string | null> {\n const goSumPath = path.join(projectPath, 'go.sum');\n return existsSync(goSumPath) ? goSumPath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [goSumRaw, goModRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'go.mod'), 'utf-8').catch(() => null),\n ]);\n\n const { directNames, indirectNames } = goModRaw\n ? this.parseGoMod(goModRaw)\n : { directNames: new Set<string>(), indirectNames: new Set<string>() };\n\n const dependencies = this.parseGoSum(goSumRaw, lockfilePath, directNames, indirectNames);\n\n return {\n projectPath,\n ecosystem: 'go',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses go.sum and extracts unique module dependencies.\n *\n * Each module may appear twice in go.sum (once for the source archive,\n * once for go.mod). We deduplicate by module path + version, keeping\n * only the `h1:` entry (not the `/go.mod` entry).\n */\n private parseGoSum(\n content: string,\n lockfilePath: string,\n directNames: Set<string>,\n indirectNames: Set<string>,\n ): Dependency[] {\n const depMap = new Map<string, Dependency>();\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n if (!line) continue;\n\n // Format: \"module version hash\"\n const parts = line.split(/\\s+/);\n if (parts.length < 3) continue;\n\n const modulePath = parts[0];\n let version = parts[1];\n\n // Skip /go.mod checksum lines\n if (version.endsWith('/go.mod')) continue;\n\n // Strip any +incompatible suffix for cleaner versions\n version = version.replace(/\\+incompatible$/, '');\n\n const key = `${modulePath}@${version}`;\n if (depMap.has(key)) continue;\n\n // Determine direct/indirect from go.mod data\n // If go.mod is available: explicit direct or not marked indirect = direct\n // If go.mod is not available: default to direct (conservative)\n const isDirect = directNames.size > 0 || indirectNames.size > 0\n ? directNames.has(modulePath) || (!indirectNames.has(modulePath) && !directNames.has(modulePath) ? false : directNames.has(modulePath))\n : true;\n\n depMap.set(key, {\n name: modulePath,\n version,\n direct: isDirect,\n ecosystem: 'go',\n purl: this.buildPurl(modulePath, version),\n });\n }\n\n return Array.from(depMap.values());\n }\n\n /**\n * Parses go.mod to extract direct and indirect dependency names.\n *\n * Handles both single-line and block `require` directives:\n * ```\n * require github.com/pkg/errors v0.9.1\n *\n * require (\n * github.com/gin-gonic/gin v1.9.1\n * golang.org/x/text v0.14.0 // indirect\n * )\n * ```\n */\n private parseGoMod(content: string): { directNames: Set<string>; indirectNames: Set<string> } {\n const directNames = new Set<string>();\n const indirectNames = new Set<string>();\n\n let inRequireBlock = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n // Single-line require: `require github.com/pkg/errors v0.9.1`\n if (line.startsWith('require ') && !line.includes('(')) {\n const match = line.match(/^require\\s+(\\S+)\\s+\\S+(.*)$/);\n if (match) {\n const modulePath = match[1];\n const rest = match[2];\n if (rest.includes('// indirect')) {\n indirectNames.add(modulePath);\n } else {\n directNames.add(modulePath);\n }\n }\n continue;\n }\n\n // Start of require block\n if (line === 'require (' || line.startsWith('require (')) {\n inRequireBlock = true;\n continue;\n }\n\n // End of require block\n if (inRequireBlock && line === ')') {\n inRequireBlock = false;\n continue;\n }\n\n // Inside require block\n if (inRequireBlock && line && !line.startsWith('//')) {\n const match = line.match(/^(\\S+)\\s+\\S+(.*)$/);\n if (match) {\n const modulePath = match[1];\n const rest = match[2];\n if (rest.includes('// indirect')) {\n indirectNames.add(modulePath);\n } else {\n directNames.add(modulePath);\n }\n }\n }\n }\n\n return { directNames, indirectNames };\n }\n\n /**\n * Builds a purl for a Go module.\n *\n * Per purl spec, the type is \"golang\" and the module path\n * uses `/` separators (no encoding needed for path segments).\n *\n * Example: `pkg:golang/github.com/gin-gonic/gin@v1.9.1`\n */\n private buildPurl(modulePath: string, version: string): string {\n return `pkg:golang/${modulePath}@${version}`;\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Ruby dependency scanner (Bundler).\n *\n * Parses `Gemfile.lock` to extract the full resolved dependency list,\n * and cross-references the `DEPENDENCIES` section to distinguish\n * direct vs transitive gems.\n *\n * Gemfile.lock format:\n * ```\n * GEM\n * remote: https://rubygems.org/\n * specs:\n * actioncable (7.1.2)\n * actionpack (= 7.1.2)\n * activesupport (= 7.1.2)\n * rack (3.0.8)\n *\n * PLATFORMS\n * ruby\n *\n * DEPENDENCIES\n * puma (>= 5.0)\n * rails (~> 7.1.2)\n *\n * BUNDLED WITH\n * 2.5.3\n * ```\n *\n * The `GEM > specs:` section lists all resolved gems with exact versions.\n * The `DEPENDENCIES` section lists direct gems (from the Gemfile).\n */\nexport class RubyScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'ruby';\n readonly lockfileNames = ['Gemfile.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'Gemfile.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const content = await readFile(lockfilePath, 'utf-8');\n\n const specs = this.parseSpecs(content, lockfilePath);\n const directNames = this.parseDependencies(content);\n\n const dependencies: Dependency[] = specs.map(({ name, version }) => ({\n name,\n version,\n direct: directNames.has(name),\n ecosystem: 'ruby' as Ecosystem,\n purl: `pkg:gem/${name}@${version}`,\n }));\n\n return {\n projectPath,\n ecosystem: 'ruby',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses the GEM > specs section to extract all resolved gems.\n *\n * Gems at the top level of the specs section (indented 4 spaces) are\n * resolved packages. Their sub-dependencies (indented 6+ spaces) are\n * constraints, not separate entries — those sub-deps appear as their\n * own top-level spec entries elsewhere.\n *\n * Format: ` gem-name (1.2.3)`\n */\n private parseSpecs(\n content: string,\n lockfilePath: string,\n ): Array<{ name: string; version: string }> {\n const gems: Array<{ name: string; version: string }> = [];\n\n let inGemSection = false;\n let inSpecs = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine;\n\n // Detect section boundaries (lines with no leading whitespace)\n if (line.length > 0 && line[0] !== ' ') {\n if (line.startsWith('GEM')) {\n inGemSection = true;\n inSpecs = false;\n continue;\n }\n // Any other top-level section ends GEM\n inGemSection = false;\n inSpecs = false;\n continue;\n }\n\n if (inGemSection && line.trimStart().startsWith('specs:')) {\n inSpecs = true;\n continue;\n }\n\n if (!inSpecs) continue;\n\n // Top-level gems are indented exactly 4 spaces: \" gem-name (1.2.3)\"\n // Sub-dependencies are indented 6+ spaces: \" dep-name (>= 1.0)\"\n // We only want the 4-space entries (actual resolved packages)\n const match = line.match(/^ {4}(\\S+)\\s+\\(([^)]+)\\)$/);\n if (match) {\n const [, name, version] = match;\n gems.push({ name, version });\n }\n }\n\n if (gems.length === 0) {\n throw new LockfileParseError(\n lockfilePath,\n 'No gems found in GEM specs section',\n );\n }\n\n return gems;\n }\n\n /**\n * Parses the DEPENDENCIES section to get direct dependency names.\n *\n * Format: ` gem-name (>= 1.0)` or ` gem-name`\n * The version constraint is optional and we only need the name.\n */\n private parseDependencies(content: string): Set<string> {\n const directNames = new Set<string>();\n let inDependencies = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine;\n\n // Section header (no leading space)\n if (line.length > 0 && line[0] !== ' ') {\n if (line.startsWith('DEPENDENCIES')) {\n inDependencies = true;\n continue;\n }\n if (inDependencies) break; // Next section → stop\n continue;\n }\n\n if (!inDependencies) continue;\n\n // Dependencies are indented 2 spaces: \" gem-name\" or \" gem-name (>= 1.0)\"\n // The ! suffix indicates a gem loaded from a specific source/path\n const match = line.match(/^ {2}(\\S+?)!?\\s*(?:\\(|$)/);\n if (match) {\n directNames.add(match[1]);\n }\n }\n\n return directNames;\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * PHP / Composer dependency scanner.\n *\n * Parses `composer.lock` for resolved packages and uses `composer.json`\n * (if present) to identify direct dependencies.\n */\nexport class ComposerScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'composer';\n readonly lockfileNames = ['composer.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'composer.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockRaw, manifestRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'composer.json'), 'utf-8').catch(() => null),\n ]);\n\n const directNames = manifestRaw ? this.parseComposerManifest(manifestRaw) : null;\n const dependencies = this.parseComposerLock(lockRaw, lockfilePath, directNames);\n\n return {\n projectPath,\n ecosystem: 'composer',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n private parseComposerLock(\n content: string,\n lockfilePath: string,\n directNames: Set<string> | null\n ): Dependency[] {\n let lock: ComposerLock;\n try {\n lock = JSON.parse(content) as ComposerLock;\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON in composer.lock');\n }\n\n const allPackages = [...(lock.packages ?? []), ...(lock['packages-dev'] ?? [])];\n if (allPackages.length === 0) {\n throw new LockfileParseError(lockfilePath, 'No packages found in composer.lock');\n }\n\n return allPackages\n .filter((pkg) => pkg.name && pkg.version)\n .map((pkg) => ({\n name: pkg.name,\n version: this.normalizeVersion(pkg.version),\n direct: directNames ? directNames.has(pkg.name) : true,\n ecosystem: 'composer' as Ecosystem,\n purl: this.buildPurl(pkg.name, this.normalizeVersion(pkg.version)),\n }));\n }\n\n private parseComposerManifest(content: string): Set<string> {\n let manifest: ComposerManifest;\n try {\n manifest = JSON.parse(content) as ComposerManifest;\n } catch {\n return new Set<string>();\n }\n\n const names = new Set<string>();\n for (const section of [manifest.require ?? {}, manifest['require-dev'] ?? {}]) {\n for (const name of Object.keys(section)) {\n // Exclude platform constraints.\n if (name === 'php' || name.startsWith('ext-') || name.startsWith('lib-')) continue;\n names.add(name);\n }\n }\n\n return names;\n }\n\n private normalizeVersion(version: string): string {\n return version.trim();\n }\n\n private buildPurl(name: string, version: string): string {\n return `pkg:composer/${name}@${version}`;\n }\n}\n\ninterface ComposerLock {\n packages?: ComposerPackage[];\n 'packages-dev'?: ComposerPackage[];\n}\n\ninterface ComposerPackage {\n name: string;\n version: string;\n}\n\ninterface ComposerManifest {\n require?: Record<string, string>;\n 'require-dev'?: Record<string, string>;\n}\n\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport { parse as parseYaml } from 'yaml';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Yarn dependency scanner.\n *\n * Supports Yarn v1 (Classic) and Yarn v2/v3/v4 (Berry) lockfile formats.\n * \n * - Yarn v1: Plain text format with indentation\n * - Yarn v2+: YAML-based format with __metadata section\n *\n * Parses yarn.lock to extract the full resolved dependency tree.\n * Also reads package.json to determine which dependencies are direct vs transitive.\n *\n * Note: Yarn uses the npm ecosystem since it's an alternative\n * package manager for the npm registry.\n */\nexport class YarnScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'npm';\n readonly lockfileNames = ['yarn.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'yarn.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockfileRaw, packageJsonRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'package.json'), 'utf-8').catch(() => null),\n ]);\n\n // Determine direct dependency names from package.json\n const directNames = new Set<string>();\n if (packageJsonRaw) {\n try {\n const pkg = JSON.parse(packageJsonRaw);\n for (const name of Object.keys(pkg.dependencies ?? {})) {\n directNames.add(name);\n }\n for (const name of Object.keys(pkg.devDependencies ?? {})) {\n directNames.add(name);\n }\n } catch {\n // If package.json can't be parsed, all deps are \"unknown\" direct status\n }\n }\n\n const dependencies = this.parseLockfile(lockfileRaw, lockfilePath, directNames);\n\n return {\n projectPath,\n ecosystem: 'npm',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses yarn.lock file and extracts dependencies.\n * Automatically detects and handles both v1 (Classic) and v2+ (Berry) formats.\n */\n private parseLockfile(\n content: string,\n lockfilePath: string,\n directNames: Set<string>\n ): Dependency[] {\n try {\n // Detect Yarn version by checking for v2+ indicators\n const isV2Plus = this.isYarnV2Plus(content);\n\n if (isV2Plus) {\n return this.parseLockfileV2Plus(content, lockfilePath, directNames);\n } else {\n return this.parseLockfileV1(content, lockfilePath, directNames);\n }\n } catch (err) {\n throw new LockfileParseError(\n lockfilePath,\n `Failed to parse yarn.lock: ${err instanceof Error ? err.message : 'Unknown error'}`\n );\n }\n }\n\n /**\n * Detects if the lockfile is Yarn v2+ (Berry) format.\n * v2+ uses YAML format and contains __metadata section.\n */\n private isYarnV2Plus(content: string): boolean {\n return content.startsWith('__metadata:') ||\n content.includes('\\n__metadata:');\n }\n\n /**\n * Parses Yarn v2+ (Berry) lockfile format.\n * \n * Yarn v2+ format (YAML):\n * ```yaml\n * __metadata:\n * version: 6\n * \n * \"package-name@npm:^1.0.0\":\n * version: 1.2.3\n * resolution: \"package-name@npm:1.2.3\"\n * dependencies:\n * dep1: ^2.0.0\n * checksum: ...\n * languageName: node\n * linkType: hard\n * ```\n */\n private parseLockfileV2Plus(\n content: string,\n lockfilePath: string,\n directNames: Set<string>\n ): Dependency[] {\n const deps: Dependency[] = [];\n const seen = new Map<string, boolean>();\n\n try {\n const parsed = parseYaml(content);\n\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('Invalid YAML format');\n }\n\n for (const [key, value] of Object.entries(parsed)) {\n // Skip metadata and workspace entries\n if (key === '__metadata' || key.includes('@workspace:')) {\n continue;\n }\n\n if (typeof value !== 'object' || value === null) {\n continue;\n }\n\n const entry = value as Record<string, any>;\n\n // Extract package name - prefer resolution field as it contains the real package\n let name: string | null = null;\n if (entry.resolution && typeof entry.resolution === 'string') {\n name = this.extractPackageNameFromResolution(entry.resolution);\n }\n // Fall back to extracting from key if resolution is not available\n if (!name) {\n name = this.extractPackageNameV2Plus(key);\n }\n\n const version = entry.version;\n\n if (!name || !version || typeof version !== 'string') {\n continue;\n }\n\n // Deduplicate by name@version\n const depKey = `${name}@${version}`;\n if (seen.has(depKey)) {\n continue;\n }\n seen.set(depKey, true);\n\n deps.push({\n name,\n version,\n direct: directNames.has(name),\n ecosystem: 'npm',\n purl: this.buildPurl(name, version),\n });\n }\n } catch (err) {\n throw new Error(`Failed to parse Yarn v2+ lockfile: ${err instanceof Error ? err.message : 'Unknown error'}`);\n }\n\n return deps;\n }\n\n /**\n * Extracts package name from Yarn v2+ resolution field.\n * The resolution field contains the real package name.\n * Examples:\n * \"express@npm:4.18.2\" → \"express\"\n * \"@types/node@npm:20.11.5\" → \"@types/node\"\n * \"lodash@npm:4.17.21\" → \"lodash\"\n */\n private extractPackageNameFromResolution(resolution: string): string | null {\n // Resolution format: \"package-name@npm:version\"\n\n // Handle scoped packages: \"@scope/name@npm:version\"\n if (resolution.startsWith('@')) {\n const match = resolution.match(/^(@[^@]+\\/[^@]+)@/);\n if (match) {\n return match[1];\n }\n }\n\n // Handle regular packages: \"package@npm:version\"\n const match = resolution.match(/^([^@]+)@/);\n if (match) {\n return match[1];\n }\n\n return null;\n }\n\n /**\n * Extracts package name from Yarn v2+ package key.\n * Examples:\n * \"express@npm:^4.18.0\" → \"express\"\n * \"@types/node@npm:^20.0.0\" → \"@types/node\"\n * \"pkg@npm:other@npm:^1.0.0\" → \"pkg\" (aliased packages)\n */\n private extractPackageNameV2Plus(key: string): string | null {\n // Remove the @npm: protocol prefix and version specifier\n // Format: \"package-name@npm:^version\" or \"@scope/name@npm:^version\"\n\n // Handle scoped packages: \"@scope/name@npm:^version\"\n if (key.startsWith('@')) {\n const match = key.match(/^(@[^@]+\\/[^@]+)@/);\n if (match) {\n return match[1];\n }\n }\n\n // Handle regular packages: \"package@npm:^version\"\n const match = key.match(/^([^@]+)@/);\n if (match) {\n return match[1];\n }\n\n return null;\n }\n\n /**\n * Parses Yarn v1 (Classic) lockfile format.\n *\n * Yarn v1 format:\n * ```\n * \"package-name@^1.0.0\":\n * version \"1.2.3\"\n * resolved \"https://...\"\n * integrity sha512-...\n * dependencies:\n * dep1 \"^2.0.0\"\n * ```\n */\n private parseLockfileV1(\n content: string,\n lockfilePath: string,\n directNames: Set<string>\n ): Dependency[] {\n const deps: Dependency[] = [];\n const seen = new Map<string, boolean>(); // Track unique name@version pairs\n\n const lines = content.split('\\n');\n let currentPackage: { names: string[]; version?: string } | null = null;\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n\n // Skip comments and empty lines\n if (line.trim().startsWith('#') || line.trim() === '') {\n continue;\n }\n\n // Package declaration: \"name@version\", name@version (with or without quotes)\n // Can be multiple aliases separated by comma: \"pkg@^1.0.0\", pkg@1.2.3:\n if (line.match(/^[\"\\w@]/) && line.includes(':') && !line.startsWith(' ')) {\n // Save previous package\n if (currentPackage?.version) {\n this.addDependency(currentPackage, directNames, seen, deps);\n }\n\n // Parse package name(s)\n const pkgLine = line.substring(0, line.lastIndexOf(':'));\n const names = pkgLine\n .split(',')\n .map((s) => s.trim().replace(/^[\"']|[\"']$/g, ''))\n .map((s) => this.extractPackageName(s))\n .filter((s): s is string => !!s);\n\n currentPackage = { names, version: undefined };\n }\n // Version field\n else if (line.trim().startsWith('version ') && currentPackage) {\n const match = line.match(/version\\s+\"([^\"]+)\"/);\n if (match) {\n currentPackage.version = match[1];\n }\n }\n }\n\n // Don't forget the last package\n if (currentPackage?.version) {\n this.addDependency(currentPackage, directNames, seen, deps);\n }\n\n return deps;\n }\n\n /**\n * Adds a dependency to the result list (deduplicates by name@version)\n */\n private addDependency(\n pkg: { names: string[]; version?: string },\n directNames: Set<string>,\n seen: Map<string, boolean>,\n deps: Dependency[]\n ): void {\n if (!pkg.version) return;\n\n // Use the first name as the canonical package name\n const name = pkg.names[0];\n if (!name) return;\n\n // Deduplicate: only add once per name@version\n const key = `${name}@${pkg.version}`;\n if (seen.has(key)) return;\n seen.set(key, true);\n\n deps.push({\n name,\n version: pkg.version,\n direct: directNames.has(name),\n ecosystem: 'npm',\n purl: this.buildPurl(name, pkg.version),\n });\n }\n\n /**\n * Extracts package name from yarn.lock package declaration.\n * Examples:\n * \"express@^4.18.0\" → \"express\"\n * \"@types/node@^20.0.0\" → \"@types/node\"\n * \"pkg@npm:other@^1.0.0\" → \"pkg\" (aliased packages)\n */\n private extractPackageName(pkgDeclaration: string): string | null {\n // Handle npm: aliases (e.g., \"pkg@npm:other@^1.0.0\")\n if (pkgDeclaration.includes('@npm:')) {\n const beforeAlias = pkgDeclaration.split('@npm:')[0];\n return beforeAlias || null;\n }\n\n // Standard format: name@version\n // For scoped packages: @scope/name@version\n if (pkgDeclaration.startsWith('@')) {\n // Scoped package: @scope/name@version\n const parts = pkgDeclaration.split('@');\n // parts = ['', 'scope/name', 'version']\n if (parts.length >= 3) {\n return `@${parts[1]}`;\n }\n } else {\n // Regular package: name@version\n const atIndex = pkgDeclaration.indexOf('@');\n if (atIndex > 0) {\n return pkgDeclaration.substring(0, atIndex);\n }\n }\n\n return null;\n }\n\n /**\n * Builds a purl (Package URL) for an npm package.\n *\n * Per the purl spec:\n * \"The npm scope @ sign prefix is always percent encoded.\"\n *\n * So @types/node@20.11.5 → pkg:npm/%40types/node@20.11.5\n * And express@4.18.2 → pkg:npm/express@4.18.2\n */\n private buildPurl(name: string, version: string): string {\n if (name.startsWith('@')) {\n // Scoped: encode the @ as %40 per purl spec\n return `pkg:npm/%40${name.slice(1)}@${version}`;\n }\n return `pkg:npm/${name}@${version}`;\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport { parse as parseYaml } from 'yaml';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * pnpm dependency scanner.\n *\n * Parses pnpm-lock.yaml to extract the full resolved dependency tree\n * and determine which dependencies are direct vs transitive.\n *\n * Note: pnpm uses the npm ecosystem since it's an alternative\n * package manager for the npm registry.\n */\nexport class PnpmScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'npm';\n readonly lockfileNames = ['pnpm-lock.yaml'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'pnpm-lock.yaml');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const lockfileRaw = await readFile(lockfilePath, 'utf-8');\n\n const dependencies = this.parseLockfile(lockfileRaw, lockfilePath);\n\n return {\n projectPath,\n ecosystem: 'npm',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses pnpm-lock.yaml file and extracts dependencies.\n * \n * pnpm-lock.yaml format (v5.4+):\n * ```yaml\n * lockfileVersion: 5.4\n * \n * dependencies:\n * express: 4.18.2\n * \n * devDependencies:\n * typescript: 5.0.0\n * \n * packages:\n * /express/4.18.2:\n * resolution: {integrity: sha512-...}\n * dependencies:\n * accepts: 1.3.8\n * /@types/node/20.11.5:\n * resolution: {integrity: sha512-...}\n * dev: true\n * ```\n * \n * pnpm-lock.yaml format (v6.0+):\n * ```yaml\n * lockfileVersion: '6.0'\n * \n * dependencies:\n * express:\n * specifier: ^4.18.0\n * version: 4.18.2\n * \n * packages:\n * /express@4.18.2:\n * resolution: {integrity: sha512-...}\n * ```\n */\n private parseLockfile(\n content: string,\n lockfilePath: string\n ): Dependency[] {\n try {\n const parsed = parseYaml(content);\n\n if (!parsed || typeof parsed !== 'object') {\n throw new Error('Invalid YAML format');\n }\n\n const lockfile = parsed as PnpmLockfile;\n\n // Determine lockfile version format\n const lockfileVersion = this.parseLockfileVersion(lockfile.lockfileVersion);\n\n // Extract direct dependency names from lockfile\n const directNames = this.extractDirectDependencies(lockfile);\n\n return this.extractDependencies(lockfile, lockfileVersion, directNames);\n } catch (err) {\n throw new LockfileParseError(\n lockfilePath,\n `Failed to parse pnpm-lock.yaml: ${err instanceof Error ? err.message : 'Unknown error'}`\n );\n }\n }\n\n /**\n * Extracts direct dependency names from pnpm lockfile.\n * \n * Supports both formats:\n * - pnpm v5.x: root-level dependencies/devDependencies\n * - pnpm v6+: importers['.'].dependencies/devDependencies\n */\n private extractDirectDependencies(lockfile: PnpmLockfile): Set<string> {\n const directNames = new Set<string>();\n\n // Try modern format first (v6+): importers section\n if (lockfile.importers && typeof lockfile.importers === 'object') {\n const rootImporter = lockfile.importers['.'];\n if (rootImporter && typeof rootImporter === 'object') {\n // Extract from importers['.'].dependencies\n if (rootImporter.dependencies && typeof rootImporter.dependencies === 'object') {\n for (const name of Object.keys(rootImporter.dependencies)) {\n directNames.add(name);\n }\n }\n\n // Extract from importers['.'].devDependencies\n if (rootImporter.devDependencies && typeof rootImporter.devDependencies === 'object') {\n for (const name of Object.keys(rootImporter.devDependencies)) {\n directNames.add(name);\n }\n }\n }\n }\n\n // Fallback to legacy format (v5.x): root-level dependencies\n if (directNames.size === 0) {\n // Extract from root-level dependencies section\n if (lockfile.dependencies && typeof lockfile.dependencies === 'object') {\n for (const name of Object.keys(lockfile.dependencies)) {\n directNames.add(name);\n }\n }\n\n // Extract from root-level devDependencies section\n if (lockfile.devDependencies && typeof lockfile.devDependencies === 'object') {\n for (const name of Object.keys(lockfile.devDependencies)) {\n directNames.add(name);\n }\n }\n }\n\n return directNames;\n }\n\n /**\n * Parses lockfile version (can be string or number)\n */\n private parseLockfileVersion(version: string | number | undefined): number {\n if (typeof version === 'number') {\n return version;\n }\n if (typeof version === 'string') {\n const parsed = parseFloat(version);\n return isNaN(parsed) ? 5.4 : parsed;\n }\n return 5.4; // Default to 5.4 if not specified\n }\n\n /**\n * Extracts dependencies from the lockfile packages section\n */\n private extractDependencies(\n lockfile: PnpmLockfile,\n lockfileVersion: number,\n directNames: Set<string>\n ): Dependency[] {\n const deps: Dependency[] = [];\n const seen = new Map<string, boolean>();\n\n if (!lockfile.packages || typeof lockfile.packages !== 'object') {\n return deps;\n }\n\n for (const [pkgPath, pkgInfo] of Object.entries(lockfile.packages)) {\n if (!pkgInfo || typeof pkgInfo !== 'object') {\n continue;\n }\n\n // Skip workspace packages (e.g., \"/project@workspace:*\")\n if (pkgPath.includes('@workspace:')) {\n continue;\n }\n\n // Extract package name and version from path\n // v5.x format: \"/express/4.18.2\", \"/@types/node/20.11.5\"\n // v6+ format: \"/express@4.18.2\", \"/@types/node@20.11.5\"\n const { name, version } = this.parsePackagePath(pkgPath, lockfileVersion);\n\n if (!name || !version) {\n continue;\n }\n\n // Deduplicate by name@version\n const depKey = `${name}@${version}`;\n if (seen.has(depKey)) {\n continue;\n }\n seen.set(depKey, true);\n\n deps.push({\n name,\n version,\n direct: directNames.has(name),\n ecosystem: 'npm',\n purl: this.buildPurl(name, version),\n });\n }\n\n return deps;\n }\n\n /**\n * Parses package path to extract name and version.\n * \n * pnpm v5.x format:\n * \"/express/4.18.2\" → name: \"express\", version: \"4.18.2\"\n * \"/@types/node/20.11.5\" → name: \"@types/node\", version: \"20.11.5\"\n * \"/accepts/1.3.8\" → name: \"accepts\", version: \"1.3.8\"\n * \n * pnpm v6+ format:\n * \"/express@4.18.2\" → name: \"express\", version: \"4.18.2\"\n * \"/@types/node@20.11.5\" → name: \"@types/node\", version: \"20.11.5\"\n * \"/accepts@1.3.8\" → name: \"accepts\", version: \"1.3.8\"\n * \n * Also handles peer dependency suffixes:\n * \"/pkg@1.0.0_dep@2.0.0\" → name: \"pkg\", version: \"1.0.0\"\n * \"/pkg@1.0.0(dep@2.0.0)\" → name: \"pkg\", version: \"1.0.0\"\n */\n private parsePackagePath(pkgPath: string, lockfileVersion: number): { name: string | null; version: string | null } {\n // Remove leading slash\n const path = pkgPath.startsWith('/') ? pkgPath.slice(1) : pkgPath;\n\n // Remove peer dependency suffixes (anything after _ or opening parenthesis)\n const cleanPath = path.split('_')[0].split('(')[0];\n\n if (!cleanPath) {\n return { name: null, version: null };\n }\n\n // v6+ format uses @ separator between name and version\n if (lockfileVersion >= 6) {\n return this.parseV6Format(cleanPath);\n }\n\n // v5.x format uses / separator between name and version\n return this.parseV5Format(cleanPath);\n }\n\n /**\n * Parses v6+ format: \"express@4.18.2\" or \"@types/node@20.11.5\"\n */\n private parseV6Format(path: string): { name: string | null; version: string | null } {\n // Handle scoped packages: \"@scope/name@version\"\n if (path.startsWith('@')) {\n // Find the last @ which separates the version\n const lastAtIndex = path.lastIndexOf('@');\n if (lastAtIndex <= 0) {\n return { name: null, version: null };\n }\n\n const name = path.substring(0, lastAtIndex);\n const version = path.substring(lastAtIndex + 1);\n\n return { name, version };\n }\n\n // Regular packages: \"name@version\"\n const atIndex = path.indexOf('@');\n if (atIndex < 0) {\n return { name: null, version: null };\n }\n\n const name = path.substring(0, atIndex);\n const version = path.substring(atIndex + 1);\n\n return { name, version };\n }\n\n /**\n * Parses v5.x format: \"express/4.18.2\" or \"@types/node/20.11.5\"\n */\n private parseV5Format(path: string): { name: string | null; version: string | null } {\n // Handle scoped packages: \"@scope/name/version\"\n if (path.startsWith('@')) {\n const parts = path.split('/');\n // parts = ['@scope', 'name', 'version']\n if (parts.length < 3) {\n return { name: null, version: null };\n }\n\n const name = `${parts[0]}/${parts[1]}`;\n const version = parts[2];\n\n return { name, version };\n }\n\n // Regular packages: \"name/version\"\n const slashIndex = path.indexOf('/');\n if (slashIndex < 0) {\n return { name: null, version: null };\n }\n\n const name = path.substring(0, slashIndex);\n const version = path.substring(slashIndex + 1);\n\n return { name, version };\n }\n\n /**\n * Builds a purl (Package URL) for an npm package.\n *\n * Per the purl spec:\n * \"The npm scope @ sign prefix is always percent encoded.\"\n *\n * So @types/node@20.11.5 → pkg:npm/%40types/node@20.11.5\n * And express@4.18.2 → pkg:npm/express@4.18.2\n */\n private buildPurl(name: string, version: string): string {\n if (name.startsWith('@')) {\n // Scoped: encode the @ as %40 per purl spec\n return `pkg:npm/%40${name.slice(1)}@${version}`;\n }\n return `pkg:npm/${name}@${version}`;\n }\n}\n\n// ─── Types for pnpm-lock.yaml parsing ─────────────────────────\n\ninterface PnpmLockfile {\n lockfileVersion?: string | number;\n // Legacy format (v5.x): root-level dependencies\n dependencies?: Record<string, string | PnpmDependencyEntry>;\n devDependencies?: Record<string, string | PnpmDependencyEntry>;\n // Modern format (v6+): importers section\n importers?: Record<string, PnpmImporter>;\n packages?: Record<string, PnpmPackageInfo>;\n}\n\ninterface PnpmImporter {\n dependencies?: Record<string, PnpmDependencyEntry>;\n devDependencies?: Record<string, PnpmDependencyEntry>;\n specifiers?: Record<string, string>;\n}\n\ninterface PnpmDependencyEntry {\n specifier?: string;\n version?: string;\n}\n\ninterface PnpmPackageInfo {\n resolution?: {\n integrity?: string;\n tarball?: string;\n };\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n dev?: boolean;\n optional?: boolean;\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Deno dependency scanner.\n *\n * Limitations:\n * - HTTPS/remote imports (deno.lock `remote` section) are not scanned.\n * - JSR packages are not directly supported by OSV so vulnerability scan will fail\n *\n * deno.lock v4/v5 format:\n * ```json\n * {\n * \"version\": \"4\",\n * \"specifiers\": {\n * \"jsr:@std/assert@^1.0.0\": \"1.0.10\",\n * \"npm:express@^4.18.0\": \"4.21.2\"\n * },\n * \"jsr\": {\n * \"@std/assert@1.0.10\": { \"integrity\": \"...\" },\n * \"@std/internal@1.0.6\": { \"integrity\": \"...\" }\n * },\n * \"npm\": {\n * \"express@4.21.2\": { \"integrity\": \"...\", \"dependencies\": {...} }\n * }\n * }\n * ```\n *\n * deno.lock v3 format:\n * ```json\n * {\n * \"version\": \"3\",\n * \"packages\": {\n * \"specifiers\": {\n * \"jsr:@std/path@^1.0.0\": \"jsr:@std/path@1.0.8\",\n * \"npm:hono@^4.0.0\": \"npm:hono@4.6.20\"\n * },\n * \"jsr\": {\n * \"@std/path@1.0.8\": { \"integrity\": \"...\" },\n * \"@std/internal@1.0.6\": { \"integrity\": \"...\" }\n * },\n * \"npm\": {\n * \"hono@4.6.20\": { \"integrity\": \"...\", \"dependencies\": {...} }\n * }\n * }\n * }\n * ```\n */\nexport class DenoScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'deno';\n readonly lockfileNames = ['deno.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n for (const name of this.lockfileNames) {\n const lockfilePath = path.join(projectPath, name);\n if (existsSync(lockfilePath)) return lockfilePath;\n }\n return null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const lockfileRaw = await readFile(lockfilePath, 'utf-8');\n\n let lockfile: DenoLockfile;\n try {\n lockfile = JSON.parse(lockfileRaw);\n } catch {\n throw new LockfileParseError(lockfilePath, 'Invalid JSON');\n }\n\n const dependencies = this.parseLockfile(lockfile);\n\n return {\n projectPath,\n ecosystem: 'deno',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n\n\n /**\n * Parses the lockfile and extracts dependencies from both\n * npm and jsr package registries.\n *\n * Supports v3 (packages nested under `packages`), v4, and v5 (top-level jsr/npm sections).\n * Uses lockfile specifiers to determine direct vs transitive dependencies.\n */\n private parseLockfile(lockfile: DenoLockfile): Dependency[] {\n const deps: Dependency[] = [];\n\n // Extract direct dependencies from lockfile specifiers\n const directNames = this.extractDirectDependencies(lockfile);\n\n // Normalize v3 vs v4/v5 structure (prefer top-level, fall back to nested)\n const jsrPackages = lockfile.jsr ?? lockfile.packages?.jsr ?? {};\n const npmPackages = lockfile.npm ?? lockfile.packages?.npm ?? {};\n\n // Parse JSR packages: keys are like \"@std/assert@1.0.10\"\n for (const key of Object.keys(jsrPackages)) {\n const parsed = this.parsePackageKey(key);\n if (!parsed) continue;\n\n deps.push({\n name: parsed.name,\n version: parsed.version,\n direct: directNames.has(`jsr:${parsed.name}`),\n ecosystem: 'deno', // JSR packages belong to Deno ecosystem\n purl: this.buildJsrPurl(parsed.name, parsed.version),\n });\n }\n\n // Parse npm packages: keys are like \"express@4.21.2\"\n for (const key of Object.keys(npmPackages)) {\n const parsed = this.parsePackageKey(key);\n if (!parsed) continue;\n\n deps.push({\n name: parsed.name,\n version: parsed.version,\n direct: directNames.has(`npm:${parsed.name}`),\n ecosystem: 'npm', // npm packages belong to npm ecosystem (for CVE tracking)\n purl: this.buildNpmPurl(parsed.name, parsed.version),\n });\n }\n\n return deps;\n }\n\n /**\n * Extracts direct dependency names from lockfile specifiers.\n *\n * Returns a set of ecosystem-qualified package names like \"jsr:@std/assert\" or \"npm:express\".\n * The ecosystem prefix prevents collisions if the same package name exists in both registries.\n */\n private extractDirectDependencies(lockfile: DenoLockfile): Set<string> {\n const directNames = new Set<string>();\n\n // Get specifiers from v3 (packages.specifiers) or v4/v5 (top-level specifiers)\n const specifiers = lockfile.specifiers ?? lockfile.packages?.specifiers ?? {};\n\n for (const [constraint, resolved] of Object.entries(specifiers)) {\n // Skip non-package URL schemes (file:, https:, data:, etc.)\n if (resolved.startsWith('file:') || resolved.startsWith('https:') || resolved.startsWith('http:') || resolved.startsWith('data:')) {\n continue;\n }\n\n // Determine ecosystem from constraint\n let ecosystem: 'jsr' | 'npm' | null = null;\n if (constraint.startsWith('jsr:')) {\n ecosystem = 'jsr';\n } else if (constraint.startsWith('npm:')) {\n ecosystem = 'npm';\n }\n\n if (!ecosystem) continue; // Skip if we can't determine ecosystem\n\n let resolvedKey = resolved;\n\n if (resolved.startsWith('jsr:') || resolved.startsWith('npm:')) {\n // v3 or some v4 formats: resolved is like \"jsr:@std/path@1.0.8\" or \"npm:express@4.21.2\"\n resolvedKey = resolved.replace(/^(jsr:|npm:)/, '');\n const parsed = this.parsePackageKey(resolvedKey);\n if (parsed) {\n directNames.add(`${ecosystem}:${parsed.name}`);\n }\n } else {\n // v4/v5 format: resolved is just the version like \"1.0.10\"\n // Extract name from constraint key and use directly\n const name = this.extractNameFromSpecifier(constraint);\n if (name) {\n directNames.add(`${ecosystem}:${name}`);\n }\n }\n }\n\n return directNames;\n }\n\n /**\n * Parses a package key like \"@std/assert@1.0.10\" or \"express@4.21.2\"\n * into { name, version }.\n *\n * Handles scoped packages where the name starts with @ (e.g., @std/assert).\n * In that case the version separator is the LAST @ sign.\n */\n private parsePackageKey(key: string): { name: string; version: string } | null {\n // Find the last @ which separates name from version\n const lastAtIndex = key.lastIndexOf('@');\n if (lastAtIndex <= 0) return null;\n\n const name = key.slice(0, lastAtIndex);\n const version = key.slice(lastAtIndex + 1);\n\n if (!name || !version) return null;\n return { name, version };\n }\n\n\n\n /**\n * Extracts the package name from a Deno import specifier.\n *\n * Examples:\n * \"jsr:@std/assert@^1.0.0\" → \"@std/assert\"\n * \"npm:express@^4.18.0\" → \"express\"\n * \"npm:@hono/hono@^4.0.0\" → \"@hono/hono\"\n * \"lodash\" (bare) → \"lodash\"\n */\n private extractNameFromSpecifier(specifier: string): string | null {\n // Strip jsr: or npm: prefix\n const withoutPrefix = specifier.replace(/^(jsr:|npm:)/, '');\n if (!withoutPrefix) return null;\n\n // For scoped packages (@scope/name@version), find the version @\n if (withoutPrefix.startsWith('@')) {\n // Find the @ after the scoped name portion\n const slashIndex = withoutPrefix.indexOf('/');\n if (slashIndex === -1) return null;\n const afterSlash = withoutPrefix.indexOf('@', slashIndex);\n if (afterSlash === -1) return withoutPrefix; // No version constraint\n return withoutPrefix.slice(0, afterSlash);\n }\n\n // Unscoped: \"express@^4.18.0\" → \"express\" or bare \"lodash\" → \"lodash\"\n const atIndex = withoutPrefix.indexOf('@');\n if (atIndex === -1) return withoutPrefix;\n return withoutPrefix.slice(0, atIndex);\n }\n\n /**\n * Builds a purl for a JSR package.\n *\n * JSR packages use the \"jsr\" purl type (non-standard but descriptive).\n * For scoped packages, both @ and all / characters are percent-encoded.\n * Example: `pkg:jsr/%40std%2Fassert@1.0.10`\n */\n private buildJsrPurl(name: string, version: string): string {\n if (name.startsWith('@')) {\n // Encode @ and all / characters: @scope/sub/name -> %40scope%2Fsub%2Fname\n const encoded = '%40' + name.slice(1).replace(/\\//g, '%2F');\n return `pkg:jsr/${encoded}@${version}`;\n }\n return `pkg:jsr/${name}@${version}`;\n }\n\n /**\n * Builds a purl for an npm package used via Deno.\n *\n * Uses the standard npm purl type since these are npm packages.\n * Per npm purl spec, only @ is encoded, / remains as namespace separator.\n * Example: `pkg:npm/%40std/assert@1.0.10` or `pkg:npm/express@4.21.2`\n */\n private buildNpmPurl(name: string, version: string): string {\n if (name.startsWith('@')) {\n return `pkg:npm/%40${name.slice(1)}@${version}`;\n }\n return `pkg:npm/${name}@${version}`;\n }\n}\n\n// ─── Types for deno.lock parsing ─────────────────────────────────\n\n/** Represents the deno.lock file structure (supports v3, v4, and v5) */\ninterface DenoLockfile {\n version?: string;\n /** v4/v5: top-level specifiers mapping (constraint -> resolved version) */\n specifiers?: Record<string, string>;\n /** v4/v5: top-level jsr packages */\n jsr?: Record<string, DenoLockfilePackage>;\n /** v4/v5: top-level npm packages */\n npm?: Record<string, DenoLockfilePackage>;\n /** v3: all packages nested under this key */\n packages?: {\n specifiers?: Record<string, string>;\n jsr?: Record<string, DenoLockfilePackage>;\n npm?: Record<string, DenoLockfilePackage>;\n };\n}\n\ninterface DenoLockfilePackage {\n integrity?: string;\n /** Dependencies: npm uses Record<string, string> (name -> version), JSR uses string[] (specifier list) */\n dependencies?: Record<string, string> | string[];\n}","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Python / Poetry dependency scanner.\n *\n * Parses `poetry.lock` (TOML format) to extract the full resolved\n * dependency tree. Reads `pyproject.toml` to determine which packages\n * are direct dependencies vs transitive.\n *\n * poetry.lock format:\n * ```toml\n * [[package]]\n * name = \"requests\"\n * version = \"2.31.0\"\n * description = \"Python HTTP for Humans.\"\n * optional = false\n * python-versions = \">=3.8\"\n * ```\n *\n * Direct dependencies are identified by parsing the `[tool.poetry.dependencies]`\n * and `[tool.poetry.group.*.dependencies]` sections of `pyproject.toml`.\n *\n * Note: Uses a simple TOML parser since poetry.lock has a very\n * regular structure (just [[package]] entries). No need for a full\n * TOML library.\n */\nexport class PoetryScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'poetry';\n readonly lockfileNames = ['poetry.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'poetry.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockfileRaw, pyprojectRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'pyproject.toml'), 'utf-8').catch(() => null),\n ]);\n\n const packages = this.parseLockfile(lockfileRaw, lockfilePath);\n const directNames = pyprojectRaw ? this.parsePyprojectToml(pyprojectRaw) : new Set<string>();\n\n const dependencies: Dependency[] = [];\n for (const pkg of packages) {\n dependencies.push({\n name: this.normalizePipName(pkg.name),\n version: pkg.version,\n direct: directNames.size > 0 ? directNames.has(this.normalizePipName(pkg.name)) : true,\n ecosystem: 'poetry',\n purl: this.buildPurl(pkg.name, pkg.version),\n });\n }\n\n return {\n projectPath,\n ecosystem: 'poetry',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses poetry.lock by splitting on [[package]] blocks.\n * Lightweight parser that handles the regular structure\n * without needing a full TOML library.\n */\n private parseLockfile(content: string, lockfilePath: string): PoetryPackage[] {\n const packages: PoetryPackage[] = [];\n const blocks = content.split(/^\\[\\[package\\]\\]$/m);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const name = this.extractField(block, 'name');\n const version = this.extractField(block, 'version');\n\n if (name && version) {\n packages.push({ name, version });\n }\n }\n\n if (packages.length === 0 && content.includes('[[package]]')) {\n throw new LockfileParseError(lockfilePath, 'Failed to parse any packages from poetry.lock');\n }\n\n return packages;\n }\n\n /**\n * Extracts a string field value from a TOML block.\n * Handles: `name = \"value\"` format.\n */\n private extractField(block: string, fieldName: string): string | null {\n const regex = new RegExp(`^${fieldName}\\\\s*=\\\\s*\"([^\"]*)\"`, 'm');\n const match = block.match(regex);\n return match ? match[1] : null;\n }\n\n /**\n * Parses `pyproject.toml` to extract direct dependency names.\n *\n * Looks for:\n * - `[tool.poetry.dependencies]` — main dependencies\n * - `[tool.poetry.group.dev.dependencies]` — dev dependencies\n * - `[tool.poetry.group.*.dependencies]` — other groups\n *\n * Supports formats:\n * - `requests = \"^2.31.0\"`\n * - `requests = { version = \"^2.31.0\", optional = true }`\n * - `python = \"^3.12\"` — skipped (the Python interpreter itself)\n */\n private parsePyprojectToml(content: string): Set<string> {\n const directNames = new Set<string>();\n let inDepsSection = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n // Detect section headers\n if (line.startsWith('[')) {\n inDepsSection =\n line === '[tool.poetry.dependencies]' ||\n /^\\[tool\\.poetry\\.group\\.[^\\]]+\\.dependencies\\]$/.test(line);\n continue;\n }\n\n if (inDepsSection && line && !line.startsWith('#')) {\n // Extract package name from \"name = ...\" lines\n const match = line.match(/^([a-zA-Z0-9_][a-zA-Z0-9._-]*)\\s*=/);\n if (match && match[1]) {\n const name = this.normalizePipName(match[1]);\n // Skip the python version constraint\n if (name !== 'python') {\n directNames.add(name);\n }\n }\n }\n }\n\n return directNames;\n }\n\n /**\n * Normalizes a pip package name per PEP 503.\n * Converts to lowercase and replaces any run of [-_.] with a single hyphen.\n */\n private normalizePipName(name: string): string {\n return name.toLowerCase().replace(/[-_.]+/g, '-');\n }\n\n /**\n * Builds a purl for a PyPI package.\n * Per purl spec, the type is \"pypi\" (not \"poetry\").\n */\n private buildPurl(name: string, version: string): string {\n return `pkg:pypi/${this.normalizePipName(name)}@${version}`;\n }\n}\n\n// ─── Internal types ──────────────────────────────────────────────\n\ninterface PoetryPackage {\n name: string;\n version: string;\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport path from 'path';\nimport type { DependencyScanner } from '../scanner.interface.js';\nimport type { Dependency, Ecosystem, ScanResult } from '../../core/types.js';\nimport { LockfileParseError } from '../../core/errors.js';\n\n/**\n * Python / UV dependency scanner.\n *\n * Parses `uv.lock` to extract the full resolved dependency tree.\n * Reads `pyproject.toml` to determine which packages are direct\n * dependencies vs transitive.\n *\n * uv.lock format:\n * ```toml\n * version = 1\n * requires-python = \">=3.12\"\n *\n * [[package]]\n * name = \"requests\"\n * version = \"2.31.0\"\n * source = { registry = \"https://pypi.org/simple\" }\n * dependencies = [\n * { name = \"certifi\" },\n * { name = \"charset-normalizer\" },\n * ]\n * ```\n *\n * The root project is typically included as a `[[package]]` entry\n * without a `source` field (or with `source = { editable = \".\" }`).\n * It is excluded from the dependency list.\n *\n * Note: Uses a simple TOML parser since uv.lock has a very\n * regular structure. No need for a full TOML library.\n */\nexport class UvScanner implements DependencyScanner {\n readonly ecosystem: Ecosystem = 'uv';\n readonly lockfileNames = ['uv.lock'];\n\n async detect(projectPath: string): Promise<string | null> {\n const lockfilePath = path.join(projectPath, 'uv.lock');\n return existsSync(lockfilePath) ? lockfilePath : null;\n }\n\n async scan(projectPath: string, lockfilePath: string): Promise<ScanResult> {\n const [lockfileRaw, pyprojectRaw] = await Promise.all([\n readFile(lockfilePath, 'utf-8'),\n readFile(path.join(projectPath, 'pyproject.toml'), 'utf-8').catch(() => null),\n ]);\n\n const packages = this.parseLockfile(lockfileRaw, lockfilePath);\n const projectName = pyprojectRaw ? this.extractProjectName(pyprojectRaw) : null;\n const directNames = pyprojectRaw ? this.parsePyprojectDeps(pyprojectRaw) : new Set<string>();\n\n const dependencies: Dependency[] = [];\n for (const pkg of packages) {\n // Skip the root project itself\n if (pkg.isEditable) continue;\n if (projectName && this.normalizePipName(pkg.name) === this.normalizePipName(projectName)) {\n continue;\n }\n\n dependencies.push({\n name: this.normalizePipName(pkg.name),\n version: pkg.version,\n direct: directNames.size > 0 ? directNames.has(this.normalizePipName(pkg.name)) : true,\n ecosystem: 'uv',\n purl: this.buildPurl(pkg.name, pkg.version),\n });\n }\n\n return {\n projectPath,\n ecosystem: 'uv',\n dependencies,\n lockfilePath,\n scannedAt: new Date().toISOString(),\n };\n }\n\n /**\n * Parses uv.lock by splitting on [[package]] blocks.\n * Lightweight parser that handles the regular structure\n * without needing a full TOML library.\n */\n private parseLockfile(content: string, lockfilePath: string): UvPackage[] {\n const packages: UvPackage[] = [];\n const blocks = content.split(/^\\[\\[package\\]\\]$/m);\n\n for (const block of blocks) {\n if (!block.trim()) continue;\n\n const name = this.extractField(block, 'name');\n const version = this.extractField(block, 'version');\n\n if (name && version) {\n // Detect editable/virtual packages (root project)\n const isEditable = /source\\s*=\\s*\\{[^}]*editable\\s*=/.test(block) ||\n /source\\s*=\\s*\\{[^}]*virtual\\s*=/.test(block);\n packages.push({ name, version, isEditable });\n }\n }\n\n if (packages.length === 0 && content.includes('[[package]]')) {\n throw new LockfileParseError(lockfilePath, 'Failed to parse any packages from uv.lock');\n }\n\n return packages;\n }\n\n /**\n * Extracts a string field value from a TOML block.\n * Handles: `name = \"value\"` format.\n */\n private extractField(block: string, fieldName: string): string | null {\n const regex = new RegExp(`^${fieldName}\\\\s*=\\\\s*\"([^\"]*)\"`, 'm');\n const match = block.match(regex);\n return match ? match[1] : null;\n }\n\n /**\n * Extracts the project name from `pyproject.toml`.\n * Looks for `name = \"...\"` under `[project]`.\n */\n private extractProjectName(content: string): string | null {\n let inProjectSection = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n if (line.startsWith('[')) {\n inProjectSection = line === '[project]';\n continue;\n }\n\n if (inProjectSection) {\n const match = line.match(/^name\\s*=\\s*\"([^\"]*)\"/);\n if (match) return match[1];\n }\n }\n\n return null;\n }\n\n /**\n * Parses `pyproject.toml` to extract direct dependency names.\n *\n * Looks for:\n * - `[project]` → `dependencies = [...]` (PEP 621)\n * - `[project.optional-dependencies]` (extras)\n * - `[dependency-groups]` (PEP 735, used by uv for dev deps)\n *\n * Dependency strings follow PEP 508:\n * - `\"requests>=2.31.0\"`\n * - `\"flask[dotenv]>=3.0\"`\n * - `\"black\"` (bare name)\n */\n private parsePyprojectDeps(content: string): Set<string> {\n const directNames = new Set<string>();\n\n // Extract dependencies from PEP 621 [project] dependencies array\n this.extractInlineArray(content, directNames);\n\n // Extract from [dependency-groups] sections (PEP 735)\n this.extractDependencyGroups(content, directNames);\n\n return directNames;\n }\n\n /**\n * Extracts dependency names from PEP 621 `dependencies = [...]` arrays\n * and `[project.optional-dependencies]` sections.\n */\n private extractInlineArray(content: string, directNames: Set<string>): void {\n // Match multi-line array patterns like:\n // dependencies = [\n // \"requests>=2.31.0\",\n // \"flask\",\n // ]\n const arrayRegex = /(?:^dependencies|^[a-zA-Z0-9_-]+)\\s*=\\s*\\[([^\\]]*)\\]/gm;\n let match;\n while ((match = arrayRegex.exec(content)) !== null) {\n const arrayContent = match[1];\n this.extractPepNames(arrayContent, directNames);\n }\n }\n\n /**\n * Extracts dependency names from [dependency-groups] sections.\n * Format:\n * ```toml\n * [dependency-groups]\n * dev = [\"pytest>=7.0\", \"black\"]\n * ```\n */\n private extractDependencyGroups(content: string, directNames: Set<string>): void {\n let inDepGroups = false;\n\n for (const rawLine of content.split('\\n')) {\n const line = rawLine.trim();\n\n if (line.startsWith('[')) {\n inDepGroups = line === '[dependency-groups]';\n continue;\n }\n\n if (inDepGroups && line && !line.startsWith('#')) {\n // Match: dev = [\"pytest>=7.0\", \"black\"]\n const arrayMatch = line.match(/^[a-zA-Z0-9_-]+\\s*=\\s*\\[([^\\]]*)\\]/);\n if (arrayMatch) {\n this.extractPepNames(arrayMatch[1], directNames);\n }\n }\n }\n }\n\n /**\n * Extracts PEP 508 package names from a comma-separated\n * list of quoted dependency strings.\n */\n private extractPepNames(content: string, directNames: Set<string>): void {\n const depStrings = content.match(/\"([^\"]*)\"/g);\n if (!depStrings) return;\n\n for (const quoted of depStrings) {\n const depStr = quoted.replace(/\"/g, '').trim();\n if (!depStr) continue;\n\n // PEP 508: name is everything before the first specifier char\n const nameMatch = depStr.match(/^([a-zA-Z0-9_][a-zA-Z0-9._-]*)/);\n if (nameMatch && nameMatch[1]) {\n directNames.add(this.normalizePipName(nameMatch[1]));\n }\n }\n }\n\n /**\n * Normalizes a pip package name per PEP 503.\n * Converts to lowercase and replaces any run of [-_.] with a single hyphen.\n */\n private normalizePipName(name: string): string {\n return name.toLowerCase().replace(/[-_.]+/g, '-');\n }\n\n /**\n * Builds a purl for a PyPI package.\n * Per purl spec, the type is \"pypi\" (not \"uv\").\n */\n private buildPurl(name: string, version: string): string {\n return `pkg:pypi/${this.normalizePipName(name)}@${version}`;\n }\n}\n\n// ─── Internal types ──────────────────────────────────────────────\n\ninterface UvPackage {\n name: string;\n version: string;\n isEditable: boolean;\n}\n","import type { DependencyScanner } from './scanner.interface.js';\nimport type { ScanResult } from '../core/types.js';\nimport { NpmScanner } from './npm/npm-scanner.js';\nimport { NugetScanner } from './nuget/nuget-scanner.js';\nimport { CargoScanner } from './cargo/cargo-scanner.js';\nimport { PipScanner } from './pip/pip-scanner.js';\nimport { MavenScanner } from './maven/maven-scanner.js';\nimport { GoScanner } from './go/go-scanner.js';\nimport { RubyScanner } from './ruby/ruby-scanner.js';\nimport { ComposerScanner } from './composer/composer-scanner.js';\nimport { NoLockfileError } from '../core/errors.js';\nimport { YarnScanner } from './yarn/yarn-scanner.js';\nimport { PnpmScanner } from './pnpm/pnpm-scanner.js';\nimport { DenoScanner } from './deno/deno-scanner.js';\nimport { PoetryScanner } from './poetry/poetry-scanner.js';\nimport { UvScanner } from './uv/uv-scanner.js';\n\n/**\n * Registry of all available dependency scanners.\n * Auto-detects the correct scanner for a given project.\n */\nexport class ScannerRegistry {\n private scanners: DependencyScanner[];\n\n constructor() {\n this.scanners = [\n new NpmScanner(),\n new NugetScanner(),\n new CargoScanner(),\n new PipScanner(),\n new PoetryScanner(),\n new UvScanner(),\n new MavenScanner(),\n new GoScanner(),\n new RubyScanner(),\n new ComposerScanner(),\n new YarnScanner(),\n new PnpmScanner(),\n new DenoScanner(),\n ];\n }\n\n /**\n * Auto-detects the project's ecosystem and scans dependencies.\n * Tries each registered scanner in order until one matches.\n */\n async detectAndScan(projectPath: string): Promise<ScanResult> {\n for (const scanner of this.scanners) {\n const lockfilePath = await scanner.detect(projectPath);\n if (lockfilePath) {\n return scanner.scan(projectPath, lockfilePath);\n }\n }\n throw new NoLockfileError(projectPath);\n }\n\n /** Returns a specific scanner by ecosystem name */\n getScanner(ecosystem: string): DependencyScanner | undefined {\n return this.scanners.find((s) => s.ecosystem === ecosystem);\n }\n\n /** Lists all registered ecosystems */\n listEcosystems(): string[] {\n return this.scanners.map((s) => s.ecosystem);\n }\n}\n","import { randomUUID } from 'crypto';\nimport type { SbomGenerator } from './generator.interface.js';\nimport type { ScanResult, Sbom, SbomFormat, Dependency } from '../core/types.js';\nimport {\n DEFAULT_TOOL_VERSION,\n VERIMU_TOOL_DESCRIPTION,\n VERIMU_TOOL_NAME,\n VERIMU_TOOL_WEBSITE,\n deriveSupplierName,\n extractProjectName,\n} from './shared.js';\n\n/** Supported CycloneDX spec versions */\nexport type CycloneDxSpecVersion = '1.4' | '1.5' | '1.6' | '1.7';\n\nconst SCHEMA_URLS: Record<CycloneDxSpecVersion, string> = {\n '1.4': 'http://cyclonedx.org/schema/bom-1.4.schema.json',\n '1.5': 'http://cyclonedx.org/schema/bom-1.5.schema.json',\n '1.6': 'http://cyclonedx.org/schema/bom-1.6.schema.json',\n '1.7': 'http://cyclonedx.org/schema/bom-1.7.schema.json',\n};\n\n/**\n * Generates CycloneDX JSON SBOMs (versions 1.4 – 1.7).\n *\n * CycloneDX is the preferred SBOM format for CRA compliance.\n * Spec: https://cyclonedx.org/specification/overview/\n *\n * NTIA minimum elements are satisfied:\n * - metadata.supplier (supplier of the root software)\n * - components[].supplier (supplier of each dependency)\n * - components[].name, version, purl, bom-ref\n * - dependencies[] graph\n *\n * Schema differences handled per version:\n * - 1.4: metadata.tools is a flat array of { vendor, name, version, ... }\n * - 1.5+: metadata.tools is { components: [...] }\n */\nexport class CycloneDxGenerator implements SbomGenerator {\n readonly format: SbomFormat = 'cyclonedx-json';\n\n constructor(private readonly specVersion: CycloneDxSpecVersion = '1.7') { }\n\n generate(scanResult: ScanResult, toolVersion: string = DEFAULT_TOOL_VERSION): Sbom {\n const bom = this.buildBom(scanResult, toolVersion);\n const content = JSON.stringify(bom, null, 2);\n\n return {\n format: 'cyclonedx-json',\n specVersion: this.specVersion,\n content,\n componentCount: scanResult.dependencies.length,\n generatedAt: new Date().toISOString(),\n };\n }\n\n private buildBom(scanResult: ScanResult, toolVersion: string): CycloneDxBom {\n const projectName = extractProjectName(scanResult.projectPath);\n\n return {\n $schema: SCHEMA_URLS[this.specVersion],\n bomFormat: 'CycloneDX',\n specVersion: this.specVersion,\n serialNumber: `urn:uuid:${randomUUID()}`,\n version: 1,\n metadata: {\n timestamp: new Date().toISOString(),\n tools: this.buildTools(toolVersion),\n // NTIA: metadata.supplier — the org supplying the root software\n supplier: {\n name: projectName,\n },\n component: {\n type: 'application',\n name: projectName,\n 'bom-ref': 'root-component',\n supplier: { name: projectName },\n },\n },\n components: scanResult.dependencies.map((dep) => this.toComponent(dep)),\n dependencies: this.buildDependencyGraph(scanResult),\n };\n }\n\n /** Converts a Verimu Dependency to a CycloneDX component */\n private toComponent(dep: Dependency): CycloneDxComponent {\n return {\n type: 'library',\n name: dep.name,\n version: dep.version,\n purl: dep.purl,\n 'bom-ref': dep.purl,\n scope: dep.direct ? 'required' : 'optional',\n // NTIA: component.supplier — derived from npm scope or package name\n supplier: {\n name: deriveSupplierName(dep.name),\n },\n };\n }\n\n /**\n * Builds the dependency graph section of the SBOM.\n *\n * The root component depends on all dependencies (direct + transitive).\n * This ensures a single root node in the graph, which NTIA validators expect.\n *\n * We include ALL deps under root (not just direct) because from a flat lockfile\n * we can't reliably reconstruct which transitive dep belongs to which direct dep.\n * This is still valid per the CycloneDX spec — it represents a complete but flat\n * dependency relationship.\n */\n private buildDependencyGraph(scanResult: ScanResult): CycloneDxDependencyEntry[] {\n const allDepPurls = scanResult.dependencies.map((d) => d.purl);\n\n return [\n {\n ref: 'root-component',\n dependsOn: allDepPurls,\n },\n ];\n }\n\n /**\n * Builds the tools metadata section.\n *\n * CycloneDX 1.4: tools is a flat array of { vendor, name, version, ... }\n * CycloneDX 1.5+: tools is an object { components: [...] }\n */\n private buildTools(toolVersion: string): CycloneDxTools {\n if (this.specVersion === '1.4') {\n return [\n {\n vendor: 'Verimu',\n name: VERIMU_TOOL_NAME,\n version: toolVersion,\n externalReferences: [{ type: 'website', url: VERIMU_TOOL_WEBSITE }],\n },\n ];\n }\n\n return {\n components: [\n {\n type: 'application',\n name: VERIMU_TOOL_NAME,\n version: toolVersion,\n description: VERIMU_TOOL_DESCRIPTION,\n supplier: { name: 'Verimu' },\n externalReferences: [{ type: 'website', url: VERIMU_TOOL_WEBSITE }],\n },\n ],\n };\n }\n}\n\n// ─── CycloneDX JSON Types ─────────────────────────────────────\n\ninterface OrganizationalEntity {\n name: string;\n url?: string[];\n contact?: Array<{ name?: string; email?: string; phone?: string }>;\n}\n\n/** tools format for CycloneDX 1.4 — a flat array of tool objects */\ntype CycloneDxToolsV14 = Array<{\n vendor?: string;\n name: string;\n version: string;\n externalReferences?: Array<{ type: string; url: string }>;\n}>;\n\n/** tools format for CycloneDX 1.5+ — object with a components array */\ntype CycloneDxToolsV15Plus = {\n components: Array<{\n type: string;\n name: string;\n version: string;\n description?: string;\n supplier?: OrganizationalEntity;\n externalReferences?: Array<{ type: string; url: string }>;\n }>;\n};\n\ntype CycloneDxTools = CycloneDxToolsV14 | CycloneDxToolsV15Plus;\n\ninterface CycloneDxBom {\n $schema: string;\n bomFormat: string;\n specVersion: string;\n serialNumber: string;\n version: number;\n metadata: {\n timestamp: string;\n tools: CycloneDxTools;\n supplier: OrganizationalEntity;\n component: {\n type: string;\n name: string;\n 'bom-ref': string;\n supplier: OrganizationalEntity;\n };\n };\n components: CycloneDxComponent[];\n dependencies: CycloneDxDependencyEntry[];\n}\n\ninterface CycloneDxComponent {\n type: string;\n name: string;\n version: string;\n purl: string;\n 'bom-ref': string;\n scope?: string;\n supplier: OrganizationalEntity;\n}\n\ninterface CycloneDxDependencyEntry {\n ref: string;\n dependsOn: string[];\n}\n","import { randomUUID } from 'crypto';\nimport type { SbomGenerator } from './generator.interface.js';\nimport type { ScanResult, Sbom, SbomFormat } from '../core/types.js';\nimport {\n DEFAULT_TOOL_VERSION,\n VERIMU_TOOL_NAME,\n extractProjectName,\n normalizeDependencies,\n} from './shared.js';\n\nconst SPDX_VERSION = '2.3';\n\n/** Generates SPDX 2.3 JSON SBOMs. */\nexport class SpdxJsonGenerator implements SbomGenerator {\n readonly format: SbomFormat = 'spdx-json';\n\n generate(scanResult: ScanResult, toolVersion: string = DEFAULT_TOOL_VERSION): Sbom {\n const timestamp = new Date().toISOString();\n const projectName = extractProjectName(scanResult.projectPath);\n const rootPackageId = 'SPDXRef-Package-root';\n const dependencies = normalizeDependencies(scanResult.dependencies);\n\n const document = {\n spdxVersion: `SPDX-${SPDX_VERSION}`,\n dataLicense: 'CC0-1.0',\n SPDXID: 'SPDXRef-DOCUMENT',\n name: `${projectName}-sbom`,\n documentNamespace: `https://verimu.com/spdxdocs/${projectName}-${randomUUID()}`,\n creationInfo: {\n created: timestamp,\n creators: [`Tool: ${VERIMU_TOOL_NAME}@${toolVersion}`],\n },\n documentDescribes: [rootPackageId],\n packages: [\n {\n name: projectName,\n SPDXID: rootPackageId,\n versionInfo: 'NOASSERTION',\n supplier: `Organization: ${projectName}`,\n downloadLocation: 'NOASSERTION',\n filesAnalyzed: false,\n licenseConcluded: 'NOASSERTION',\n licenseDeclared: 'NOASSERTION',\n primaryPackagePurpose: 'APPLICATION',\n },\n ...dependencies.map((dep, index) => ({\n name: dep.name,\n SPDXID: `SPDXRef-Package-${index + 1}`,\n versionInfo: dep.version,\n supplier: `Organization: ${dep.supplierName}`,\n downloadLocation: 'NOASSERTION',\n filesAnalyzed: false,\n licenseConcluded: 'NOASSERTION',\n licenseDeclared: 'NOASSERTION',\n primaryPackagePurpose: 'LIBRARY',\n externalRefs: [\n {\n referenceCategory: 'PACKAGE-MANAGER',\n referenceType: 'purl',\n referenceLocator: dep.purl,\n },\n ],\n })),\n ],\n relationships: [\n {\n spdxElementId: 'SPDXRef-DOCUMENT',\n relationshipType: 'DESCRIBES',\n relatedSpdxElement: rootPackageId,\n },\n ...dependencies.map((_dep, index) => ({\n spdxElementId: rootPackageId,\n relationshipType: 'DEPENDS_ON',\n relatedSpdxElement: `SPDXRef-Package-${index + 1}`,\n })),\n ],\n };\n\n return {\n format: 'spdx-json',\n specVersion: SPDX_VERSION,\n content: JSON.stringify(document, null, 2),\n componentCount: scanResult.dependencies.length,\n generatedAt: timestamp,\n };\n }\n}\n\n","import { randomUUID } from 'crypto';\nimport type { SbomGenerator } from './generator.interface.js';\nimport type { ScanResult, Sbom, SbomFormat } from '../core/types.js';\nimport {\n DEFAULT_SWID_VERSION,\n DEFAULT_TOOL_VERSION,\n VERIMU_TOOL_NAME,\n extractProjectName,\n} from './shared.js';\n\nconst SWID_SPEC_VERSION = 'ISO/IEC 19770-2:2015';\n\n/** Generates a minimal SWID XML tag for the root software product. */\nexport class SwidTagGenerator implements SbomGenerator {\n readonly format: SbomFormat = 'swid-xml';\n\n generate(scanResult: ScanResult, toolVersion: string = DEFAULT_TOOL_VERSION): Sbom {\n const timestamp = new Date().toISOString();\n const projectName = extractProjectName(scanResult.projectPath);\n const tagId = `com.verimu:${sanitizeTagId(projectName)}:${DEFAULT_SWID_VERSION}:${randomUUID()}`;\n\n const content = [\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>',\n '<SoftwareIdentity',\n ' xmlns=\"http://standards.iso.org/iso/19770/-2/2015/schema.xsd\"',\n ` name=\"${escapeXml(projectName)}\"`,\n ` tagId=\"${escapeXml(tagId)}\"`,\n ' tagVersion=\"1\"',\n ` version=\"${DEFAULT_SWID_VERSION}\"`,\n ' versionScheme=\"semver\">',\n ` <Entity name=\"${escapeXml(projectName)}\" role=\"softwareCreator\" />`,\n ' <Entity name=\"Verimu\" role=\"tagCreator\" />',\n ` <Meta product=\"${escapeXml(projectName)}\" generator=\"${VERIMU_TOOL_NAME}\" toolVersion=\"${toolVersion}\" generated=\"${timestamp}\" />`,\n ' <!-- TODO: Consider adding dependency/package evidence if we need richer SWID coverage. -->',\n ' <Link rel=\"describedby\" href=\"https://verimu.com\" />',\n '</SoftwareIdentity>',\n ].join('\\n');\n\n return {\n format: 'swid-xml',\n specVersion: SWID_SPEC_VERSION,\n content,\n componentCount: 1,\n generatedAt: timestamp,\n };\n }\n}\n\nfunction escapeXml(value: string): string {\n return value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll(\"'\", ''');\n}\n\nfunction sanitizeTagId(value: string): string {\n return value\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n","import type { ScanResult, SbomArtifacts } from '../core/types.js';\nimport { DEFAULT_TOOL_VERSION } from './shared.js';\nimport { CycloneDxGenerator } from './cyclonedx.js';\nimport type { CycloneDxSpecVersion } from './cyclonedx.js';\nimport { SpdxJsonGenerator } from './spdx.js';\nimport { SwidTagGenerator } from './swid.js';\n\n/** Generates every supported software inventory artifact for a scan. */\nexport function generateSbomArtifacts(\n scanResult: ScanResult,\n toolVersion: string = DEFAULT_TOOL_VERSION,\n cyclonedxVersion: CycloneDxSpecVersion = '1.7'\n): SbomArtifacts {\n // TODO: Make artifact selection configurable instead of always generating all supported formats.\n return {\n cyclonedx: new CycloneDxGenerator(cyclonedxVersion).generate(scanResult, toolVersion),\n spdx: new SpdxJsonGenerator().generate(scanResult, toolVersion),\n swid: new SwidTagGenerator().generate(scanResult, toolVersion),\n };\n}\n\n","import type { CveSource } from './source.interface.js';\nimport type { Dependency, Vulnerability, VulnerabilitySource, Severity } from '../core/types.js';\n\nconst OSV_API_BASE = 'https://api.osv.dev/v1';\nconst BATCH_SIZE = 1000; // OSV querybatch supports up to 1000\n\n/**\n * OSV.dev (Google Open Source Vulnerabilities) CVE source.\n *\n * Primary CVE source for Verimu because:\n * - Supports direct package name + ecosystem + version queries\n * - Has batch query endpoint for efficiency\n * - No authentication required\n * - Covers npm, PyPI, Go, Rust, Maven, NuGet, etc.\n * - Aggregates data from GitHub Advisory, NVD, and others\n *\n * API docs: https://google.github.io/osv.dev/api/\n *\n * Note: /v1/querybatch only returns minimal data (id, modified).\n * Full vulnerability details must be fetched via /v1/vulns/{id}.\n */\nexport class OsvSource implements CveSource {\n readonly sourceId: VulnerabilitySource = 'osv';\n readonly name = 'OSV.dev (Google Open Source Vulnerabilities)';\n\n private fetchFn: typeof fetch;\n\n constructor(fetchImpl?: typeof fetch) {\n // Allow injecting fetch for testing\n this.fetchFn = fetchImpl ?? globalThis.fetch;\n }\n\n async checkDependencies(dependencies: Dependency[]): Promise<Vulnerability[]> {\n if (dependencies.length === 0) return [];\n\n const allVulns: Vulnerability[] = [];\n\n // Process in batches of BATCH_SIZE\n for (let i = 0; i < dependencies.length; i += BATCH_SIZE) {\n const batch = dependencies.slice(i, i + BATCH_SIZE);\n const batchVulns = await this.queryBatch(batch);\n allVulns.push(...batchVulns);\n }\n\n return allVulns;\n }\n\n /**\n * Uses OSV's /querybatch endpoint to get vulnerability IDs,\n * then fetches full details for each unique vulnerability.\n */\n private async queryBatch(dependencies: Dependency[]): Promise<Vulnerability[]> {\n const queries = dependencies.map((dep) => ({\n version: dep.version,\n package: {\n name: dep.name,\n ecosystem: this.mapEcosystem(dep.ecosystem),\n },\n }));\n\n const response = await this.fetchFn(`${OSV_API_BASE}/querybatch`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ queries }),\n });\n\n if (!response.ok) {\n throw new Error(`OSV API error: ${response.status} ${response.statusText}`);\n }\n\n const data = (await response.json()) as OsvBatchResponse;\n\n // Collect unique vuln IDs and track which dependencies they affect\n const vulnIdToDeps = new Map<string, Dependency[]>();\n\n for (let i = 0; i < data.results.length; i++) {\n const result = data.results[i];\n const dep = dependencies[i];\n\n if (result.vulns && result.vulns.length > 0) {\n for (const vuln of result.vulns) {\n const existing = vulnIdToDeps.get(vuln.id);\n if (existing) {\n existing.push(dep);\n } else {\n vulnIdToDeps.set(vuln.id, [dep]);\n }\n }\n }\n }\n\n if (vulnIdToDeps.size === 0) {\n return [];\n }\n\n // Fetch full details for each unique vulnerability\n const vulnIds = Array.from(vulnIdToDeps.keys());\n const fullVulns = await this.fetchVulnerabilityDetails(vulnIds);\n\n // Map full vulnerability data to our format, linking to affected deps\n const vulnerabilities: Vulnerability[] = [];\n\n for (const osvVuln of fullVulns) {\n const affectedDeps = vulnIdToDeps.get(osvVuln.id) ?? [];\n for (const dep of affectedDeps) {\n vulnerabilities.push(this.mapVulnerability(osvVuln, dep));\n }\n }\n\n return vulnerabilities;\n }\n\n /**\n * Fetches full vulnerability details from /v1/vulns/{id} for each ID.\n * Makes parallel requests for efficiency.\n */\n private async fetchVulnerabilityDetails(vulnIds: string[]): Promise<OsvVulnerability[]> {\n const results: OsvVulnerability[] = [];\n\n // Fetch in parallel with a reasonable concurrency limit\n const CONCURRENCY = 10;\n for (let i = 0; i < vulnIds.length; i += CONCURRENCY) {\n const batch = vulnIds.slice(i, i + CONCURRENCY);\n const promises = batch.map(async (id) => {\n try {\n const response = await this.fetchFn(`${OSV_API_BASE}/vulns/${encodeURIComponent(id)}`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n // Log but don't fail the entire scan for individual vuln fetch errors\n console.warn(`Failed to fetch vulnerability ${id}: ${response.status}`);\n return null;\n }\n\n return (await response.json()) as OsvVulnerability;\n } catch (err) {\n console.warn(`Error fetching vulnerability ${id}:`, err);\n return null;\n }\n });\n\n const batchResults = await Promise.all(promises);\n results.push(...batchResults.filter((v): v is OsvVulnerability => v !== null));\n }\n\n return results;\n }\n\n /** Maps an OSV vulnerability record to our Vulnerability type */\n private mapVulnerability(osvVuln: OsvVulnerability, dep: Dependency): Vulnerability {\n const cveId = this.extractCveId(osvVuln);\n const severity = this.extractSeverity(osvVuln);\n\n return {\n id: cveId || osvVuln.id,\n aliases: Array.from(new Set([osvVuln.id, ...(osvVuln.aliases ?? [])])),\n summary: osvVuln.summary ?? osvVuln.details?.slice(0, 200) ?? 'No description available',\n severity: severity.level,\n cvssScore: severity.score,\n packageName: dep.name,\n ecosystem: dep.ecosystem,\n affectedVersionRange: this.extractAffectedRange(osvVuln, dep.name),\n fixedVersion: this.extractFixedVersion(osvVuln, dep.name),\n exploitedInWild: false, // OSV doesn't track this — CISA KEV does\n source: 'osv',\n referenceUrl: `https://osv.dev/vulnerability/${osvVuln.id}`,\n publishedAt: osvVuln.published,\n };\n }\n\n /** Extracts CVE ID from aliases (prefers CVE-xxxx over GHSA-xxxx) */\n private extractCveId(vuln: OsvVulnerability): string | null {\n // Check the main ID first\n if (vuln.id.startsWith('CVE-')) return vuln.id;\n\n // Check aliases\n if (vuln.aliases) {\n const cve = vuln.aliases.find((a) => a.startsWith('CVE-'));\n if (cve) return cve;\n }\n\n return null;\n }\n\n /** Extracts severity from CVSS scores in the OSV record */\n private extractSeverity(vuln: OsvVulnerability): { level: Severity; score?: number } {\n // Try database_specific first (often has CVSS)\n if (vuln.severity && vuln.severity.length > 0) {\n for (const sev of vuln.severity) {\n if (sev.type === 'CVSS_V3') {\n const score = this.parseCvssScore(sev.score);\n if (score !== null) {\n return { level: this.scoreToSeverity(score), score };\n }\n }\n }\n }\n\n // Try to extract from database_specific\n if (vuln.database_specific?.severity) {\n const s = vuln.database_specific.severity.toUpperCase();\n if (['CRITICAL', 'HIGH', 'MEDIUM', 'LOW'].includes(s)) {\n return { level: s as Severity };\n }\n }\n\n return { level: 'UNKNOWN' };\n }\n\n /** Parses CVSS v3 vector string to extract/calculate the base score */\n private parseCvssScore(vectorOrScore: string): number | null {\n // Could be a raw score like \"7.5\"\n const num = parseFloat(vectorOrScore);\n if (!isNaN(num) && num >= 0 && num <= 10) return num;\n\n // Parse CVSS v3.x vector string like \"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H\"\n if (vectorOrScore.startsWith('CVSS:3')) {\n return this.calculateCvss3Score(vectorOrScore);\n }\n\n return null;\n }\n\n /** Calculate CVSS v3.x base score from vector string */\n private calculateCvss3Score(vector: string): number | null {\n // CVSS v3 metric values\n const metricValues: Record<string, Record<string, number>> = {\n AV: { N: 0.85, A: 0.62, L: 0.55, P: 0.2 }, // Attack Vector\n AC: { L: 0.77, H: 0.44 }, // Attack Complexity\n PR: { // Privileges Required (varies by Scope)\n N_U: 0.85, L_U: 0.62, H_U: 0.27,\n N_C: 0.85, L_C: 0.68, H_C: 0.5,\n },\n UI: { N: 0.85, R: 0.62 }, // User Interaction\n C: { H: 0.56, L: 0.22, N: 0 }, // Confidentiality Impact\n I: { H: 0.56, L: 0.22, N: 0 }, // Integrity Impact\n A: { H: 0.56, L: 0.22, N: 0 }, // Availability Impact\n };\n\n // Parse vector string\n const parts = vector.split('/');\n const metrics: Record<string, string> = {};\n for (const part of parts) {\n const [key, value] = part.split(':');\n if (key && value) metrics[key] = value;\n }\n\n // Extract required metrics\n const av = metricValues.AV[metrics.AV];\n const ac = metricValues.AC[metrics.AC];\n const ui = metricValues.UI[metrics.UI];\n const scope = metrics.S; // 'U' = Unchanged, 'C' = Changed\n const c = metricValues.C[metrics.C];\n const i = metricValues.I[metrics.I];\n const a = metricValues.A[metrics.A];\n\n // PR depends on Scope\n const prKey = `${metrics.PR}_${scope}`;\n const pr = metricValues.PR[prKey];\n\n if ([av, ac, pr, ui, c, i, a].some((v) => v === undefined)) {\n return null; // Missing required metrics\n }\n\n // Calculate Impact Sub Score (ISS)\n const iss = 1 - (1 - c) * (1 - i) * (1 - a);\n\n // Calculate Impact\n let impact: number;\n if (scope === 'U') {\n impact = 6.42 * iss;\n } else {\n impact = 7.52 * (iss - 0.029) - 3.25 * Math.pow(iss - 0.02, 15);\n }\n\n // Calculate Exploitability\n const exploitability = 8.22 * av * ac * pr * ui;\n\n // Calculate Base Score\n if (impact <= 0) return 0;\n\n let baseScore: number;\n if (scope === 'U') {\n baseScore = Math.min(impact + exploitability, 10);\n } else {\n baseScore = Math.min(1.08 * (impact + exploitability), 10);\n }\n\n // Round up to 1 decimal place (CVSS spec)\n return Math.ceil(baseScore * 10) / 10;\n }\n\n /** Converts a CVSS score (0-10) to a severity level */\n private scoreToSeverity(score: number): Severity {\n if (score >= 9.0) return 'CRITICAL';\n if (score >= 7.0) return 'HIGH';\n if (score >= 4.0) return 'MEDIUM';\n if (score > 0.0) return 'LOW';\n return 'UNKNOWN';\n }\n\n /** Extracts affected version range for a specific package */\n private extractAffectedRange(vuln: OsvVulnerability, packageName: string): string | undefined {\n if (!vuln.affected) return undefined;\n\n for (const affected of vuln.affected) {\n if (affected.package?.name === packageName && affected.ranges) {\n for (const range of affected.ranges) {\n if (range.events) {\n const introduced = range.events.find((e) => e.introduced)?.introduced;\n const fixed = range.events.find((e) => e.fixed)?.fixed;\n if (introduced && fixed) return `>=${introduced}, <${fixed}`;\n if (introduced) return `>=${introduced}`;\n }\n }\n }\n }\n return undefined;\n }\n\n /** Extracts the fixed version for a specific package */\n private extractFixedVersion(vuln: OsvVulnerability, packageName: string): string | undefined {\n if (!vuln.affected) return undefined;\n\n for (const affected of vuln.affected) {\n if (affected.package?.name === packageName && affected.ranges) {\n for (const range of affected.ranges) {\n if (range.events) {\n const fixed = range.events.find((e) => e.fixed)?.fixed;\n if (fixed) return fixed;\n }\n }\n }\n }\n return undefined;\n }\n\n /** Maps our ecosystem names to OSV ecosystem names */\n private mapEcosystem(ecosystem: string): string {\n const map: Record<string, string> = {\n npm: 'npm',\n nuget: 'NuGet',\n cargo: 'crates.io',\n maven: 'Maven',\n pip: 'PyPI',\n poetry: 'PyPI',\n uv: 'PyPI',\n go: 'Go',\n ruby: 'RubyGems',\n composer: 'Packagist',\n deno: 'JSR', // JSR packages (Deno registry)\n };\n return map[ecosystem] ?? ecosystem;\n }\n}\n\n// ─── OSV API Response Types ─────────────────────────────────────\n\n/** Response from /v1/querybatch - returns minimal vuln info (just id and modified) */\ninterface OsvBatchResponse {\n results: Array<{\n vulns?: OsvBatchVuln[];\n }>;\n}\n\n/** Minimal vulnerability info returned by /v1/querybatch */\ninterface OsvBatchVuln {\n id: string;\n modified?: string;\n}\n\n/** Full vulnerability details from /v1/vulns/{id} */\ninterface OsvVulnerability {\n id: string;\n summary?: string;\n details?: string;\n aliases?: string[];\n published?: string;\n modified?: string;\n severity?: Array<{\n type: string;\n score: string;\n }>;\n affected?: Array<{\n package?: {\n name: string;\n ecosystem: string;\n };\n ranges?: Array<{\n type: string;\n events: Array<{\n introduced?: string;\n fixed?: string;\n last_affected?: string;\n }>;\n }>;\n versions?: string[];\n }>;\n database_specific?: {\n severity?: string;\n [key: string]: unknown;\n };\n references?: Array<{\n type: string;\n url: string;\n }>;\n}\n","import type { CveSource } from './source.interface.js';\nimport type { Dependency, CveCheckResult, Vulnerability, VulnerabilitySource } from '../core/types.js';\nimport { OsvSource } from './osv.js';\n\n/**\n * Aggregates vulnerability data from multiple CVE sources.\n * Deduplicates results by CVE ID across sources.\n */\nexport class CveAggregator {\n private sources: CveSource[];\n\n constructor(sources?: CveSource[]) {\n this.sources = sources ?? [\n new OsvSource(),\n // Future: new NvdSource(), new EuvdSource(), new CisaKevSource()\n ];\n }\n\n /**\n * Checks dependencies against all registered CVE sources.\n * Runs sources in parallel and merges/deduplicates results.\n */\n async check(dependencies: Dependency[]): Promise<CveCheckResult> {\n const startTime = Date.now();\n const sourcesQueried: VulnerabilitySource[] = [];\n const sourceErrors: { source: VulnerabilitySource; error: string }[] = [];\n const allVulns: Vulnerability[] = [];\n\n // Run all sources in parallel\n const results = await Promise.allSettled(\n this.sources.map(async (source) => {\n const vulns = await source.checkDependencies(dependencies);\n return { sourceId: source.sourceId, vulns };\n })\n );\n\n for (const result of results) {\n if (result.status === 'fulfilled') {\n sourcesQueried.push(result.value.sourceId);\n allVulns.push(...result.value.vulns);\n } else {\n // Extract the source ID from the error context\n const sourceIndex = results.indexOf(result);\n const sourceId = this.sources[sourceIndex].sourceId;\n sourceErrors.push({\n source: sourceId,\n error: result.reason instanceof Error ? result.reason.message : String(result.reason),\n });\n }\n }\n\n // Deduplicate by CVE ID (prefer the entry with more data)\n const deduplicated = this.deduplicateVulnerabilities(allVulns);\n\n return {\n vulnerabilities: deduplicated,\n sourcesQueried,\n sourceErrors,\n checkDurationMs: Date.now() - startTime,\n };\n }\n\n /**\n * Deduplicates vulnerabilities by ID.\n * When the same CVE appears from multiple sources,\n * keeps the one with more complete data (has CVSS score, has fix version, etc.)\n */\n private deduplicateVulnerabilities(vulns: Vulnerability[]): Vulnerability[] {\n const byKey = new Map<string, Vulnerability>();\n\n for (const vuln of vulns) {\n // Key by (vulnerability ID + package name) to handle the same CVE\n // affecting multiple packages\n const key = `${vuln.id}::${vuln.packageName}`;\n const existing = byKey.get(key);\n\n if (!existing) {\n byKey.set(key, vuln);\n } else {\n // Keep the one with more data\n byKey.set(key, this.pickBetterEntry(existing, vuln));\n }\n }\n\n return Array.from(byKey.values());\n }\n\n /** Picks the vulnerability entry with more complete data */\n private pickBetterEntry(a: Vulnerability, b: Vulnerability): Vulnerability {\n let scoreA = 0;\n let scoreB = 0;\n\n if (a.cvssScore !== undefined) scoreA++;\n if (b.cvssScore !== undefined) scoreB++;\n if (a.fixedVersion) scoreA++;\n if (b.fixedVersion) scoreB++;\n if (a.affectedVersionRange) scoreA++;\n if (b.affectedVersionRange) scoreB++;\n if (a.severity !== 'UNKNOWN') scoreA++;\n if (b.severity !== 'UNKNOWN') scoreB++;\n\n // Merge: start with the lesser entry, overlay with the better one.\n // Strip undefined/null values so they don't overwrite real data.\n const strip = (obj: Record<string, unknown>) =>\n Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined && v !== null));\n\n const winner = scoreB > scoreA\n ? { ...strip(a as unknown as Record<string, unknown>), ...strip(b as unknown as Record<string, unknown>) } as unknown as Vulnerability\n : { ...strip(b as unknown as Record<string, unknown>), ...strip(a as unknown as Record<string, unknown>) } as unknown as Vulnerability;\n\n // Merge aliases\n const allAliases = new Set([...a.aliases, ...b.aliases]);\n winner.aliases = Array.from(allAliases);\n\n // If either says exploited, it's exploited\n winner.exploitedInWild = a.exploitedInWild || b.exploitedInWild;\n\n return winner;\n }\n}\n","import type { Reporter } from './reporter.interface.js';\nimport type { VerimuReport, Vulnerability, Severity } from '../core/types.js';\n\n/** Outputs a human-readable console report */\nexport class ConsoleReporter implements Reporter {\n readonly name = 'console';\n\n report(result: VerimuReport): string {\n const lines: string[] = [];\n\n lines.push('');\n lines.push('┌─────────────────────────────────────────────┐');\n lines.push('│ VERIMU CRA COMPLIANCE SCAN │');\n lines.push('└─────────────────────────────────────────────┘');\n lines.push('');\n\n // Project info\n lines.push(` Project: ${result.project.path}`);\n lines.push(` Ecosystem: ${result.project.ecosystem}`);\n lines.push(` Dependencies: ${result.project.dependencyCount}`);\n lines.push(` Scanned at: ${result.generatedAt}`);\n lines.push('');\n\n // SBOM info\n lines.push(` ✓ SBOM generated (${result.sbom.format}, ${result.sbom.specVersion})`);\n lines.push(` Components: ${result.sbom.componentCount}`);\n if (result.artifacts) {\n lines.push(` Also wrote: ${result.artifacts.spdx.format}, ${result.artifacts.swid.format}`);\n }\n lines.push('');\n\n // CVE results\n const vulns = result.cveCheck.vulnerabilities;\n if (vulns.length === 0) {\n lines.push(' ✓ No known vulnerabilities found');\n } else {\n lines.push(` ⚠ ${vulns.length} vulnerabilit${vulns.length === 1 ? 'y' : 'ies'} found:`);\n lines.push('');\n\n // Sort by severity: CRITICAL → HIGH → MEDIUM → LOW → UNKNOWN\n const sorted = [...vulns].sort((a, b) => severityOrder(a.severity) - severityOrder(b.severity));\n\n for (const vuln of sorted) {\n const badge = severityBadge(vuln.severity);\n const fix = vuln.fixedVersion ? ` → fix: ${vuln.fixedVersion}` : '';\n lines.push(` ${badge} ${vuln.id}`);\n lines.push(` ${vuln.packageName}@${vuln.affectedVersionRange ?? '?'}${fix}`);\n lines.push(` ${vuln.summary.slice(0, 100)}`);\n if (vuln.exploitedInWild) {\n lines.push(` 🔴 ACTIVELY EXPLOITED — 24h CRA reporting required`);\n }\n lines.push('');\n }\n }\n\n // Sources\n const sources = result.cveCheck.sourcesQueried.join(', ');\n lines.push(` Sources queried: ${sources} (${result.cveCheck.checkDurationMs}ms)`);\n\n if (result.cveCheck.sourceErrors.length > 0) {\n for (const err of result.cveCheck.sourceErrors) {\n lines.push(` ⚠ ${err.source}: ${err.error}`);\n }\n }\n\n // Summary\n lines.push('');\n lines.push(' ─── Summary ───');\n lines.push(` Total: ${result.summary.totalVulnerabilities} | ` +\n `Critical: ${result.summary.critical} | ` +\n `High: ${result.summary.high} | ` +\n `Medium: ${result.summary.medium} | ` +\n `Low: ${result.summary.low}`);\n\n if (result.summary.exploitedInWild > 0) {\n lines.push(` 🔴 ${result.summary.exploitedInWild} actively exploited — immediate action required`);\n }\n\n lines.push('');\n return lines.join('\\n');\n }\n}\n\nfunction severityOrder(s: Severity): number {\n const order: Record<Severity, number> = {\n CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3, UNKNOWN: 4,\n };\n return order[s] ?? 5;\n}\n\nfunction severityBadge(s: Severity): string {\n const badges: Record<Severity, string> = {\n CRITICAL: '[CRIT]',\n HIGH: '[HIGH]',\n MEDIUM: '[MED] ',\n LOW: '[LOW] ',\n UNKNOWN: '[???] ',\n };\n return badges[s] ?? '[???] ';\n}\n","/**\n * Verimu API client — communicates with the Verimu backend.\n *\n * Used by the CLI and scan pipeline to:\n * 1. Upsert a project (create-if-not-exists)\n * 2. Upload SBOM + trigger CVE scan\n */\n\nimport type { Ecosystem } from '../core/types.js';\n\nconst DEFAULT_API_BASE = 'https://api.verimu.com';\n\nexport interface UpsertProjectResponse {\n project: {\n id: string;\n name: string;\n ecosystem: string;\n repository_url: string | null;\n platform: string | null;\n };\n created: boolean;\n}\n\nexport interface ScanResponse {\n project: {\n id: string;\n name: string;\n };\n scan_results: Array<{\n dependency_id: string;\n dependency_name: string;\n version: string;\n vulnerabilities: Array<{\n cve_id: string;\n severity?: string | null;\n summary?: string | null;\n description?: string | null;\n fixed_version?: string | null;\n sources?: Array<{\n name?: string;\n url?: string;\n data?: {\n fixed_version?: string | null;\n } | null;\n }> | null;\n }> | null;\n }>;\n summary: {\n total_dependencies: number;\n vulnerable_dependencies: number;\n };\n}\n\nexport interface SbomUploadBundle {\n cyclonedx: Record<string, unknown>;\n spdx?: Record<string, unknown>;\n swid?: string;\n}\n\nexport class VerimuApiClient {\n private readonly baseUrl: string;\n private readonly apiKey: string;\n\n constructor(apiKey: string, baseUrl?: string) {\n this.apiKey = apiKey;\n this.baseUrl = (baseUrl ?? DEFAULT_API_BASE).replace(/\\/+$/, '');\n }\n\n /**\n * Upsert a project — finds by name or creates it.\n * Used so `npx verimu` auto-registers projects without manual dashboard setup.\n */\n async upsertProject(opts: {\n name: string;\n ecosystem: Ecosystem;\n repositoryUrl?: string;\n platform?: string;\n }): Promise<UpsertProjectResponse> {\n const res = await fetch(`${this.baseUrl}/api/projects/upsert`, {\n method: 'POST',\n headers: this.headers(),\n body: JSON.stringify({\n name: opts.name,\n ecosystem: this.mapEcosystem(opts.ecosystem),\n repository_url: opts.repositoryUrl ?? null,\n platform: opts.platform ?? null,\n }),\n });\n\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Verimu API: upsert project failed (${res.status}): ${body}`);\n }\n\n return res.json() as Promise<UpsertProjectResponse>;\n }\n\n /**\n * Upload a software inventory artifact payload to a project and trigger CVE scanning.\n *\n * Backward-compatible:\n * - string payloads are treated as legacy raw CycloneDX JSON\n * - object payloads can include CycloneDX + SPDX + SWID together\n */\n async uploadSbom(projectId: string, payload: string | SbomUploadBundle): Promise<ScanResponse> {\n const body = typeof payload === 'string'\n ? JSON.stringify(JSON.parse(payload))\n : JSON.stringify(payload);\n\n const res = await fetch(`${this.baseUrl}/api/projects/${projectId}/scan`, {\n method: 'POST',\n headers: this.headers(),\n body,\n });\n\n if (!res.ok) {\n const body = await res.text();\n throw new Error(`Verimu API: upload SBOM failed (${res.status}): ${body}`);\n }\n\n return res.json() as Promise<ScanResponse>;\n }\n\n private headers(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n 'X-API-Key': this.apiKey,\n };\n }\n\n /**\n * Maps internal ecosystem names to what the backend expects.\n * Currently 1:1, but keeps the mapping explicit.\n */\n private mapEcosystem(eco: Ecosystem): string {\n const map: Record<Ecosystem, string> = {\n npm: 'npm',\n pip: 'pip',\n poetry: 'poetry',\n uv: 'uv',\n maven: 'maven',\n nuget: 'nuget',\n go: 'gomod',\n cargo: 'cargo',\n ruby: 'bundler',\n composer: 'composer',\n deno: 'deno',\n };\n return map[eco] ?? eco;\n }\n}\n"],"mappings":"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","names":["randomUUID","randomUUID","randomUUID","randomUUID","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","match","readFile","existsSync","path","parseYaml","path","existsSync","readFile","parseYaml","name","version","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","readFile","existsSync","path","path","existsSync","readFile","randomUUID","randomUUID","randomUUID","SPDX_VERSION","randomUUID","randomUUID","SWID_SPEC_VERSION","sanitizeTagId","randomUUID","escapeXml","body","severityOrder"]}
|