import { createHash } from 'node:crypto'; import { lstat, mkdir, mkdtemp, readFile, realpath, rename, rm, writeFile, } from 'node:fs/promises'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { unzipSync, zipSync } from 'fflate'; const repositoryRoot = path.resolve( path.dirname(fileURLToPath(import.meta.url)), '..' ); const lockPath = path.join( repositoryRoot, 'scripts', 'reviewed-ffmpeg-core-lock.json' ); const maximumAssetBytes = 64 * 1024 * 1024; const fixedTimestamp = new Date(1980, 0, 1, 0, 0, 0, 0); const requiredAssetPaths = new Set([ 'st/ffmpeg-core.js', 'st/ffmpeg-core.wasm', 'mt/ffmpeg-core.js', 'mt/ffmpeg-core.wasm', 'mt/ffmpeg-core.worker.js', ]); function sha256(bytes) { return createHash('sha256').update(bytes).digest('hex'); } function assertSafeRelativePath(value) { if ( typeof value !== 'string' || value.length === 0 || value.includes('\\') || value.includes('\0') || value.startsWith('/') || path.posix.normalize(value) !== value || value.split('/').some((part) => !part || part === '.' || part === '..') ) { throw new Error(`Unsafe reviewed-core path: ${String(value)}`); } return value; } async function requireRealDirectory(directory, create = false) { let details = await lstat(directory).catch((error) => { if (error && typeof error === 'object' && error.code === 'ENOENT') { return undefined; } throw error; }); if (!details && create) { await mkdir(directory); details = await lstat(directory); } if (!details || details.isSymbolicLink() || !details.isDirectory()) { throw new Error(`Required directory is unsafe: ${directory}`); } } async function readLock() { const lock = JSON.parse(await readFile(lockPath, 'utf8')); const lockedAssetPaths = new Set( Array.isArray(lock.assets) ? lock.assets.map((asset) => asset.path) : [] ); if ( lock.schemaVersion !== 1 || typeof lock.buildId !== 'string' || !/^[a-zA-Z0-9._-]+$/.test(lock.buildId) || lock.coreVersion !== '0.12.10' || lock.profile !== 'reviewed-stack5m' || lock.sourceArchive?.path !== 'release/ffmpeg-core-0.12.10-reviewed-stack5m.1-corresponding-source.tar.xz' || !Number.isSafeInteger(lock.sourceArchive?.bytes) || !/^[a-f0-9]{64}$/.test(lock.sourceArchive?.sha256 ?? '') || lock.sourceLock?.path !== 'scripts/ffmpeg-source-lock.json' || !/^[a-f0-9]{64}$/.test(lock.sourceLock?.sha256 ?? '') || lock.buildDriver?.path !== 'scripts/build-reviewed-ffmpeg-core.sh' || !/^[a-f0-9]{64}$/.test(lock.buildDriver?.sha256 ?? '') || !Array.isArray(lock.assets) || lock.assets.length !== 5 || lock.assets.some( (asset) => !requiredAssetPaths.has(asset.path) || !Number.isSafeInteger(asset.bytes) || asset.bytes < 1 || asset.bytes > maximumAssetBytes || !/^[a-f0-9]{64}$/.test(asset.sha256 ?? '') ) || lockedAssetPaths.size !== requiredAssetPaths.size || [...requiredAssetPaths].some( (assetPath) => !lockedAssetPaths.has(assetPath) ) || typeof lock.artifact?.path !== 'string' || !Number.isSafeInteger(lock.artifact?.bytes) || lock.artifact.bytes < 1 || lock.artifact.bytes > 64 * 1024 * 1024 || !/^[a-f0-9]{64}$/.test(lock.artifact?.sha256 ?? '') ) { throw new Error(`Invalid reviewed FFmpeg core lock: ${lockPath}`); } return lock; } async function readBuildAsset(asset) { const relativeAssetPath = assertSafeRelativePath(asset.path); const [mode, fileName, ...extra] = relativeAssetPath.split('/'); if ( extra.length > 0 || !['st', 'mt'].includes(mode) || !['ffmpeg-core.js', 'ffmpeg-core.wasm', 'ffmpeg-core.worker.js'].includes( fileName ) ) { throw new Error(`Unexpected reviewed-core asset: ${relativeAssetPath}`); } const outputDirectory = path.join( repositoryRoot, '.core-build', 'reviewed-stack5m', mode, 'output' ); const source = path.join(outputDirectory, fileName); const details = await lstat(source); if (details.isSymbolicLink() || !details.isFile()) { throw new Error(`Reviewed-core input must be a regular file: ${source}`); } const canonicalOutput = await realpath(outputDirectory); const canonicalSource = await realpath(source); if (!canonicalSource.startsWith(`${canonicalOutput}${path.sep}`)) { throw new Error(`Reviewed-core input resolves outside output: ${source}`); } const bytes = await readFile(canonicalSource); if ( bytes.byteLength < 1 || bytes.byteLength > maximumAssetBytes || bytes.byteLength !== asset.bytes || sha256(bytes) !== asset.sha256 ) { throw new Error(`Reviewed-core build does not match lock: ${asset.path}`); } return bytes; } function verifyArchiveEntries(archive, lock) { const lockedEntries = new Map( lock.assets.map((asset) => [asset.path, asset]) ); const seen = new Set(); const uncompressed = unzipSync(archive, { filter: (entry) => { const relative = assertSafeRelativePath(entry.name); const locked = lockedEntries.get(relative); if ( !locked || seen.has(relative) || entry.originalSize !== locked.bytes || entry.originalSize < 1 || entry.originalSize > maximumAssetBytes || entry.size < 1 || entry.size > 64 * 1024 * 1024 ) { throw new Error( `Reviewed-core archive contains an unexpected entry: ${entry.name}` ); } seen.add(relative); return true; }, }); if (seen.size !== lockedEntries.size) { throw new Error('Reviewed-core archive is missing an expected entry.'); } for (const asset of lock.assets) { const bytes = uncompressed[asset.path]; if ( !bytes || bytes.byteLength !== asset.bytes || sha256(bytes) !== asset.sha256 ) { throw new Error( `Reviewed-core archive entry does not match lock: ${asset.path}` ); } } } const lock = await readLock(); const buildDriver = await readFile( path.join(repositoryRoot, ...lock.buildDriver.path.split('/')) ); if (sha256(buildDriver) !== lock.buildDriver.sha256) { throw new Error( 'Reviewed-core build driver does not match its locked identity.' ); } const sourceLock = await readFile( path.join(repositoryRoot, ...lock.sourceLock.path.split('/')) ); if (sha256(sourceLock) !== lock.sourceLock.sha256) { throw new Error('FFmpeg source lock does not match its locked identity.'); } const sourceArchive = await readFile( path.join(repositoryRoot, ...lock.sourceArchive.path.split('/')) ); if ( sourceArchive.byteLength !== lock.sourceArchive.bytes || sha256(sourceArchive) !== lock.sourceArchive.sha256 ) { throw new Error( 'Reviewed-core Corresponding Source archive does not match its locked identity.' ); } const artifactRelativePath = assertSafeRelativePath(lock.artifact.path); const expectedArtifactRelativePath = `vendor/ffmpeg-core-${lock.buildId}.zip`; if (artifactRelativePath !== expectedArtifactRelativePath) { throw new Error( `Reviewed-core artifact path must be ${expectedArtifactRelativePath}.` ); } const artifactPath = path.join( repositoryRoot, ...artifactRelativePath.split('/') ); const entries = {}; for (const asset of [...lock.assets].sort((left, right) => left.path.localeCompare(right.path, 'en') )) { entries[asset.path] = [ await readBuildAsset(asset), { level: 9, mtime: fixedTimestamp }, ]; } const archive = Buffer.from(zipSync(entries, { level: 9 })); verifyArchiveEntries(archive, lock); const archiveDigest = sha256(archive); if ( archive.byteLength !== lock.artifact.bytes || archiveDigest !== lock.artifact.sha256 ) { throw new Error( `Deterministic reviewed-core archive does not match its locked identity: received ${String(archive.byteLength)} bytes/${archiveDigest}.` ); } const artifactDirectory = path.dirname(artifactPath); await requireRealDirectory(repositoryRoot); await requireRealDirectory(artifactDirectory, true); const stagingRoot = await mkdtemp( path.join(artifactDirectory, '.reviewed-core-package-') ); if ( !stagingRoot.startsWith( `${path.join(artifactDirectory, '.reviewed-core-package-')}` ) ) { throw new Error(`Unexpected reviewed-core staging path: ${stagingRoot}`); } try { const stagedArtifact = path.join(stagingRoot, path.basename(artifactPath)); await writeFile(stagedArtifact, archive, { flag: 'wx', mode: 0o644 }); await rename(stagedArtifact, artifactPath); } finally { await rm(stagingRoot, { force: true, recursive: true }); } console.log( `Packaged ${lock.assets.length} reviewed FFmpeg assets as ${artifactRelativePath} (${archive.byteLength} bytes, ${archiveDigest})` );