@attest-it/core 0.0.2 → 0.2.0
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/dist/core-alpha.d.ts +14 -0
- package/dist/core-beta.d.ts +14 -0
- package/dist/core-public.d.ts +14 -0
- package/dist/core-unstripped.d.ts +14 -0
- package/dist/index.cjs +8 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/crypto.ts","../src/config.ts","../src/fingerprint.ts","../src/attestation.ts","../src/index.ts","../src/verify.ts"],"names":["resolve","spawn","os","path2","fs2","z","parseYaml","join","readFileSync","readFile","path","crypto","hash","fs","glob","globSync","canonicalizeNamespace","fs3","path3","os2","sign","verify","fs4","path4"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Cryptographic utilities for key generation, signing, and verification.\n *\n * @remarks\n * This module provides cryptographic operations using OpenSSL for key management\n * and signature verification. It uses RSA-2048 with SHA-256 for signatures,\n * which is universally supported across all OpenSSL and LibreSSL versions.\n *\n * @packageDocumentation\n */\n\nimport { spawn } from 'node:child_process'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\n\n/**\n * Paths to a generated keypair.\n * @public\n */\nexport interface KeyPaths {\n /** Path to the private key file */\n privatePath: string\n /** Path to the public key file */\n publicPath: string\n}\n\n/**\n * Options for key generation.\n * @public\n */\nexport interface KeygenOptions {\n /** Path for private key (default: OS-specific config dir) */\n privatePath?: string\n /** Path for public key (default: repo root) */\n publicPath?: string\n /** Overwrite existing keys (default: false) */\n force?: boolean\n}\n\n/**\n * Options for signing data.\n * @public\n */\nexport interface SignOptions {\n /** Path to the private key file */\n privateKeyPath: string\n /** Data to sign (string or Buffer) */\n data: string | Buffer\n}\n\n/**\n * Options for verifying signatures.\n * @public\n */\nexport interface VerifyOptions {\n /** Path to the public key file */\n publicKeyPath: string\n /** Original data that was signed */\n data: string | Buffer\n /** Base64-encoded signature to verify */\n signature: string\n}\n\n/**\n * Result from spawning an OpenSSL process.\n * @internal\n */\ninterface SpawnResult {\n /** Process exit code */\n exitCode: number\n /** Standard output as Buffer */\n stdout: Buffer\n /** Standard error as string */\n stderr: string\n}\n\n/**\n * Run OpenSSL with the given arguments.\n * @param args - Command-line arguments for OpenSSL\n * @param stdin - Optional data to write to stdin\n * @returns Process result with exit code and outputs\n * @internal\n */\nasync function runOpenSSL(args: string[], stdin?: Buffer): Promise<SpawnResult> {\n return new Promise((resolve, reject) => {\n const child = spawn('openssl', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n const stdoutChunks: Buffer[] = []\n let stderr = ''\n\n child.stdout.on('data', (chunk: Buffer) => {\n stdoutChunks.push(chunk)\n })\n\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n })\n\n child.on('error', (err) => {\n reject(new Error(`Failed to spawn OpenSSL: ${err.message}`))\n })\n\n child.on('close', (code) => {\n resolve({\n exitCode: code ?? 1,\n stdout: Buffer.concat(stdoutChunks),\n stderr,\n })\n })\n\n if (stdin) {\n child.stdin.write(stdin)\n }\n child.stdin.end()\n })\n}\n\n/**\n * Check if OpenSSL is available and get version info.\n * @returns OpenSSL version string\n * @throws Error if OpenSSL is not available\n * @public\n */\nexport async function checkOpenSSL(): Promise<string> {\n const result = await runOpenSSL(['version'])\n\n if (result.exitCode !== 0) {\n throw new Error(`OpenSSL check failed: ${result.stderr}`)\n }\n\n return result.stdout.toString().trim()\n}\n\n/**\n * Cached result of OpenSSL availability check.\n * @internal\n */\nlet openSSLChecked = false\n\n/**\n * Ensure OpenSSL is available before performing cryptographic operations.\n * @throws Error with installation instructions if OpenSSL is not available\n * @internal\n */\nasync function ensureOpenSSLAvailable(): Promise<void> {\n if (openSSLChecked) {\n return\n }\n\n try {\n await checkOpenSSL()\n openSSLChecked = true\n } catch {\n throw new Error(\n 'OpenSSL is not installed or not in PATH. ' +\n 'Please install OpenSSL to use attest-it. ' +\n 'On macOS: brew install openssl. ' +\n 'On Ubuntu: apt-get install openssl',\n )\n }\n}\n\n/**\n * Get the default private key path based on OS.\n * - macOS/Linux: ~/.config/attest-it/private.pem\n * - Windows: %APPDATA%\\attest-it\\private.pem\n * @public\n */\nexport function getDefaultPrivateKeyPath(): string {\n const homeDir = os.homedir()\n\n if (process.platform === 'win32') {\n const appData = process.env.APPDATA ?? path.join(homeDir, 'AppData', 'Roaming')\n return path.join(appData, 'attest-it', 'private.pem')\n }\n\n return path.join(homeDir, '.config', 'attest-it', 'private.pem')\n}\n\n/**\n * Get the default public key path (in repo).\n * @public\n */\nexport function getDefaultPublicKeyPath(): string {\n return path.join(process.cwd(), 'attest-it-public.pem')\n}\n\n/**\n * Ensure a directory exists, creating it and parent directories if needed.\n * @param dirPath - Directory path to create\n * @internal\n */\nasync function ensureDir(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true })\n } catch (err) {\n if (err instanceof Error && 'code' in err && err.code !== 'EEXIST') {\n throw err\n }\n }\n}\n\n/**\n * Check if a file exists.\n * @param filePath - File path to check\n * @returns true if file exists\n * @internal\n */\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Clean up one or more files, ignoring errors if files don't exist.\n * @param paths - File paths to delete\n * @internal\n */\nasync function cleanupFiles(...paths: string[]): Promise<void> {\n for (const filePath of paths) {\n try {\n await fs.unlink(filePath)\n } catch {\n // Ignore cleanup errors - file may not exist\n }\n }\n}\n\n/**\n * Generate a new RSA-2048 keypair using OpenSSL.\n *\n * RSA-2048 with SHA-256 is used because it's universally supported across\n * all OpenSSL and LibreSSL versions, including older macOS systems.\n *\n * @param options - Generation options\n * @returns Paths to generated keys\n * @throws Error if OpenSSL fails or keys exist without force\n * @public\n */\nexport async function generateKeyPair(options: KeygenOptions = {}): Promise<KeyPaths> {\n // Ensure OpenSSL is available before proceeding\n await ensureOpenSSLAvailable()\n\n const {\n privatePath = getDefaultPrivateKeyPath(),\n publicPath = getDefaultPublicKeyPath(),\n force = false,\n } = options\n\n // Check if keys already exist\n const privateExists = await fileExists(privatePath)\n const publicExists = await fileExists(publicPath)\n\n if ((privateExists || publicExists) && !force) {\n const existing = [privateExists ? privatePath : null, publicExists ? publicPath : null].filter(\n Boolean,\n )\n throw new Error(\n `Key files already exist: ${existing.join(', ')}. Use force: true to overwrite.`,\n )\n }\n\n // Ensure parent directories exist\n await ensureDir(path.dirname(privatePath))\n await ensureDir(path.dirname(publicPath))\n\n try {\n // Generate RSA-2048 private key\n const genArgs = [\n 'genpkey',\n '-algorithm',\n 'RSA',\n '-pkeyopt',\n 'rsa_keygen_bits:2048',\n '-out',\n privatePath,\n ]\n\n const genResult = await runOpenSSL(genArgs)\n if (genResult.exitCode !== 0) {\n throw new Error(`Failed to generate private key: ${genResult.stderr}`)\n }\n\n // Set restrictive permissions on private key\n await setKeyPermissions(privatePath)\n\n // Extract public key\n const pubResult = await runOpenSSL(['pkey', '-in', privatePath, '-pubout', '-out', publicPath])\n\n if (pubResult.exitCode !== 0) {\n throw new Error(`Failed to extract public key: ${pubResult.stderr}`)\n }\n\n return {\n privatePath,\n publicPath,\n }\n } catch (err) {\n // Clean up both key files on any failure\n await cleanupFiles(privatePath, publicPath)\n throw err\n }\n}\n\n/**\n * Sign data using an RSA private key with SHA-256.\n *\n * Uses `openssl dgst -sha256 -sign` which is universally supported across\n * all OpenSSL and LibreSSL versions.\n *\n * @param options - Signing options\n * @returns Base64-encoded signature\n * @throws Error if signing fails\n * @public\n */\nexport async function sign(options: SignOptions): Promise<string> {\n // Ensure OpenSSL is available before proceeding\n await ensureOpenSSLAvailable()\n\n const { privateKeyPath, data } = options\n\n // Check if private key exists\n if (!(await fileExists(privateKeyPath))) {\n throw new Error(`Private key not found: ${privateKeyPath}`)\n }\n\n // Convert data to Buffer\n const dataBuffer = typeof data === 'string' ? Buffer.from(data, 'utf8') : data\n\n // OpenSSL dgst cannot handle empty files, so we need to add a single byte\n // for empty data and document this limitation\n const processBuffer = dataBuffer.length === 0 ? Buffer.from([0x00]) : dataBuffer\n\n // Create temporary directory with OS-level uniqueness guarantees\n // This prevents TOCTOU race conditions that Math.random() would allow\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'attest-it-'))\n const dataFile = path.join(tmpDir, 'data.bin')\n const sigFile = path.join(tmpDir, 'sig.bin')\n\n try {\n // Write data to temp file\n await fs.writeFile(dataFile, processBuffer)\n\n // Sign using openssl dgst -sha256 (cross-platform compatible)\n const signArgs = ['dgst', '-sha256', '-sign', privateKeyPath, '-out', sigFile, dataFile]\n const result = await runOpenSSL(signArgs)\n\n if (result.exitCode !== 0) {\n throw new Error(`Failed to sign data: ${result.stderr}`)\n }\n\n // Read the signature\n const sigBuffer = await fs.readFile(sigFile)\n return sigBuffer.toString('base64')\n } finally {\n // Clean up temp directory and all files within it\n try {\n await fs.rm(tmpDir, { recursive: true, force: true })\n } catch {\n // Ignore cleanup errors - OS will eventually clean tmpdir\n }\n }\n}\n\n/**\n * Verify a signature using an RSA public key with SHA-256.\n *\n * Uses `openssl dgst -sha256 -verify` which is universally supported across\n * all OpenSSL and LibreSSL versions.\n *\n * @param options - Verification options\n * @returns true if signature is valid\n * @throws Error if verification fails (not just invalid signature)\n * @public\n */\nexport async function verify(options: VerifyOptions): Promise<boolean> {\n // Ensure OpenSSL is available before proceeding\n await ensureOpenSSLAvailable()\n\n const { publicKeyPath, data, signature } = options\n\n // Check if public key exists\n if (!(await fileExists(publicKeyPath))) {\n throw new Error(`Public key not found: ${publicKeyPath}`)\n }\n\n // Convert data to Buffer\n const dataBuffer = typeof data === 'string' ? Buffer.from(data, 'utf8') : data\n\n // OpenSSL dgst cannot handle empty files, so we use the same workaround\n // as in sign() - add a single byte for empty data\n const processBuffer = dataBuffer.length === 0 ? Buffer.from([0x00]) : dataBuffer\n\n // Decode signature from base64\n const sigBuffer = Buffer.from(signature, 'base64')\n\n // Create temporary directory with OS-level uniqueness guarantees\n // This prevents TOCTOU race conditions that Math.random() would allow\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'attest-it-'))\n const dataFile = path.join(tmpDir, 'data.bin')\n const sigFile = path.join(tmpDir, 'sig.bin')\n\n try {\n // Write data and signature to temp files\n await fs.writeFile(dataFile, processBuffer)\n await fs.writeFile(sigFile, sigBuffer)\n\n // Verify using openssl dgst -sha256 (cross-platform compatible)\n const verifyArgs = [\n 'dgst',\n '-sha256',\n '-verify',\n publicKeyPath,\n '-signature',\n sigFile,\n dataFile,\n ]\n const result = await runOpenSSL(verifyArgs)\n\n // dgst -verify: Exit code 0 means valid, non-0 means invalid\n // Output contains \"Verified OK\" on success\n return result.exitCode === 0 && result.stdout.toString().includes('Verified OK')\n } finally {\n // Clean up temp directory and all files within it\n try {\n await fs.rm(tmpDir, { recursive: true, force: true })\n } catch {\n // Ignore cleanup errors - OS will eventually clean tmpdir\n }\n }\n}\n\n/**\n * Set restrictive permissions on a private key file.\n * @param keyPath - Path to the private key\n * @public\n */\nexport async function setKeyPermissions(keyPath: string): Promise<void> {\n // On Windows, use fs.chmod which has limited effect\n // On Unix, set to 0o600 (read/write for owner only)\n if (process.platform === 'win32') {\n // Windows doesn't support Unix-style permissions in the same way\n // But we still call chmod for consistency\n await fs.chmod(keyPath, 0o600)\n } else {\n await fs.chmod(keyPath, 0o600)\n }\n}\n","/**\n * Configuration loading and validation for attest-it.\n */\n\nimport { readFileSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join, resolve } from 'node:path'\nimport { parse as parseYaml } from 'yaml'\nimport { z } from 'zod'\n\n/**\n * Zod schema for settings with defaults applied.\n */\nconst settingsSchema = z\n .object({\n maxAgeDays: z.number().int().positive().default(30),\n publicKeyPath: z.string().default('.attest-it/pubkey.pem'),\n attestationsPath: z.string().default('.attest-it/attestations.json'),\n defaultCommand: z.string().optional(),\n // Note: algorithm field was removed - RSA is the only supported algorithm\n })\n .passthrough()\n\n/**\n * Zod schema for a suite configuration.\n */\nconst suiteSchema = z\n .object({\n description: z.string().optional(),\n packages: z\n .array(z.string().min(1, 'Package path cannot be empty'))\n .min(1, 'At least one package pattern is required'),\n files: z.array(z.string().min(1, 'File path cannot be empty')).optional(),\n ignore: z.array(z.string().min(1, 'Ignore pattern cannot be empty')).optional(),\n command: z.string().optional(),\n invalidates: z.array(z.string().min(1, 'Invalidated suite name cannot be empty')).optional(),\n })\n .strict()\n\n/**\n * Zod schema for the full configuration file.\n */\nconst configSchema = z\n .object({\n version: z.literal(1),\n settings: settingsSchema.default({}),\n suites: z.record(z.string(), suiteSchema).refine((suites) => Object.keys(suites).length >= 1, {\n message: 'At least one suite must be defined',\n }),\n })\n .strict()\n\n/**\n * Type inference from Zod schema (should match AttestItConfig).\n * This is the same as AttestItConfig but with defaults applied.\n * @public\n */\nexport type Config = z.infer<typeof configSchema>\n\n/**\n * Error thrown when configuration is invalid.\n * @public\n */\nexport class ConfigValidationError extends Error {\n constructor(\n message: string,\n public readonly issues: z.ZodIssue[],\n ) {\n super(message)\n this.name = 'ConfigValidationError'\n }\n}\n\n/**\n * Error thrown when configuration file cannot be found.\n * @public\n */\nexport class ConfigNotFoundError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'ConfigNotFoundError'\n }\n}\n\n/**\n * Parse configuration content from a string.\n *\n * @param content - The configuration file content\n * @param format - The format of the content ('yaml' or 'json')\n * @returns Parsed and validated configuration\n * @throws {ConfigValidationError} If validation fails\n */\nfunction parseConfigContent(content: string, format: 'yaml' | 'json'): Config {\n let rawConfig: unknown\n\n try {\n if (format === 'yaml') {\n rawConfig = parseYaml(content)\n } else {\n rawConfig = JSON.parse(content)\n }\n } catch (error) {\n throw new ConfigValidationError(\n `Failed to parse ${format.toUpperCase()}: ${error instanceof Error ? error.message : String(error)}`,\n [],\n )\n }\n\n const result = configSchema.safeParse(rawConfig)\n\n if (!result.success) {\n throw new ConfigValidationError(\n 'Configuration validation failed:\\n' +\n result.error.issues\n .map((issue) => ` - ${issue.path.join('.')}: ${issue.message}`)\n .join('\\n'),\n result.error.issues,\n )\n }\n\n return result.data\n}\n\n/**\n * Determine the format of a config file from its extension.\n *\n * @param filePath - Path to the config file\n * @returns 'yaml' or 'json'\n */\nfunction getConfigFormat(filePath: string): 'yaml' | 'json' {\n const ext = filePath.toLowerCase()\n if (ext.endsWith('.yaml') || ext.endsWith('.yml')) {\n return 'yaml'\n }\n if (ext.endsWith('.json')) {\n return 'json'\n }\n // Default to yaml for extensionless files\n return 'yaml'\n}\n\n/**\n * Find the configuration file in default locations.\n *\n * Searches in this order:\n * 1. .attest-it/config.yaml\n * 2. .attest-it/config.yml\n * 3. .attest-it/config.json\n *\n * @param startDir - Directory to start searching from (defaults to cwd)\n * @returns Absolute path to the config file, or null if not found\n * @public\n */\nexport function findConfigPath(startDir: string = process.cwd()): string | null {\n const configDir = join(startDir, '.attest-it')\n const candidates = ['config.yaml', 'config.yml', 'config.json']\n\n for (const candidate of candidates) {\n const configPath = join(configDir, candidate)\n try {\n readFileSync(configPath, 'utf8')\n return configPath\n } catch {\n // File doesn't exist or can't be read, try next candidate\n continue\n }\n }\n\n return null\n}\n\n/**\n * Load and validate configuration from a file (async).\n *\n * @param configPath - Optional path to config file. If not provided, searches default locations.\n * @returns Validated configuration object\n * @throws {@link ConfigNotFoundError} If config file cannot be found\n * @throws {@link ConfigValidationError} If validation fails\n * @public\n */\nexport async function loadConfig(configPath?: string): Promise<Config> {\n const resolvedPath = configPath ?? findConfigPath()\n\n if (!resolvedPath) {\n throw new ConfigNotFoundError(\n 'Configuration file not found. Expected .attest-it/config.yaml, .attest-it/config.yml, or .attest-it/config.json',\n )\n }\n\n try {\n const content = await readFile(resolvedPath, 'utf8')\n const format = getConfigFormat(resolvedPath)\n return parseConfigContent(content, format)\n } catch (error) {\n if (error instanceof ConfigValidationError) {\n throw error\n }\n throw new ConfigNotFoundError(\n `Failed to read configuration file at ${resolvedPath}: ${String(error)}`,\n )\n }\n}\n\n/**\n * Load and validate configuration from a file (sync).\n *\n * @param configPath - Optional path to config file. If not provided, searches default locations.\n * @returns Validated configuration object\n * @throws {@link ConfigNotFoundError} If config file cannot be found\n * @throws {@link ConfigValidationError} If validation fails\n * @public\n */\nexport function loadConfigSync(configPath?: string): Config {\n const resolvedPath = configPath ?? findConfigPath()\n\n if (!resolvedPath) {\n throw new ConfigNotFoundError(\n 'Configuration file not found. Expected .attest-it/config.yaml, .attest-it/config.yml, or .attest-it/config.json',\n )\n }\n\n try {\n const content = readFileSync(resolvedPath, 'utf8')\n const format = getConfigFormat(resolvedPath)\n return parseConfigContent(content, format)\n } catch (error) {\n if (error instanceof ConfigValidationError) {\n throw error\n }\n throw new ConfigNotFoundError(\n `Failed to read configuration file at ${resolvedPath}: ${String(error)}`,\n )\n }\n}\n\n/**\n * Resolve relative paths in the configuration against the repository root.\n *\n * This converts relative paths in settings.publicKeyPath and settings.attestationsPath\n * to absolute paths relative to the repository root.\n *\n * @param config - The configuration object\n * @param repoRoot - Absolute path to the repository root\n * @returns Configuration with resolved absolute paths\n * @public\n */\nexport function resolveConfigPaths(config: Config, repoRoot: string): Config {\n return {\n ...config,\n settings: {\n ...config.settings,\n publicKeyPath: resolve(repoRoot, config.settings.publicKeyPath),\n attestationsPath: resolve(repoRoot, config.settings.attestationsPath),\n },\n }\n}\n\n/**\n * Convert Zod-validated Config to AttestItConfig by removing undefined values.\n *\n * The Config type (from Zod) has optional fields as `T | undefined`,\n * while AttestItConfig has optional fields as `T?` (can be absent, not undefined).\n *\n * This adapter removes any undefined values to match the AttestItConfig interface\n * that the core functions expect.\n *\n * @param config - The Zod-validated configuration from loadConfig()\n * @returns Configuration compatible with AttestItConfig\n * @public\n */\nexport function toAttestItConfig(config: Config): import('./types.js').AttestItConfig {\n return {\n version: config.version,\n settings: {\n maxAgeDays: config.settings.maxAgeDays,\n publicKeyPath: config.settings.publicKeyPath,\n attestationsPath: config.settings.attestationsPath,\n ...(config.settings.defaultCommand !== undefined && {\n defaultCommand: config.settings.defaultCommand,\n }),\n },\n suites: Object.fromEntries(\n Object.entries(config.suites).map(([name, suite]) => [\n name,\n {\n packages: suite.packages,\n ...(suite.description !== undefined && { description: suite.description }),\n ...(suite.files !== undefined && { files: suite.files }),\n ...(suite.ignore !== undefined && { ignore: suite.ignore }),\n ...(suite.command !== undefined && { command: suite.command }),\n ...(suite.invalidates !== undefined && { invalidates: suite.invalidates }),\n },\n ]),\n ),\n }\n}\n","import * as crypto from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { glob, globSync } from 'tinyglobby'\n\n/**\n * Threshold for streaming large files instead of reading into memory.\n * Files larger than this will be hashed via streaming to avoid memory issues.\n */\nconst LARGE_FILE_THRESHOLD = 50 * 1024 * 1024 // 50MB\n\n/**\n * Options for computing a package fingerprint.\n * @public\n */\nexport interface FingerprintOptions {\n /** Package directories to include */\n packages: string[]\n /** Glob patterns to exclude from fingerprint */\n ignore?: string[]\n /** Base directory for resolving paths */\n baseDir?: string\n}\n\n/**\n * Result of computing a package fingerprint.\n * @public\n */\nexport interface FingerprintResult {\n /** The fingerprint in \"sha256:...\" format */\n fingerprint: string\n /** List of files included in fingerprint calculation */\n files: string[]\n /** Number of files processed */\n fileCount: number\n}\n\n/**\n * Internal representation of a file hash for fingerprint computation.\n */\ninterface FileHashInput {\n /** The normalized relative path of the file */\n relativePath: string\n /** The computed hash of the file content */\n hash: Buffer\n}\n\n/**\n * Sort files lexicographically (locale-independent).\n */\nfunction sortFiles(files: string[]): string[] {\n return [...files].sort((a, b) => {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n })\n}\n\n/**\n * Normalize path separators to forward slashes.\n */\nfunction normalizePath(filePath: string): string {\n return filePath.split(path.sep).join('/')\n}\n\n/**\n * Compute final fingerprint from file hashes.\n */\nfunction computeFinalFingerprint(fileHashes: FileHashInput[]): string {\n // Sort by relative path\n const sorted = [...fileHashes].sort((a, b) => {\n if (a.relativePath < b.relativePath) return -1\n if (a.relativePath > b.relativePath) return 1\n return 0\n })\n\n // Concatenate all file hashes\n const hashes = sorted.map((input) => input.hash)\n const concatenated = Buffer.concat(hashes)\n\n // Compute final hash\n const finalHash = crypto.createHash('sha256').update(concatenated).digest()\n return `sha256:${finalHash.toString('hex')}`\n}\n\n/**\n * Hash a file's content using streaming for large files (async).\n * For files larger than LARGE_FILE_THRESHOLD, uses streaming to avoid memory issues.\n */\nasync function hashFileAsync(\n realPath: string,\n normalizedPath: string,\n stats: fs.Stats,\n): Promise<Buffer> {\n if (stats.size > LARGE_FILE_THRESHOLD) {\n // Stream large files to avoid memory issues\n return new Promise((resolve, reject) => {\n const hash = crypto.createHash('sha256')\n hash.update(normalizedPath)\n hash.update('\\0')\n\n const stream = fs.createReadStream(realPath)\n stream.on('data', (chunk: string | Buffer) => {\n hash.update(chunk)\n })\n stream.on('end', () => {\n resolve(hash.digest())\n })\n stream.on('error', reject)\n })\n }\n\n // Read small files into memory (faster than streaming)\n const content = await fs.promises.readFile(realPath)\n const hash = crypto.createHash('sha256')\n hash.update(normalizedPath)\n hash.update('\\0')\n hash.update(content)\n return hash.digest()\n}\n\n/**\n * Hash a file's content synchronously.\n * Note: Cannot stream synchronously, so large files are read into memory.\n */\nfunction hashFileSync(realPath: string, normalizedPath: string): Buffer {\n const content = fs.readFileSync(realPath)\n const hash = crypto.createHash('sha256')\n hash.update(normalizedPath)\n hash.update('\\0')\n hash.update(content)\n return hash.digest()\n}\n\n/**\n * Validate fingerprint options and return base directory.\n */\nfunction validateOptions(options: FingerprintOptions): string {\n if (options.packages.length === 0) {\n throw new Error('packages array must not be empty')\n }\n\n const baseDir = options.baseDir ?? process.cwd()\n\n // Verify all package paths exist\n for (const pkg of options.packages) {\n const pkgPath = path.resolve(baseDir, pkg)\n if (!fs.existsSync(pkgPath)) {\n throw new Error(`Package path does not exist: ${pkgPath}`)\n }\n }\n\n return baseDir\n}\n\n/**\n * Compute a deterministic fingerprint for a set of packages (async).\n *\n * Algorithm:\n * 1. List all files in packages (respecting ignore globs)\n * 2. Sort files lexicographically by relative path\n * 3. For each file: compute SHA256(relativePath + \"\\0\" + content)\n * 4. Concatenate all file hashes in sorted order\n * 5. Compute final SHA256 of concatenated hashes\n * 6. Return \"sha256:\" + hex(fingerprint)\n *\n * @param options - Configuration for fingerprint computation\n * @returns Result containing the fingerprint hash and list of files processed\n * @throws Error if packages array is empty or if package paths don't exist\n * @public\n */\nexport async function computeFingerprint(options: FingerprintOptions): Promise<FingerprintResult> {\n const baseDir = validateOptions(options)\n\n // List all files in packages\n const files = await listPackageFiles(options.packages, options.ignore, baseDir)\n\n // Sort files lexicographically\n const sortedFiles = sortFiles(files)\n\n // Track visited files to handle multiple symlinks pointing to the same file\n // Key: realpath, Value: file hash\n const fileHashCache = new Map<string, Buffer>()\n\n // Compute individual file hashes\n const fileHashInputs: FileHashInput[] = []\n for (const file of sortedFiles) {\n const filePath = path.resolve(baseDir, file)\n\n // Handle symlinks\n let realPath = filePath\n let stats = await fs.promises.lstat(filePath)\n\n if (stats.isSymbolicLink()) {\n try {\n realPath = await fs.promises.realpath(filePath)\n } catch {\n // Skip broken symlinks\n continue\n }\n\n // Get stats of the target\n try {\n stats = await fs.promises.stat(realPath)\n } catch {\n // Skip broken symlinks\n continue\n }\n }\n\n // Skip if not a file (e.g., directories)\n if (!stats.isFile()) {\n continue\n }\n\n // Normalize path separators to forward slashes\n const normalizedPath = normalizePath(file)\n\n // Check if we've already hashed this file (via symlinks)\n let hash: Buffer\n const cachedHash = fileHashCache.get(realPath)\n if (cachedHash !== undefined) {\n // Reuse cached hash for files we've already seen (via symlinks)\n hash = cachedHash\n } else {\n // Hash the file content\n hash = await hashFileAsync(realPath, normalizedPath, stats)\n fileHashCache.set(realPath, hash)\n }\n\n fileHashInputs.push({ relativePath: normalizedPath, hash })\n }\n\n // Compute final fingerprint\n const fingerprint = computeFinalFingerprint(fileHashInputs)\n\n return {\n fingerprint,\n files: sortedFiles,\n fileCount: sortedFiles.length,\n }\n}\n\n/**\n * Compute a deterministic fingerprint for a set of packages (sync).\n *\n * @param options - Configuration for fingerprint computation\n * @returns Result containing the fingerprint hash and list of files processed\n * @throws Error if packages array is empty or if package paths don't exist\n * @public\n * @see {@link computeFingerprint} for the async version\n */\nexport function computeFingerprintSync(options: FingerprintOptions): FingerprintResult {\n const baseDir = validateOptions(options)\n\n // List all files in packages (sync version)\n const files = listPackageFilesSync(options.packages, options.ignore, baseDir)\n\n // Sort files lexicographically\n const sortedFiles = sortFiles(files)\n\n // Track visited files to handle multiple symlinks pointing to the same file\n // Key: realpath, Value: file hash\n const fileHashCache = new Map<string, Buffer>()\n\n // Compute individual file hashes\n const fileHashInputs: FileHashInput[] = []\n for (const file of sortedFiles) {\n const filePath = path.resolve(baseDir, file)\n\n // Handle symlinks\n let realPath = filePath\n let stats = fs.lstatSync(filePath)\n\n if (stats.isSymbolicLink()) {\n try {\n realPath = fs.realpathSync(filePath)\n } catch {\n // Skip broken symlinks\n continue\n }\n\n // Get stats of the target\n try {\n stats = fs.statSync(realPath)\n } catch {\n // Skip broken symlinks\n continue\n }\n }\n\n // Skip if not a file (e.g., directories)\n if (!stats.isFile()) {\n continue\n }\n\n // Normalize path separators to forward slashes\n const normalizedPath = normalizePath(file)\n\n // Check if we've already hashed this file (via symlinks)\n let hash: Buffer\n const cachedHash = fileHashCache.get(realPath)\n if (cachedHash !== undefined) {\n // Reuse cached hash for files we've already seen (via symlinks)\n hash = cachedHash\n } else {\n // Hash the file content (sync version cannot stream)\n hash = hashFileSync(realPath, normalizedPath)\n fileHashCache.set(realPath, hash)\n }\n\n fileHashInputs.push({ relativePath: normalizedPath, hash })\n }\n\n // Compute final fingerprint\n const fingerprint = computeFinalFingerprint(fileHashInputs)\n\n return {\n fingerprint,\n files: sortedFiles,\n fileCount: sortedFiles.length,\n }\n}\n\n/**\n * List files in packages, respecting ignore patterns (async).\n *\n * @param packages - Array of package directory paths\n * @param ignore - Optional glob patterns to exclude\n * @param baseDir - Base directory for resolving paths (defaults to cwd)\n * @returns Array of relative file paths\n * @public\n */\nexport async function listPackageFiles(\n packages: string[],\n ignore: string[] = [],\n baseDir: string = process.cwd(),\n): Promise<string[]> {\n const allFiles: string[] = []\n\n for (const pkg of packages) {\n // Build glob patterns for this package\n const patterns = [`${pkg}/**/*`]\n\n // Use tinyglobby to find files\n const files = await glob(patterns, {\n cwd: baseDir,\n ignore,\n onlyFiles: true,\n dot: true, // Include dotfiles\n absolute: false, // Return relative paths\n })\n\n allFiles.push(...files)\n }\n\n return allFiles\n}\n\n/**\n * Synchronous version of listPackageFiles\n */\nfunction listPackageFilesSync(\n packages: string[],\n ignore: string[] = [],\n baseDir: string = process.cwd(),\n): string[] {\n const allFiles: string[] = []\n\n for (const pkg of packages) {\n // Build glob patterns for this package\n const patterns = [`${pkg}/**/*`]\n\n // Use tinyglobby to find files (sync version)\n const files = globSync(patterns, {\n cwd: baseDir,\n ignore,\n onlyFiles: true,\n dot: true, // Include dotfiles\n absolute: false, // Return relative paths\n })\n\n allFiles.push(...files)\n }\n\n return allFiles\n}\n","/**\n * Attestation file I/O module with JSON canonicalization.\n */\n\nimport * as fs from 'node:fs'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\nimport * as canonicalizeNamespace from 'canonicalize'\nimport { z } from 'zod'\nimport type { Attestation, AttestationsFile } from './types.js'\n\n/**\n * Extract the serialize function from the canonicalize CommonJS module.\n *\n * Context: The canonicalize package uses `module.exports = function(...)`, which is\n * a CommonJS pattern. With `esModuleInterop: false` (required for library code to\n * avoid leaking tsconfig options to consumers), TypeScript treats the namespace import\n * as the module object itself, not as an object with a default export.\n *\n * The package's type definitions declare `export default function serialize(...)`,\n * which TypeScript interprets as `{ default: function }` when esModuleInterop is off.\n * However, at runtime with NodeNext module resolution, the actual value is just the\n * function itself.\n *\n * This type assertion is safe because:\n * 1. We verified the runtime export structure of the canonicalize module\n * 2. The type matches the published @types signature\n * 3. This is the standard pattern for importing CommonJS modules in strict ESM\n */\n// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\nconst canonicalize = canonicalizeNamespace as unknown as {\n default: (input: unknown) => string | undefined\n}\nconst serialize = canonicalize.default\n\n// Zod schema for attestation validation\nconst attestationSchema = z.object({\n suite: z.string().min(1),\n fingerprint: z.string().regex(/^sha256:[a-f0-9]{64}$/),\n attestedAt: z.string().datetime(),\n attestedBy: z.string().min(1),\n command: z.string().min(1),\n exitCode: z.literal(0),\n})\n\nconst attestationsFileSchema = z.object({\n schemaVersion: z.literal('1'),\n attestations: z.array(attestationSchema),\n signature: z.string(), // Will be validated by crypto module\n})\n\n/**\n * Type guard to check if an error is a Node.js file system error with code.\n * We need to disable the type assertion rule here because TypeScript doesn't\n * provide a way to narrow an object type after checking for a property without\n * using type assertions or indexed access. This is a safe assertion because we\n * check for the property existence and type before using it.\n */\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n if (error === null || typeof error !== 'object') {\n return false\n }\n if (!('code' in error)) {\n return false\n }\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const errorObj = error as Record<string, unknown>\n return typeof errorObj.code === 'string'\n}\n\n/**\n * Read attestations file from disk (async).\n *\n * @param filePath - Absolute path to the attestations JSON file\n * @returns Parsed attestations file, or null if the file doesn't exist\n * @throws Error on parse or validation errors\n * @public\n */\nexport async function readAttestations(filePath: string): Promise<AttestationsFile | null> {\n try {\n const content = await fs.promises.readFile(filePath, 'utf-8')\n const parsed: unknown = JSON.parse(content)\n // Zod validates and returns the correct type\n return attestationsFileSchema.parse(parsed)\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n return null\n }\n throw error\n }\n}\n\n/**\n * Read attestations file from disk (sync).\n *\n * @param filePath - Absolute path to the attestations JSON file\n * @returns Parsed attestations file, or null if the file doesn't exist\n * @throws Error on parse or validation errors\n * @public\n */\nexport function readAttestationsSync(filePath: string): AttestationsFile | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const parsed: unknown = JSON.parse(content)\n // Zod validates and returns the correct type\n return attestationsFileSchema.parse(parsed)\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n return null\n }\n throw error\n }\n}\n\n/**\n * Write attestations file to disk (async).\n *\n * Creates parent directories if needed. The signature should be computed\n * separately and passed in.\n *\n * @param filePath - Absolute path to write the attestations file\n * @param attestations - Array of attestation entries\n * @param signature - Cryptographic signature of the attestations\n * @throws Error on validation or write errors\n * @public\n */\nexport async function writeAttestations(\n filePath: string,\n attestations: Attestation[],\n signature: string,\n): Promise<void> {\n const fileContent: AttestationsFile = {\n schemaVersion: '1',\n attestations,\n signature,\n }\n\n // Validate before writing\n attestationsFileSchema.parse(fileContent)\n\n // Create parent directories if needed\n const dir = path.dirname(filePath)\n await fs.promises.mkdir(dir, { recursive: true })\n\n // Write with pretty formatting for readability\n const json = JSON.stringify(fileContent, null, 2)\n await fs.promises.writeFile(filePath, json, 'utf-8')\n}\n\n/**\n * Write attestations file to disk (sync).\n *\n * Creates parent directories if needed. The signature should be computed\n * separately and passed in.\n *\n * @param filePath - Absolute path to write the attestations file\n * @param attestations - Array of attestation entries\n * @param signature - Cryptographic signature of the attestations\n * @throws Error on validation or write errors\n * @public\n */\nexport function writeAttestationsSync(\n filePath: string,\n attestations: Attestation[],\n signature: string,\n): void {\n const fileContent: AttestationsFile = {\n schemaVersion: '1',\n attestations,\n signature,\n }\n\n // Validate before writing\n attestationsFileSchema.parse(fileContent)\n\n // Create parent directories if needed\n const dir = path.dirname(filePath)\n fs.mkdirSync(dir, { recursive: true })\n\n // Write with pretty formatting for readability\n const json = JSON.stringify(fileContent, null, 2)\n fs.writeFileSync(filePath, json, 'utf-8')\n}\n\n/**\n * Find an attestation for a specific suite.\n *\n * @param attestations - Attestations file containing all attestations\n * @param suite - Name of the suite to find\n * @returns The attestation if found, undefined otherwise\n * @public\n */\nexport function findAttestation(\n attestations: AttestationsFile,\n suite: string,\n): Attestation | undefined {\n return attestations.attestations.find((a) => a.suite === suite)\n}\n\n/**\n * Add or update an attestation for a suite.\n *\n * This is an immutable operation that returns a new array.\n *\n * @param attestations - Current array of attestations\n * @param newAttestation - Attestation to add or update\n * @returns New attestations array with the upserted attestation\n * @throws Error if the new attestation fails validation\n * @public\n */\nexport function upsertAttestation(\n attestations: Attestation[],\n newAttestation: Attestation,\n): Attestation[] {\n // Validate the new attestation\n attestationSchema.parse(newAttestation)\n\n const existingIndex = attestations.findIndex((a) => a.suite === newAttestation.suite)\n\n if (existingIndex === -1) {\n // Add new attestation\n return [...attestations, newAttestation]\n } else {\n // Update existing attestation\n const updated = [...attestations]\n // eslint-disable-next-line security/detect-object-injection -- False positive: existingIndex is a safe number from findIndex\n updated[existingIndex] = newAttestation\n return updated\n }\n}\n\n/**\n * Remove attestations for a suite.\n *\n * This is an immutable operation that returns a new array.\n *\n * @param attestations - Current array of attestations\n * @param suite - Name of the suite to remove\n * @returns New attestations array without the specified suite\n * @public\n */\nexport function removeAttestation(attestations: Attestation[], suite: string): Attestation[] {\n return attestations.filter((a) => a.suite !== suite)\n}\n\n/**\n * Compute canonical JSON representation for signing.\n *\n * Implements RFC 8785 JSON Canonicalization Scheme. The canonicalize package provides:\n * 1. Keys sorted lexicographically (Unicode code point order)\n * 2. No whitespace between tokens\n * 3. No trailing commas\n * 4. Strings escaped using \\uXXXX for control characters\n * 5. Numbers: no leading zeros, no +, use lowercase 'e' for exponent\n * 6. UTF-8 encoding\n *\n * @param attestations - Array of attestations to canonicalize\n * @returns Canonical JSON string representation\n * @throws Error if canonicalization fails\n * @public\n */\nexport function canonicalizeAttestations(attestations: Attestation[]): string {\n const canonical = serialize(attestations)\n if (canonical === undefined) {\n throw new Error('Failed to canonicalize attestations')\n }\n return canonical\n}\n\n/**\n * Create a new attestation entry.\n *\n * @param params - Parameters for creating the attestation\n * @param params.suite - Name of the test suite\n * @param params.fingerprint - Fingerprint of the packages in sha256 format\n * @param params.command - Command that was executed\n * @param params.attestedBy - Optional username (defaults to current OS user)\n * @returns Validated attestation object\n * @throws Error if attestation validation fails\n * @public\n */\nexport function createAttestation(params: {\n suite: string\n fingerprint: string\n command: string\n attestedBy?: string\n}): Attestation {\n const attestation: Attestation = {\n suite: params.suite,\n fingerprint: params.fingerprint,\n attestedAt: new Date().toISOString(),\n attestedBy: params.attestedBy ?? os.userInfo().username,\n command: params.command,\n exitCode: 0,\n }\n\n // Validate before returning\n attestationSchema.parse(attestation)\n\n return attestation\n}\n\n/**\n * Options for writing signed attestations.\n * @public\n */\nexport interface WriteSignedAttestationsOptions {\n /** Path to write the attestations file */\n filePath: string\n /** Array of attestations to write */\n attestations: Attestation[]\n /** Path to the private key for signing */\n privateKeyPath: string\n}\n\n/**\n * Options for reading and verifying signed attestations.\n * @public\n */\nexport interface ReadSignedAttestationsOptions {\n /** Path to read the attestations file from */\n filePath: string\n /** Path to the public key for verification */\n publicKeyPath: string\n}\n\n/**\n * Write attestations with a cryptographic signature.\n *\n * This function canonicalizes the attestations, signs them with the private key,\n * and writes the attestations file with the signature.\n *\n * @param options - Options for writing signed attestations\n * @throws Error if signing or writing fails\n * @public\n */\nexport async function writeSignedAttestations(\n options: WriteSignedAttestationsOptions,\n): Promise<void> {\n // Import sign function here to avoid circular dependency\n const { sign } = await import('./crypto.js')\n\n const canonical = canonicalizeAttestations(options.attestations)\n const signature = await sign({\n privateKeyPath: options.privateKeyPath,\n data: canonical,\n })\n await writeAttestations(options.filePath, options.attestations, signature)\n}\n\n/**\n * Read attestations and verify the signature.\n *\n * This function reads the attestations file, canonicalizes the attestations,\n * and verifies the signature using the public key. It throws an error if the\n * file doesn't exist or if signature verification fails.\n *\n * @param options - Options for reading and verifying attestations\n * @returns The attestations file if signature is valid\n * @throws Error if attestations file not found\n * @throws SignatureInvalidError if signature verification fails\n * @public\n */\nexport async function readAndVerifyAttestations(\n options: ReadSignedAttestationsOptions,\n): Promise<AttestationsFile> {\n // Import verify function here to avoid circular dependency\n const { verify } = await import('./crypto.js')\n\n const file = await readAttestations(options.filePath)\n if (!file) {\n throw new Error(`Attestations file not found: ${options.filePath}`)\n }\n\n const canonical = canonicalizeAttestations(file.attestations)\n const isValid = await verify({\n publicKeyPath: options.publicKeyPath,\n data: canonical,\n signature: file.signature,\n })\n\n if (!isValid) {\n throw new SignatureInvalidError(options.filePath)\n }\n\n return file\n}\n\n/**\n * Error thrown when signature verification fails.\n * @public\n */\nexport class SignatureInvalidError extends Error {\n /**\n * Create a new SignatureInvalidError.\n * @param filePath - Path to the file that failed verification\n */\n constructor(filePath: string) {\n super(`Signature verification failed for: ${filePath}`)\n this.name = 'SignatureInvalidError'\n }\n}\n","/**\n * @attest-it/core\n *\n * Core functionality for the attest-it testing framework.\n * @packageDocumentation\n */\n\n/**\n * Package version\n * @public\n */\nexport const version = '0.0.0'\n\n// Types\nexport type {\n AttestItSettings,\n SuiteConfig,\n AttestItConfig,\n Attestation,\n AttestationsFile,\n VerificationStatus,\n SuiteVerificationResult,\n} from './types.js'\n\n// Config\nexport {\n loadConfig,\n loadConfigSync,\n findConfigPath,\n resolveConfigPaths,\n toAttestItConfig,\n ConfigValidationError,\n ConfigNotFoundError,\n type Config,\n} from './config.js'\n\n// Fingerprinting\nexport { computeFingerprint, computeFingerprintSync, listPackageFiles } from './fingerprint.js'\nexport type { FingerprintOptions, FingerprintResult } from './fingerprint.js'\n\n// Attestations\nexport {\n readAttestations,\n readAttestationsSync,\n writeAttestations,\n writeAttestationsSync,\n findAttestation,\n upsertAttestation,\n removeAttestation,\n canonicalizeAttestations,\n createAttestation,\n writeSignedAttestations,\n readAndVerifyAttestations,\n SignatureInvalidError,\n} from './attestation.js'\nexport type {\n WriteSignedAttestationsOptions,\n ReadSignedAttestationsOptions,\n} from './attestation.js'\n\n// Cryptography\nexport {\n checkOpenSSL,\n getDefaultPrivateKeyPath,\n getDefaultPublicKeyPath,\n generateKeyPair,\n sign,\n verify,\n setKeyPermissions,\n} from './crypto.js'\nexport type {\n KeyPaths,\n KeygenOptions,\n SignOptions,\n VerifyOptions as CryptoVerifyOptions,\n} from './crypto.js'\n\n// Verification\nexport { verifyAttestations } from './verify.js'\nexport type { VerifyOptions, VerifyResult } from './verify.js'\n","/**\n * Verification logic for attestations.\n * @packageDocumentation\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type {\n AttestItConfig,\n Attestation,\n AttestationsFile,\n SuiteVerificationResult,\n} from './types.js'\nimport { computeFingerprint } from './fingerprint.js'\nimport { readAndVerifyAttestations, SignatureInvalidError } from './attestation.js'\n\n/**\n * Options for verifying attestations.\n * @public\n */\nexport interface VerifyOptions {\n /** Configuration object */\n config: AttestItConfig\n /** Repository root directory (defaults to process.cwd()) */\n repoRoot?: string\n}\n\n/**\n * Result of verifying all attestations.\n * @public\n */\nexport interface VerifyResult {\n /** Overall success - true if all attestations are valid */\n success: boolean\n /** Whether the attestations file signature is valid */\n signatureValid: boolean\n /** Verification results for each suite */\n suites: SuiteVerificationResult[]\n /** Error messages encountered during verification */\n errors: string[]\n}\n\n/**\n * Verify all attestations against current code state.\n *\n * Verification algorithm:\n * 1. Load and verify attestations file signature\n * 2. For each suite in config:\n * a. Compute current fingerprint\n * b. Find matching attestation\n * c. Compare fingerprints\n * d. Check age\n * 3. Check invalidation chains\n * 4. Return aggregated results\n *\n * @param options - Verification options\n * @returns Verification result with status for each suite\n * @public\n */\nexport async function verifyAttestations(options: VerifyOptions): Promise<VerifyResult> {\n const { config, repoRoot = process.cwd() } = options\n const errors: string[] = []\n const suiteResults: SuiteVerificationResult[] = []\n let signatureValid = true\n let attestationsFile: AttestationsFile | null = null\n\n // Resolve paths\n const attestationsPath = resolvePath(config.settings.attestationsPath, repoRoot)\n const publicKeyPath = resolvePath(config.settings.publicKeyPath, repoRoot)\n\n // Step 1: Load and verify attestations\n try {\n if (!fs.existsSync(attestationsPath)) {\n // No attestations file - all suites need attestation\n attestationsFile = null\n } else if (!fs.existsSync(publicKeyPath)) {\n errors.push(`Public key not found: ${publicKeyPath}`)\n signatureValid = false\n } else {\n attestationsFile = await readAndVerifyAttestations({\n filePath: attestationsPath,\n publicKeyPath,\n })\n }\n } catch (err) {\n if (err instanceof SignatureInvalidError) {\n signatureValid = false\n errors.push(err.message)\n } else if (err instanceof Error) {\n errors.push(err.message)\n }\n }\n\n const attestations = attestationsFile?.attestations ?? []\n\n // Step 2: Check each suite\n for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {\n const result = await verifySuite({\n suiteName,\n suiteConfig,\n attestations,\n maxAgeDays: config.settings.maxAgeDays,\n repoRoot,\n })\n suiteResults.push(result)\n }\n\n // Step 3: Check invalidation chains\n checkInvalidationChains(config, suiteResults)\n\n // Step 4: Aggregate results\n const allValid =\n signatureValid && suiteResults.every((r) => r.status === 'VALID') && errors.length === 0\n\n return {\n success: allValid,\n signatureValid,\n suites: suiteResults,\n errors,\n }\n}\n\n/**\n * Options for verifying a single suite.\n * @internal\n */\ninterface VerifySuiteOptions {\n /** Name of the suite */\n suiteName: string\n /** Suite configuration */\n suiteConfig: { packages: string[]; ignore?: string[] }\n /** All attestations from the attestations file */\n attestations: Attestation[]\n /** Maximum age in days before attestation expires */\n maxAgeDays: number\n /** Repository root directory */\n repoRoot: string\n}\n\n/**\n * Verify a single suite's attestation.\n * @internal\n */\nasync function verifySuite(options: VerifySuiteOptions): Promise<SuiteVerificationResult> {\n const { suiteName, suiteConfig, attestations, maxAgeDays, repoRoot } = options\n\n // Compute current fingerprint\n const fingerprintOptions = {\n packages: suiteConfig.packages.map((p) => resolvePath(p, repoRoot)),\n baseDir: repoRoot,\n ...(suiteConfig.ignore && { ignore: suiteConfig.ignore }),\n }\n const fingerprintResult = await computeFingerprint(fingerprintOptions)\n\n // Find attestation for this suite\n const attestation = attestations.find((a) => a.suite === suiteName)\n\n // No attestation found\n if (!attestation) {\n return {\n suite: suiteName,\n status: 'NEEDS_ATTESTATION',\n fingerprint: fingerprintResult.fingerprint,\n message: 'No attestation found for this suite',\n }\n }\n\n // Check fingerprint\n if (attestation.fingerprint !== fingerprintResult.fingerprint) {\n return {\n suite: suiteName,\n status: 'FINGERPRINT_CHANGED',\n fingerprint: fingerprintResult.fingerprint,\n attestation,\n message: `Fingerprint changed from ${attestation.fingerprint.slice(0, 20)}... to ${fingerprintResult.fingerprint.slice(0, 20)}...`,\n }\n }\n\n // Check age\n const attestedAt = new Date(attestation.attestedAt)\n const ageMs = Date.now() - attestedAt.getTime()\n const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24))\n\n if (ageDays > maxAgeDays) {\n return {\n suite: suiteName,\n status: 'EXPIRED',\n fingerprint: fingerprintResult.fingerprint,\n attestation,\n age: ageDays,\n message: `Attestation expired (${String(ageDays)} days old, max ${String(maxAgeDays)} days)`,\n }\n }\n\n // All checks passed\n return {\n suite: suiteName,\n status: 'VALID',\n fingerprint: fingerprintResult.fingerprint,\n attestation,\n age: ageDays,\n }\n}\n\n/**\n * Check invalidation chains.\n *\n * If suite A invalidates suite B, and A's attestation is newer than B's,\n * then B should be marked as INVALIDATED_BY_PARENT.\n *\n * @param config - Full configuration\n * @param results - Array of suite verification results to mutate\n * @internal\n */\nfunction checkInvalidationChains(config: AttestItConfig, results: SuiteVerificationResult[]): void {\n for (const [parentName, parentConfig] of Object.entries(config.suites)) {\n const invalidates = parentConfig.invalidates ?? []\n const parentResult = results.find((r) => r.suite === parentName)\n\n if (!parentResult?.attestation) continue\n\n const parentTime = new Date(parentResult.attestation.attestedAt).getTime()\n\n for (const childName of invalidates) {\n const childResult = results.find((r) => r.suite === childName)\n if (!childResult?.attestation) continue\n\n const childTime = new Date(childResult.attestation.attestedAt).getTime()\n\n // If parent was attested AFTER child, child is invalidated\n if (parentTime > childTime && childResult.status === 'VALID') {\n childResult.status = 'INVALIDATED_BY_PARENT'\n childResult.message = `Invalidated by ${parentName} (attested later)`\n }\n }\n }\n}\n\n/**\n * Resolve a path relative to a base directory.\n * @param relativePath - Path that may be relative or absolute\n * @param baseDir - Base directory for resolving relative paths\n * @returns Absolute path\n * @internal\n */\nfunction resolvePath(relativePath: string, baseDir: string): string {\n if (path.isAbsolute(relativePath)) {\n return relativePath\n }\n return path.join(baseDir, relativePath)\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/crypto.ts","../src/config.ts","../src/fingerprint.ts","../src/attestation.ts","../src/index.ts","../src/verify.ts"],"names":["resolve","spawn","os","path2","fs2","z","parseYaml","join","readFileSync","readFile","path","crypto","hash","fs","glob","globSync","canonicalizeNamespace","fs3","path3","os2","sign","verify","fs4","path4"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Cryptographic utilities for key generation, signing, and verification.\n *\n * @remarks\n * This module provides cryptographic operations using OpenSSL for key management\n * and signature verification. It uses RSA-2048 with SHA-256 for signatures,\n * which is universally supported across all OpenSSL and LibreSSL versions.\n *\n * @packageDocumentation\n */\n\nimport { spawn } from 'node:child_process'\nimport * as fs from 'node:fs/promises'\nimport * as path from 'node:path'\nimport * as os from 'node:os'\n\n/**\n * Paths to a generated keypair.\n * @public\n */\nexport interface KeyPaths {\n /** Path to the private key file */\n privatePath: string\n /** Path to the public key file */\n publicPath: string\n}\n\n/**\n * Options for key generation.\n * @public\n */\nexport interface KeygenOptions {\n /** Path for private key (default: OS-specific config dir) */\n privatePath?: string\n /** Path for public key (default: repo root) */\n publicPath?: string\n /** Overwrite existing keys (default: false) */\n force?: boolean\n}\n\n/**\n * Options for signing data.\n * @public\n */\nexport interface SignOptions {\n /** Path to the private key file */\n privateKeyPath: string\n /** Data to sign (string or Buffer) */\n data: string | Buffer\n}\n\n/**\n * Options for verifying signatures.\n * @public\n */\nexport interface VerifyOptions {\n /** Path to the public key file */\n publicKeyPath: string\n /** Original data that was signed */\n data: string | Buffer\n /** Base64-encoded signature to verify */\n signature: string\n}\n\n/**\n * Result from spawning an OpenSSL process.\n * @internal\n */\ninterface SpawnResult {\n /** Process exit code */\n exitCode: number\n /** Standard output as Buffer */\n stdout: Buffer\n /** Standard error as string */\n stderr: string\n}\n\n/**\n * Run OpenSSL with the given arguments.\n * @param args - Command-line arguments for OpenSSL\n * @param stdin - Optional data to write to stdin\n * @returns Process result with exit code and outputs\n * @internal\n */\nasync function runOpenSSL(args: string[], stdin?: Buffer): Promise<SpawnResult> {\n return new Promise((resolve, reject) => {\n const child = spawn('openssl', args, {\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n\n const stdoutChunks: Buffer[] = []\n let stderr = ''\n\n child.stdout.on('data', (chunk: Buffer) => {\n stdoutChunks.push(chunk)\n })\n\n child.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString()\n })\n\n child.on('error', (err) => {\n reject(new Error(`Failed to spawn OpenSSL: ${err.message}`))\n })\n\n child.on('close', (code) => {\n resolve({\n exitCode: code ?? 1,\n stdout: Buffer.concat(stdoutChunks),\n stderr,\n })\n })\n\n if (stdin) {\n child.stdin.write(stdin)\n }\n child.stdin.end()\n })\n}\n\n/**\n * Check if OpenSSL is available and get version info.\n * @returns OpenSSL version string\n * @throws Error if OpenSSL is not available\n * @public\n */\nexport async function checkOpenSSL(): Promise<string> {\n const result = await runOpenSSL(['version'])\n\n if (result.exitCode !== 0) {\n throw new Error(`OpenSSL check failed: ${result.stderr}`)\n }\n\n return result.stdout.toString().trim()\n}\n\n/**\n * Cached result of OpenSSL availability check.\n * @internal\n */\nlet openSSLChecked = false\n\n/**\n * Ensure OpenSSL is available before performing cryptographic operations.\n * @throws Error with installation instructions if OpenSSL is not available\n * @internal\n */\nasync function ensureOpenSSLAvailable(): Promise<void> {\n if (openSSLChecked) {\n return\n }\n\n try {\n await checkOpenSSL()\n openSSLChecked = true\n } catch {\n throw new Error(\n 'OpenSSL is not installed or not in PATH. ' +\n 'Please install OpenSSL to use attest-it. ' +\n 'On macOS: brew install openssl. ' +\n 'On Ubuntu: apt-get install openssl',\n )\n }\n}\n\n/**\n * Get the default private key path based on OS.\n * - macOS/Linux: ~/.config/attest-it/private.pem\n * - Windows: %APPDATA%\\attest-it\\private.pem\n * @public\n */\nexport function getDefaultPrivateKeyPath(): string {\n const homeDir = os.homedir()\n\n if (process.platform === 'win32') {\n const appData = process.env.APPDATA ?? path.join(homeDir, 'AppData', 'Roaming')\n return path.join(appData, 'attest-it', 'private.pem')\n }\n\n return path.join(homeDir, '.config', 'attest-it', 'private.pem')\n}\n\n/**\n * Get the default public key path (in repo).\n * @public\n */\nexport function getDefaultPublicKeyPath(): string {\n return path.join(process.cwd(), 'attest-it-public.pem')\n}\n\n/**\n * Ensure a directory exists, creating it and parent directories if needed.\n * @param dirPath - Directory path to create\n * @internal\n */\nasync function ensureDir(dirPath: string): Promise<void> {\n try {\n await fs.mkdir(dirPath, { recursive: true })\n } catch (err) {\n if (err instanceof Error && 'code' in err && err.code !== 'EEXIST') {\n throw err\n }\n }\n}\n\n/**\n * Check if a file exists.\n * @param filePath - File path to check\n * @returns true if file exists\n * @internal\n */\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Clean up one or more files, ignoring errors if files don't exist.\n * @param paths - File paths to delete\n * @internal\n */\nasync function cleanupFiles(...paths: string[]): Promise<void> {\n for (const filePath of paths) {\n try {\n await fs.unlink(filePath)\n } catch {\n // Ignore cleanup errors - file may not exist\n }\n }\n}\n\n/**\n * Generate a new RSA-2048 keypair using OpenSSL.\n *\n * RSA-2048 with SHA-256 is used because it's universally supported across\n * all OpenSSL and LibreSSL versions, including older macOS systems.\n *\n * @param options - Generation options\n * @returns Paths to generated keys\n * @throws Error if OpenSSL fails or keys exist without force\n * @public\n */\nexport async function generateKeyPair(options: KeygenOptions = {}): Promise<KeyPaths> {\n // Ensure OpenSSL is available before proceeding\n await ensureOpenSSLAvailable()\n\n const {\n privatePath = getDefaultPrivateKeyPath(),\n publicPath = getDefaultPublicKeyPath(),\n force = false,\n } = options\n\n // Check if keys already exist\n const privateExists = await fileExists(privatePath)\n const publicExists = await fileExists(publicPath)\n\n if ((privateExists || publicExists) && !force) {\n const existing = [privateExists ? privatePath : null, publicExists ? publicPath : null].filter(\n Boolean,\n )\n throw new Error(\n `Key files already exist: ${existing.join(', ')}. Use force: true to overwrite.`,\n )\n }\n\n // Ensure parent directories exist\n await ensureDir(path.dirname(privatePath))\n await ensureDir(path.dirname(publicPath))\n\n try {\n // Generate RSA-2048 private key\n const genArgs = [\n 'genpkey',\n '-algorithm',\n 'RSA',\n '-pkeyopt',\n 'rsa_keygen_bits:2048',\n '-out',\n privatePath,\n ]\n\n const genResult = await runOpenSSL(genArgs)\n if (genResult.exitCode !== 0) {\n throw new Error(`Failed to generate private key: ${genResult.stderr}`)\n }\n\n // Set restrictive permissions on private key\n await setKeyPermissions(privatePath)\n\n // Extract public key\n const pubResult = await runOpenSSL(['pkey', '-in', privatePath, '-pubout', '-out', publicPath])\n\n if (pubResult.exitCode !== 0) {\n throw new Error(`Failed to extract public key: ${pubResult.stderr}`)\n }\n\n return {\n privatePath,\n publicPath,\n }\n } catch (err) {\n // Clean up both key files on any failure\n await cleanupFiles(privatePath, publicPath)\n throw err\n }\n}\n\n/**\n * Sign data using an RSA private key with SHA-256.\n *\n * Uses `openssl dgst -sha256 -sign` which is universally supported across\n * all OpenSSL and LibreSSL versions.\n *\n * @param options - Signing options\n * @returns Base64-encoded signature\n * @throws Error if signing fails\n * @public\n */\nexport async function sign(options: SignOptions): Promise<string> {\n // Ensure OpenSSL is available before proceeding\n await ensureOpenSSLAvailable()\n\n const { privateKeyPath, data } = options\n\n // Check if private key exists\n if (!(await fileExists(privateKeyPath))) {\n throw new Error(`Private key not found: ${privateKeyPath}`)\n }\n\n // Convert data to Buffer\n const dataBuffer = typeof data === 'string' ? Buffer.from(data, 'utf8') : data\n\n // OpenSSL dgst cannot handle empty files, so we need to add a single byte\n // for empty data and document this limitation\n const processBuffer = dataBuffer.length === 0 ? Buffer.from([0x00]) : dataBuffer\n\n // Create temporary directory with OS-level uniqueness guarantees\n // This prevents TOCTOU race conditions that Math.random() would allow\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'attest-it-'))\n const dataFile = path.join(tmpDir, 'data.bin')\n const sigFile = path.join(tmpDir, 'sig.bin')\n\n try {\n // Write data to temp file\n await fs.writeFile(dataFile, processBuffer)\n\n // Sign using openssl dgst -sha256 (cross-platform compatible)\n const signArgs = ['dgst', '-sha256', '-sign', privateKeyPath, '-out', sigFile, dataFile]\n const result = await runOpenSSL(signArgs)\n\n if (result.exitCode !== 0) {\n throw new Error(`Failed to sign data: ${result.stderr}`)\n }\n\n // Read the signature\n const sigBuffer = await fs.readFile(sigFile)\n return sigBuffer.toString('base64')\n } finally {\n // Clean up temp directory and all files within it\n try {\n await fs.rm(tmpDir, { recursive: true, force: true })\n } catch {\n // Ignore cleanup errors - OS will eventually clean tmpdir\n }\n }\n}\n\n/**\n * Verify a signature using an RSA public key with SHA-256.\n *\n * Uses `openssl dgst -sha256 -verify` which is universally supported across\n * all OpenSSL and LibreSSL versions.\n *\n * @param options - Verification options\n * @returns true if signature is valid\n * @throws Error if verification fails (not just invalid signature)\n * @public\n */\nexport async function verify(options: VerifyOptions): Promise<boolean> {\n // Ensure OpenSSL is available before proceeding\n await ensureOpenSSLAvailable()\n\n const { publicKeyPath, data, signature } = options\n\n // Check if public key exists\n if (!(await fileExists(publicKeyPath))) {\n throw new Error(`Public key not found: ${publicKeyPath}`)\n }\n\n // Convert data to Buffer\n const dataBuffer = typeof data === 'string' ? Buffer.from(data, 'utf8') : data\n\n // OpenSSL dgst cannot handle empty files, so we use the same workaround\n // as in sign() - add a single byte for empty data\n const processBuffer = dataBuffer.length === 0 ? Buffer.from([0x00]) : dataBuffer\n\n // Decode signature from base64\n const sigBuffer = Buffer.from(signature, 'base64')\n\n // Create temporary directory with OS-level uniqueness guarantees\n // This prevents TOCTOU race conditions that Math.random() would allow\n const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'attest-it-'))\n const dataFile = path.join(tmpDir, 'data.bin')\n const sigFile = path.join(tmpDir, 'sig.bin')\n\n try {\n // Write data and signature to temp files\n await fs.writeFile(dataFile, processBuffer)\n await fs.writeFile(sigFile, sigBuffer)\n\n // Verify using openssl dgst -sha256 (cross-platform compatible)\n const verifyArgs = [\n 'dgst',\n '-sha256',\n '-verify',\n publicKeyPath,\n '-signature',\n sigFile,\n dataFile,\n ]\n const result = await runOpenSSL(verifyArgs)\n\n // dgst -verify: Exit code 0 means valid, non-0 means invalid\n // Output contains \"Verified OK\" on success\n return result.exitCode === 0 && result.stdout.toString().includes('Verified OK')\n } finally {\n // Clean up temp directory and all files within it\n try {\n await fs.rm(tmpDir, { recursive: true, force: true })\n } catch {\n // Ignore cleanup errors - OS will eventually clean tmpdir\n }\n }\n}\n\n/**\n * Set restrictive permissions on a private key file.\n * @param keyPath - Path to the private key\n * @public\n */\nexport async function setKeyPermissions(keyPath: string): Promise<void> {\n // On Windows, use fs.chmod which has limited effect\n // On Unix, set to 0o600 (read/write for owner only)\n if (process.platform === 'win32') {\n // Windows doesn't support Unix-style permissions in the same way\n // But we still call chmod for consistency\n await fs.chmod(keyPath, 0o600)\n } else {\n await fs.chmod(keyPath, 0o600)\n }\n}\n","/**\n * Configuration loading and validation for attest-it.\n */\n\nimport { readFileSync } from 'node:fs'\nimport { readFile } from 'node:fs/promises'\nimport { join, resolve } from 'node:path'\nimport { parse as parseYaml } from 'yaml'\nimport { z } from 'zod'\n\n/**\n * Zod schema for settings with defaults applied.\n */\nconst settingsSchema = z\n .object({\n maxAgeDays: z.number().int().positive().default(30),\n publicKeyPath: z.string().default('.attest-it/pubkey.pem'),\n attestationsPath: z.string().default('.attest-it/attestations.json'),\n defaultCommand: z.string().optional(),\n // Note: algorithm field was removed - RSA is the only supported algorithm\n })\n .passthrough()\n\n/**\n * Zod schema for a suite configuration.\n */\nconst suiteSchema = z\n .object({\n description: z.string().optional(),\n packages: z\n .array(z.string().min(1, 'Package path cannot be empty'))\n .min(1, 'At least one package pattern is required'),\n files: z.array(z.string().min(1, 'File path cannot be empty')).optional(),\n ignore: z.array(z.string().min(1, 'Ignore pattern cannot be empty')).optional(),\n command: z.string().optional(),\n invalidates: z.array(z.string().min(1, 'Invalidated suite name cannot be empty')).optional(),\n depends_on: z.array(z.string().min(1, 'Dependency suite name cannot be empty')).optional(),\n })\n .strict()\n\n/**\n * Zod schema for the full configuration file.\n */\nconst configSchema = z\n .object({\n version: z.literal(1),\n settings: settingsSchema.default({}),\n suites: z.record(z.string(), suiteSchema).refine((suites) => Object.keys(suites).length >= 1, {\n message: 'At least one suite must be defined',\n }),\n groups: z\n .record(z.string(), z.array(z.string().min(1, 'Suite name in group cannot be empty')))\n .optional(),\n })\n .strict()\n\n/**\n * Type inference from Zod schema (should match AttestItConfig).\n * This is the same as AttestItConfig but with defaults applied.\n * @public\n */\nexport type Config = z.infer<typeof configSchema>\n\n/**\n * Error thrown when configuration is invalid.\n * @public\n */\nexport class ConfigValidationError extends Error {\n constructor(\n message: string,\n public readonly issues: z.ZodIssue[],\n ) {\n super(message)\n this.name = 'ConfigValidationError'\n }\n}\n\n/**\n * Error thrown when configuration file cannot be found.\n * @public\n */\nexport class ConfigNotFoundError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'ConfigNotFoundError'\n }\n}\n\n/**\n * Parse configuration content from a string.\n *\n * @param content - The configuration file content\n * @param format - The format of the content ('yaml' or 'json')\n * @returns Parsed and validated configuration\n * @throws {ConfigValidationError} If validation fails\n */\nfunction parseConfigContent(content: string, format: 'yaml' | 'json'): Config {\n let rawConfig: unknown\n\n try {\n if (format === 'yaml') {\n rawConfig = parseYaml(content)\n } else {\n rawConfig = JSON.parse(content)\n }\n } catch (error) {\n throw new ConfigValidationError(\n `Failed to parse ${format.toUpperCase()}: ${error instanceof Error ? error.message : String(error)}`,\n [],\n )\n }\n\n const result = configSchema.safeParse(rawConfig)\n\n if (!result.success) {\n throw new ConfigValidationError(\n 'Configuration validation failed:\\n' +\n result.error.issues\n .map((issue) => ` - ${issue.path.join('.')}: ${issue.message}`)\n .join('\\n'),\n result.error.issues,\n )\n }\n\n return result.data\n}\n\n/**\n * Determine the format of a config file from its extension.\n *\n * @param filePath - Path to the config file\n * @returns 'yaml' or 'json'\n */\nfunction getConfigFormat(filePath: string): 'yaml' | 'json' {\n const ext = filePath.toLowerCase()\n if (ext.endsWith('.yaml') || ext.endsWith('.yml')) {\n return 'yaml'\n }\n if (ext.endsWith('.json')) {\n return 'json'\n }\n // Default to yaml for extensionless files\n return 'yaml'\n}\n\n/**\n * Find the configuration file in default locations.\n *\n * Searches in this order:\n * 1. .attest-it/config.yaml\n * 2. .attest-it/config.yml\n * 3. .attest-it/config.json\n *\n * @param startDir - Directory to start searching from (defaults to cwd)\n * @returns Absolute path to the config file, or null if not found\n * @public\n */\nexport function findConfigPath(startDir: string = process.cwd()): string | null {\n const configDir = join(startDir, '.attest-it')\n const candidates = ['config.yaml', 'config.yml', 'config.json']\n\n for (const candidate of candidates) {\n const configPath = join(configDir, candidate)\n try {\n readFileSync(configPath, 'utf8')\n return configPath\n } catch {\n // File doesn't exist or can't be read, try next candidate\n continue\n }\n }\n\n return null\n}\n\n/**\n * Load and validate configuration from a file (async).\n *\n * @param configPath - Optional path to config file. If not provided, searches default locations.\n * @returns Validated configuration object\n * @throws {@link ConfigNotFoundError} If config file cannot be found\n * @throws {@link ConfigValidationError} If validation fails\n * @public\n */\nexport async function loadConfig(configPath?: string): Promise<Config> {\n const resolvedPath = configPath ?? findConfigPath()\n\n if (!resolvedPath) {\n throw new ConfigNotFoundError(\n 'Configuration file not found. Expected .attest-it/config.yaml, .attest-it/config.yml, or .attest-it/config.json',\n )\n }\n\n try {\n const content = await readFile(resolvedPath, 'utf8')\n const format = getConfigFormat(resolvedPath)\n return parseConfigContent(content, format)\n } catch (error) {\n if (error instanceof ConfigValidationError) {\n throw error\n }\n throw new ConfigNotFoundError(\n `Failed to read configuration file at ${resolvedPath}: ${String(error)}`,\n )\n }\n}\n\n/**\n * Load and validate configuration from a file (sync).\n *\n * @param configPath - Optional path to config file. If not provided, searches default locations.\n * @returns Validated configuration object\n * @throws {@link ConfigNotFoundError} If config file cannot be found\n * @throws {@link ConfigValidationError} If validation fails\n * @public\n */\nexport function loadConfigSync(configPath?: string): Config {\n const resolvedPath = configPath ?? findConfigPath()\n\n if (!resolvedPath) {\n throw new ConfigNotFoundError(\n 'Configuration file not found. Expected .attest-it/config.yaml, .attest-it/config.yml, or .attest-it/config.json',\n )\n }\n\n try {\n const content = readFileSync(resolvedPath, 'utf8')\n const format = getConfigFormat(resolvedPath)\n return parseConfigContent(content, format)\n } catch (error) {\n if (error instanceof ConfigValidationError) {\n throw error\n }\n throw new ConfigNotFoundError(\n `Failed to read configuration file at ${resolvedPath}: ${String(error)}`,\n )\n }\n}\n\n/**\n * Resolve relative paths in the configuration against the repository root.\n *\n * This converts relative paths in settings.publicKeyPath and settings.attestationsPath\n * to absolute paths relative to the repository root.\n *\n * @param config - The configuration object\n * @param repoRoot - Absolute path to the repository root\n * @returns Configuration with resolved absolute paths\n * @public\n */\nexport function resolveConfigPaths(config: Config, repoRoot: string): Config {\n return {\n ...config,\n settings: {\n ...config.settings,\n publicKeyPath: resolve(repoRoot, config.settings.publicKeyPath),\n attestationsPath: resolve(repoRoot, config.settings.attestationsPath),\n },\n }\n}\n\n/**\n * Convert Zod-validated Config to AttestItConfig by removing undefined values.\n *\n * The Config type (from Zod) has optional fields as `T | undefined`,\n * while AttestItConfig has optional fields as `T?` (can be absent, not undefined).\n *\n * This adapter removes any undefined values to match the AttestItConfig interface\n * that the core functions expect.\n *\n * @param config - The Zod-validated configuration from loadConfig()\n * @returns Configuration compatible with AttestItConfig\n * @public\n */\nexport function toAttestItConfig(config: Config): import('./types.js').AttestItConfig {\n return {\n version: config.version,\n settings: {\n maxAgeDays: config.settings.maxAgeDays,\n publicKeyPath: config.settings.publicKeyPath,\n attestationsPath: config.settings.attestationsPath,\n ...(config.settings.defaultCommand !== undefined && {\n defaultCommand: config.settings.defaultCommand,\n }),\n },\n suites: Object.fromEntries(\n Object.entries(config.suites).map(([name, suite]) => [\n name,\n {\n packages: suite.packages,\n ...(suite.description !== undefined && { description: suite.description }),\n ...(suite.files !== undefined && { files: suite.files }),\n ...(suite.ignore !== undefined && { ignore: suite.ignore }),\n ...(suite.command !== undefined && { command: suite.command }),\n ...(suite.invalidates !== undefined && { invalidates: suite.invalidates }),\n ...(suite.depends_on !== undefined && { depends_on: suite.depends_on }),\n },\n ]),\n ),\n ...(config.groups !== undefined && { groups: config.groups }),\n }\n}\n","import * as crypto from 'node:crypto'\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport { glob, globSync } from 'tinyglobby'\n\n/**\n * Threshold for streaming large files instead of reading into memory.\n * Files larger than this will be hashed via streaming to avoid memory issues.\n */\nconst LARGE_FILE_THRESHOLD = 50 * 1024 * 1024 // 50MB\n\n/**\n * Options for computing a package fingerprint.\n * @public\n */\nexport interface FingerprintOptions {\n /** Package directories to include */\n packages: string[]\n /** Glob patterns to exclude from fingerprint */\n ignore?: string[]\n /** Base directory for resolving paths */\n baseDir?: string\n}\n\n/**\n * Result of computing a package fingerprint.\n * @public\n */\nexport interface FingerprintResult {\n /** The fingerprint in \"sha256:...\" format */\n fingerprint: string\n /** List of files included in fingerprint calculation */\n files: string[]\n /** Number of files processed */\n fileCount: number\n}\n\n/**\n * Internal representation of a file hash for fingerprint computation.\n */\ninterface FileHashInput {\n /** The normalized relative path of the file */\n relativePath: string\n /** The computed hash of the file content */\n hash: Buffer\n}\n\n/**\n * Sort files lexicographically (locale-independent).\n */\nfunction sortFiles(files: string[]): string[] {\n return [...files].sort((a, b) => {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n })\n}\n\n/**\n * Normalize path separators to forward slashes.\n */\nfunction normalizePath(filePath: string): string {\n return filePath.split(path.sep).join('/')\n}\n\n/**\n * Compute final fingerprint from file hashes.\n */\nfunction computeFinalFingerprint(fileHashes: FileHashInput[]): string {\n // Sort by relative path\n const sorted = [...fileHashes].sort((a, b) => {\n if (a.relativePath < b.relativePath) return -1\n if (a.relativePath > b.relativePath) return 1\n return 0\n })\n\n // Concatenate all file hashes\n const hashes = sorted.map((input) => input.hash)\n const concatenated = Buffer.concat(hashes)\n\n // Compute final hash\n const finalHash = crypto.createHash('sha256').update(concatenated).digest()\n return `sha256:${finalHash.toString('hex')}`\n}\n\n/**\n * Hash a file's content using streaming for large files (async).\n * For files larger than LARGE_FILE_THRESHOLD, uses streaming to avoid memory issues.\n */\nasync function hashFileAsync(\n realPath: string,\n normalizedPath: string,\n stats: fs.Stats,\n): Promise<Buffer> {\n if (stats.size > LARGE_FILE_THRESHOLD) {\n // Stream large files to avoid memory issues\n return new Promise((resolve, reject) => {\n const hash = crypto.createHash('sha256')\n hash.update(normalizedPath)\n hash.update('\\0')\n\n const stream = fs.createReadStream(realPath)\n stream.on('data', (chunk: string | Buffer) => {\n hash.update(chunk)\n })\n stream.on('end', () => {\n resolve(hash.digest())\n })\n stream.on('error', reject)\n })\n }\n\n // Read small files into memory (faster than streaming)\n const content = await fs.promises.readFile(realPath)\n const hash = crypto.createHash('sha256')\n hash.update(normalizedPath)\n hash.update('\\0')\n hash.update(content)\n return hash.digest()\n}\n\n/**\n * Hash a file's content synchronously.\n * Note: Cannot stream synchronously, so large files are read into memory.\n */\nfunction hashFileSync(realPath: string, normalizedPath: string): Buffer {\n const content = fs.readFileSync(realPath)\n const hash = crypto.createHash('sha256')\n hash.update(normalizedPath)\n hash.update('\\0')\n hash.update(content)\n return hash.digest()\n}\n\n/**\n * Validate fingerprint options and return base directory.\n */\nfunction validateOptions(options: FingerprintOptions): string {\n if (options.packages.length === 0) {\n throw new Error('packages array must not be empty')\n }\n\n const baseDir = options.baseDir ?? process.cwd()\n\n // Verify all package paths exist\n for (const pkg of options.packages) {\n const pkgPath = path.resolve(baseDir, pkg)\n if (!fs.existsSync(pkgPath)) {\n throw new Error(`Package path does not exist: ${pkgPath}`)\n }\n }\n\n return baseDir\n}\n\n/**\n * Compute a deterministic fingerprint for a set of packages (async).\n *\n * Algorithm:\n * 1. List all files in packages (respecting ignore globs)\n * 2. Sort files lexicographically by relative path\n * 3. For each file: compute SHA256(relativePath + \"\\0\" + content)\n * 4. Concatenate all file hashes in sorted order\n * 5. Compute final SHA256 of concatenated hashes\n * 6. Return \"sha256:\" + hex(fingerprint)\n *\n * @param options - Configuration for fingerprint computation\n * @returns Result containing the fingerprint hash and list of files processed\n * @throws Error if packages array is empty or if package paths don't exist\n * @public\n */\nexport async function computeFingerprint(options: FingerprintOptions): Promise<FingerprintResult> {\n const baseDir = validateOptions(options)\n\n // List all files in packages\n const files = await listPackageFiles(options.packages, options.ignore, baseDir)\n\n // Sort files lexicographically\n const sortedFiles = sortFiles(files)\n\n // Track visited files to handle multiple symlinks pointing to the same file\n // Key: realpath, Value: file hash\n const fileHashCache = new Map<string, Buffer>()\n\n // Compute individual file hashes\n const fileHashInputs: FileHashInput[] = []\n for (const file of sortedFiles) {\n const filePath = path.resolve(baseDir, file)\n\n // Handle symlinks\n let realPath = filePath\n let stats = await fs.promises.lstat(filePath)\n\n if (stats.isSymbolicLink()) {\n try {\n realPath = await fs.promises.realpath(filePath)\n } catch {\n // Skip broken symlinks\n continue\n }\n\n // Get stats of the target\n try {\n stats = await fs.promises.stat(realPath)\n } catch {\n // Skip broken symlinks\n continue\n }\n }\n\n // Skip if not a file (e.g., directories)\n if (!stats.isFile()) {\n continue\n }\n\n // Normalize path separators to forward slashes\n const normalizedPath = normalizePath(file)\n\n // Check if we've already hashed this file (via symlinks)\n let hash: Buffer\n const cachedHash = fileHashCache.get(realPath)\n if (cachedHash !== undefined) {\n // Reuse cached hash for files we've already seen (via symlinks)\n hash = cachedHash\n } else {\n // Hash the file content\n hash = await hashFileAsync(realPath, normalizedPath, stats)\n fileHashCache.set(realPath, hash)\n }\n\n fileHashInputs.push({ relativePath: normalizedPath, hash })\n }\n\n // Compute final fingerprint\n const fingerprint = computeFinalFingerprint(fileHashInputs)\n\n return {\n fingerprint,\n files: sortedFiles,\n fileCount: sortedFiles.length,\n }\n}\n\n/**\n * Compute a deterministic fingerprint for a set of packages (sync).\n *\n * @param options - Configuration for fingerprint computation\n * @returns Result containing the fingerprint hash and list of files processed\n * @throws Error if packages array is empty or if package paths don't exist\n * @public\n * @see {@link computeFingerprint} for the async version\n */\nexport function computeFingerprintSync(options: FingerprintOptions): FingerprintResult {\n const baseDir = validateOptions(options)\n\n // List all files in packages (sync version)\n const files = listPackageFilesSync(options.packages, options.ignore, baseDir)\n\n // Sort files lexicographically\n const sortedFiles = sortFiles(files)\n\n // Track visited files to handle multiple symlinks pointing to the same file\n // Key: realpath, Value: file hash\n const fileHashCache = new Map<string, Buffer>()\n\n // Compute individual file hashes\n const fileHashInputs: FileHashInput[] = []\n for (const file of sortedFiles) {\n const filePath = path.resolve(baseDir, file)\n\n // Handle symlinks\n let realPath = filePath\n let stats = fs.lstatSync(filePath)\n\n if (stats.isSymbolicLink()) {\n try {\n realPath = fs.realpathSync(filePath)\n } catch {\n // Skip broken symlinks\n continue\n }\n\n // Get stats of the target\n try {\n stats = fs.statSync(realPath)\n } catch {\n // Skip broken symlinks\n continue\n }\n }\n\n // Skip if not a file (e.g., directories)\n if (!stats.isFile()) {\n continue\n }\n\n // Normalize path separators to forward slashes\n const normalizedPath = normalizePath(file)\n\n // Check if we've already hashed this file (via symlinks)\n let hash: Buffer\n const cachedHash = fileHashCache.get(realPath)\n if (cachedHash !== undefined) {\n // Reuse cached hash for files we've already seen (via symlinks)\n hash = cachedHash\n } else {\n // Hash the file content (sync version cannot stream)\n hash = hashFileSync(realPath, normalizedPath)\n fileHashCache.set(realPath, hash)\n }\n\n fileHashInputs.push({ relativePath: normalizedPath, hash })\n }\n\n // Compute final fingerprint\n const fingerprint = computeFinalFingerprint(fileHashInputs)\n\n return {\n fingerprint,\n files: sortedFiles,\n fileCount: sortedFiles.length,\n }\n}\n\n/**\n * List files in packages, respecting ignore patterns (async).\n *\n * @param packages - Array of package directory paths\n * @param ignore - Optional glob patterns to exclude\n * @param baseDir - Base directory for resolving paths (defaults to cwd)\n * @returns Array of relative file paths\n * @public\n */\nexport async function listPackageFiles(\n packages: string[],\n ignore: string[] = [],\n baseDir: string = process.cwd(),\n): Promise<string[]> {\n const allFiles: string[] = []\n\n for (const pkg of packages) {\n // Build glob patterns for this package\n const patterns = [`${pkg}/**/*`]\n\n // Use tinyglobby to find files\n const files = await glob(patterns, {\n cwd: baseDir,\n ignore,\n onlyFiles: true,\n dot: true, // Include dotfiles\n absolute: false, // Return relative paths\n })\n\n allFiles.push(...files)\n }\n\n return allFiles\n}\n\n/**\n * Synchronous version of listPackageFiles\n */\nfunction listPackageFilesSync(\n packages: string[],\n ignore: string[] = [],\n baseDir: string = process.cwd(),\n): string[] {\n const allFiles: string[] = []\n\n for (const pkg of packages) {\n // Build glob patterns for this package\n const patterns = [`${pkg}/**/*`]\n\n // Use tinyglobby to find files (sync version)\n const files = globSync(patterns, {\n cwd: baseDir,\n ignore,\n onlyFiles: true,\n dot: true, // Include dotfiles\n absolute: false, // Return relative paths\n })\n\n allFiles.push(...files)\n }\n\n return allFiles\n}\n","/**\n * Attestation file I/O module with JSON canonicalization.\n */\n\nimport * as fs from 'node:fs'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\nimport * as canonicalizeNamespace from 'canonicalize'\nimport { z } from 'zod'\nimport type { Attestation, AttestationsFile } from './types.js'\n\n/**\n * Extract the serialize function from the canonicalize CommonJS module.\n *\n * Context: The canonicalize package uses `module.exports = function(...)`, which is\n * a CommonJS pattern. With `esModuleInterop: false` (required for library code to\n * avoid leaking tsconfig options to consumers), TypeScript treats the namespace import\n * as the module object itself, not as an object with a default export.\n *\n * The package's type definitions declare `export default function serialize(...)`,\n * which TypeScript interprets as `{ default: function }` when esModuleInterop is off.\n * However, at runtime with NodeNext module resolution, the actual value is just the\n * function itself.\n *\n * This type assertion is safe because:\n * 1. We verified the runtime export structure of the canonicalize module\n * 2. The type matches the published @types signature\n * 3. This is the standard pattern for importing CommonJS modules in strict ESM\n */\n// eslint-disable-next-line @typescript-eslint/consistent-type-assertions\nconst canonicalize = canonicalizeNamespace as unknown as {\n default: (input: unknown) => string | undefined\n}\nconst serialize = canonicalize.default\n\n// Zod schema for attestation validation\nconst attestationSchema = z.object({\n suite: z.string().min(1),\n fingerprint: z.string().regex(/^sha256:[a-f0-9]{64}$/),\n attestedAt: z.string().datetime(),\n attestedBy: z.string().min(1),\n command: z.string().min(1),\n exitCode: z.literal(0),\n})\n\nconst attestationsFileSchema = z.object({\n schemaVersion: z.literal('1'),\n attestations: z.array(attestationSchema),\n signature: z.string(), // Will be validated by crypto module\n})\n\n/**\n * Type guard to check if an error is a Node.js file system error with code.\n * We need to disable the type assertion rule here because TypeScript doesn't\n * provide a way to narrow an object type after checking for a property without\n * using type assertions or indexed access. This is a safe assertion because we\n * check for the property existence and type before using it.\n */\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n if (error === null || typeof error !== 'object') {\n return false\n }\n if (!('code' in error)) {\n return false\n }\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const errorObj = error as Record<string, unknown>\n return typeof errorObj.code === 'string'\n}\n\n/**\n * Read attestations file from disk (async).\n *\n * @param filePath - Absolute path to the attestations JSON file\n * @returns Parsed attestations file, or null if the file doesn't exist\n * @throws Error on parse or validation errors\n * @public\n */\nexport async function readAttestations(filePath: string): Promise<AttestationsFile | null> {\n try {\n const content = await fs.promises.readFile(filePath, 'utf-8')\n const parsed: unknown = JSON.parse(content)\n // Zod validates and returns the correct type\n return attestationsFileSchema.parse(parsed)\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n return null\n }\n throw error\n }\n}\n\n/**\n * Read attestations file from disk (sync).\n *\n * @param filePath - Absolute path to the attestations JSON file\n * @returns Parsed attestations file, or null if the file doesn't exist\n * @throws Error on parse or validation errors\n * @public\n */\nexport function readAttestationsSync(filePath: string): AttestationsFile | null {\n try {\n const content = fs.readFileSync(filePath, 'utf-8')\n const parsed: unknown = JSON.parse(content)\n // Zod validates and returns the correct type\n return attestationsFileSchema.parse(parsed)\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') {\n return null\n }\n throw error\n }\n}\n\n/**\n * Write attestations file to disk (async).\n *\n * Creates parent directories if needed. The signature should be computed\n * separately and passed in.\n *\n * @param filePath - Absolute path to write the attestations file\n * @param attestations - Array of attestation entries\n * @param signature - Cryptographic signature of the attestations\n * @throws Error on validation or write errors\n * @public\n */\nexport async function writeAttestations(\n filePath: string,\n attestations: Attestation[],\n signature: string,\n): Promise<void> {\n const fileContent: AttestationsFile = {\n schemaVersion: '1',\n attestations,\n signature,\n }\n\n // Validate before writing\n attestationsFileSchema.parse(fileContent)\n\n // Create parent directories if needed\n const dir = path.dirname(filePath)\n await fs.promises.mkdir(dir, { recursive: true })\n\n // Write with pretty formatting for readability\n const json = JSON.stringify(fileContent, null, 2)\n await fs.promises.writeFile(filePath, json, 'utf-8')\n}\n\n/**\n * Write attestations file to disk (sync).\n *\n * Creates parent directories if needed. The signature should be computed\n * separately and passed in.\n *\n * @param filePath - Absolute path to write the attestations file\n * @param attestations - Array of attestation entries\n * @param signature - Cryptographic signature of the attestations\n * @throws Error on validation or write errors\n * @public\n */\nexport function writeAttestationsSync(\n filePath: string,\n attestations: Attestation[],\n signature: string,\n): void {\n const fileContent: AttestationsFile = {\n schemaVersion: '1',\n attestations,\n signature,\n }\n\n // Validate before writing\n attestationsFileSchema.parse(fileContent)\n\n // Create parent directories if needed\n const dir = path.dirname(filePath)\n fs.mkdirSync(dir, { recursive: true })\n\n // Write with pretty formatting for readability\n const json = JSON.stringify(fileContent, null, 2)\n fs.writeFileSync(filePath, json, 'utf-8')\n}\n\n/**\n * Find an attestation for a specific suite.\n *\n * @param attestations - Attestations file containing all attestations\n * @param suite - Name of the suite to find\n * @returns The attestation if found, undefined otherwise\n * @public\n */\nexport function findAttestation(\n attestations: AttestationsFile,\n suite: string,\n): Attestation | undefined {\n return attestations.attestations.find((a) => a.suite === suite)\n}\n\n/**\n * Add or update an attestation for a suite.\n *\n * This is an immutable operation that returns a new array.\n *\n * @param attestations - Current array of attestations\n * @param newAttestation - Attestation to add or update\n * @returns New attestations array with the upserted attestation\n * @throws Error if the new attestation fails validation\n * @public\n */\nexport function upsertAttestation(\n attestations: Attestation[],\n newAttestation: Attestation,\n): Attestation[] {\n // Validate the new attestation\n attestationSchema.parse(newAttestation)\n\n const existingIndex = attestations.findIndex((a) => a.suite === newAttestation.suite)\n\n if (existingIndex === -1) {\n // Add new attestation\n return [...attestations, newAttestation]\n } else {\n // Update existing attestation\n const updated = [...attestations]\n // eslint-disable-next-line security/detect-object-injection -- False positive: existingIndex is a safe number from findIndex\n updated[existingIndex] = newAttestation\n return updated\n }\n}\n\n/**\n * Remove attestations for a suite.\n *\n * This is an immutable operation that returns a new array.\n *\n * @param attestations - Current array of attestations\n * @param suite - Name of the suite to remove\n * @returns New attestations array without the specified suite\n * @public\n */\nexport function removeAttestation(attestations: Attestation[], suite: string): Attestation[] {\n return attestations.filter((a) => a.suite !== suite)\n}\n\n/**\n * Compute canonical JSON representation for signing.\n *\n * Implements RFC 8785 JSON Canonicalization Scheme. The canonicalize package provides:\n * 1. Keys sorted lexicographically (Unicode code point order)\n * 2. No whitespace between tokens\n * 3. No trailing commas\n * 4. Strings escaped using \\uXXXX for control characters\n * 5. Numbers: no leading zeros, no +, use lowercase 'e' for exponent\n * 6. UTF-8 encoding\n *\n * @param attestations - Array of attestations to canonicalize\n * @returns Canonical JSON string representation\n * @throws Error if canonicalization fails\n * @public\n */\nexport function canonicalizeAttestations(attestations: Attestation[]): string {\n const canonical = serialize(attestations)\n if (canonical === undefined) {\n throw new Error('Failed to canonicalize attestations')\n }\n return canonical\n}\n\n/**\n * Create a new attestation entry.\n *\n * @param params - Parameters for creating the attestation\n * @param params.suite - Name of the test suite\n * @param params.fingerprint - Fingerprint of the packages in sha256 format\n * @param params.command - Command that was executed\n * @param params.attestedBy - Optional username (defaults to current OS user)\n * @returns Validated attestation object\n * @throws Error if attestation validation fails\n * @public\n */\nexport function createAttestation(params: {\n suite: string\n fingerprint: string\n command: string\n attestedBy?: string\n}): Attestation {\n const attestation: Attestation = {\n suite: params.suite,\n fingerprint: params.fingerprint,\n attestedAt: new Date().toISOString(),\n attestedBy: params.attestedBy ?? os.userInfo().username,\n command: params.command,\n exitCode: 0,\n }\n\n // Validate before returning\n attestationSchema.parse(attestation)\n\n return attestation\n}\n\n/**\n * Options for writing signed attestations.\n * @public\n */\nexport interface WriteSignedAttestationsOptions {\n /** Path to write the attestations file */\n filePath: string\n /** Array of attestations to write */\n attestations: Attestation[]\n /** Path to the private key for signing */\n privateKeyPath: string\n}\n\n/**\n * Options for reading and verifying signed attestations.\n * @public\n */\nexport interface ReadSignedAttestationsOptions {\n /** Path to read the attestations file from */\n filePath: string\n /** Path to the public key for verification */\n publicKeyPath: string\n}\n\n/**\n * Write attestations with a cryptographic signature.\n *\n * This function canonicalizes the attestations, signs them with the private key,\n * and writes the attestations file with the signature.\n *\n * @param options - Options for writing signed attestations\n * @throws Error if signing or writing fails\n * @public\n */\nexport async function writeSignedAttestations(\n options: WriteSignedAttestationsOptions,\n): Promise<void> {\n // Import sign function here to avoid circular dependency\n const { sign } = await import('./crypto.js')\n\n const canonical = canonicalizeAttestations(options.attestations)\n const signature = await sign({\n privateKeyPath: options.privateKeyPath,\n data: canonical,\n })\n await writeAttestations(options.filePath, options.attestations, signature)\n}\n\n/**\n * Read attestations and verify the signature.\n *\n * This function reads the attestations file, canonicalizes the attestations,\n * and verifies the signature using the public key. It throws an error if the\n * file doesn't exist or if signature verification fails.\n *\n * @param options - Options for reading and verifying attestations\n * @returns The attestations file if signature is valid\n * @throws Error if attestations file not found\n * @throws SignatureInvalidError if signature verification fails\n * @public\n */\nexport async function readAndVerifyAttestations(\n options: ReadSignedAttestationsOptions,\n): Promise<AttestationsFile> {\n // Import verify function here to avoid circular dependency\n const { verify } = await import('./crypto.js')\n\n const file = await readAttestations(options.filePath)\n if (!file) {\n throw new Error(`Attestations file not found: ${options.filePath}`)\n }\n\n const canonical = canonicalizeAttestations(file.attestations)\n const isValid = await verify({\n publicKeyPath: options.publicKeyPath,\n data: canonical,\n signature: file.signature,\n })\n\n if (!isValid) {\n throw new SignatureInvalidError(options.filePath)\n }\n\n return file\n}\n\n/**\n * Error thrown when signature verification fails.\n * @public\n */\nexport class SignatureInvalidError extends Error {\n /**\n * Create a new SignatureInvalidError.\n * @param filePath - Path to the file that failed verification\n */\n constructor(filePath: string) {\n super(`Signature verification failed for: ${filePath}`)\n this.name = 'SignatureInvalidError'\n }\n}\n","/**\n * @attest-it/core\n *\n * Core functionality for the attest-it testing framework.\n * @packageDocumentation\n */\n\n/**\n * Package version\n * @public\n */\nexport const version = '0.0.0'\n\n// Types\nexport type {\n AttestItSettings,\n SuiteConfig,\n AttestItConfig,\n Attestation,\n AttestationsFile,\n VerificationStatus,\n SuiteVerificationResult,\n} from './types.js'\n\n// Config\nexport {\n loadConfig,\n loadConfigSync,\n findConfigPath,\n resolveConfigPaths,\n toAttestItConfig,\n ConfigValidationError,\n ConfigNotFoundError,\n type Config,\n} from './config.js'\n\n// Fingerprinting\nexport { computeFingerprint, computeFingerprintSync, listPackageFiles } from './fingerprint.js'\nexport type { FingerprintOptions, FingerprintResult } from './fingerprint.js'\n\n// Attestations\nexport {\n readAttestations,\n readAttestationsSync,\n writeAttestations,\n writeAttestationsSync,\n findAttestation,\n upsertAttestation,\n removeAttestation,\n canonicalizeAttestations,\n createAttestation,\n writeSignedAttestations,\n readAndVerifyAttestations,\n SignatureInvalidError,\n} from './attestation.js'\nexport type {\n WriteSignedAttestationsOptions,\n ReadSignedAttestationsOptions,\n} from './attestation.js'\n\n// Cryptography\nexport {\n checkOpenSSL,\n getDefaultPrivateKeyPath,\n getDefaultPublicKeyPath,\n generateKeyPair,\n sign,\n verify,\n setKeyPermissions,\n} from './crypto.js'\nexport type {\n KeyPaths,\n KeygenOptions,\n SignOptions,\n VerifyOptions as CryptoVerifyOptions,\n} from './crypto.js'\n\n// Verification\nexport { verifyAttestations } from './verify.js'\nexport type { VerifyOptions, VerifyResult } from './verify.js'\n","/**\n * Verification logic for attestations.\n * @packageDocumentation\n */\n\nimport * as fs from 'node:fs'\nimport * as path from 'node:path'\nimport type {\n AttestItConfig,\n Attestation,\n AttestationsFile,\n SuiteVerificationResult,\n} from './types.js'\nimport { computeFingerprint } from './fingerprint.js'\nimport { readAndVerifyAttestations, SignatureInvalidError } from './attestation.js'\n\n/**\n * Options for verifying attestations.\n * @public\n */\nexport interface VerifyOptions {\n /** Configuration object */\n config: AttestItConfig\n /** Repository root directory (defaults to process.cwd()) */\n repoRoot?: string\n}\n\n/**\n * Result of verifying all attestations.\n * @public\n */\nexport interface VerifyResult {\n /** Overall success - true if all attestations are valid */\n success: boolean\n /** Whether the attestations file signature is valid */\n signatureValid: boolean\n /** Verification results for each suite */\n suites: SuiteVerificationResult[]\n /** Error messages encountered during verification */\n errors: string[]\n}\n\n/**\n * Verify all attestations against current code state.\n *\n * Verification algorithm:\n * 1. Load and verify attestations file signature\n * 2. For each suite in config:\n * a. Compute current fingerprint\n * b. Find matching attestation\n * c. Compare fingerprints\n * d. Check age\n * 3. Check invalidation chains\n * 4. Return aggregated results\n *\n * @param options - Verification options\n * @returns Verification result with status for each suite\n * @public\n */\nexport async function verifyAttestations(options: VerifyOptions): Promise<VerifyResult> {\n const { config, repoRoot = process.cwd() } = options\n const errors: string[] = []\n const suiteResults: SuiteVerificationResult[] = []\n let signatureValid = true\n let attestationsFile: AttestationsFile | null = null\n\n // Resolve paths\n const attestationsPath = resolvePath(config.settings.attestationsPath, repoRoot)\n const publicKeyPath = resolvePath(config.settings.publicKeyPath, repoRoot)\n\n // Step 1: Load and verify attestations\n try {\n if (!fs.existsSync(attestationsPath)) {\n // No attestations file - all suites need attestation\n attestationsFile = null\n } else if (!fs.existsSync(publicKeyPath)) {\n errors.push(`Public key not found: ${publicKeyPath}`)\n signatureValid = false\n } else {\n attestationsFile = await readAndVerifyAttestations({\n filePath: attestationsPath,\n publicKeyPath,\n })\n }\n } catch (err) {\n if (err instanceof SignatureInvalidError) {\n signatureValid = false\n errors.push(err.message)\n } else if (err instanceof Error) {\n errors.push(err.message)\n }\n }\n\n const attestations = attestationsFile?.attestations ?? []\n\n // Step 2: Check each suite\n for (const [suiteName, suiteConfig] of Object.entries(config.suites)) {\n const result = await verifySuite({\n suiteName,\n suiteConfig,\n attestations,\n maxAgeDays: config.settings.maxAgeDays,\n repoRoot,\n })\n suiteResults.push(result)\n }\n\n // Step 3: Check invalidation chains\n checkInvalidationChains(config, suiteResults)\n\n // Step 4: Aggregate results\n const allValid =\n signatureValid && suiteResults.every((r) => r.status === 'VALID') && errors.length === 0\n\n return {\n success: allValid,\n signatureValid,\n suites: suiteResults,\n errors,\n }\n}\n\n/**\n * Options for verifying a single suite.\n * @internal\n */\ninterface VerifySuiteOptions {\n /** Name of the suite */\n suiteName: string\n /** Suite configuration */\n suiteConfig: { packages: string[]; ignore?: string[] }\n /** All attestations from the attestations file */\n attestations: Attestation[]\n /** Maximum age in days before attestation expires */\n maxAgeDays: number\n /** Repository root directory */\n repoRoot: string\n}\n\n/**\n * Verify a single suite's attestation.\n * @internal\n */\nasync function verifySuite(options: VerifySuiteOptions): Promise<SuiteVerificationResult> {\n const { suiteName, suiteConfig, attestations, maxAgeDays, repoRoot } = options\n\n // Compute current fingerprint\n const fingerprintOptions = {\n packages: suiteConfig.packages.map((p) => resolvePath(p, repoRoot)),\n baseDir: repoRoot,\n ...(suiteConfig.ignore && { ignore: suiteConfig.ignore }),\n }\n const fingerprintResult = await computeFingerprint(fingerprintOptions)\n\n // Find attestation for this suite\n const attestation = attestations.find((a) => a.suite === suiteName)\n\n // No attestation found\n if (!attestation) {\n return {\n suite: suiteName,\n status: 'NEEDS_ATTESTATION',\n fingerprint: fingerprintResult.fingerprint,\n message: 'No attestation found for this suite',\n }\n }\n\n // Check fingerprint\n if (attestation.fingerprint !== fingerprintResult.fingerprint) {\n return {\n suite: suiteName,\n status: 'FINGERPRINT_CHANGED',\n fingerprint: fingerprintResult.fingerprint,\n attestation,\n message: `Fingerprint changed from ${attestation.fingerprint.slice(0, 20)}... to ${fingerprintResult.fingerprint.slice(0, 20)}...`,\n }\n }\n\n // Check age\n const attestedAt = new Date(attestation.attestedAt)\n const ageMs = Date.now() - attestedAt.getTime()\n const ageDays = Math.floor(ageMs / (1000 * 60 * 60 * 24))\n\n if (ageDays > maxAgeDays) {\n return {\n suite: suiteName,\n status: 'EXPIRED',\n fingerprint: fingerprintResult.fingerprint,\n attestation,\n age: ageDays,\n message: `Attestation expired (${String(ageDays)} days old, max ${String(maxAgeDays)} days)`,\n }\n }\n\n // All checks passed\n return {\n suite: suiteName,\n status: 'VALID',\n fingerprint: fingerprintResult.fingerprint,\n attestation,\n age: ageDays,\n }\n}\n\n/**\n * Check invalidation chains.\n *\n * If suite A invalidates suite B, and A's attestation is newer than B's,\n * then B should be marked as INVALIDATED_BY_PARENT.\n *\n * @param config - Full configuration\n * @param results - Array of suite verification results to mutate\n * @internal\n */\nfunction checkInvalidationChains(config: AttestItConfig, results: SuiteVerificationResult[]): void {\n for (const [parentName, parentConfig] of Object.entries(config.suites)) {\n const invalidates = parentConfig.invalidates ?? []\n const parentResult = results.find((r) => r.suite === parentName)\n\n if (!parentResult?.attestation) continue\n\n const parentTime = new Date(parentResult.attestation.attestedAt).getTime()\n\n for (const childName of invalidates) {\n const childResult = results.find((r) => r.suite === childName)\n if (!childResult?.attestation) continue\n\n const childTime = new Date(childResult.attestation.attestedAt).getTime()\n\n // If parent was attested AFTER child, child is invalidated\n if (parentTime > childTime && childResult.status === 'VALID') {\n childResult.status = 'INVALIDATED_BY_PARENT'\n childResult.message = `Invalidated by ${parentName} (attested later)`\n }\n }\n }\n}\n\n/**\n * Resolve a path relative to a base directory.\n * @param relativePath - Path that may be relative or absolute\n * @param baseDir - Base directory for resolving relative paths\n * @returns Absolute path\n * @internal\n */\nfunction resolvePath(relativePath: string, baseDir: string): string {\n if (path.isAbsolute(relativePath)) {\n return relativePath\n }\n return path.join(baseDir, relativePath)\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -35,6 +35,8 @@ interface SuiteConfig {
|
|
|
35
35
|
command?: string;
|
|
36
36
|
/** Other suite names that, when changed, invalidate this suite's attestation */
|
|
37
37
|
invalidates?: string[];
|
|
38
|
+
/** Array of suite names this suite depends on */
|
|
39
|
+
depends_on?: string[];
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
40
42
|
* Full configuration file structure.
|
|
@@ -47,6 +49,8 @@ interface AttestItConfig {
|
|
|
47
49
|
settings: AttestItSettings;
|
|
48
50
|
/** Named test suites with their configurations */
|
|
49
51
|
suites: Record<string, SuiteConfig>;
|
|
52
|
+
/** Named groups of suites */
|
|
53
|
+
groups?: Record<string, string[]>;
|
|
50
54
|
}
|
|
51
55
|
/**
|
|
52
56
|
* A single attestation entry.
|
|
@@ -132,6 +136,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
132
136
|
ignore: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
133
137
|
command: z.ZodOptional<z.ZodString>;
|
|
134
138
|
invalidates: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
139
|
+
depends_on: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
135
140
|
}, "strict", z.ZodTypeAny, {
|
|
136
141
|
packages: string[];
|
|
137
142
|
description?: string | undefined;
|
|
@@ -139,6 +144,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
139
144
|
ignore?: string[] | undefined;
|
|
140
145
|
command?: string | undefined;
|
|
141
146
|
invalidates?: string[] | undefined;
|
|
147
|
+
depends_on?: string[] | undefined;
|
|
142
148
|
}, {
|
|
143
149
|
packages: string[];
|
|
144
150
|
description?: string | undefined;
|
|
@@ -146,6 +152,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
146
152
|
ignore?: string[] | undefined;
|
|
147
153
|
command?: string | undefined;
|
|
148
154
|
invalidates?: string[] | undefined;
|
|
155
|
+
depends_on?: string[] | undefined;
|
|
149
156
|
}>>, Record<string, {
|
|
150
157
|
packages: string[];
|
|
151
158
|
description?: string | undefined;
|
|
@@ -153,6 +160,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
153
160
|
ignore?: string[] | undefined;
|
|
154
161
|
command?: string | undefined;
|
|
155
162
|
invalidates?: string[] | undefined;
|
|
163
|
+
depends_on?: string[] | undefined;
|
|
156
164
|
}>, Record<string, {
|
|
157
165
|
packages: string[];
|
|
158
166
|
description?: string | undefined;
|
|
@@ -160,7 +168,9 @@ declare const configSchema: z.ZodObject<{
|
|
|
160
168
|
ignore?: string[] | undefined;
|
|
161
169
|
command?: string | undefined;
|
|
162
170
|
invalidates?: string[] | undefined;
|
|
171
|
+
depends_on?: string[] | undefined;
|
|
163
172
|
}>>;
|
|
173
|
+
groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
164
174
|
}, "strict", z.ZodTypeAny, {
|
|
165
175
|
version: 1;
|
|
166
176
|
settings: {
|
|
@@ -178,7 +188,9 @@ declare const configSchema: z.ZodObject<{
|
|
|
178
188
|
ignore?: string[] | undefined;
|
|
179
189
|
command?: string | undefined;
|
|
180
190
|
invalidates?: string[] | undefined;
|
|
191
|
+
depends_on?: string[] | undefined;
|
|
181
192
|
}>;
|
|
193
|
+
groups?: Record<string, string[]> | undefined;
|
|
182
194
|
}, {
|
|
183
195
|
version: 1;
|
|
184
196
|
suites: Record<string, {
|
|
@@ -188,6 +200,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
188
200
|
ignore?: string[] | undefined;
|
|
189
201
|
command?: string | undefined;
|
|
190
202
|
invalidates?: string[] | undefined;
|
|
203
|
+
depends_on?: string[] | undefined;
|
|
191
204
|
}>;
|
|
192
205
|
settings?: z.objectInputType<{
|
|
193
206
|
maxAgeDays: z.ZodDefault<z.ZodNumber>;
|
|
@@ -195,6 +208,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
195
208
|
attestationsPath: z.ZodDefault<z.ZodString>;
|
|
196
209
|
defaultCommand: z.ZodOptional<z.ZodString>;
|
|
197
210
|
}, z.ZodTypeAny, "passthrough"> | undefined;
|
|
211
|
+
groups?: Record<string, string[]> | undefined;
|
|
198
212
|
}>;
|
|
199
213
|
/**
|
|
200
214
|
* Type inference from Zod schema (should match AttestItConfig).
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ interface SuiteConfig {
|
|
|
35
35
|
command?: string;
|
|
36
36
|
/** Other suite names that, when changed, invalidate this suite's attestation */
|
|
37
37
|
invalidates?: string[];
|
|
38
|
+
/** Array of suite names this suite depends on */
|
|
39
|
+
depends_on?: string[];
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
40
42
|
* Full configuration file structure.
|
|
@@ -47,6 +49,8 @@ interface AttestItConfig {
|
|
|
47
49
|
settings: AttestItSettings;
|
|
48
50
|
/** Named test suites with their configurations */
|
|
49
51
|
suites: Record<string, SuiteConfig>;
|
|
52
|
+
/** Named groups of suites */
|
|
53
|
+
groups?: Record<string, string[]>;
|
|
50
54
|
}
|
|
51
55
|
/**
|
|
52
56
|
* A single attestation entry.
|
|
@@ -108,6 +112,7 @@ interface SuiteVerificationResult {
|
|
|
108
112
|
* Zod schema for the full configuration file.
|
|
109
113
|
*/
|
|
110
114
|
declare const configSchema: z.ZodObject<{
|
|
115
|
+
groups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
|
|
111
116
|
settings: z.ZodDefault<z.ZodObject<{
|
|
112
117
|
attestationsPath: z.ZodDefault<z.ZodString>;
|
|
113
118
|
defaultCommand: z.ZodOptional<z.ZodString>;
|
|
@@ -126,6 +131,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
126
131
|
}, z.ZodTypeAny, "passthrough">>>;
|
|
127
132
|
suites: z.ZodEffects<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
128
133
|
command: z.ZodOptional<z.ZodString>;
|
|
134
|
+
depends_on: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
129
135
|
description: z.ZodOptional<z.ZodString>;
|
|
130
136
|
files: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
131
137
|
ignore: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
@@ -133,6 +139,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
133
139
|
packages: z.ZodArray<z.ZodString, "many">;
|
|
134
140
|
}, "strict", z.ZodTypeAny, {
|
|
135
141
|
command?: string | undefined;
|
|
142
|
+
depends_on?: string[] | undefined;
|
|
136
143
|
description?: string | undefined;
|
|
137
144
|
files?: string[] | undefined;
|
|
138
145
|
ignore?: string[] | undefined;
|
|
@@ -140,6 +147,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
140
147
|
packages: string[];
|
|
141
148
|
}, {
|
|
142
149
|
command?: string | undefined;
|
|
150
|
+
depends_on?: string[] | undefined;
|
|
143
151
|
description?: string | undefined;
|
|
144
152
|
files?: string[] | undefined;
|
|
145
153
|
ignore?: string[] | undefined;
|
|
@@ -147,6 +155,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
147
155
|
packages: string[];
|
|
148
156
|
}>>, Record<string, {
|
|
149
157
|
command?: string | undefined;
|
|
158
|
+
depends_on?: string[] | undefined;
|
|
150
159
|
description?: string | undefined;
|
|
151
160
|
files?: string[] | undefined;
|
|
152
161
|
ignore?: string[] | undefined;
|
|
@@ -154,6 +163,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
154
163
|
packages: string[];
|
|
155
164
|
}>, Record<string, {
|
|
156
165
|
command?: string | undefined;
|
|
166
|
+
depends_on?: string[] | undefined;
|
|
157
167
|
description?: string | undefined;
|
|
158
168
|
files?: string[] | undefined;
|
|
159
169
|
ignore?: string[] | undefined;
|
|
@@ -162,6 +172,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
162
172
|
}>>;
|
|
163
173
|
version: z.ZodLiteral<1>;
|
|
164
174
|
}, "strict", z.ZodTypeAny, {
|
|
175
|
+
groups?: Record<string, string[]> | undefined;
|
|
165
176
|
settings: {
|
|
166
177
|
attestationsPath: string;
|
|
167
178
|
defaultCommand?: string | undefined;
|
|
@@ -170,6 +181,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
170
181
|
} & { [k: string]: unknown };
|
|
171
182
|
suites: Record<string, {
|
|
172
183
|
command?: string | undefined;
|
|
184
|
+
depends_on?: string[] | undefined;
|
|
173
185
|
description?: string | undefined;
|
|
174
186
|
files?: string[] | undefined;
|
|
175
187
|
ignore?: string[] | undefined;
|
|
@@ -178,6 +190,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
178
190
|
}>;
|
|
179
191
|
version: 1;
|
|
180
192
|
}, {
|
|
193
|
+
groups?: Record<string, string[]> | undefined;
|
|
181
194
|
settings?: undefined | z.objectInputType<{
|
|
182
195
|
attestationsPath: z.ZodDefault<z.ZodString>;
|
|
183
196
|
defaultCommand: z.ZodOptional<z.ZodString>;
|
|
@@ -186,6 +199,7 @@ declare const configSchema: z.ZodObject<{
|
|
|
186
199
|
}, z.ZodTypeAny, "passthrough">;
|
|
187
200
|
suites: Record<string, {
|
|
188
201
|
command?: string | undefined;
|
|
202
|
+
depends_on?: string[] | undefined;
|
|
189
203
|
description?: string | undefined;
|
|
190
204
|
files?: string[] | undefined;
|
|
191
205
|
ignore?: string[] | undefined;
|