mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 16:03:17 +00:00
227 lines
8.0 KiB
JavaScript
227 lines
8.0 KiB
JavaScript
import { constants } from 'node:fs'
|
|
import { copyFile, lstat, mkdir, realpath, rm, writeFile } from 'node:fs/promises'
|
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path'
|
|
import { pathToFileURL } from 'node:url'
|
|
|
|
import { writeSshRelayRuntimeManifestTupleDescriptor } from './ssh-relay-runtime-manifest-tuple.mjs'
|
|
import {
|
|
buildSshRelayRuntimeNativeSigningPlan,
|
|
readSshRelayRuntimeNativeSigningIdentity
|
|
} from './ssh-relay-runtime-native-signing-plan.mjs'
|
|
import { verifySshRelayRuntime } from './verify-ssh-relay-runtime.mjs'
|
|
|
|
const MAX_RECEIPT_BYTES = 32 * 1024 * 1024
|
|
const PATH_ARGUMENTS = new Map([
|
|
['--source-output-directory', 'sourceOutputDirectory'],
|
|
['--identity', 'identityPath'],
|
|
['--output-directory', 'outputDirectory']
|
|
])
|
|
const VALUE_ARGUMENTS = new Map([
|
|
['--verified-at', 'verifiedAt'],
|
|
['--native-verification-tool-version', 'nativeVerificationToolVersion']
|
|
])
|
|
|
|
function containsPath(parent, candidate) {
|
|
const path = relative(parent, candidate)
|
|
return path === '' || (path !== '..' && !path.startsWith(`..${sep}`) && !isAbsolute(path))
|
|
}
|
|
|
|
async function physicalDirectory(path, label) {
|
|
const metadata = await lstat(path)
|
|
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
throw new Error(`Runtime Linux finalization ${label} must be a real directory`)
|
|
}
|
|
return realpath(path)
|
|
}
|
|
|
|
async function assertAbsent(path) {
|
|
try {
|
|
await lstat(path)
|
|
} catch (error) {
|
|
if (error.code === 'ENOENT') {
|
|
return
|
|
}
|
|
throw error
|
|
}
|
|
throw new Error('Runtime Linux finalization requires an exclusive output directory')
|
|
}
|
|
|
|
async function copyRegularFile(source, destination, label) {
|
|
const before = await lstat(source, { bigint: true })
|
|
if (!before.isFile() || before.isSymbolicLink() || before.size <= 0n) {
|
|
throw new Error(`Runtime Linux finalization ${label} must be a non-empty regular file`)
|
|
}
|
|
await copyFile(source, destination, constants.COPYFILE_EXCL)
|
|
const after = await lstat(source, { bigint: true })
|
|
if (
|
|
before.dev !== after.dev ||
|
|
before.ino !== after.ino ||
|
|
before.size !== after.size ||
|
|
before.mtimeNs !== after.mtimeNs ||
|
|
before.ctimeNs !== after.ctimeNs
|
|
) {
|
|
throw new Error(`Runtime Linux finalization ${label} changed while copying`)
|
|
}
|
|
}
|
|
|
|
function aggregateAssetNames(identity) {
|
|
if (
|
|
typeof identity.archive?.name !== 'string' ||
|
|
basename(identity.archive.name) !== identity.archive.name
|
|
) {
|
|
throw new Error('Runtime Linux finalization archive identity is invalid')
|
|
}
|
|
const prefix = `orca-ssh-relay-runtime-${identity.tupleId}`
|
|
return {
|
|
archive: identity.archive.name,
|
|
sbom: `${prefix}.spdx.json`,
|
|
provenance: `${prefix}.provenance.json`
|
|
}
|
|
}
|
|
|
|
function linuxVerificationReport(identity, plan) {
|
|
return {
|
|
tupleId: identity.tupleId,
|
|
sourceContentId: identity.contentId,
|
|
finalContentId: identity.contentId,
|
|
verifiedFiles: plan.verificationFiles.map((file) => ({
|
|
path: file.path,
|
|
role: file.role,
|
|
sha256: file.sourceSha256
|
|
}))
|
|
}
|
|
}
|
|
|
|
async function writeReceipt(path, receipt) {
|
|
const bytes = Buffer.from(`${JSON.stringify(receipt, null, 2)}\n`, 'utf8')
|
|
if (bytes.length === 0 || bytes.length > MAX_RECEIPT_BYTES) {
|
|
throw new Error('Runtime Linux finalization receipt exceeds its size limit')
|
|
}
|
|
await writeFile(path, bytes, { flag: 'wx', mode: 0o600 })
|
|
}
|
|
|
|
export async function finalizeSshRelayRuntimeLinuxArtifact({
|
|
sourceOutputDirectory,
|
|
identityPath,
|
|
outputDirectory,
|
|
verifiedAt,
|
|
nativeVerificationTool,
|
|
readIdentityImpl = readSshRelayRuntimeNativeSigningIdentity,
|
|
buildPlanImpl = buildSshRelayRuntimeNativeSigningPlan,
|
|
verifyRuntimeImpl = verifySshRelayRuntime,
|
|
writeDescriptorImpl = writeSshRelayRuntimeManifestTupleDescriptor
|
|
}) {
|
|
const source = await physicalDirectory(resolve(sourceOutputDirectory), 'source output')
|
|
const absoluteIdentity = resolve(identityPath)
|
|
const physicalIdentity = await realpath(absoluteIdentity)
|
|
const identity = await readIdentityImpl(physicalIdentity)
|
|
if (identity.os !== 'linux' || !identity.tupleId?.startsWith('linux-')) {
|
|
throw new Error('Runtime Linux finalization accepts only Linux identities')
|
|
}
|
|
const plan = buildPlanImpl(identity)
|
|
if (plan.platform !== 'linux') {
|
|
throw new Error('Runtime Linux finalization signing plan must be hash-only Linux')
|
|
}
|
|
const expectedIdentityPath = join(
|
|
source,
|
|
`orca-ssh-relay-runtime-${identity.tupleId}.identity.json`
|
|
)
|
|
if (physicalIdentity !== expectedIdentityPath) {
|
|
throw new Error('Runtime Linux finalization identity must be the exact source output identity')
|
|
}
|
|
const outputParent = await physicalDirectory(dirname(resolve(outputDirectory)), 'output parent')
|
|
const output = resolve(outputParent, basename(resolve(outputDirectory)))
|
|
if (containsPath(source, output) || containsPath(output, source)) {
|
|
throw new Error('Runtime Linux finalization source and output must be physically disjoint')
|
|
}
|
|
await assertAbsent(output)
|
|
|
|
const names = aggregateAssetNames(identity)
|
|
const runtimeDirectory = join(source, 'runtime')
|
|
await physicalDirectory(runtimeDirectory, 'runtime')
|
|
const finalIdentity = { ...identity }
|
|
delete finalIdentity.archive
|
|
let outputCreated = false
|
|
try {
|
|
await mkdir(output)
|
|
outputCreated = true
|
|
const assetsRoot = join(output, 'assets')
|
|
const evidenceRoot = join(output, 'evidence')
|
|
await Promise.all([mkdir(assetsRoot), mkdir(evidenceRoot)])
|
|
for (const [label, name] of Object.entries(names)) {
|
|
await copyRegularFile(join(source, name), join(assetsRoot, name), label)
|
|
}
|
|
|
|
const verification = await verifyRuntimeImpl({
|
|
runtimeDirectory,
|
|
identityPath: physicalIdentity,
|
|
archivePath: join(source, names.archive)
|
|
})
|
|
// Why: Linux has no signing mutation, but its complete native hash projection must still be final.
|
|
const verificationReport = linuxVerificationReport(identity, plan)
|
|
const descriptor = await writeDescriptorImpl({
|
|
runtimeRoot: runtimeDirectory,
|
|
inputDirectory: assetsRoot,
|
|
finalIdentity,
|
|
verificationReport,
|
|
nativeVerificationTool,
|
|
verifiedAt
|
|
})
|
|
const receipt = {
|
|
tupleId: identity.tupleId,
|
|
contentId: identity.contentId,
|
|
verification,
|
|
aggregateInput: descriptor.input
|
|
}
|
|
const receiptPath = join(evidenceRoot, `${identity.tupleId}.linux-finalization.json`)
|
|
await writeReceipt(receiptPath, receipt)
|
|
return {
|
|
...receipt,
|
|
assetsRoot,
|
|
evidenceRoot,
|
|
receiptPath
|
|
}
|
|
} catch (error) {
|
|
if (outputCreated) {
|
|
// Why: an incomplete descriptor set must never become an aggregate candidate.
|
|
await rm(output, { recursive: true, force: true })
|
|
}
|
|
throw error
|
|
}
|
|
}
|
|
|
|
export function parseSshRelayRuntimeLinuxFinalizationArguments(argv) {
|
|
const result = {}
|
|
for (let index = 0; index < argv.length; index += 2) {
|
|
const flag = argv[index]
|
|
const field = PATH_ARGUMENTS.get(flag) ?? VALUE_ARGUMENTS.get(flag)
|
|
const value = argv[index + 1]
|
|
if (!field || !value || value.startsWith('--') || result[field]) {
|
|
throw new Error(`Invalid runtime Linux finalization argument: ${flag}`)
|
|
}
|
|
result[field] = PATH_ARGUMENTS.has(flag) ? resolve(value) : value
|
|
}
|
|
for (const field of [...PATH_ARGUMENTS.values(), ...VALUE_ARGUMENTS.values()]) {
|
|
if (!result[field]) {
|
|
throw new Error(`Missing runtime Linux finalization argument: ${field}`)
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
async function main() {
|
|
const options = parseSshRelayRuntimeLinuxFinalizationArguments(process.argv.slice(2))
|
|
const result = await finalizeSshRelayRuntimeLinuxArtifact({
|
|
...options,
|
|
nativeVerificationTool: { name: 'node', version: options.nativeVerificationToolVersion }
|
|
})
|
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
|
|
}
|
|
|
|
if (process.argv[1] && pathToFileURL(resolve(process.argv[1])).href === import.meta.url) {
|
|
main().catch((error) => {
|
|
process.stderr.write(`SSH relay runtime Linux finalization failed: ${error.stack ?? error}\n`)
|
|
process.exitCode = 1
|
|
})
|
|
}
|