Files
orca/config/scripts/ssh-relay-runtime-baseline.mjs
T
2026-07-14 20:00:11 -04:00

259 lines
8.6 KiB
JavaScript

import { execFile } from 'node:child_process'
import { readdir, realpath } from 'node:fs/promises'
import { release } from 'node:os'
import { basename, join, resolve } from 'node:path'
import { promisify } from 'node:util'
import { sshRelayRuntimeCompatibility } from './ssh-relay-runtime-compatibility.mjs'
const execFileAsync = promisify(execFile)
const COMMAND_TIMEOUT_MS = 30_000
const COMMAND_OUTPUT_BYTES = 1024 * 1024
const MAX_RUNTIME_ENTRIES = 100
const MAX_RUNTIME_DEPTH = 8
const BASELINES = Object.freeze({
'linux-x64-glibc': {
platform: 'linux',
architecture: 'x64',
glibc: sshRelayRuntimeCompatibility['linux-x64-glibc'].libc.minimumVersion,
libstdcxx: sshRelayRuntimeCompatibility['linux-x64-glibc'].libc.minimumLibstdcxxVersion,
kernel: sshRelayRuntimeCompatibility['linux-x64-glibc'].minimumKernelVersion
},
'linux-arm64-glibc': {
platform: 'linux',
architecture: 'arm64',
glibc: sshRelayRuntimeCompatibility['linux-arm64-glibc'].libc.minimumVersion,
libstdcxx: sshRelayRuntimeCompatibility['linux-arm64-glibc'].libc.minimumLibstdcxxVersion,
kernel: sshRelayRuntimeCompatibility['linux-arm64-glibc'].minimumKernelVersion
},
'darwin-x64': {
platform: 'darwin',
architecture: 'x64',
osVersion: sshRelayRuntimeCompatibility['darwin-x64'].minimumVersion
},
'darwin-arm64': {
platform: 'darwin',
architecture: 'arm64',
osVersion: sshRelayRuntimeCompatibility['darwin-arm64'].minimumVersion
},
'win32-x64': {
platform: 'win32',
architecture: 'x64',
// Why: the reviewed x64 floor permits either Windows 10 22H2 or Server 2022.
osBuilds: [sshRelayRuntimeCompatibility['win32-x64'].minimumBuild, 20348]
},
'win32-arm64': {
platform: 'win32',
architecture: 'arm64',
osBuilds: [sshRelayRuntimeCompatibility['win32-arm64'].minimumBuild]
}
})
function majorMinor(value) {
const match = /^(\d+)\.(\d+)(?:\.|$)/.exec(value ?? '')
return match ? `${Number(match[1])}.${Number(match[2])}` : null
}
function windowsBuild(value) {
const match = /^\d+\.\d+\.(\d+)(?:\.|$)/.exec(value ?? '')
return match ? Number(match[1]) : null
}
export function evaluateSshRelayRuntimeBaseline({
tuple,
scope = 'full',
platform,
architecture,
osVersion,
kernelVersion,
glibcVersion,
libstdcxxVersion
}) {
const contract = BASELINES[tuple]
if (!contract) {
throw new Error(`Unknown SSH relay runtime baseline tuple: ${tuple}`)
}
if (scope !== 'full' && !(scope === 'linux-userland' && contract.platform === 'linux')) {
throw new Error(`Unsupported SSH relay runtime baseline scope: ${scope}`)
}
const checks = {
platform: platform === contract.platform,
architecture: architecture === contract.architecture
}
// Why: execution on a newer host does not prove the declared oldest floor can run these bytes.
if (contract.platform === 'linux') {
checks.glibc = majorMinor(glibcVersion) === contract.glibc
checks.libstdcxx = libstdcxxVersion === contract.libstdcxx
checks.kernel = majorMinor(kernelVersion) === contract.kernel
} else if (contract.platform === 'darwin') {
checks.osVersion = majorMinor(osVersion) === contract.osVersion
} else {
checks.osBuild = contract.osBuilds.includes(windowsBuild(osVersion))
}
const requiredChecks =
scope === 'linux-userland'
? ['platform', 'architecture', 'glibc', 'libstdcxx']
: Object.keys(checks)
const residualGaps = Object.keys(checks).filter((name) => !requiredChecks.includes(name))
return {
tuple,
scope,
qualified: requiredChecks.every((name) => checks[name]),
residualGaps,
contract,
observed: {
platform,
architecture,
osVersion: osVersion ?? null,
kernelVersion: kernelVersion ?? null,
glibcVersion: glibcVersion ?? null,
libstdcxxVersion: libstdcxxVersion ?? null
},
checks
}
}
export function parseSshRelayRuntimeLddLibstdcxxPaths(output) {
if (typeof output !== 'string' || Buffer.byteLength(output) > COMMAND_OUTPUT_BYTES) {
throw new Error('Runtime baseline ldd output is missing or oversized')
}
if (/=>\s+not found|version\s+[`'][^`']+['`]\s+not found/i.test(output)) {
// Why: an exact userland version is irrelevant when the staged native module cannot load.
throw new Error('Runtime baseline ldd output contains an unresolved native dependency')
}
return output
.split(/\r?\n/)
.map((line) => /libstdc\+\+\.so\.6\s+=>\s+(\/\S+)/.exec(line)?.[1])
.filter(Boolean)
}
export function parseSshRelayRuntimeLibstdcxxVersion(libraryPaths) {
if (!Array.isArray(libraryPaths) || libraryPaths.length === 0) {
throw new Error('Runtime baseline did not resolve a libstdc++ ABI library')
}
const versions = new Set(
libraryPaths.map((path) => /^libstdc\+\+\.so\.(\d+\.\d+\.\d+)$/.exec(basename(path))?.[1])
)
if (versions.has(undefined) || versions.size !== 1) {
throw new Error('Runtime baseline did not resolve one bounded libstdc++ ABI library')
}
return versions.values().next().value
}
async function runtimeNativeModules(runtimeDirectory) {
const modules = []
let entries = 0
async function visit(directory, depth) {
if (depth > MAX_RUNTIME_DEPTH) {
throw new Error('Runtime baseline tree exceeds the depth bound')
}
for (const entry of await readdir(directory, { withFileTypes: true })) {
entries += 1
if (entries > MAX_RUNTIME_ENTRIES) {
throw new Error('Runtime baseline tree exceeds the entry bound')
}
const path = join(directory, entry.name)
if (entry.isDirectory()) {
await visit(path, depth + 1)
} else if (entry.isFile() && entry.name.endsWith('.node')) {
modules.push(path)
} else if (!entry.isFile()) {
throw new Error('Runtime baseline tree contains a special entry')
}
}
}
await visit(runtimeDirectory, 0)
if (modules.length === 0) {
throw new Error('Runtime baseline tree contains no native modules')
}
return modules
}
async function linuxLibstdcxxVersion(runtimeDirectory) {
const libraryPaths = new Set()
for (const modulePath of await runtimeNativeModules(runtimeDirectory)) {
const { stdout, stderr } = await execFileAsync('ldd', [modulePath], {
encoding: 'utf8',
maxBuffer: COMMAND_OUTPUT_BYTES,
timeout: COMMAND_TIMEOUT_MS
})
for (const path of parseSshRelayRuntimeLddLibstdcxxPaths(`${stderr}\n${stdout}`)) {
libraryPaths.add(await realpath(path))
}
}
return parseSshRelayRuntimeLibstdcxxVersion([...libraryPaths])
}
async function collectObservedBaseline({ tuple, runtimeDirectory }) {
const contract = BASELINES[tuple]
if (!contract) {
throw new Error(`Unknown SSH relay runtime baseline tuple: ${tuple}`)
}
const observed = {
platform: process.platform,
architecture: process.arch,
kernelVersion: release()
}
if (contract.platform === 'linux') {
if (!runtimeDirectory) {
throw new Error('--runtime-directory is required for a Linux baseline')
}
observed.glibcVersion = process.report.getReport().header.glibcVersionRuntime
observed.libstdcxxVersion = await linuxLibstdcxxVersion(runtimeDirectory)
} else if (contract.platform === 'darwin') {
const { stdout } = await execFileAsync('sw_vers', ['-productVersion'], {
encoding: 'utf8',
maxBuffer: COMMAND_OUTPUT_BYTES,
timeout: COMMAND_TIMEOUT_MS
})
observed.osVersion = stdout.trim()
} else {
observed.osVersion = release()
}
return observed
}
function parseArguments(argv) {
const result = { scope: 'full' }
for (let index = 0; index < argv.length; index += 2) {
const flag = argv[index]
const value = argv[index + 1]
if (!value || value.startsWith('--')) {
throw new Error(`${flag} requires a value`)
}
if (flag === '--tuple') {
result.tuple = value
} else if (flag === '--scope') {
result.scope = value
} else if (flag === '--runtime-directory') {
result.runtimeDirectory = resolve(value)
} else {
throw new Error(`Unknown argument: ${flag}`)
}
}
if (!result.tuple) {
throw new Error('--tuple is required')
}
return result
}
async function main() {
const options = parseArguments(process.argv.slice(2))
const result = evaluateSshRelayRuntimeBaseline({
...options,
...(await collectObservedBaseline(options))
})
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`)
if (!result.qualified) {
process.exitCode = 1
}
}
if (process.argv[1] && resolve(process.argv[1]) === import.meta.filename) {
main().catch((error) => {
process.stderr.write(`SSH relay runtime baseline verification failed: ${error.message}\n`)
process.exitCode = 1
})
}