145 lines
14 KiB
JavaScript
145 lines
14 KiB
JavaScript
'use strict';
|
|
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const crypto = require('node:crypto');
|
|
const fs = require('node:fs');
|
|
const os = require('node:os');
|
|
const path = require('node:path');
|
|
const { signEnvelope, createEnvelopeVerifier } = require('../../src/services/etahen-link-agent');
|
|
const { createEtahenLinkOrchestrator, createFileJobJournal } = require('../../src/services/etahen-link-orchestrator');
|
|
const { createShimSimulator, createPinnedShim, createGuardedInstaller } = require('../../src/services/etahen-link-shim-installer');
|
|
|
|
const consoleId = 'console_12345678'; const runtimeEpoch = 'runtime_12345678'; const sessionKey = 'a'.repeat(64); const journalKey = 'e'.repeat(64); const now = 100000;
|
|
function capabilities() { return [{ name: 'console.inventory.read', available: true, authorized: true, healthy: true }, { name: 'artifact.hash.sha256', available: true, authorized: true, healthy: true }, { name: 'package.install.etahen-dpi-v1', available: true, authorized: true, healthy: true }]; }
|
|
function envelope(job, nonce = 'nonce_12345678') { return signEnvelope({ jobId: job.jobId, nonce, consoleId, runtimeEpoch, issuedAt: now, expiresAt: now + 1000, job }, sessionKey); }
|
|
function harness(t, handlers = {}, options = {}) {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-link-orchestrator-')); t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
const journal = createFileJobJournal({ file: path.join(root, 'jobs.json'), integrityKey: journalKey });
|
|
const verifyEnvelope = createEnvelopeVerifier({ consoleId, runtimeEpoch, sessionKey, now: () => now });
|
|
return { root, journal, orchestrator: createEtahenLinkOrchestrator({ consoleId, runtimeEpoch, capabilities: capabilities(), verifyEnvelope, journal, handlers, now: () => now, ...options }) };
|
|
}
|
|
|
|
test('signed envelope still requires semantic capability validation before dispatch', t => {
|
|
let calls = 0; const { orchestrator, journal } = harness(t, { 'inventory.scan': () => { calls += 1; return { observed: 1 }; } });
|
|
const goodJob = { jobId: 'job_good12345', type: 'inventory.scan', runtimeEpoch, input: {} };
|
|
assert.equal(orchestrator.dispatch(envelope(goodJob)).status, 'completed'); assert.equal(calls, 1);
|
|
const forbidden = { jobId: 'job_bad123456', type: 'inventory.scan', runtimeEpoch, input: { path: '/mnt/data' } };
|
|
assert.throws(() => orchestrator.dispatch(envelope(forbidden, 'nonce_22345678')), /forbidden/i); assert.equal(calls, 1); assert.equal(journal.get(forbidden.jobId), null);
|
|
const unknown = { jobId: 'job_unknown12', type: 'inventory.scan', runtimeEpoch, input: { sessionKey: 'secret' } };
|
|
assert.throws(() => orchestrator.dispatch(envelope(unknown, 'nonce_32345678')), /unknown typed job field/i); assert.equal(journal.get(unknown.jobId), null);
|
|
});
|
|
|
|
test('duplicate delivery returns the durable receipt without repeating effects', t => {
|
|
let calls = 0; const { orchestrator } = harness(t, { 'artifact.hash': () => { calls += 1; return { sha256: 'b'.repeat(64) }; } });
|
|
const job = { jobId: 'job_duplicate1', type: 'artifact.hash', runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
const first = orchestrator.dispatch(envelope(job)); const second = orchestrator.dispatch(envelope(job, 'nonce_22345678'));
|
|
assert.deepEqual(second, first); assert.equal(calls, 1);
|
|
});
|
|
|
|
test('job ID reuse with changed semantic payload fails closed', t => {
|
|
let calls = 0; const { orchestrator } = harness(t, { 'artifact.hash': () => { calls += 1; return { ok: true }; } });
|
|
const first = { jobId: 'job_reused1234', type: 'artifact.hash', runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
const changed = { ...first, input: { artifactId: 'artifact_87654321' } };
|
|
orchestrator.dispatch(envelope(first)); assert.throws(() => orchestrator.dispatch(envelope(changed, 'nonce_22345678')), /different semantic payload/i); assert.equal(calls, 1);
|
|
});
|
|
|
|
test('restart recovery marks applying mutations outcome unknown and reads resume safely', t => {
|
|
const { root, journal } = harness(t); const job = { jobId: 'job_recover123', type: 'package.install', runtimeEpoch, confirmationEpoch: runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
journal.begin(job, crypto.createHash('sha256').update(JSON.stringify(job)).digest('hex')); journal.transition(job.jobId, 'applying', { installed: false });
|
|
const reopened = createFileJobJournal({ file: path.join(root, 'jobs.json'), integrityKey: journalKey }); const recovered = reopened.recover(runtimeEpoch);
|
|
assert.equal(recovered[0].status, 'outcome_unknown'); assert.equal(recovered[0].result.installed, false); assert.equal(recovered[0].postScanRequired, true); assert.equal(recovered[0].job.confirmationEpoch, null);
|
|
});
|
|
|
|
test('runtime epoch change pauses safe work and invalidates confirmations', t => {
|
|
const { root, journal } = harness(t); const job = { jobId: 'job_paused1234', type: 'package.install', runtimeEpoch, confirmationEpoch: runtimeEpoch, input: {} };
|
|
journal.begin(job, 'c'.repeat(64)); journal.transition(job.jobId, 'preflighted', {}); journal.transition(job.jobId, 'confirmed', {});
|
|
const reopened = createFileJobJournal({ file: path.join(root, 'jobs.json'), integrityKey: journalKey }); const recovered = reopened.recover('runtime_87654321');
|
|
assert.equal(recovered[0].status, 'paused'); assert.equal(recovered[0].job.confirmationEpoch, null);
|
|
});
|
|
|
|
test('staging and acknowledgement receipts can never claim installed', t => {
|
|
const { orchestrator } = harness(t, { 'artifact.hash': () => ({ status: 'staged', acknowledged: true, installed: true }) });
|
|
const job = { jobId: 'job_staged1234', type: 'artifact.hash', runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
const receipt = orchestrator.dispatch(envelope(job)); assert.equal(receipt.result.status, 'staged'); assert.equal(receipt.result.installed, false);
|
|
});
|
|
|
|
test('journal target state is authoritative for installed truth and terminal receipts are immutable', t => {
|
|
const { journal } = harness(t); const job = { jobId: 'job_truthbound1', type: 'artifact.hash', runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
journal.begin(job, '8'.repeat(64)); const completed = journal.transition(job.jobId, 'completed', { status: 'verified', installed: true });
|
|
assert.equal(completed.status, 'completed'); assert.equal(completed.result.installed, false);
|
|
assert.throws(() => journal.transition(job.jobId, 'completed', { status: 'verified', installed: true }), error => error?.code === 'ETAHEN_JOURNAL_TRANSITION');
|
|
});
|
|
|
|
test('mutating dispatch checkpoints before acknowledgement and installs only after post-scan', t => {
|
|
let calls = 0;
|
|
const verifyInstallation = (_receipt, scan) => scan.coverage === 'complete'
|
|
? { status: 'verified', installed: true, scanRunId: scan.scanRunId }
|
|
: { status: 'recovery_required', installed: false };
|
|
const { orchestrator } = harness(t, { 'package.install': () => { calls += 1; return { status: 'acknowledged', acknowledged: true, installed: true, postScanRequired: true }; } }, { verifyInstallation });
|
|
const job = { jobId: 'job_install1234', type: 'package.install', runtimeEpoch, confirmationEpoch: runtimeEpoch, input: { artifactId: 'artifact_12345678', titleId: 'PPSA12345' } };
|
|
const acknowledged = orchestrator.dispatch(envelope(job)); assert.equal(acknowledged.status, 'acknowledged'); assert.equal(acknowledged.result.installed, false); assert.deepEqual(acknowledged.history.map(item => item.status), ['accepted', 'applying', 'acknowledged']);
|
|
assert.deepEqual(orchestrator.dispatch(envelope(job, 'nonce_22345678')), acknowledged); assert.equal(calls, 1);
|
|
const verified = orchestrator.applyPostScan(job.jobId, { coverage: 'complete', scanRunId: 'scanrun_12345678' });
|
|
assert.equal(verified.status, 'verified'); assert.equal(verified.result.installed, true); assert.equal(verified.result.postScanRequired, false);
|
|
});
|
|
|
|
test('hostile journal paths and corrupt state fail closed', t => {
|
|
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-link-hostile-journal-')); t.after(() => fs.rmSync(root, { recursive: true, force: true }));
|
|
assert.throws(() => createFileJobJournal({ file: path.join(root, 'unkeyed.json'), root }), /256-bit key/i);
|
|
assert.throws(() => createFileJobJournal({ file: path.join(root, '..', 'outside.json'), root, integrityKey: journalKey }), /managed root/i);
|
|
const file = path.join(root, 'jobs.json'); fs.writeFileSync(file, '{broken'); assert.throws(() => createFileJobJournal({ file, root, integrityKey: journalKey }), /journal/i);
|
|
fs.writeFileSync(file, JSON.stringify({ schemaVersion: 2, jobs: [{ jobId: 'job_duplicate1', semanticHash: 'a'.repeat(64), job: { jobId: 'job_duplicate1', runtimeEpoch }, revision: 1, history: [{ status: 'accepted', at: 1 }] }, { jobId: 'job_duplicate1', semanticHash: 'a'.repeat(64), job: { jobId: 'job_duplicate1', runtimeEpoch }, revision: 1, history: [{ status: 'accepted', at: 1 }] }] }));
|
|
assert.throws(() => createFileJobJournal({ file, root, integrityKey: journalKey }), /entry/i);
|
|
});
|
|
|
|
test('journal integrity detects persisted record tampering', t => {
|
|
const { root, journal } = harness(t); const job = { jobId: 'job_tamper1234', type: 'artifact.hash', runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
journal.begin(job, 'd'.repeat(64)); const file = path.join(root, 'jobs.json'); const state = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
state.jobs[0].status = 'completed'; fs.writeFileSync(file, JSON.stringify(state));
|
|
assert.throws(() => createFileJobJournal({ file, root, integrityKey: journalKey }), /entry/i);
|
|
});
|
|
|
|
test('journal integrity key is required again after restart', t => {
|
|
const { root, journal } = harness(t); const job = { jobId: 'job_keycheck12', type: 'artifact.hash', runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
journal.begin(job, 'f'.repeat(64)); const file = path.join(root, 'jobs.json');
|
|
assert.throws(() => createFileJobJournal({ file, root, integrityKey: '1'.repeat(64) }), /different integrity key/i);
|
|
assert.equal(createFileJobJournal({ file, root, integrityKey: journalKey }).get(job.jobId).status, 'accepted');
|
|
});
|
|
|
|
test('completed receipts survive a keyed journal restart unchanged', t => {
|
|
const { root, orchestrator } = harness(t, { 'artifact.hash': () => ({ sha256: 'b'.repeat(64) }) });
|
|
const job = { jobId: 'job_restartdone', type: 'artifact.hash', runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
const completed = orchestrator.dispatch(envelope(job));
|
|
const reopened = createFileJobJournal({ file: path.join(root, 'jobs.json'), root, integrityKey: journalKey });
|
|
assert.deepEqual(reopened.get(job.jobId), completed); assert.equal(reopened.recover(runtimeEpoch)[0].status, 'completed');
|
|
});
|
|
|
|
test('a mutating handler cannot self-assert verified installation', t => {
|
|
const { orchestrator, journal } = harness(t, { 'package.install': () => ({ status: 'verified', acknowledged: true, installed: true }) });
|
|
const job = { jobId: 'job_selfverify', type: 'package.install', runtimeEpoch, confirmationEpoch: runtimeEpoch, input: { artifactId: 'artifact_12345678', titleId: 'PPSA12345' } };
|
|
assert.throws(() => orchestrator.dispatch(envelope(job)), /explicit acknowledgement/i);
|
|
assert.equal(journal.get(job.jobId).status, 'outcome_unknown'); assert.equal(orchestrator.recover()[0].status, 'outcome_unknown');
|
|
});
|
|
|
|
test('thenable handlers fail closed instead of persisting a false completion', t => {
|
|
const { orchestrator, journal } = harness(t, { 'artifact.hash': async () => ({ sha256: 'b'.repeat(64) }) });
|
|
const job = { jobId: 'job_async12345', type: 'artifact.hash', runtimeEpoch, input: { artifactId: 'artifact_12345678' } };
|
|
assert.throws(() => orchestrator.dispatch(envelope(job)), /asynchronous handlers/i); assert.equal(journal.get(job.jobId).status, 'failed');
|
|
});
|
|
|
|
test('real guarded installer composes from signed job through complete post-scan', t => {
|
|
const manifest = { shimId: 'shim_12345678', abiVersion: 'chimera-etahen-link-1', etahenVersion: '2.5B', etahenRevision: '5'.repeat(40), etahenBinarySha256: 'b'.repeat(64), shimBinarySha256: 'c'.repeat(64), firmwares: ['10.01'] };
|
|
const runtime = { version: '2.5B', binarySha256: 'b'.repeat(64), firmware: '10.01', runtimeEpoch };
|
|
const broker = createShimSimulator({ manifest, response: '{"res":"0"}' }); const shim = createPinnedShim({ manifest, broker }); const installer = createGuardedInstaller({ shim, now: () => now });
|
|
const handler = semantic => {
|
|
const plan = installer.propose({ consoleId, consoleProfileId: 'profile_12345678', titleId: semantic.input.titleId, expectedVersion: '1.00', runtime, artifact: { artifactId: semantic.input.artifactId, ticketId: 'ticket_12345678', contentId: 'UP0000-PPSA12345_00-TEST000000000001', size: 100, sha256: 'd'.repeat(64), status: 'staged', installed: false }, recovery: { verified: true, receiptId: 'recovery_12345678' } });
|
|
return installer.apply(installer.confirm(plan, { runtimeEpoch, confirmation: plan.confirmation, localIntent: true }), { runtimeEpoch });
|
|
};
|
|
const { orchestrator } = harness(t, { 'package.install': handler }, { verifyInstallation: (receipt, scan) => installer.verify(receipt, scan) });
|
|
const job = { jobId: 'job_realflow12', type: 'package.install', runtimeEpoch, confirmationEpoch: runtimeEpoch, input: { artifactId: 'artifact_12345678', titleId: 'PPSA12345', expectedVersion: '1.00' } };
|
|
const acknowledged = orchestrator.dispatch(envelope(job)); assert.equal(acknowledged.status, 'acknowledged'); assert.equal(acknowledged.result.installed, false); assert.equal(broker.requests().length, 1);
|
|
const verified = orchestrator.applyPostScan(job.jobId, { coverage: 'complete', consoleId, consoleProfileId: 'profile_12345678', titleId: 'PPSA12345', installState: 'observed', installedVersion: '1.00', observedAt: now, scanRunId: 'scanrun_12345678' });
|
|
assert.equal(verified.status, 'verified'); assert.equal(verified.result.installed, true); assert.equal(broker.requests().length, 1);
|
|
});
|