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

60 lines
3.2 KiB
JavaScript

'use strict';
// Proba CLI-ului Control Plane cap la cap (B1 milestone 2), cu CONTROL NEGATIV: un dosar cu cod vulnerabil produce actiunile
// corecte; un dosar tot-PQ produce plan gol. Isi face singura fixturile intr-un temp si le sterge.
// node proba-control-plane.mjs -> 0 toate cum trebuia, 1 altfel
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const CLI = path.join(AICI, 'control-plane.mjs');
const T = fs.mkdtempSync(path.join(os.tmpdir(), 'cp-proba-'));
let rele = 0;
const cer = (n, c) => { console.log(` [${c ? 'OK ' : 'RAU '}] ${n}`); if (!c) rele++; };
function ruleaza(dir, scanFile) {
const args = [CLI, '--code', dir, '--json'];
if (scanFile) args.push('--scan', scanFile);
return JSON.parse(execFileSync(process.execPath, args, { encoding: 'utf8' }));
}
try {
// fixtura vulnerabila
const v = path.join(T, 'vuln'); fs.mkdirSync(v);
fs.writeFileSync(path.join(v, 'a.js'), "const c=require('crypto');\nc.createECDH('prime256v1');\nc.createSign('RSA-SHA256');\n");
// fixtura PQ
const pq = path.join(T, 'pq'); fs.mkdirSync(pq);
fs.writeFileSync(path.join(pq, 'b.js'), "// ML-KEM-768 si ML-DSA-65, doar nume\nconst a='ML-KEM-768', s='ML-DSA-65';\n");
// scanare de proba (readiness): un schimb de cheie clasic pe un host
const scanF = path.join(T, 'scan.json');
fs.writeFileSync(scanF, JSON.stringify({ domain: 'client.test', findings: [{ id: 'hndl-exposed', severity: 'high', title: 'No post-quantum key exchange: harvest-now-decrypt-later exposure' }] }));
const pv = ruleaza(v);
const byRef = Object.fromEntries(pv.actions.map((a) => [a.ref.split(':').slice(0, 3).join(':'), a]));
cer('dosar vulnerabil: 2 folosiri vulnerabile', pv.inventory.quantumVulnerable === 2);
cer('dosar vulnerabil: 2 actiuni in plan', pv.summary.total === 2);
const ecdh = pv.actions.find((a) => /ECDH/i.test(a.name || a.asset));
const rsa = pv.actions.find((a) => /RSA/i.test(a.name || a.asset));
cer('ECDH -> gateway (schimb de cheie, auto)', ecdh && ecdh.product === 'gateway' && ecdh.method === 'auto-aere');
cer('RSA -> pki (semnatura, auto)', rsa && rsa.product === 'pki');
cer('HNDL: ECDH mai urgent decat RSA', ecdh && rsa && ({ CRITICAL: 0, HIGH: 1, MEDIUM: 2 }[ecdh.urgency] < { CRITICAL: 0, HIGH: 1, MEDIUM: 2 }[rsa.urgency]));
cer('fiecare actiune are locatia (fisier:linie)', pv.actions.every((a) => a.location && /\.js:\d/.test(a.location)));
// cu scanare: apare si actiunea de schimb de cheie TLS (gateway, CRITICA)
const pvs = ruleaza(v, scanF);
const kex = pvs.actions.find((a) => /tls-kex/.test(a.ref));
cer('cu scanare: schimbul de cheie TLS e in plan, gateway CRITICA', kex && kex.product === 'gateway' && kex.urgency === 'CRITICAL');
// CONTROL NEGATIV: dosar tot-PQ -> 0 vulnerabile, plan gol
const ppq = ruleaza(pq);
cer('CONTROL: dosar tot-PQ -> 0 vulnerabile', ppq.inventory.quantumVulnerable === 0);
cer('CONTROL: dosar tot-PQ -> plan gol', ppq.summary.total === 0);
} finally {
fs.rmSync(T, { recursive: true, force: true });
}
console.log(`\nB1 control-plane CLI: ${9 - rele}/9 cum trebuia`);
process.exit(rele ? 1 : 0);