'use strict'; // Proba sidecar-ului B3: inregistreaza evenimente, verifica lantul, si controale NEGATIVE - o intrare manipulata rupe lantul exact la // seq-ul ei, un hash schimbat se prinde, adaugarea peste un lant rupt e refuzata. Plus moatul: fiecare plic inregistrat e VALID // pentru verificatorul AIP-23 comun (zero cod de verificare nou). Din 2026-09-29 (revizuirea adversariala, pista B), si: // R un lant REFACUT de la geneza de cine poate scrie fisierul iese INTREG la verify-log (limita spusa), dar e prins fata de capul // atestat (verify-log --attested); la fel un jurnal TAIAT si un cap atestat manipulat; un jurnal care continua capul trece // C doi scriitori concurenti nu rup lantul (lacatul) // N un rand care nu e JSON iese RUPT la seq-ul lui, nu cadere; --attested care nu e digest e refuzat // S capul SEMNAT ML-DSA-65 (punctul 34): semnatura verificata de verificatorul AIP-23 de referinta; --signer cere cheia // operatorului; un cap rescris si re-semnat de alta cheie, unul nesemnat cerut semnat, o semnatura stricata: refuzate // node proba-sidecar.mjs -> 0 toate cum trebuia, 1 cel putin una rea, 2 cel putin una SARITA (verificatorul AIP-23 lipseste) // Mediu: AERE_SIDECAR_MODUL (copia masurata, pentru controlul negativ), AERE_VERIFY_PROOF (verificatorul AIP-23; implicit cel din // depozitul de dezvoltare, tools/aere-proof-protocol/verify.mjs, sau verify-proof.mjs din aere-node/tools intr-o copie publica). import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import crypto from 'node:crypto'; import { execFileSync, spawn } from 'node:child_process'; import { fileURLToPath, pathToFileURL } from 'node:url'; const AICI = path.dirname(fileURLToPath(import.meta.url)); const SIDE = process.env.AERE_SIDECAR_MODUL ? path.resolve(process.env.AERE_SIDECAR_MODUL) : path.join(AICI, 'sidecar.mjs'); const { verificaJurnal, hashIntrare } = await import(pathToFileURL(SIDE).href); const VERIFY = process.env.AERE_VERIFY_PROOF ? path.resolve(process.env.AERE_VERIFY_PROOF) : path.resolve(AICI, '..', 'aere-proof-protocol', 'verify.mjs'); const areVerificator = fs.existsSync(VERIFY); const T = fs.mkdtempSync(path.join(os.tmpdir(), 'avl-')); let rele = 0, bune = 0, sarite = 0; const cer = (n, c) => { console.log(` [${c ? 'OK ' : 'RAU '}] ${n}`); if (c) bune++; else rele++; }; const sari = (n) => { console.log(` [SARIT] ${n} (verificatorul AIP-23 nu e la ${VERIFY}; dati AERE_VERIFY_PROOF)`); sarite++; }; function side(args) { try { return { cod: 0, out: execFileSync(process.execPath, [SIDE, ...args], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }) }; } catch (e) { return { cod: e.status ?? 1, out: (e.stdout || '') + (e.stderr || '') }; } } const linii = (p) => fs.readFileSync(p, 'utf8').split('\n').filter((l) => l.trim()).map((l) => JSON.parse(l)); const valid = (f) => { try { execFileSync(process.execPath, [VERIFY, f], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); return true; } catch { return false; } }; const pk = await import(pathToFileURL(path.resolve(path.dirname(SIDE), '..', 'proof-kinds', 'proof-kinds.mjs')).href).catch(() => import(pathToFileURL(path.resolve(AICI, '..', 'proof-kinds', 'proof-kinds.mjs')).href)); try { const log = path.join(T, 'audit.log'); const bin = path.join(T, 'aere-node'); fs.writeFileSync(bin, 'BINAR-VIU'); const sh = '0x' + crypto.createHash('sha256').update(fs.readFileSync(bin)).digest('hex'); const artefact = path.join(T, 'artefact.tar'); fs.writeFileSync(artefact, 'ARTEFACT-DESF'); cer('record runtime -> seq 0', side(['record', '--kind', 'runtime', '--artifact', bin, '--attested', sh, '--host', 'h1', '--log', log, '--at', '2026-09-26T09:00:00Z']).out.includes('seq=0')); cer('record deployment -> seq 1', side(['record', '--kind', 'deployment', '--name', 'app', '--version', '2.0', '--content-file', artefact, '--log', log, '--at', '2026-09-26T09:01:00Z']).out.includes('seq=1')); cer('verify-log -> INTACT cod 0', side(['verify-log', '--log', log]).cod === 0); // moatul: fiecare plic e VALID pentru verificatorul AIP-23 const intrari = linii(log); if (areVerificator) { let toateValide = true; for (const e of intrari) { const f = path.join(T, `e${e.seq}.json`); fs.writeFileSync(f, JSON.stringify(e.proof)); if (!valid(f)) toateValide = false; } cer('moat: fiecare plic inregistrat e VALID pentru verificatorul AIP-23 comun', toateValide); } else sari('moat: fiecare plic inregistrat e VALID pentru verificatorul AIP-23 comun'); // CONTROL NEGATIV 1: manipulez CONTINUTUL unei intrari trecute (fara sa refac hash-ul) -> lantul se rupe la ea const man1 = intrari.map((e) => ({ ...e })); man1[0].proof = { ...man1[0].proof, statement: { ...man1[0].proof.statement, host: 'ATACATOR' } }; cer('CONTROL: continut manipulat -> lant RUPT la seq 0', verificaJurnal(man1).rupt === 0); // CONTROL NEGATIV 2: schimb un hash din mijloc -> ruptura la intrarea urmatoare (prev nu mai leaga) const man2 = intrari.map((e) => ({ ...e })); man2[0].hash = '0x' + 'ff'.repeat(32); cer('CONTROL: hash schimbat -> lant RUPT', verificaJurnal(man2).ok === false); // CONTROL NEGATIV 3: adaugarea peste un lant rupt e REFUZATA const logRupt = path.join(T, 'rupt.log'); fs.writeFileSync(logRupt, JSON.stringify(man1[0]) + '\n'); cer('CONTROL: record peste lant rupt -> refuzat (cod != 0)', side(['record', '--kind', 'runtime', '--artifact', bin, '--attested', sh, '--log', logRupt, '--at', '2026-09-26T09:02:00Z']).cod !== 0); // bundle: chainOk true pe jurnalul bun const bout = path.join(T, 'bundle.json'); cer('bundle -> chainOk true', side(['bundle', '--log', log, '--out', bout, '--host', 'h1', '--at', '2026-09-26T09:03:00Z']).cod === 0 && JSON.parse(fs.readFileSync(bout, 'utf8')).chainOk === true); // attest-head: capul devine un plic AIP-23 valid pentru verificatorul comun; notarizabil -> finalitate PQ const hout = path.join(T, 'head.json'); cer('attest-head -> plic scris', side(['attest-head', '--log', log, '--out', hout, '--host', 'h1', '--at', '2026-09-26T09:04:00Z']).cod === 0 && fs.existsSync(hout)); const ph = JSON.parse(fs.readFileSync(hout, 'utf8')); ph.statement.head = '0x' + 'ee'.repeat(32); const hrau = path.join(T, 'head-rau.json'); fs.writeFileSync(hrau, JSON.stringify(ph)); if (areVerificator) { cer('attest-head: capul e VALID pentru verificatorul AIP-23 (integritate)', valid(hout)); // CONTROL NEGATIV 4: manipulez capul din plic dupa hash -> verificatorul da integritate PICAT cer('CONTROL: cap manipulat in plic -> verificator INVALID', !valid(hrau)); } else { sari('attest-head: capul e VALID pentru verificatorul AIP-23 (integritate)'); sari('CONTROL: cap manipulat in plic -> verificator INVALID'); } // ---- R: lantul fata de un cap atestat -------------------------------------------------------------------------------------- cer('R: jurnalul neatins CONTINUA capul atestat (cod 0)', side(['verify-log', '--log', log, '--attested', hout]).cod === 0); // un scriitor cu acces la fisier reface TOT lantul de la geneza cu o intrare schimbata: verify-log singur nu are cum sa vada const refacut = []; let prev = '0x' + '00'.repeat(32); for (const e of intrari) { const proof = e.seq === 0 ? { ...e.proof, statement: { ...e.proof.statement, host: 'ATACATOR' } } : e.proof; const hash = hashIntrare(e.seq, prev, proof); refacut.push({ seq: e.seq, prev, proof, hash }); prev = hash; } const logRef = path.join(T, 'refacut.log'); fs.writeFileSync(logRef, refacut.map((e) => JSON.stringify(e)).join('\n') + '\n'); cer('R: lant refacut de la geneza -> verify-log singur iese INTACT (limita, spusa in README)', side(['verify-log', '--log', logRef]).cod === 0); const rR = side(['verify-log', '--log', logRef, '--attested', hout]); cer('R CONTROL: acelasi lant refacut fata de capul atestat -> NU continua, istoria rescrisa (cod 1)', rR.cod === 1 && /rewritten/.test(rR.out)); const logTaiat = path.join(T, 'taiat.log'); fs.writeFileSync(logTaiat, JSON.stringify(intrari[0]) + '\n'); const rT = side(['verify-log', '--log', logTaiat, '--attested', hout]); cer('R CONTROL: jurnal taiat sub capul atestat -> NU continua, intrari scoase (cod 1)', rT.cod === 1 && /removed/.test(rT.out)); const logMai = path.join(T, 'continuat.log'); fs.copyFileSync(log, logMai); side(['record', '--kind', 'deployment', '--name', 'app', '--version', '2.1', '--content-file', artefact, '--log', logMai, '--at', '2026-09-26T09:05:00Z']); const rC = side(['verify-log', '--log', logMai, '--attested', hout]); cer('R: jurnal continuat dupa cap -> continua, o intrare scrisa dupa (cod 0)', rC.cod === 0 && /1 written after/.test(rC.out)); const capM = JSON.parse(fs.readFileSync(hout, 'utf8')); capM.statement.count = 1; const capMf = path.join(T, 'cap-man.json'); fs.writeFileSync(capMf, JSON.stringify(capM)); const rM = side(['verify-log', '--log', log, '--attested', capMf]); cer('R CONTROL: cap atestat manipulat (count schimbat, statementHash vechi) -> refuzat (cod 1)', rM.cod === 1 && /modified attestation/.test(rM.out)); // ---- S: capul semnat (2026-09-29, punctul 34) ------------------------------------------------------------------------------ // citirile de mai jos nu arunca: un fisier nescris (o versiune fara capete semnate) inroseste verificarea lui, nu opreste proba // (masurat 2026-09-29: pe versiunea veche proba se oprea aici si verificarile N de dupa nu mai rulau) const jr = (f) => { try { return JSON.parse(fs.readFileSync(f, 'utf8')); } catch { return {}; } }; const kOp = path.join(T, 'op'), kAt = path.join(T, 'atacator'); const rk = side(['keygen', '--out', kOp]); side(['keygen', '--out', kAt]); cer('S: keygen scrie cheia capetelor (ML-DSA-65) si tipareste numai amprenta', rk.cod === 0 && /key 0x[0-9a-f]{64}/.test(rk.out) && !/PRIVATE KEY/.test(rk.out) && fs.existsSync(path.join(kOp, 'head.key.pem'))); cer('S CONTROL: keygen nu suprascrie o cheie existenta (cod 2)', side(['keygen', '--out', kOp]).cod === 2); const hs = path.join(T, 'head-semnat.json'); cer('S: attest-head --sign-key -> cap semnat, acelasi statementHash ca cel nesemnat (semnatura e in afara declaratiei)', side(['attest-head', '--log', log, '--out', hs, '--host', 'h1', '--at', '2026-09-26T09:04:00Z', '--sign-key', path.join(kOp, 'head.key.pem')]).cod === 0 && jr(hs).statementHash === jr(hout).statementHash && (jr(hs).signature || {}).scheme === 'ml-dsa-65'); if (areVerificator) { const vs = (() => { try { return execFileSync(process.execPath, [VERIFY, hs], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }); } catch (e) { return String(e.stdout || ''); } })(); cer('S: semnatura capului e PASSED la nivelul signature al verificatorului AIP-23', /PASSED\s*\]\s*signature/.test(vs)); } else sari('S: semnatura capului e PASSED la nivelul signature al verificatorului AIP-23'); const rs = side(['verify-log', '--log', log, '--attested', hs, '--signer', path.join(kOp, 'head.pub.pem')]); cer('S: jurnalul continua capul semnat de operator, cu --signer = cheia lui (cod 0)', rs.cod === 0 && /the expected signer/.test(rs.out)); cer('S: fara --signer, verify-log spune cine a semnat si ca nu s-a comparat cu o cheie asteptata', /not checked against an expected signer/.test(side(['verify-log', '--log', log, '--attested', hs]).out)); // atacatorul reface lantul de la geneza (ca la R), isi face capul si il semneaza cu cheia LUI const hAt = path.join(T, 'head-atacator.json'); side(['attest-head', '--log', logRef, '--out', hAt, '--host', 'h1', '--at', '2026-09-26T09:04:00Z', '--sign-key', path.join(kAt, 'head.key.pem')]); cer('S: fara --signer, lantul refacut si capul lui semnat de atacator trec (limita: o semnatura spune cine, nu care)', side(['verify-log', '--log', logRef, '--attested', hAt]).cod === 0); const rAt = side(['verify-log', '--log', logRef, '--attested', hAt, '--signer', path.join(kOp, 'head.pub.pem')]); cer('S CONTROL: capul semnat de alta cheie, cu --signer = operatorul -> refuzat (cod 1)', rAt.cod === 1 && /another key/.test(rAt.out)); const rNe = side(['verify-log', '--log', log, '--attested', hout, '--signer', path.join(kOp, 'head.pub.pem')]); cer('S CONTROL: un cap nesemnat, cu --signer -> refuzat (cod 1)', rNe.cod === 1 && /not signed/.test(rNe.out)); const cs = jr(hs); if (cs.signature) { const sg = Buffer.from(cs.signature.signature, 'base64'); sg[100] ^= 1; cs.signature.signature = sg.toString('base64'); } const hsr = path.join(T, 'head-semnat-rau.json'); fs.writeFileSync(hsr, JSON.stringify(cs)); const rSr = side(['verify-log', '--log', log, '--attested', hsr]); cer('S CONTROL: o semnatura de cap stricata -> refuzat chiar fara --signer (cod 1)', rSr.cod === 1 && /does not verify/.test(rSr.out)); // ---- N: intrari nevalide ------------------------------------------------------------------------------------------------- const logN = path.join(T, 'necitibil.log'); fs.writeFileSync(logN, JSON.stringify(intrari[0]) + '\n{nu e json\n'); const rN = side(['verify-log', '--log', logN]); cer('N: rand care nu e JSON -> RUPT la seq 1, nu cadere (cod 1)', rN.cod === 1 && /BROKEN at seq 1/.test(rN.out)); cer('N CONTROL: --attested care nu e digest -> refuzat (cod 2), nimic scris', side(['record', '--kind', 'runtime', '--artifact', bin, '--attested', 'abc', '--log', path.join(T, 'n2.log')]).cod === 2 && !fs.existsSync(path.join(T, 'n2.log'))); // ---- C: doi scriitori concurenti pe acelasi jurnal ------------------------------------------------------------------------ // fiecare scriitor e un proces care adauga in bucla stransa prin modul (o pornire de proces pe inregistrare ar lasa fereastra de // cursa prea rara ca sa se vada: masurat, controlul fara lacat iesea verde asa); la prima adaugare refuzata scriitorul iese cu 1 const logC = path.join(T, 'concurent.log'); const N = 150; const scriitor = (id) => new Promise((rez) => { const cod = `const s = await import(${JSON.stringify(pathToFileURL(SIDE).href)}); for (let i = 0; i < ${N}; i++) { try { s.adaugaPlicuri(${JSON.stringify(logC)}, [{ v: 1, kind: 'proba-concurenta', scriitor: '${id}', i }]); } catch { process.exitCode = 1; break; } }`; const p = spawn(process.execPath, ['--input-type=module', '-e', cod], { stdio: 'ignore' }); p.on('exit', (c) => rez(c)); }); const coduri = await Promise.all([scriitor('a'), scriitor('b')]); const vC = verificaJurnal(fs.readFileSync(logC, 'utf8').split('\n').filter((l) => l.trim()).map((l) => { try { return JSON.parse(l); } catch { return { necitibil: true }; } })); cer(`C: doi scriitori concurenti x ${N} -> lant INTACT cu ${2 * N} intrari, niciun scriitor refuzat`, coduri.every((c) => c === 0) && vC.ok && vC.count === 2 * N && !fs.existsSync(logC + '.lock')); // record --kind proof: leaga plicuri AIP-23 gata facute (provenienta agentilor AI in jurnalul inviolabil); cu Proof-of-AI si // decizia de agent cand modulele lor sunt alaturi (depozitul de dezvoltare), altfel cu doua plicuri proof-kinds const log2 = path.join(T, 'agent.log'); const poaiP = path.resolve(AICI, '..', 'proof-of-ai', 'proof-of-ai.mjs'); const polP = path.resolve(AICI, '..', 'agent-policy', 'agent-policy.mjs'); let plic1, plic2; if (fs.existsSync(poaiP) && fs.existsSync(polP)) { const { buildProofOfAi } = await import(pathToFileURL(poaiP).href); const { definePolicy, checkAction, decisionEnvelope } = await import(pathToFileURL(polP).href); plic1 = buildProofOfAi({ model: { name: 'claude', version: 'opus-4.8' }, prompt: 'rezuma', output: 'rezumat', tools: ['search'], agentId: 'agent-7', createdAt: '2026-09-26T09:10:00Z' }); const { policy, policyHash } = definePolicy({ agentId: 'agent-7', spend: { amount: '100', windowSeconds: 3600, asset: 'AERE' }, tools: ['search'], recipients: null }); const act = { kind: 'payment', to: '0x1', amount: '50', at: 1 }; plic2 = decisionEnvelope({ policyHash, action: act, decision: checkAction(policy, act, []), createdAt: '2026-09-26T09:11:00Z' }); } else { plic1 = pk.buildProof('execution', { program: 'agent-7', input: 'rezuma', output: 'rezumat', createdAt: '2026-09-26T09:10:00Z' }); plic2 = pk.buildProof('identity', { subjectId: 'agent-7', publicKey: 'cheie-publica-de-proba', createdAt: '2026-09-26T09:11:00Z' }); } const p1F = path.join(T, 'p1.json'); fs.writeFileSync(p1F, JSON.stringify(plic1)); const p2F = path.join(T, 'p2.json'); fs.writeFileSync(p2F, JSON.stringify(plic2)); cer('record --kind proof (primul plic) -> seq 0', side(['record', '--kind', 'proof', '--proof-file', p1F, '--host', 'agent', '--log', log2, '--at', '2026-09-26T09:10:00Z']).out.includes('seq=0')); cer('record --kind proof (al doilea plic) -> seq 1', side(['record', '--kind', 'proof', '--proof-file', p2F, '--host', 'agent', '--log', log2, '--at', '2026-09-26T09:11:00Z']).out.includes('seq=1')); cer('lantul de provenienta al agentului e INTACT', side(['verify-log', '--log', log2]).cod === 0); // CONTROL NEGATIV 5: plic manipulat (statementHash nu se potriveste) -> record refuzat const rauP = JSON.parse(JSON.stringify(plic1)); rauP.statement.createdAt = '2030-01-01T00:00:00Z'; const rauPF = path.join(T, 'p-rau.json'); fs.writeFileSync(rauPF, JSON.stringify(rauP)); cer('CONTROL: record --kind proof cu plic manipulat -> refuzat', side(['record', '--kind', 'proof', '--proof-file', rauPF, '--log', log2, '--at', '2026-09-26T09:12:00Z']).cod !== 0); // ---- adaptorul de runtime (scan docker | kubernetes) ---------------------------------------------------------------------- const SECRET = 'AERE-SINTETIC-supersecret-XYZ-123'; const PAROLA = 'AERE-SINTETIC-hunter2-parola'; const dockerExport = [ { Name: '/app-api', Image: 'sha256:' + 'ab'.repeat(32), Path: '/usr/local/bin/api', Args: ['--data-path=/var/lib/api', `--db-password=${PAROLA}`], Config: { Image: 'example/api:3', Env: [`SECRET_TOKEN=${SECRET}`, 'JAVA_OPTS=-Xmx4g', 'PATH=/usr/bin'] }, Mounts: [{ Source: '/root/chei', Destination: '/var/lib/api' }], State: { Running: true, StartedAt: '2026-09-27T00:00:00Z' }, RestartCount: 0 }, { Name: '/sidecar-ntp', Image: 'sha256:' + 'cd'.repeat(32), Path: 'chronyd', Args: [], Config: { Image: 'ntp:4', Env: [] }, Mounts: [], State: { Running: true, StartedAt: '2026-09-27T00:01:00Z' }, RestartCount: 2 }, { Name: '/oprit', Image: 'sha256:' + 'ef'.repeat(32), Path: 'x', Args: [], Config: { Image: 'x:1', Env: [] }, Mounts: [], State: { Running: false } }, ]; const dExp = path.join(T, 'docker.json'); fs.writeFileSync(dExp, JSON.stringify(dockerExport)); const log3 = path.join(T, 'runtime.log'); const des3 = log3 + '.descriptori.jsonl'; const s1 = side(['scan', '--source', 'docker', '--input', dExp, '--host', 'gazda-1', '--log', log3, '--at', '2026-09-27T01:00:00Z']); cer('scan docker -> 2 plicuri (numai containerele care ruleaza; cel oprit NU)', s1.cod === 0 && linii(log3).length === 2 && !fs.readFileSync(des3, 'utf8').includes('oprit')); cer('scan: jurnalul de runtime e INTACT', side(['verify-log', '--log', log3]).cod === 0); if (areVerificator) { let valide3 = true; for (const e of linii(log3)) { const f = path.join(T, `r${e.seq}.json`); fs.writeFileSync(f, JSON.stringify(e.proof)); if (!valid(f)) valide3 = false; } cer('scan: fiecare plic de runtime e VALID pentru verificatorul AIP-23 comun', valide3); } else sari('scan: fiecare plic de runtime e VALID pentru verificatorul AIP-23 comun'); const textLog = fs.readFileSync(log3, 'utf8'); const textDes = fs.readFileSync(des3, 'utf8'); cer('CONFIDENTIALITATE: valoarea de mediu si parola din argumente NU apar nici in jurnal, nici in descriptori', !textLog.includes(SECRET) && !textDes.includes(SECRET) && !textLog.includes(PAROLA) && !textDes.includes(PAROLA)); cer('control pozitiv al confidentialitatii: NUMELE variabilei (SECRET_TOKEN) e in descriptor', textDes.includes('SECRET_TOKEN') && !textDes.includes('/root/chei')); const desLinii = textDes.split('\n').filter((l) => l.trim()).map((l) => JSON.parse(l)); const shaDe = (o) => '0x' + crypto.createHash('sha256').update(Buffer.from(JSON.stringify(o, Object.keys(o).sort()), 'utf8')).digest('hex'); cer('descriptorii re-hashati = digestul din plicurile lantului', desLinii.length === 2 && desLinii.every((d) => shaDe(d.descriptor) === d.sha256 && linii(log3)[d.seq].proof.statement.sha256 === d.sha256)); // CONTROL NEGATIV 6: un descriptor schimbat dupa inregistrare nu mai are digestul din lant const falsificat = { ...desLinii[0].descriptor, imageId: 'sha256:' + '00'.repeat(32) }; cer('CONTROL: descriptor falsificat -> digest diferit de cel din lant', shaDe(falsificat) !== linii(log3)[0].proof.statement.sha256); // kubernetes: un pod cu doua containere, unul ruleaza, unul asteapta const k8s = { items: [{ metadata: { namespace: 'prod', name: 'api-7f' }, spec: { containers: [{ name: 'api', image: 'api:3', command: ['node'], args: ['srv.js', `--token=${PAROLA}`], env: [{ name: 'DB_PASS', value: SECRET }], volumeMounts: [{ mountPath: '/data' }] }, { name: 'init-side', image: 'side:1' }] }, status: { containerStatuses: [{ name: 'api', image: 'api:3', imageID: 'docker-pullable://api@sha256:' + '12'.repeat(32), restartCount: 1, state: { running: { startedAt: '2026-09-27T00:05:00Z' } } }, { name: 'init-side', image: 'side:1', imageID: '', restartCount: 0, state: { waiting: { reason: 'PodInitializing' } } }] } }] }; const kExp = path.join(T, 'k8s.json'); fs.writeFileSync(kExp, JSON.stringify(k8s)); const log4 = path.join(T, 'k8s.log'); const s2 = side(['scan', '--source', 'kubernetes', '--input', kExp, '--host', 'cluster-a', '--log', log4, '--at', '2026-09-27T01:01:00Z']); const t4 = fs.readFileSync(log4 + '.descriptori.jsonl', 'utf8'); cer('scan kubernetes -> 1 plic (containerul care asteapta NU), fara valori, cu numele DB_PASS', s2.cod === 0 && linii(log4).length === 1 && !t4.includes(SECRET) && !t4.includes(PAROLA) && t4.includes('DB_PASS')); // CONTROL NEGATIV 7: un export fara niciun container care ruleaza nu se inregistreaza ca "zero desfasurari" const gol = path.join(T, 'gol.json'); fs.writeFileSync(gol, JSON.stringify([dockerExport[2]])); cer('CONTROL: export fara containere care ruleaza -> refuzat (cod 2), nimic scris', side(['scan', '--source', 'docker', '--input', gol, '--log', path.join(T, 'gol.log')]).cod === 2 && !fs.existsSync(path.join(T, 'gol.log'))); // CONTROL NEGATIV 8: forma gresita (un obiect in loc de tabloul lui docker inspect) -> refuzat const rau = path.join(T, 'rau.json'); fs.writeFileSync(rau, JSON.stringify({ items: [] })); cer('CONTROL: intrare care nu e docker inspect -> refuzata (cod 2)', side(['scan', '--source', 'docker', '--input', rau, '--log', path.join(T, 'rau.log')]).cod === 2); } catch (e) { console.log(` [RAU ] proba a cazut: ${e.message}`); rele++; } finally { fs.rmSync(T, { recursive: true, force: true }); } console.log(`\nB3 sidecar (verify layer): ${bune}/${bune + rele + sarite} cum trebuia${sarite ? `, ${sarite} SARITE` : ''} (sidecar: ${SIDE})`); process.exitCode = rele ? 1 : sarite ? 2 : 0;