import { spawnSync } from "node:child_process"; import { mkdtemp, readFile, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; type ScanResult = Readonly<{ error?: Error; status: number | null; signal: NodeJS.Signals | null; stdout: string; stderr: string; }>; type SecurityFixtureCheckDependencies = Readonly<{ createTempDirectory?: () => Promise; runScan?: (artifactPath: string, policyPath: string) => ScanResult; readArtifact?: (artifactPath: string) => Promise; cleanup?: (directory: string) => Promise; }>; type Document = Record; function record(value: unknown, label: string): Document { if (!value || typeof value !== "object" || Array.isArray(value)) { throw new TypeError(`${label} must be an object`); } return value as Document; } function defaultScan(artifactPath: string, policyPath: string): ScanResult { const scan = spawnSync( "node", [ "scripts/security-scan.ts", "--policy", policyPath, "--artifact", artifactPath, ], { encoding: "utf8" }, ); return { ...(scan.error ? { error: scan.error } : {}), status: scan.status, signal: scan.signal, stdout: scan.stdout ?? "", stderr: scan.stderr ?? "", }; } function assertExactFindings(rawArtifact: string): void { const sarif = record(JSON.parse(rawArtifact), "security fixture SARIF"); const runs = Array.isArray(sarif.runs) ? sarif.runs : []; const run = record(runs[0], "security fixture SARIF run"); const results = Array.isArray(run.results) ? run.results : []; const actual = results .map((rawResult) => { const result = record(rawResult, "security fixture result"); const locations = Array.isArray(result.locations) ? result.locations : []; const location = record(locations[0], "security fixture location"); const physical = record( location.physicalLocation, "security fixture physical location", ); const artifactLocation = record( physical.artifactLocation, "security fixture artifact location", ); return `${String(artifactLocation.uri)}:${String(result.ruleId)}`; }) .sort(); const root = "tests/fixtures/security/secret-detection/forbidden"; const expected = [ `${root}/config.json:assigned-secret`, `${root}/dist.ts:assigned-secret`, `${root}/source.ts:aws-access-key`, ].sort(); if (JSON.stringify(actual) !== JSON.stringify(expected)) { throw new Error( `security fixture findings mismatch: expected ${expected.join(", ")}; received ${actual.join(", ")}`, ); } } export async function checkSecurityFixtures( dependencies: SecurityFixtureCheckDependencies = {}, ): Promise { const createTempDirectory = dependencies.createTempDirectory ?? (() => mkdtemp(path.join(tmpdir(), "ca-security-fixture-"))); const runScan = dependencies.runScan ?? defaultScan; const readArtifact = dependencies.readArtifact ?? ((target) => readFile(target, "utf8")); const cleanup = dependencies.cleanup ?? ((directory) => rm(directory, { recursive: true, force: true })); const directory = await createTempDirectory(); const artifactPath = path.join(directory, "scan-fixture.sarif"); try { const scan = runScan( artifactPath, "tests/fixtures/security/secret-detection/forbidden-policy.json", ); const expectedDiagnostic = "Security scan found 3 blocking result(s)."; if ( scan.error || scan.status !== 1 || scan.signal !== null || scan.stderr !== `${expectedDiagnostic}\n` ) { throw new Error( `forbidden security fixture did not fail exactly: ${scan.error?.message ?? scan.stderr}`, ); } assertExactFindings(await readArtifact(artifactPath)); } finally { await cleanup(directory); } } export async function checkNonmatchingSecurityIncludeFixture( dependencies: SecurityFixtureCheckDependencies = {}, ): Promise { const createTempDirectory = dependencies.createTempDirectory ?? (() => mkdtemp(path.join(tmpdir(), "ca-security-include-fixture-"))); const runScan = dependencies.runScan ?? defaultScan; const readArtifact = dependencies.readArtifact ?? ((target) => readFile(target, "utf8")); const cleanup = dependencies.cleanup ?? ((directory) => rm(directory, { recursive: true, force: true })); const directory = await createTempDirectory(); const artifactPath = path.join(directory, "scan-fixture.sarif"); try { const includedPath = "tests/fixtures/security/secret-detection/misspelled"; const scan = runScan( artifactPath, "tests/fixtures/security/secret-detection/nonmatching-policy.json", ); if ( scan.error || scan.status !== 1 || scan.signal !== null || !scan.stderr.includes( `secret scan included path matches no inventory file: ${includedPath}`, ) ) { throw new Error( `nonmatching security include fixture did not fail closed: ${scan.error?.message ?? scan.stderr}`, ); } try { await readArtifact(artifactPath); } catch (error) { if ( typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT" ) { return; } throw error; } throw new Error("nonmatching security include fixture wrote an artifact"); } finally { await cleanup(directory); } }