build(ssh): complete runtime artifact provenance

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
Jinwoo-H
2026-07-14 17:06:44 -04:00
co-authored by Orca
parent ace3d4f416
commit 26bbd7b67a
9 changed files with 815 additions and 101 deletions
@@ -43,6 +43,8 @@ jobs:
node_archive: node-v24.18.0-darwin-arm64.tar.xz
runs-on: ${{ matrix.runner }}
timeout-minutes: 20
env:
ORCA_RUNTIME_REQUESTED_RUNNER: ${{ matrix.runner }}
steps:
- name: Checkout exact source revision
@@ -109,6 +111,11 @@ jobs:
node --check config/scripts/ssh-relay-runtime-build.test.mjs
node --check config/scripts/ssh-relay-runtime-closure.mjs
node --check config/scripts/ssh-relay-runtime-closure.test.mjs
node --check config/scripts/ssh-relay-runtime-sbom.mjs
node --check config/scripts/ssh-relay-runtime-sbom.test.mjs
node --check config/scripts/ssh-relay-runtime-provenance.test.mjs
node --check config/scripts/ssh-relay-runtime-toolchain.mjs
node --check config/scripts/ssh-relay-runtime-toolchain.test.mjs
pnpm exec vitest run --config config/vitest.config.ts \
config/scripts/ssh-relay-node-release-verification.test.mjs \
config/scripts/ssh-relay-node-tar-inspection.test.mjs \
@@ -122,6 +129,9 @@ jobs:
config/scripts/ssh-relay-runtime-pty-smoke.test.mjs \
config/scripts/ssh-relay-runtime-reproducibility.test.mjs \
config/scripts/ssh-relay-runtime-resource-diagnostics.test.mjs \
config/scripts/ssh-relay-runtime-provenance.test.mjs \
config/scripts/ssh-relay-runtime-sbom.test.mjs \
config/scripts/ssh-relay-runtime-toolchain.test.mjs \
config/scripts/ssh-relay-runtime-windows-tree.test.mjs \
config/scripts/ssh-relay-runtime-zip.test.mjs \
config/scripts/ssh-relay-runtime-workflow.test.mjs
@@ -208,6 +218,8 @@ jobs:
msvc_arch: arm64
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
env:
ORCA_RUNTIME_REQUESTED_RUNNER: ${{ matrix.runner }}
steps:
- name: Checkout exact source revision
@@ -286,6 +298,16 @@ jobs:
if ($LASTEXITCODE -ne 0) { throw 'runtime closure script syntax check failed' }
node --check config/scripts/ssh-relay-runtime-closure.test.mjs
if ($LASTEXITCODE -ne 0) { throw 'runtime closure test syntax check failed' }
node --check config/scripts/ssh-relay-runtime-sbom.mjs
if ($LASTEXITCODE -ne 0) { throw 'runtime SBOM script syntax check failed' }
node --check config/scripts/ssh-relay-runtime-sbom.test.mjs
if ($LASTEXITCODE -ne 0) { throw 'runtime SBOM test syntax check failed' }
node --check config/scripts/ssh-relay-runtime-provenance.test.mjs
if ($LASTEXITCODE -ne 0) { throw 'runtime provenance test syntax check failed' }
node --check config/scripts/ssh-relay-runtime-toolchain.mjs
if ($LASTEXITCODE -ne 0) { throw 'runtime toolchain script syntax check failed' }
node --check config/scripts/ssh-relay-runtime-toolchain.test.mjs
if ($LASTEXITCODE -ne 0) { throw 'runtime toolchain test syntax check failed' }
pnpm exec vitest run --config config/vitest.config.ts `
config/scripts/ssh-relay-node-release-verification.test.mjs `
config/scripts/ssh-relay-node-tar-inspection.test.mjs `
@@ -299,6 +321,9 @@ jobs:
config/scripts/ssh-relay-runtime-pty-smoke.test.mjs `
config/scripts/ssh-relay-runtime-reproducibility.test.mjs `
config/scripts/ssh-relay-runtime-resource-diagnostics.test.mjs `
config/scripts/ssh-relay-runtime-provenance.test.mjs `
config/scripts/ssh-relay-runtime-sbom.test.mjs `
config/scripts/ssh-relay-runtime-toolchain.test.mjs `
config/scripts/ssh-relay-runtime-windows-pe-diagnostic.test.mjs `
config/scripts/ssh-relay-runtime-windows-tree.test.mjs `
config/scripts/ssh-relay-runtime-zip.test.mjs `
+8 -32
View File
@@ -11,6 +11,11 @@ import {
inspectSshRelayRuntimeArchive
} from './ssh-relay-runtime-archive.mjs'
import { writeSshRelayRuntimeMetadata } from './ssh-relay-runtime-provenance.mjs'
import {
collectSshRelayRuntimeToolchain,
sshRelayRuntimeBuilderIdentity,
sshRelayRuntimeRunnerIdentity
} from './ssh-relay-runtime-toolchain.mjs'
import { assembleSshRelayRuntimeTree } from './ssh-relay-runtime-tree.mjs'
import { validateSshRelayNodeReleaseContract } from './ssh-relay-node-release-verification.mjs'
import { verifyNodeReleaseInputs } from './verify-ssh-relay-node-release-inputs.mjs'
@@ -130,36 +135,6 @@ async function run(command, args, options = {}) {
return result.stdout.trim()
}
async function toolchainDetails(nodePath) {
const commands =
process.platform === 'win32'
? [
['bundledNode', nodePath, ['--version']],
['compiler', 'cl.exe', []],
['python', 'python.exe', ['--version']]
]
: [
['bundledNode', nodePath, ['--version']],
['xz', 'xz', ['--version']],
['compiler', 'c++', ['--version']],
[
'strip',
process.platform === 'darwin' ? 'xcrun' : 'strip',
process.platform === 'darwin' ? ['--find', 'strip'] : ['--version']
],
['python', 'python3', ['--version']]
]
const details = {}
for (const [name, command, args] of commands) {
details[name] = (await run(command, args)).split(/\r?\n/)[0]
}
if (process.platform === 'win32') {
details.zip = 'yazl 3.3.1'
}
details.buildNode = process.version
return details
}
function relayPlatform(tuple) {
return tuple.replace('-glibc', '')
}
@@ -241,7 +216,7 @@ export async function buildSshRelayRuntime(options) {
sourceDateEpoch: options.sourceDateEpoch
})
const inspection = await inspectSshRelayRuntimeArchive(archive.path, identity)
const toolchain = await toolchainDetails(extracted.nodePath)
const toolchain = await collectSshRelayRuntimeToolchain(extracted.nodePath)
const metadata = await writeSshRelayRuntimeMetadata({
outputDirectory: options.outputDirectory,
identity,
@@ -249,7 +224,8 @@ export async function buildSshRelayRuntime(options) {
nodeRelease: release,
sourceDateEpoch: options.sourceDateEpoch,
gitCommit: options.gitCommit,
builder: process.env.GITHUB_WORKFLOW_REF ?? `local://${process.platform}/${process.arch}`,
builder: sshRelayRuntimeBuilderIdentity({ gitCommit: options.gitCommit }),
runner: sshRelayRuntimeRunnerIdentity(),
toolchain
})
return {
@@ -1,43 +1,14 @@
import { createHash } from 'node:crypto'
import { readFile, stat, writeFile } from 'node:fs/promises'
import { createRequire } from 'node:module'
import { join } from 'node:path'
const require = createRequire(import.meta.url)
import { createSshRelayRuntimeSbom } from './ssh-relay-runtime-sbom.mjs'
import { assertSshRelayRuntimeToolchain } from './ssh-relay-runtime-toolchain.mjs'
function sha256(bytes) {
return `sha256:${createHash('sha256').update(bytes).digest('hex')}`
}
function spdxId(value) {
return `SPDXRef-${value.replace(/[^A-Za-z0-9.-]/g, '-')}`
}
function packageRecord(name, version, license) {
return {
name,
SPDXID: spdxId(`Package-${name}`),
versionInfo: version,
downloadLocation: 'NOASSERTION',
filesAnalyzed: false,
licenseConcluded: license,
licenseDeclared: license,
copyrightText: 'NOASSERTION'
}
}
function installedPackage(name) {
const parsed = require(`${name}/package.json`)
return packageRecord(parsed.name, parsed.version, parsed.license ?? 'NOASSERTION')
}
function watcherNativePackage(tuple) {
if (tuple.startsWith('linux-')) {
return `@parcel/watcher-linux-${tuple.slice('linux-'.length)}`
}
return `@parcel/watcher-${tuple}`
}
async function writeJson(path, value) {
const bytes = Buffer.from(`${JSON.stringify(value, null, 2)}\n`, 'utf8')
await writeFile(path, bytes, { flag: 'wx', mode: 0o600 })
@@ -52,48 +23,14 @@ export async function writeSshRelayRuntimeMetadata({
sourceDateEpoch,
gitCommit,
builder,
runner,
toolchain
}) {
const createdAt = new Date(sourceDateEpoch * 1000).toISOString()
assertSshRelayRuntimeToolchain(toolchain, identity.tupleId)
const sbomName = `orca-ssh-relay-runtime-${identity.tupleId}.spdx.json`
const provenanceName = `orca-ssh-relay-runtime-${identity.tupleId}.provenance.json`
const identityName = `orca-ssh-relay-runtime-${identity.tupleId}.identity.json`
const files = identity.entries
.filter((entry) => entry.type === 'file')
.map((entry, index) => ({
fileName: entry.path,
SPDXID: `SPDXRef-File-${index + 1}`,
checksums: [{ algorithm: 'SHA256', checksumValue: entry.sha256.slice('sha256:'.length) }],
licenseConcluded: 'NOASSERTION',
copyrightText: 'NOASSERTION'
}))
const packages = [
packageRecord('node', identity.nodeVersion, 'NOASSERTION'),
packageRecord('orca-ssh-relay', '0.1.0', 'NOASSERTION'),
installedPackage('node-pty'),
installedPackage('@parcel/watcher'),
installedPackage(watcherNativePackage(identity.tupleId)),
installedPackage('detect-libc'),
installedPackage('is-glob'),
installedPackage('is-extglob'),
installedPackage('picomatch')
]
const documentId = identity.contentId.slice('sha256:'.length)
const sbom = {
spdxVersion: 'SPDX-2.3',
dataLicense: 'CC0-1.0',
SPDXID: 'SPDXRef-DOCUMENT',
name: `Orca SSH relay runtime ${identity.tupleId}`,
documentNamespace: `https://github.com/stablyai/orca/ssh-relay-runtime/${documentId}`,
creationInfo: { created: createdAt, creators: ['Organization: Stably AI'] },
packages,
files,
relationships: packages.map((component) => ({
spdxElementId: 'SPDXRef-DOCUMENT',
relationshipType: 'DESCRIBES',
relatedSpdxElement: component.SPDXID
}))
}
const sbom = createSshRelayRuntimeSbom({ identity, archive, sourceDateEpoch })
const nativeFiles = identity.entries
.filter(
(entry) =>
@@ -139,7 +76,10 @@ export async function writeSshRelayRuntimeMetadata({
},
runDetails: {
builder: { id: builder },
metadata: { invocationId: process.env.GITHUB_RUN_ID ?? 'local-unpublished-build' },
metadata: {
invocationId: process.env.GITHUB_RUN_ID ?? 'local-unpublished-build',
runner
},
byproducts: [{ name: 'native-files', content: nativeFiles }]
}
}
@@ -0,0 +1,158 @@
import { mkdtemp, readFile, rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { writeSshRelayRuntimeMetadata } from './ssh-relay-runtime-provenance.mjs'
const temporaryDirectories = []
afterEach(async () => {
await Promise.all(temporaryDirectories.splice(0).map((path) => rm(path, { recursive: true })))
})
const sha256 = (character) => `sha256:${character.repeat(64)}`
function file(path, role, character) {
return { path, type: 'file', role, size: 1, mode: 0o644, sha256: sha256(character) }
}
function fixture() {
const tuple = 'linux-x64-glibc'
const identity = {
tupleId: tuple,
os: 'linux',
architecture: 'x64',
nodeVersion: '24.18.0',
contentId: sha256('f'),
fileCount: 5,
expandedSize: 5,
entries: [
file('bin/node', 'node', '1'),
file('relay.js', 'relay', '2'),
file('node_modules/node-pty/build/Release/pty.node', 'node-pty-native', '3'),
file(
'node_modules/@parcel/watcher-linux-x64-glibc/watcher.node',
'parcel-watcher-native',
'4'
),
file('THIRD_PARTY_LICENSES.txt', 'license', '5')
]
}
return {
tuple,
identity,
archive: {
name: `orca-ssh-relay-runtime-v1-${tuple}-${'f'.repeat(64)}.tar.xz`,
size: 123,
sha256: sha256('a')
},
nodeRelease: {
baseUrl: 'https://nodejs.org/dist/v24.18.0',
archives: {
[tuple]: { name: 'node-v24.18.0-linux-x64.tar.xz', sha256: 'b'.repeat(64) }
},
signature: {
key: {
sourceUrl: 'https://github.com/nodejs/release-keys/raw/commit/gpg/key.asc',
sha256: 'c'.repeat(64)
}
}
}
}
}
describe('SSH relay runtime published metadata', () => {
it('binds SPDX, SLSA provenance, and identity to the exact archive and runner', async () => {
const outputDirectory = await mkdtemp(join(tmpdir(), 'orca-runtime-metadata-'))
temporaryDirectories.push(outputDirectory)
const input = fixture()
const runner = {
os: 'Linux',
architecture: 'X64',
environment: 'github-hosted',
requestedLabel: 'ubuntu-24.04',
image: { os: 'ubuntu24', version: '20260705.232.1' }
}
const toolchain = Object.fromEntries(
[
'buildNode',
'bundledNode',
'compiler',
'buildSystem',
'python',
'archive',
'nodeAddonApi',
'nodeGyp',
'strip'
].map((name, index) => [
name,
{ version: `${name} version`, sha256: sha256((index + 6).toString(16)) }
])
)
const builder = `https://github.com/stablyai/orca/blob/${'1'.repeat(40)}/.github/workflows/ssh-relay-runtime-artifacts.yml`
const assets = await writeSshRelayRuntimeMetadata({
outputDirectory,
identity: input.identity,
archive: input.archive,
nodeRelease: input.nodeRelease,
sourceDateEpoch: 1_752_710_400,
gitCommit: '1'.repeat(40),
builder,
runner,
toolchain
})
const sbom = JSON.parse(await readFile(join(outputDirectory, assets.sbom.name), 'utf8'))
const provenance = JSON.parse(
await readFile(join(outputDirectory, assets.provenance.name), 'utf8')
)
const identity = JSON.parse(await readFile(join(outputDirectory, assets.identity.name), 'utf8'))
expect(sbom.files).toHaveLength(input.identity.fileCount)
expect(
sbom.relationships.filter((entry) => entry.relationshipType === 'CONTAINS')
).toHaveLength(input.identity.fileCount)
expect(provenance.subject).toEqual([
{ name: input.archive.name, digest: { sha256: 'a'.repeat(64) } }
])
expect(provenance.predicate.runDetails).toMatchObject({
builder: { id: builder },
metadata: { runner }
})
expect(provenance.predicate.buildDefinition).toMatchObject({
internalParameters: { toolchain },
resolvedDependencies: expect.arrayContaining([
{
uri: 'https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.xz',
digest: { sha256: 'b'.repeat(64) }
},
{
uri: `git+https://github.com/stablyai/orca@${'1'.repeat(40)}`,
digest: { gitCommit: '1'.repeat(40) }
}
])
})
expect(identity.archive).toEqual({
name: input.archive.name,
size: 123,
expandedSize: 5,
fileCount: 5,
sha256: sha256('a')
})
await expect(
writeSshRelayRuntimeMetadata({
outputDirectory,
identity: input.identity,
archive: input.archive,
nodeRelease: input.nodeRelease,
sourceDateEpoch: 1_752_710_400,
gitCommit: '1'.repeat(40),
builder,
runner,
toolchain
})
).rejects.toThrow()
})
})
+125
View File
@@ -0,0 +1,125 @@
import { createRequire } from 'node:module'
import { sshRelayRuntimeWatcherPackage } from './ssh-relay-runtime-closure.mjs'
const require = createRequire(import.meta.url)
function spdxId(value) {
return `SPDXRef-${value.replace(/[^A-Za-z0-9.-]/g, '-')}`
}
function packageRecord(name, version, license) {
return {
name,
SPDXID: spdxId(`Package-${name}`),
versionInfo: version,
downloadLocation: 'NOASSERTION',
filesAnalyzed: false,
licenseConcluded: license,
licenseDeclared: license,
copyrightText: 'NOASSERTION'
}
}
function installedPackage(name) {
const parsed = require(`${name}/package.json`)
return packageRecord(parsed.name, parsed.version, parsed.license ?? 'NOASSERTION')
}
function fileType(role) {
return ['node', 'node-pty-native', 'parcel-watcher-native', 'native-runtime'].includes(role)
? 'BINARY'
: role === 'license'
? 'TEXT'
: 'SOURCE'
}
function ownerPackage(path, packages) {
const packagePath = packages.find((entry) => path.startsWith(`node_modules/${entry.name}/`))
if (packagePath) {
return packagePath
}
if (path.startsWith('node_modules/')) {
return undefined
}
if (path === 'bin/node' || path === 'bin/node.exe') {
return packages.find((entry) => entry.name === 'node')
}
return packages.find((entry) => entry.name === 'orca-ssh-relay')
}
export function createSshRelayRuntimeSbom({ identity, archive, sourceDateEpoch }) {
if (!/^sha256:[0-9a-f]{64}$/.test(archive?.sha256 ?? '')) {
throw new Error('Runtime SBOM requires the exact archive SHA-256')
}
const watcherPackage = sshRelayRuntimeWatcherPackage(identity.tupleId)
const packages = [
packageRecord('node', identity.nodeVersion, 'MIT'),
packageRecord('orca-ssh-relay', identity.contentId, 'NOASSERTION'),
installedPackage('node-pty'),
installedPackage('@parcel/watcher'),
installedPackage(watcherPackage),
installedPackage('detect-libc'),
installedPackage('is-glob'),
installedPackage('is-extglob'),
installedPackage('picomatch')
]
const files = identity.entries
.filter((entry) => entry.type === 'file')
.map((entry) => ({
fileName: entry.path,
SPDXID: spdxId(`File-${entry.path}`),
fileTypes: [fileType(entry.role)],
checksums: [{ algorithm: 'SHA256', checksumValue: entry.sha256.slice('sha256:'.length) }],
licenseConcluded: 'NOASSERTION',
copyrightText: 'NOASSERTION'
}))
const identifiers = new Set()
for (const element of [...packages, ...files]) {
if (identifiers.has(element.SPDXID)) {
throw new Error(`Runtime SBOM contains a duplicate SPDX identifier: ${element.SPDXID}`)
}
identifiers.add(element.SPDXID)
}
const contains = files.map((file) => {
const owner = ownerPackage(file.fileName, packages)
if (!owner) {
throw new Error(`Runtime SBOM cannot assign a package owner: ${file.fileName}`)
}
return {
spdxElementId: owner.SPDXID,
relationshipType: 'CONTAINS',
relatedSpdxElement: file.SPDXID
}
})
const relay = packages.find((entry) => entry.name === 'orca-ssh-relay')
const relationships = [
...packages.map((component) => ({
spdxElementId: 'SPDXRef-DOCUMENT',
relationshipType: 'DESCRIBES',
relatedSpdxElement: component.SPDXID
})),
...packages
.filter((component) => component !== relay)
.map((component) => ({
spdxElementId: relay.SPDXID,
relationshipType: 'DEPENDS_ON',
relatedSpdxElement: component.SPDXID
})),
...contains
]
return {
spdxVersion: 'SPDX-2.3',
dataLicense: 'CC0-1.0',
SPDXID: 'SPDXRef-DOCUMENT',
name: `Orca SSH relay runtime ${identity.tupleId}`,
documentNamespace: `https://github.com/stablyai/orca/ssh-relay-runtime/spdx/${archive.sha256.slice('sha256:'.length)}`,
creationInfo: {
created: new Date(sourceDateEpoch * 1000).toISOString(),
creators: ['Organization: Stably AI']
},
packages,
files,
relationships
}
}
@@ -0,0 +1,122 @@
import { describe, expect, it } from 'vitest'
import { createSshRelayRuntimeSbom } from './ssh-relay-runtime-sbom.mjs'
const sha256 = (character) => `sha256:${character.repeat(64)}`
function file(path, role, character) {
return { path, type: 'file', role, size: 1, mode: 0o644, sha256: sha256(character) }
}
function identity(tuple = 'linux-arm64-glibc') {
return {
tupleId: tuple,
nodeVersion: '24.18.0',
contentId: sha256('f'),
entries: [
file('bin/node', 'node', '1'),
file('relay.js', 'relay', '2'),
file('THIRD_PARTY_LICENSES.txt', 'license', '3'),
file('node_modules/node-pty/lib/index.js', 'runtime-javascript', '4'),
file('node_modules/@parcel/watcher/index.js', 'runtime-javascript', '5'),
file(`node_modules/@parcel/watcher-${tuple}/watcher.node`, 'parcel-watcher-native', '6'),
file('node_modules/detect-libc/lib/detect-libc.js', 'runtime-javascript', '7'),
file('node_modules/is-glob/index.js', 'runtime-javascript', '8'),
file('node_modules/is-extglob/index.js', 'runtime-javascript', '9'),
file('node_modules/picomatch/index.js', 'runtime-javascript', 'a')
]
}
}
const archive = { sha256: sha256('e') }
function ownerName(sbom, fileName) {
const fileId = sbom.files.find((entry) => entry.fileName === fileName).SPDXID
const owners = sbom.relationships.filter(
(entry) => entry.relationshipType === 'CONTAINS' && entry.relatedSpdxElement === fileId
)
expect(owners).toHaveLength(1)
return sbom.packages.find((entry) => entry.SPDXID === owners[0].spdxElementId).name
}
describe('SSH relay runtime SPDX SBOM', () => {
it('inventories every runtime file under exactly one package', () => {
const fixture = identity()
const sbom = createSshRelayRuntimeSbom({
identity: fixture,
archive,
sourceDateEpoch: 1_752_710_400
})
expect(sbom.spdxVersion).toBe('SPDX-2.3')
expect(sbom.documentNamespace).toBe(
`https://github.com/stablyai/orca/ssh-relay-runtime/spdx/${'e'.repeat(64)}`
)
expect(sbom.creationInfo.created).toBe('2025-07-17T00:00:00.000Z')
expect(sbom.packages.map((entry) => entry.name)).toEqual([
'node',
'orca-ssh-relay',
'node-pty',
'@parcel/watcher',
'@parcel/watcher-linux-arm64-glibc',
'detect-libc',
'is-glob',
'is-extglob',
'picomatch'
])
expect(sbom.packages.find((entry) => entry.name === 'node')).toMatchObject({
versionInfo: '24.18.0',
licenseDeclared: 'MIT'
})
expect(sbom.packages.find((entry) => entry.name === 'orca-ssh-relay')).toMatchObject({
versionInfo: fixture.contentId,
licenseDeclared: 'NOASSERTION'
})
expect(sbom.files).toHaveLength(fixture.entries.length)
expect(new Set([...sbom.packages, ...sbom.files].map((entry) => entry.SPDXID)).size).toBe(
sbom.packages.length + sbom.files.length
)
expect(ownerName(sbom, 'bin/node')).toBe('node')
expect(ownerName(sbom, 'relay.js')).toBe('orca-ssh-relay')
expect(ownerName(sbom, 'THIRD_PARTY_LICENSES.txt')).toBe('orca-ssh-relay')
expect(ownerName(sbom, 'node_modules/node-pty/lib/index.js')).toBe('node-pty')
expect(ownerName(sbom, 'node_modules/@parcel/watcher/index.js')).toBe('@parcel/watcher')
expect(ownerName(sbom, 'node_modules/@parcel/watcher-linux-arm64-glibc/watcher.node')).toBe(
'@parcel/watcher-linux-arm64-glibc'
)
expect(ownerName(sbom, 'node_modules/detect-libc/lib/detect-libc.js')).toBe('detect-libc')
expect(ownerName(sbom, 'node_modules/is-glob/index.js')).toBe('is-glob')
expect(ownerName(sbom, 'node_modules/is-extglob/index.js')).toBe('is-extglob')
expect(ownerName(sbom, 'node_modules/picomatch/index.js')).toBe('picomatch')
expect(
sbom.relationships.filter((entry) => entry.relationshipType === 'DEPENDS_ON')
).toHaveLength(8)
})
it('rejects an unowned node_modules path and colliding SPDX identifiers', () => {
const unowned = identity()
unowned.entries.push(file('node_modules/unreviewed/index.js', 'runtime-javascript', 'b'))
expect(() =>
createSshRelayRuntimeSbom({ identity: unowned, archive, sourceDateEpoch: 1_752_710_400 })
).toThrow(/cannot assign a package owner/i)
const collision = identity()
collision.entries.push(
file('relay/a+b.js', 'runtime-javascript', 'b'),
file('relay/a@b.js', 'runtime-javascript', 'c')
)
expect(() =>
createSshRelayRuntimeSbom({ identity: collision, archive, sourceDateEpoch: 1_752_710_400 })
).toThrow(/duplicate SPDX identifier/i)
})
it('requires the exact archive digest to make the document namespace immutable', () => {
expect(() =>
createSshRelayRuntimeSbom({
identity: identity(),
archive: { sha256: 'missing-prefix' },
sourceDateEpoch: 1_752_710_400
})
).toThrow(/archive SHA-256/i)
})
})
@@ -0,0 +1,252 @@
import { execFile } from 'node:child_process'
import { createHash } from 'node:crypto'
import { createReadStream } from 'node:fs'
import { readdir, realpath, stat } from 'node:fs/promises'
import { createRequire } from 'node:module'
import { dirname, isAbsolute, join, relative } from 'node:path'
import { isDeepStrictEqual, promisify } from 'node:util'
const require = createRequire(import.meta.url)
const execFileAsync = promisify(execFile)
const MAX_TOOL_BYTES = 300 * 1024 * 1024
const TOOL_TIMEOUT_MS = 60_000
const TOOL_OUTPUT_BYTES = 1024 * 1024
const MAX_PACKAGE_FILES = 500
const MAX_PACKAGE_BYTES = 32 * 1024 * 1024
async function sha256File(path) {
const metadata = await stat(path)
if (!metadata.isFile() || metadata.size > MAX_TOOL_BYTES) {
throw new Error(`Runtime build tool is not a bounded regular file: ${path}`)
}
const hash = createHash('sha256')
for await (const chunk of createReadStream(path)) {
hash.update(chunk)
}
return `sha256:${hash.digest('hex')}`
}
async function capture(command, args) {
try {
return await execFileAsync(command, args, {
encoding: 'utf8',
maxBuffer: TOOL_OUTPUT_BYTES,
timeout: TOOL_TIMEOUT_MS,
windowsHide: true
})
} catch (error) {
if (typeof error?.stdout === 'string' || typeof error?.stderr === 'string') {
return { stdout: error.stdout ?? '', stderr: error.stderr ?? '' }
}
throw error
}
}
export function selectSshRelayRuntimeToolVersion({ stdout = '', stderr = '' }, pattern) {
const lines = `${stderr}\n${stdout}`
.split(/\r?\n/)
.map((line) => line.trim())
.filter(Boolean)
const version = pattern ? lines.find((line) => pattern.test(line)) : lines[0]
if (!version || Buffer.byteLength(version) > 512) {
throw new Error('Runtime build tool did not report a bounded version line')
}
return version
}
async function locateExecutable(command, { xcrun = false } = {}) {
if (isAbsolute(command)) {
return realpath(command)
}
const locator = xcrun
? ['xcrun', ['--find', command]]
: process.platform === 'win32'
? ['where.exe', [command]]
: ['which', [command]]
const result = await capture(...locator)
const path = result.stdout
.split(/\r?\n/)
.map((line) => line.trim())
.find(Boolean)
if (!path || !isAbsolute(path)) {
throw new Error(`Runtime build tool executable could not be resolved: ${command}`)
}
return realpath(path)
}
async function executableRecord({
command,
args,
versionCommand,
versionArgs,
versionPattern,
xcrun = false
}) {
const path = await locateExecutable(command, { xcrun })
const result = await capture(versionCommand ?? path, versionArgs ?? args)
return {
version: selectSshRelayRuntimeToolVersion(result, versionPattern),
sha256: await sha256File(path)
}
}
async function packageTreeRecord(name) {
const parsed = require(`${name}/package.json`)
const root = dirname(require.resolve(`${name}/package.json`))
const files = []
let totalBytes = 0
async function visit(directory) {
const entries = await readdir(directory, { withFileTypes: true })
entries.sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0))
for (const entry of entries) {
const path = join(directory, entry.name)
if (entry.isDirectory()) {
await visit(path)
} else if (entry.isFile()) {
files.push(path)
totalBytes += (await stat(path)).size
} else {
throw new Error(`Runtime build package contains a special entry: ${name}`)
}
if (files.length > MAX_PACKAGE_FILES) {
throw new Error(`Runtime build package exceeds the file-count bound: ${name}`)
}
if (totalBytes > MAX_PACKAGE_BYTES) {
throw new Error(`Runtime build package exceeds the byte bound: ${name}`)
}
}
}
await visit(root)
const hash = createHash('sha256')
for (const path of files) {
hash.update(relative(root, path).replaceAll('\\', '/'))
hash.update('\0')
hash.update(await sha256File(path))
hash.update('\n')
}
return { version: parsed.version, sha256: `sha256:${hash.digest('hex')}`, files: files.length }
}
export function sshRelayRuntimeBuilderIdentity({ gitCommit, env = process.env }) {
if (env.GITHUB_ACTIONS !== 'true') {
return `local://${process.platform}/${process.arch}`
}
if (
!/^[0-9a-f]{40}$/.test(gitCommit) ||
!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(env.GITHUB_REPOSITORY ?? '')
) {
throw new Error('GitHub runtime builder identity is incomplete')
}
const workflow = (env.GITHUB_WORKFLOW_REF ?? '').split('@')[0]
const prefix = `${env.GITHUB_REPOSITORY}/`
if (
!workflow.startsWith(prefix) ||
workflow.slice(prefix.length) !== '.github/workflows/ssh-relay-runtime-artifacts.yml'
) {
throw new Error('GitHub runtime builder workflow identity is malformed')
}
return `https://github.com/${env.GITHUB_REPOSITORY}/blob/${gitCommit}/${workflow.slice(prefix.length)}`
}
export function sshRelayRuntimeRunnerIdentity({ env = process.env } = {}) {
const github = env.GITHUB_ACTIONS === 'true'
const identity = {
os: env.RUNNER_OS ?? process.platform,
architecture: env.RUNNER_ARCH ?? process.arch,
environment: env.RUNNER_ENVIRONMENT ?? (github ? undefined : 'local'),
requestedLabel: env.ORCA_RUNTIME_REQUESTED_RUNNER ?? (github ? undefined : 'local'),
image: {
os: env.ImageOS ?? (github ? undefined : process.platform),
version: env.ImageVersion ?? (github ? undefined : 'local')
}
}
if (
!identity.os ||
!identity.architecture ||
!identity.environment ||
!identity.requestedLabel ||
!identity.image.os ||
!identity.image.version
) {
throw new Error('Runtime runner identity is incomplete')
}
return identity
}
export function assertSshRelayRuntimeToolchain(toolchain, tuple) {
const common = ['buildNode', 'bundledNode', 'compiler', 'buildSystem', 'python', 'archive']
const expected = tuple.startsWith('win32-')
? [...common, 'linker', 'nodeAddonApi', 'nodeGyp']
: [...common, 'nodeAddonApi', 'nodeGyp', 'strip']
if (!isDeepStrictEqual(Object.keys(toolchain).sort(), expected.sort())) {
throw new Error(`Runtime toolchain record is incomplete for ${tuple}`)
}
for (const [name, record] of Object.entries(toolchain)) {
const keys = Object.keys(record).sort()
const expectedKeys =
record.files === undefined ? ['sha256', 'version'] : ['files', 'sha256', 'version']
if (
!isDeepStrictEqual(keys, expectedKeys) ||
typeof record.version !== 'string' ||
record.version.length === 0 ||
Buffer.byteLength(record.version) > 512 ||
record.version.includes('\n') ||
!/^sha256:[0-9a-f]{64}$/.test(record.sha256) ||
(record.files !== undefined && (!Number.isSafeInteger(record.files) || record.files <= 0))
) {
throw new Error(`Runtime toolchain record is malformed: ${name}`)
}
}
}
export async function collectSshRelayRuntimeToolchain(nodePath) {
const common = {
bundledNode: await executableRecord({ command: nodePath, args: ['--version'] }),
buildNode: await executableRecord({ command: process.execPath, args: ['--version'] }),
nodeGyp: await packageTreeRecord('node-gyp'),
nodeAddonApi: await packageTreeRecord('node-addon-api')
}
if (process.platform === 'win32') {
const toolchain = {
...common,
compiler: await executableRecord({
command: 'cl.exe',
args: [],
versionPattern: /Compiler Version/i
}),
linker: await executableRecord({
command: 'link.exe',
args: [],
versionPattern: /Linker Version/i
}),
buildSystem: await executableRecord({
command: 'msbuild.exe',
args: ['-version', '-nologo']
}),
python: await executableRecord({ command: 'python.exe', args: ['--version'] }),
archive: await packageTreeRecord('yazl')
}
assertSshRelayRuntimeToolchain(toolchain, 'win32-x64')
return toolchain
}
const toolchain = {
...common,
compiler: await executableRecord({
command: process.platform === 'darwin' ? 'clang++' : 'c++',
args: ['--version'],
xcrun: process.platform === 'darwin'
}),
buildSystem: await executableRecord({ command: 'make', args: ['--version'] }),
python: await executableRecord({ command: 'python3', args: ['--version'] }),
archive: await executableRecord({ command: 'xz', args: ['--version'] }),
strip: await executableRecord({
command: 'strip',
args: [],
versionCommand: process.platform === 'darwin' ? 'xcodebuild' : undefined,
versionArgs: process.platform === 'darwin' ? ['-version'] : undefined,
xcrun: process.platform === 'darwin'
})
}
assertSshRelayRuntimeToolchain(toolchain, `${process.platform}-x64`)
return toolchain
}
@@ -0,0 +1,112 @@
import { describe, expect, it } from 'vitest'
import {
assertSshRelayRuntimeToolchain,
collectSshRelayRuntimeToolchain,
selectSshRelayRuntimeToolVersion,
sshRelayRuntimeBuilderIdentity,
sshRelayRuntimeRunnerIdentity
} from './ssh-relay-runtime-toolchain.mjs'
const commit = 'a'.repeat(40)
describe('SSH relay runtime build provenance', () => {
it('selects the real Windows compiler version instead of its stdout usage line', () => {
expect(
selectSshRelayRuntimeToolVersion(
{
stdout: 'usage: cl [ option... ] filename... [ /link linkoption... ]',
stderr: [
'Microsoft (R) C/C++ Optimizing Compiler Version 19.44.35228 for ARM64',
'Copyright (C) Microsoft Corporation.'
].join('\r\n')
},
/Compiler Version/i
)
).toBe('Microsoft (R) C/C++ Optimizing Compiler Version 19.44.35228 for ARM64')
})
it('pins GitHub builder identity to the exact source commit', () => {
expect(
sshRelayRuntimeBuilderIdentity({
gitCommit: commit,
env: {
GITHUB_ACTIONS: 'true',
GITHUB_REPOSITORY: 'stablyai/orca',
GITHUB_WORKFLOW_REF:
'stablyai/orca/.github/workflows/ssh-relay-runtime-artifacts.yml@refs/pull/8741/merge'
}
})
).toBe(
`https://github.com/stablyai/orca/blob/${commit}/.github/workflows/ssh-relay-runtime-artifacts.yml`
)
expect(() =>
sshRelayRuntimeBuilderIdentity({
gitCommit: commit,
env: {
GITHUB_ACTIONS: 'true',
GITHUB_REPOSITORY: 'stablyai/orca',
GITHUB_WORKFLOW_REF: 'other/repo/.github/workflows/untrusted.yml@refs/heads/main'
}
})
).toThrow(/workflow identity/i)
})
it('requires the resolved runner label, architecture, environment, and image identity', () => {
expect(
sshRelayRuntimeRunnerIdentity({
env: {
GITHUB_ACTIONS: 'true',
RUNNER_OS: 'Windows',
RUNNER_ARCH: 'ARM64',
RUNNER_ENVIRONMENT: 'github-hosted',
ORCA_RUNTIME_REQUESTED_RUNNER: 'windows-11-arm',
ImageOS: 'win11-arm64',
ImageVersion: '20260706.102.1'
}
})
).toEqual({
os: 'Windows',
architecture: 'ARM64',
environment: 'github-hosted',
requestedLabel: 'windows-11-arm',
image: { os: 'win11-arm64', version: '20260706.102.1' }
})
expect(() =>
sshRelayRuntimeRunnerIdentity({
env: {
GITHUB_ACTIONS: 'true',
RUNNER_OS: 'Windows',
RUNNER_ARCH: 'ARM64'
}
})
).toThrow(/runner identity/i)
})
it('records bounded native tool versions and SHA-256 executable or code digests', async () => {
const toolchain = await collectSshRelayRuntimeToolchain(process.execPath)
const tuple = process.platform === 'win32' ? 'win32-x64' : 'linux-x64-glibc'
expect(() => assertSshRelayRuntimeToolchain(toolchain, tuple)).not.toThrow()
expect(toolchain).toMatchObject({
bundledNode: { version: process.version },
buildNode: { version: process.version },
nodeGyp: { version: expect.any(String) },
nodeAddonApi: { version: expect.any(String) },
compiler: { version: expect.any(String) },
buildSystem: { version: expect.any(String) },
python: { version: expect.any(String) },
archive: { version: expect.any(String) }
})
for (const record of Object.values(toolchain)) {
expect(record.sha256).toMatch(/^sha256:[0-9a-f]{64}$/)
expect(Buffer.byteLength(record.version)).toBeGreaterThan(0)
expect(Buffer.byteLength(record.version)).toBeLessThanOrEqual(512)
}
expect(() =>
assertSshRelayRuntimeToolchain(
{ ...toolchain, compiler: { version: toolchain.compiler.version } },
tuple
)
).toThrow(/compiler/i)
})
})
@@ -28,6 +28,7 @@ describe('SSH relay runtime artifact workflow', () => {
expect(posixJob['timeout-minutes']).toBe(20)
expect(windowsJob['timeout-minutes']).toBe(30)
for (const job of [posixJob, windowsJob]) {
expect(job.env.ORCA_RUNTIME_REQUESTED_RUNNER).toBe('${{ matrix.runner }}')
expect(job.steps[0].with.ref).toBe('${{ github.event.pull_request.head.sha || github.sha }}')
for (const step of job.steps.filter((candidate) => candidate.uses)) {
expect(step.uses).toMatch(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+@[0-9a-f]{40}$/)
@@ -62,6 +63,9 @@ describe('SSH relay runtime artifact workflow', () => {
expect(source).toContain('ssh-relay-runtime-reproducibility.mjs')
expect(source).toContain('ssh-relay-runtime-reproducibility.test.mjs')
expect(source.match(/ssh-relay-runtime-closure\.test\.mjs/g)).toHaveLength(4)
expect(source.match(/ssh-relay-runtime-sbom\.test\.mjs/g)).toHaveLength(4)
expect(source.match(/ssh-relay-runtime-provenance\.test\.mjs/g)).toHaveLength(4)
expect(source.match(/ssh-relay-runtime-toolchain\.test\.mjs/g)).toHaveLength(4)
expect(
source.match(/node --check config\/scripts\/ssh-relay-runtime-closure\.mjs/g)
).toHaveLength(2)