'use strict'; const assert = require('node:assert/strict'); const test = require('node:test'); const { mkdtempSync, mkdirSync, writeFileSync, readFileSync, rmSync, symlinkSync } = require('node:fs'); const path = require('node:path'); const os = require('node:os'); const { spawnSync } = require('node:child_process'); const { createHash, generateKeyPairSync } = require('node:crypto'); const { startStable, verifyStableEvidence, verifyBootstrapSession, applyExactEnvironment, calculateCompiledArtifactDigest, candidateLockChecksum, fetchReleaseEvidence, sourceCommit } = require('../start-stable'); function fixture() { const lock = { moduleCode: 'demo', candidateVersion: '1.0.1-rc.2', buildRevision: 2, buildChecksum: 'b'.repeat(64), schemaInputChecksum: 'c'.repeat(64), branchName: 'release/1.0.1-rc.2' }; const generated = { moduleCode: 'demo', revision: 2, schemaVersion: 3, schemaInputChecksum: lock.schemaInputChecksum, implementationManifestChecksum: 'd'.repeat(64), runtimeContractChecksum: 'e'.repeat(64) }; const commit = 'a'.repeat(40); const digest = `sha256:${'f'.repeat(64)}`; const release = { id: 42, moduleId: 2, releaseType: 'stable', status: 'published', lifecycleVersion: 1, version: '1.0.1', serviceVersion: '1.0.1', promotedFromReleaseId: 41, buildSnapshotId: 52, buildRevision: 2, frozenArtifactId: 62, schemaVersion: 3, schemaChecksum: '3'.repeat(64), build: { id: 52, moduleId: 2, revision: 2, schemaVersion: 3, buildChecksum: lock.buildChecksum, schemaInputChecksum: lock.schemaInputChecksum }, frozenArtifact: { id: 62, rcReleaseId: 41, moduleId: 2, buildRevision: 2, sourceBranch: lock.branchName, tagName: `v${lock.candidateVersion}`, sourceCommit: commit, generatedLockChecksum: candidateLockChecksum(lock), artifactDigest: digest, artifactIdentityChecksum: '1'.repeat(64), implementationManifestChecksum: generated.implementationManifestChecksum }, schemaSnapshot: { actionManifest: { artifactDigest: digest }, actionManifestChecksum: '2'.repeat(64), runtimeContracts: { implementationManifestChecksum: generated.implementationManifestChecksum, runtimeContractChecksum: generated.runtimeContractChecksum } } }; return { release, lock, generated, digest, commit, releaseId: 42 }; } function sessionFor(exact) { return { moduleCode: exact.RPC_MODULE_CODE, revision: Number(exact.SAAS_BUILD_REVISION), release: { releaseId: Number(exact.RPC_RELEASE_ID), releaseVersion: exact.RPC_RELEASE_VERSION, serviceVersion: exact.RPC_SERVICE_VERSION, runtimeMode: exact.SAAS_RUNTIME_MODE, buildRevision: Number(exact.SAAS_BUILD_REVISION), buildChecksum: exact.SAAS_BUILD_CHECKSUM, schemaInputChecksum: exact.SAAS_SCHEMA_INPUT_CHECKSUM, implementationManifestChecksum: exact.RPC_IMPLEMENTATION_MANIFEST_CHECKSUM, artifactDigest: exact.RPC_ARTIFACT_DIGEST, artifactIdentityChecksum: exact.RPC_ARTIFACT_IDENTITY_CHECKSUM, schemaChecksum: exact.RPC_SCHEMA_CHECKSUM, actionManifestChecksum: exact.RPC_ACTION_MANIFEST_CHECKSUM, runtimeContractChecksum: exact.RPC_RUNTIME_CONTRACT_CHECKSUM } }; } test('Stable derives its own deployment envelope from the same frozen Candidate and Build', () => { const input = fixture(); const before = JSON.stringify(input); const exact = verifyStableEvidence(input); assert.equal(exact.RPC_RELEASE_ID, '42'); assert.equal(exact.RPC_RELEASE_VERSION, '1.0.1'); assert.equal(exact.SAAS_RUNTIME_MODE, 'production'); assert.equal(exact.SAAS_BUILD_REVISION, '2'); assert.equal(exact.BUILD_SHA, input.commit); assert.equal(exact.RPC_ARTIFACT_DIGEST, input.digest); assert.equal(JSON.stringify(input), before, 'must never rewrite the Candidate or frozen evidence'); verifyBootstrapSession(sessionFor(exact), exact); }); test('fail closed on RC targets, history-only artifacts, or any frozen identity mismatch', () => { const edits = [ data => { data.release.releaseType = 'rc'; }, data => { data.release.status = 'retired'; }, data => { data.release.version = '1.0.1-rc.2'; }, data => { data.release.frozenArtifact.artifactDigest = null; }, data => { data.release.frozenArtifact.rcReleaseId++; }, data => { data.release.buildSnapshotId++; }, data => { data.release.build.buildChecksum = '9'.repeat(64); }, data => { data.release.buildRevision++; }, data => { data.release.frozenArtifact.generatedLockChecksum = '9'.repeat(64); }, data => { data.release.frozenArtifact.tagName = 'v9.0.0'; }, data => { data.commit = '9'.repeat(40); }, data => { data.digest = `sha256:${'9'.repeat(64)}`; }, data => { data.generated.revision++; }, data => { data.generated.releaseVersion = '1.0.1-rc.2'; }, data => { data.generated.implementationManifestChecksum = '9'.repeat(64); }, data => { data.generated.runtimeContractChecksum = '9'.repeat(64); }, data => { data.generated.schemaVersion++; }, data => { data.release.schemaSnapshot.actionManifest.artifactDigest = `sha256:${'9'.repeat(64)}`; } ]; for (const edit of edits) { const input = fixture(); edit(input); assert.throws(() => verifyStableEvidence(input), /Stable startup refused/); } }); test('environment and authenticated bootstrap cannot silently replace Stable with RC or a different Build', () => { const exact = verifyStableEvidence(fixture()); assert.throws(() => applyExactEnvironment({ RPC_RELEASE_ID: '41' }, exact), /conflicts/); const environment = {}; applyExactEnvironment(environment, exact); assert.equal(environment.RPC_ARTIFACT_DIGEST, exact.RPC_ARTIFACT_DIGEST); for (const key of ['releaseId', 'releaseVersion', 'buildChecksum', 'runtimeMode', 'artifactDigest', 'artifactIdentityChecksum']) { const session = sessionFor(exact); session.release[key] = 'incorrect'; assert.throws(() => verifyBootstrapSession(session, exact), /differs/); } }); test('compiled digest uses the exact finalize byte format and detects changes', t => { const root = mkdtempSync(path.join(os.tmpdir(), 'stable-digest-test-')); t.after(() => rmSync(root, { recursive: true, force: true })); mkdirSync(path.join(root, 'dist/nested'), { recursive: true }); writeFileSync(path.join(root, 'dist/a.js'), 'first'); writeFileSync(path.join(root, 'dist/nested/b.js'), 'second'); const exactHash = createHash('sha256').update('COOL_SAAS_ARTIFACT_DIRECTORY_V1\0', 'utf8'); for (const [name, content] of [['a.js', 'first'], ['nested/b.js', 'second']]) { exactHash.update(`${Buffer.byteLength(name)}:`).update(name).update(`:${Buffer.byteLength(content)}:`).update(content); } assert.equal(calculateCompiledArtifactDigest(root), `sha256:${exactHash.digest('hex')}`); const oldDigest = calculateCompiledArtifactDigest(root); writeFileSync(path.join(root, 'dist/a.js'), 'changed'); assert.notEqual(calculateCompiledArtifactDigest(root), oldDigest); }); test('missing, empty, and linked compiled directories are refused', t => { const root = mkdtempSync(path.join(os.tmpdir(), 'stable-linked-test-')); t.after(() => rmSync(root, { recursive: true, force: true })); assert.throws(() => calculateCompiledArtifactDigest(root), /does not exist/); mkdirSync(path.join(root, 'dist')); assert.throws(() => calculateCompiledArtifactDigest(root), /empty/); mkdirSync(path.join(root, 'outside')); writeFileSync(path.join(root, 'outside/escaped.js'), 'outside the frozen directory'); symlinkSync(path.join(root, 'outside'), path.join(root, 'dist/linked'), 'junction'); assert.throws(() => calculateCompiledArtifactDigest(root), /symbolic links/); }); test('control-plane evidence requires a Bearer credential and local HTTP cannot target a remote host', async () => { const environment = { NODE_ENV: 'local', RPC_RELEASE_ID: '42', SAAS_CONTROL_PLANE_URL: 'http://127.0.0.1:8080', COOL_SAAS_TOKEN: 'test-short-lived-credential' }; const release = fixture().release; assert.equal(await fetchReleaseEvidence(environment, async (url, options) => { assert.equal(url.searchParams.get('id'), '42'); assert.equal(options.headers.Authorization, `Bearer ${environment.COOL_SAAS_TOKEN}`); assert.equal(options.redirect, 'error'); return { ok: true, json: async () => ({ code: 1000, data: release }) }; }), release); await assert.rejects(fetchReleaseEvidence({ ...environment, COOL_SAAS_TOKEN: '' }), /credential/); await assert.rejects(fetchReleaseEvidence({ ...environment, SAAS_CONTROL_PLANE_URL: 'http://example.com' }), /HTTPS/); await assert.rejects(fetchReleaseEvidence(environment, async () => ({ ok: false, status: 401 })), /rejected/); }); test('actual startup verifies existing bytes, removes CLI secrets, and never invokes a build', async t => { const root = mkdtempSync(path.join(os.tmpdir(), 'stable-start-test-')); t.after(() => rmSync(root, { recursive: true, force: true })); mkdirSync(path.join(root, 'dist'), { recursive: true }); writeFileSync(path.join(root, 'dist/configuration.js'), 'frozen compiled bytes'); const data = fixture(); data.digest = calculateCompiledArtifactDigest(root); data.release.frozenArtifact.artifactDigest = data.digest; data.release.schemaSnapshot.actionManifest.artifactDigest = data.digest; writeFileSync(path.join(root, '.cool-saas-candidate.json'), JSON.stringify(data.lock)); const environment = { NODE_ENV: 'local', RPC_RELEASE_ID: '42', BUILD_SHA: data.commit, COOL_SAAS_TOKEN: 'short-lived-test', NODE_AUTH_TOKEN: 'must-not-inherit' }; let starts = 0; const options = { projectDir: root, environment, fetch: async () => ({ ok: true, json: async () => ({ code: 1000, data: data.release }) }), loadGenerated: () => data.generated, start: ({ verifySession }) => { starts++; assert.equal(environment.COOL_SAAS_TOKEN, undefined); assert.equal(environment.NODE_AUTH_TOKEN, undefined); verifySession(sessionFor(environment)); return 'started'; } }; assert.equal(await startStable(options), 'started'); assert.equal(starts, 1); assert.equal(readFileSync(path.join(root, 'dist/configuration.js'), 'utf8'), 'frozen compiled bytes'); writeFileSync(path.join(root, 'dist/configuration.js'), 'tampered'); environment.COOL_SAAS_TOKEN = 'short-lived-test'; options.loadGenerated = () => { throw new Error('must not execute unverified compiled code'); }; await assert.rejects(startStable(options), /compiled artifact digest differs/); assert.equal(starts, 1); writeFileSync(path.join(root, 'dist/configuration.js'), 'frozen compiled bytes'); environment.COOL_SAAS_TOKEN = 'short-lived-test'; await assert.rejects(startStable({ ...options, loadGenerated: () => data.generated, start: ({ verifySession }) => { writeFileSync(path.join(root, 'dist/configuration.js'), 'changed during bootstrap'); verifySession(sessionFor(environment)); } }), /changed during bootstrap/); }); test('dirty Git checkouts cannot be presented as the frozen source commit', t => { const root = mkdtempSync(path.join(os.tmpdir(), 'stable-git-test-')); t.after(() => rmSync(root, { recursive: true, force: true })); const git = (...args) => { const result = spawnSync('git', args, { cwd: root, encoding: 'utf8', windowsHide: true }); assert.equal(result.status, 0, result.stderr); return result.stdout.trim(); }; git('init'); git('config', 'user.email', 'test@example.invalid'); git('config', 'user.name', 'Stable Test'); writeFileSync(path.join(root, 'business.js'), 'original'); git('add', '.'); git('commit', '-m', 'frozen'); assert.equal(sourceCommit(root, {}), git('rev-parse', 'HEAD')); writeFileSync(path.join(root, 'business.js'), 'modified'); assert.throws(() => sourceCommit(root, {}), /dirty/); }); test('V2 frozen and Stable starts reuse signed executable and dependency bytes and reject tampering before generated code loads', async t => { const helper = require('../release-artifact'); const parent = mkdtempSync(path.join(os.tmpdir(), 'stable-v2-test-')); t.after(() => rmSync(parent, { recursive:true, force:true })); const root = path.join(parent, 'deployment'); for (const name of ['dist/saas/generated', 'scripts', 'node_modules/pkg']) mkdirSync(path.join(root,name), {recursive:true}); const data = fixture(); for (const name of ['bootstrap.js','release-environment.js','start-stable.js','start-frozen.js','release-artifact.js','dist/configuration.js','dist/saas/generated/index.js']) writeFileSync(path.join(root,name), 'module.exports = {};'); writeFileSync(path.join(root,'.cool-saas-candidate.json'),JSON.stringify(data.lock)); for (const name of ['.cool-saas-service.json','package.json','scripts/published-packages.json']) writeFileSync(path.join(root,name),'{}'); writeFileSync(path.join(root,'pnpm-lock.yaml'),'lockfileVersion: 9\n'); writeFileSync(path.join(root,'node_modules/pkg/index.js'),'exports.identity = "frozen";'); const bundle = helper.createReleaseArtifactBundle(root); const inspected = helper.inspectReleaseArtifactBundle(bundle); const keys = generateKeyPairSync('ed25519'); const privatePem = keys.privateKey.export({type:'pkcs8',format:'pem'}).toString(); const publicKey = keys.publicKey.export({type:'spki',format:'pem'}).toString(); const trustFile = path.join(parent,'trust.json'); writeFileSync(trustFile,JSON.stringify({builders:{'builder-1':{publicKey,moduleIds:[2],repositories:['https://git.example.invalid/demo.git']}}})); const artifact = data.release.frozenArtifact; Object.assign(artifact,{releaseEvidenceVersion:2,sourceRepository:'https://git.example.invalid/demo.git', artifactDigest:inspected.artifactDigest,bundleDigest:inspected.bundleDigest, dependencyLockChecksum:createHash('sha256').update(inspected.files.get('pnpm-lock.yaml')).digest('hex')}); const claims = {version:2,taskId:'build-task',taskNonce:'nonce',releaseId:41,moduleId:2,buildRevision:2,buildChecksum:data.lock.buildChecksum}; for (const name of ['sourceRepository','sourceBranch','sourceCommit','tagName','generatedLockChecksum','implementationManifestChecksum','artifactDigest','bundleDigest','dependencyLockChecksum']) claims[name] = artifact[name]; artifact.buildEvidence = helper.signReleaseEvidence('build','builder-1',claims,privatePem); data.release.releaseEvidenceVersion = 2; data.release.schemaSnapshot.actionManifest.artifactDigest = inspected.artifactDigest; const originalBytes = helper.releaseJsonChecksum(helper.createReleaseArtifactBundle(root)); let starts = 0, loads = 0; async function launch(mode, edit) { const release = structuredClone(data.release); if (mode === 'frozen') Object.assign(release,{id:41,releaseType:'rc',candidateStatus:'promoted',targetVersion:'1.0.1',version:data.lock.candidateVersion,serviceVersion:data.lock.candidateVersion,promotedReleaseId:42}); edit?.(release); const environment = {NODE_ENV:'local',RPC_RELEASE_ID:String(release.id),BUILD_SHA:data.commit,COOL_SAAS_TOKEN:'short-lived-test',SAAS_RELEASE_TRUST_FILE:trustFile}; return startStable({projectDir:root,mode,environment,fetch:async url => ({ok:true,json:async()=>({code:1000,data:url.pathname.endsWith('/artifact-bundle') ? {bundle,...inspected,files:undefined,buildEvidence:artifact.buildEvidence} : release})}), loadGenerated:()=>{loads++;return data.generated;},start:({verifySession})=>{starts++;verifySession(sessionFor(environment));return 'started';}}); } assert.equal(await launch('frozen'),'started'); assert.equal(await launch('production'),'started'); assert.equal(starts,2); assert.equal(helper.releaseJsonChecksum(helper.createReleaseArtifactBundle(root)),originalBytes); for (const name of ['bootstrap.js','node_modules/pkg/index.js','pnpm-lock.yaml']) { const file = path.join(root,name), bytes = readFileSync(file); writeFileSync(file,'tampered'); await assert.rejects(launch('production'),/differs|differ/); writeFileSync(file,bytes); } await assert.rejects(launch('production',release=>{release.frozenArtifact.buildEvidence.signature='a'.repeat(86);}),/signature/); await assert.rejects(launch('production',release=>{release.frozenArtifact.sourceCommit='9'.repeat(40);}),/sourceCommit/); assert.equal(loads,2,'tampered bytes must fail before loading generated JavaScript'); });