aere-quantum/control-plane/proba-consola.mjs

80 lines
6.3 KiB
JavaScript

'use strict';
// Proba consolei (B1 m3): un buraf bun -> verdict OK; si controale NEGATIVE care arata ca CONSOLA NU SE INCREDE in buraf:
// (1) buraf care declara chainOk:true peste un jurnal manipulat -> consola prinde minciuna (lant RUPT + autoRaportSuspect);
// (2) buraf cu lant INTERN COERENT (hash-uri refacute) dar cu un plic al carui statementHash minte -> consola prinde plicul.
// plus: planul de migrare se pliaza in stare.
// node proba-consola.mjs -> 0 toate cum trebuia, 1 altfel
import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import crypto from 'node:crypto';
import { execFileSync } from 'node:child_process'; import { fileURLToPath, pathToFileURL } from 'node:url';
import { evalueaza, caleaSidecarului } from './consola.mjs';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const RAD = path.resolve(AICI, '..', '..');
const SIDE = caleaSidecarului();
const T = fs.mkdtempSync(path.join(os.tmpdir(), 'cons-'));
let rele = 0; const cer = (n, c) => { console.log(` [${c ? 'OK ' : 'RAU '}] ${n}`); if (!c) rele++; };
const side = (args) => { try { execFileSync(process.execPath, [SIDE, ...args], { encoding: 'utf8' }); return 0; } catch (e) { return e.status ?? 1; } };
try {
const { hashIntrare } = await import(pathToFileURL(SIDE).href);
const log = path.join(T, 'audit.log');
const bin = path.join(T, 'node'); fs.writeFileSync(bin, 'BIN');
const sh = '0x' + crypto.createHash('sha256').update(fs.readFileSync(bin)).digest('hex');
side(['record', '--kind', 'runtime', '--artifact', bin, '--attested', sh, '--host', 'h1', '--log', log, '--at', '2026-09-26T09:00:00Z']);
side(['record', '--kind', 'deployment', '--name', 'app', '--version', '1.0', '--content-file', bin, '--log', log, '--at', '2026-09-26T09:01:00Z']);
const bf = path.join(T, 'b.json'); side(['bundle', '--log', log, '--out', bf, '--host', 'h1', '--at', '2026-09-26T09:02:00Z']);
const bundle = JSON.parse(fs.readFileSync(bf, 'utf8'));
const okSt = await evalueaza({ bundle });
cer('buraf bun -> verdict OK', okSt.verdict === 'OK' && okSt.auditChain.ok && okSt.envelopes.intact === okSt.envelopes.total);
// CONTROL 1: minciuna auto-raportata + jurnal manipulat (hash stale)
const b1 = JSON.parse(JSON.stringify(bundle)); b1.entries[0].proof.statement.host = 'ATACATOR'; b1.chainOk = true;
const st1 = await evalueaza({ bundle: b1 });
cer('CONTROL 1: chainOk:true peste jurnal manipulat -> STRICAT (lant rupt)', st1.verdict === 'BROKEN' && st1.auditChain.ok === false);
cer('CONTROL 1: consola marcheaza minciuna auto-raportata', !!st1.selfReportSuspect);
// CONTROL 2: lant INTERN COERENT (hash-uri refacute) dar plic cu statementHash mincinos
const b2 = JSON.parse(JSON.stringify(bundle));
b2.entries[0].proof.statement.host = 'ATACATOR'; // statementHash ramane cel vechi -> minte
// refac lantul ca sa fie intern coerent (asa cum ar face un host rau destept)
let prev = '0x' + '00'.repeat(32);
for (const e of b2.entries) { e.prev = prev; e.hash = hashIntrare(e.seq, e.prev, e.proof); prev = e.hash; }
b2.chainOk = true; b2.head = prev;
const st2 = await evalueaza({ bundle: b2 });
cer('CONTROL 2: lant coerent dar plic mincinos -> lantul trece, PLICUL cade', st2.auditChain.ok === true && st2.envelopes.bad.length === 1 && st2.verdict === 'BROKEN');
// planul de migrare se pliaza. 2026-09-27: planul vine din PLANIFICATORUL real, nu dintr-o fixtura scrisa de mana; fixtura veche
// (`items`/`mod`) era chiar forma presupusa de consola, deci proba masura presupunerea, iar pe un plan real consola afisa 0 actiuni.
const { planeaza } = await import(pathToFileURL(path.join(AICI, 'plan-migrare.mjs')).href);
const inv = [
{ assetType: 'algorithm', name: 'ECDH', primitive: 'key-agree', quantumVulnerable: true, ref: 'a:1' },
{ assetType: 'algorithm', name: 'RSA-2048', primitive: 'signature', quantumVulnerable: true, ref: 'a:2' },
{ assetType: 'algorithm', name: 'ML-KEM-768', primitive: 'kem', quantumVulnerable: false, ref: 'a:3' },
];
const plan = planeaza({ inventory: inv, scan: { domain: 'exemplu.test', findings: [{ id: 'hndl-exposed', severity: 'high', title: 'No post-quantum key exchange' }] } });
const nAuto = plan.actions.filter((a) => a.method === 'auto-aere').length;
const nCrit = plan.actions.filter((a) => a.urgency === 'CRITICAL').length;
const st3 = await evalueaza({ bundle, plan });
cer(`planul REAL al planificatorului se pliaza in stare (${plan.actions.length} actiuni, ${nCrit} critice, ${nAuto} auto)`,
st3.migration && st3.migration.total === plan.actions.length && plan.actions.length > 0 && st3.migration.auto === nAuto && st3.migration.critical === nCrit && nAuto > 0 && nCrit > 0);
// CONTROL 3: un plan fara nicio lista recunoscuta nu e "plan gol cu 0 actiuni": consola o spune si verdictul e STRICAT
const st4 = await evalueaza({ bundle, plan: { ceva: [1, 2] } });
cer('CONTROL 3: plan fara lista recunoscuta -> nu "0 actiuni", ci eroare numita si STRICAT', st4.migration && st4.migration.total === null && st4.verdict === 'BROKEN');
// executia migrarii: consola re-verifica lantul executie.json al executorului (aici in mod USCAT, fara produse)
const { executa } = await import(pathToFileURL(path.join(AICI, 'executa-migrare.mjs')).href);
const outX = path.join(T, 'exec'); const x = await executa(plan, { consent: 'all', execute: false, out: outX });
const execution = JSON.parse(fs.readFileSync(x.file, 'utf8'));
const st5 = await evalueaza({ bundle, plan, execution });
cer(`executia (uscata) se pliaza: lant intreg, ${st5.execution && st5.execution.actions} actiuni`, st5.execution && st5.execution.chainOk && st5.execution.actions === nAuto && st5.verdict === 'OK');
// CONTROL 4: o inregistrare de executie schimbata -> consola o prinde si verdictul e STRICAT
const ex2 = JSON.parse(JSON.stringify(execution)); ex2.records[1].record.verdict = 'OK-fals';
const st6 = await evalueaza({ bundle, plan, execution: ex2 });
cer('CONTROL 4: inregistrare de executie schimbata -> lant RUPT, STRICAT', st6.execution && !st6.execution.chainOk && st6.execution.brokenAtSeq === 1 && st6.verdict === 'BROKEN');
} finally { fs.rmSync(T, { recursive: true, force: true }); }
const total = 8;
console.log(`\nB1 m3 consola: ${total - rele}/${total} cum trebuia (buraf bun + 4 controale negative + plan real + executie)`);
process.exitCode = rele ? 1 : 0;