import { type RunTrackedResult } from "../tooling/exec.js"; /** * Generic acquisition of a standalone mature analyzer BINARY (gitleaks, * trufflehog, …) that has no language package-manager runner. The own-vs-acquire * policy's "acquire the mature tool" half for tools shipped as release binaries. * * Resolution order (all degrade-to-unavailable, never throw): * 1. PATH — the tool is already installed (` version` probes 0). * 2. cache — a previously-downloaded pinned binary is present + executable. * 3. download — fetch the pinned GitHub-release asset for THIS os/arch over * HTTPS, fetch that release's `checksums.txt`, SHA256-verify the * asset against it, extract via the system `tar`, cache, return. * * Security invariants: version is PINNED (reproducibility); the downloaded asset * is SHA256-verified against the official release checksums BEFORE it is extracted * or executed; the fetcher is injected so the engine is testable without network * and an absent network degrades cleanly. A checksum mismatch is fatal-to-this-tool * (returns `unavailable` with reason `checksum_mismatch`), never executed. * * The cache branch is held to the SAME bar as the download branch, on EVERY * resolution, in two layers that only work together: * - LOCATION. The default cache root is per-user (`/.audit-tools/bincache`, * created 0o700 where modes are honoured), never the shared temp dir. The path is * fully derivable from public pinned constants, so "a file exists there" is only * meaningful when the directory is not writable by other local processes. * - INTEGRITY. Each successful download records the executable's digest in a cache * manifest beside it, and a cache hit is only a hit when the bytes still hash to * that recorded digest. Anything else — no manifest, an unreadable manifest, a * digest mismatch, or a version dir left behind by a failed extraction — is PURGED * and re-acquired rather than executed. * * The manifest alone is NOT provenance: anyone who can write the cache dir can write a * self-consistent binary+manifest pair. It detects drift and partial writes; the * location layer is what makes writing the pair privileged in the first place. A * caller passing its own `cacheDir` owns that directory's permissions. */ /** Injected network fetch: returns the URL's bytes, or `null` when unavailable. */ export interface BinaryFetcher { (url: string): Promise; } /** * Injected command runner (probe PATH, run `tar`). THE async runner contract — * the same seam shape as the acquisition engine's {@link AcquisitionRunner} — * defaulting to the shared {@link runTrackedAsync}. There is no synchronous twin: * a synchronous `tar`/probe under a held file lock would block the event loop and * starve that lock's mtime heartbeat. */ export interface BinaryCommandRunner { (argv: string[], cwd: string): Promise; } /** Pinned, os/arch-aware description of one acquirable release binary. */ export interface BinarySpec { /** Executable name on PATH / inside the archive (e.g. "gitleaks"). */ binaryName: string; /** Pinned release version, no leading `v` (e.g. "8.18.4"). */ version: string; /** Probe argv proving an on-PATH install (e.g. ["gitleaks","version"]). */ versionProbeArgs: string[]; /** Asset filename for a platform/arch, or null when unsupported. */ assetFor(platform: NodeJS.Platform, arch: string): string | null; /** * Checksums-file asset name (lists ` ` lines). Either a single * release-wide file (gitleaks/osv-scanner/actionlint), or — when a project * ships one checksum file PER asset (hadolint's `.sha256`, each holding * only that asset's ` *` line) — a function deriving the * checksum-file name from the asset being downloaded. */ checksumsAsset: string | ((assetName: string) => string); /** `${releaseUrlBase}/${assetName}` is the download URL (asset + checksums). */ releaseUrlForAsset(assetName: string): string; /** * Whether the release asset is an archive (.tar.gz/.zip) needing extraction * (the default, e.g. gitleaks), or the raw executable bytes themselves (e.g. * osv-scanner, whose release assets ARE the binary — `osv-scanner_linux_amd64`, * `osv-scanner_windows_amd64.exe`). When `false`, the verified bytes are * written directly to the cache as the executable — no `tar` invocation. */ archived?: boolean; } export interface BinaryResolveOptions { fetch?: BinaryFetcher; run?: BinaryCommandRunner; /** Root cache dir for downloaded binaries; default `/.audit-tools/bincache`. */ cacheDir?: string; platform?: NodeJS.Platform; arch?: string; } /** * WHY a binary could not be resolved, as a machine-readable member rather than a * free-text note. `checksum_mismatch` is a supply-chain event and must never be * indistinguishable from `offline` in the persisted record. */ export type BinaryUnavailableReason = "offline" | "no_asset_for_platform" | "download_failed" | "download_empty" | "no_checksum_for_asset" | "checksum_mismatch" | "extract_failed" | "not_found_in_archive" | "write_failed"; export interface BinaryResolution { status: "path" | "cached" | "downloaded" | "unavailable"; /** Resolved executable (PATH name or absolute cached path); null when unavailable. */ command: string | null; /** Discriminated cause when `status === "unavailable"`. */ reason?: BinaryUnavailableReason; note?: string; } /** Parse a `␠␠` checksums file for one asset's expected digest. */ export declare function expectedSha256For(checksumsText: string, assetName: string): string | null; /** * Resolve an executable for `spec`, acquiring it if necessary. Async (download is * network I/O); never throws. The result's `command` is what the engine spawns, * or null when the tool could not be made available. */ export declare function resolveBinary(spec: BinarySpec, options?: BinaryResolveOptions): Promise; //# sourceMappingURL=binaryAcquisition.d.ts.map