// control-negativ-sidecar.mjs (2026-09-29, revizuirea adversariala, pista B): controlul negativ al verificarilor R, C si N din // proba-sidecar.mjs. Trei stari pe caz: PRINS (proba a ajuns la capat si verificarile numite sunt rosii), SCAPAT, STRICAT (copia nu // s-a incarcat, ancora nu apare exact o data, sau proba nu a ajuns la capat). // V0 sidecar-ul de dinainte, din git (sarit si spus daca revizia nu e in istoric) -> R CONTROL x3, N, N CONTROL, C rosii // P1 lacatul scos (scriitorii nu se mai asteapta) -> C rosu // P2 capul atestat nu se mai compara cu intrarea count-1 -> R CONTROL (istoria rescrisa) rosu // P3 lungimea jurnalului nu se mai compara cu count -> R CONTROL (intrari scoase) rosu // P4 statementHash-ul capului atestat nu se mai reface -> R CONTROL (cap manipulat) rosu // P5 un rand care nu e JSON arunca din nou (cadere) -> N rosu // P6 --attested nu se mai cere digest -> N CONTROL rosu // P7 semnatura capului nu se mai verifica (punctul 34, 2026-09-29) -> S CONTROL (semnatura stricata) rosu // P8 semnatarul capului nu se mai compara cu --signer -> S CONTROL (alta cheie) rosu // P9 un cap nesemnat trece cand --signer il cere semnat -> S CONTROL (nesemnat) rosu // P10 o cheie clasica sub schema ml-dsa-65 trece (2026-10-03) -> S CONTROL (cheie clasica) rosu // B15 (2026-10-18), adaptoarele pe furnizori de cloud, masurate cu proba-adaptoare-cloud.mjs pe aceeasi copie plantata, trei // plantari pe adaptor (9 adaptoare, 27 de plantari): // PR- filtrul "ruleaza" scos: incarcatura OPRITA intra in jurnal -> ": incarcatura OPRITA" rosu // PN- numele variabilelor inlocuite cu perechile nume=valoare: valoarea ajunge in jurnal -> ": CONFIDENTIALITATE" rosu // PF- validarea formei scoasa: exportul altui CLI nu mai e refuzat PE NUME -> ": CONTROL: export de alt CLI" rosu // Plantarile cloud ruleaza fara verificatorul AIP-23 (AERE_VERIFY_PROOF pe o cale inexistenta): verificarea de VALID nu e tinta lor si // ar dubla timpul; proba iese tot 1 cand are o verificare rosie. // Copiile stau LANGA original (importul lui proof-kinds e relativ) si se sterg; originalul trebuie sa ramana octet cu octet. // node control-negativ-sidecar.mjs -> 0 toate prinse, 1 una scapata, 2 STRICAT import crypto from 'node:crypto'; import fs from 'node:fs'; import path from 'node:path'; import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const AICI = path.dirname(fileURLToPath(import.meta.url)); const SRC = path.join(AICI, 'sidecar.mjs'); const PROBA = path.join(AICI, 'proba-sidecar.mjs'); const PROBA_CLOUD = path.join(AICI, 'proba-adaptoare-cloud.mjs'); const REV = process.env.AERE_SIDECAR_REV_VECHE || 'b2b08a5e'; const sha = (f) => crypto.createHash('sha256').update(fs.readFileSync(f)).digest('hex'); const inainte = sha(SRC); let prinse = 0, stricate = 0, total = 0; const T = { refacut: 'R CONTROL: acelasi lant refacut fata de capul atestat', taiat: 'R CONTROL: jurnal taiat sub capul atestat', capMan: 'R CONTROL: cap atestat manipulat', necitibil: 'N: rand care nu e JSON', digest: 'N CONTROL: --attested care nu e digest', concurent: 'C: doi scriitori concurenti', semnStricata: 'S CONTROL: o semnatura de cap stricata', altaCheie: 'S CONTROL: capul semnat de alta cheie', nesemnat: 'S CONTROL: un cap nesemnat', cheieClasica: 'S CONTROL: un cap semnat cu o cheie clasica', }; const SUMAR = { [PROBA]: /B3 sidecar \(verify layer\): \d+\/\d+ cum trebuia/, [PROBA_CLOUD]: /B15 adaptoare cloud: \d+\/\d+ cum trebuia/ }; function caz(id, text, tinte, proba = PROBA, env = {}) { total++; const copie = path.join(AICI, `.plantat-sidecar-${id}.mjs`); try { fs.writeFileSync(copie, text); const r = spawnSync(process.execPath, [proba], { encoding: 'utf8', timeout: 400000, env: { ...process.env, AERE_SIDECAR_MODUL: copie, ...env } }); const out = (r.stdout || '') + (r.stderr || ''); if (!SUMAR[proba].test(out) || !out.includes(copie)) { stricate++; console.log(` STRICAT ${id}: proba nu a ajuns la capat pe copie (${out.trim().split('\n').pop()?.slice(0, 140)})`); return; } const rosii = out.split('\n').filter((l) => l.includes('[RAU ]')); const lipsa = tinte.filter((x) => !rosii.some((l) => l.includes(x))); if (r.status === 1 && !lipsa.length) { prinse++; console.log(` PRINS ${id}: ${rosii.length} verificari rosii, intre ele: ${tinte.map((t) => t.split(':')[0]).join(', ')}`); } else console.log(` SCAPAT ${id}: cod ${r.status}, au ramas verzi: ${lipsa.join(' | ') || '-'}`); } finally { fs.rmSync(copie, { force: true }); fs.rmSync(copie + '.lock', { force: true }); } } const planta = (t, a, b) => (t.split(a).length === 2 ? t.replace(a, () => b) : null); const CALE = `${REV}:tools/aere-verify-layer/sidecar.mjs`; if (spawnSync('git', ['cat-file', '-e', CALE], { cwd: AICI }).status !== 0) console.log(` SARIT V0: revizia ${REV} nu e in istoricul acestui depozit; NEMASURAT aici`); else { const g = spawnSync('git', ['-c', 'core.autocrlf=false', 'show', CALE], { cwd: AICI, encoding: 'utf8' }); if (g.status !== 0 || g.stdout.includes('verificaFataDeCap')) { stricate++; total++; console.log(' STRICAT V0: revizia veche nu se citeste sau are deja reparatia'); } else caz('V0', g.stdout, [T.refacut, T.taiat, T.capMan, T.necitibil, T.digest]); } const nou = fs.readFileSync(SRC, 'utf8'); const NU = "process.env.AERE_PLANTA_NICIODATA === 'da'"; const P = [ ['P1', " try { return fn(); } finally { try { fs.unlinkSync(lacat); } catch { /* deja ridicat */ } }\n}", ` try { return fn(); } finally { try { fs.unlinkSync(lacat); } catch { /* deja ridicat */ } }\n}\nconst cuLacatAdevarat = cuLacat;\ncuLacat = (p, fn) => (${NU} ? cuLacatAdevarat(p, fn) : fn());`, [T.concurent]], ['P2', 'if (!(h === String(st.head).toLowerCase() /*PAZA:cap-rescris*/)) return', `if (!(h === String(st.head).toLowerCase() /*PAZA:cap-rescris*/) && ${NU}) return`, [T.refacut]], ['P3', 'if (!(intrari.length >= st.count /*PAZA:cap-scurt*/)) return', `if (!(intrari.length >= st.count /*PAZA:cap-scurt*/) && ${NU}) return`, [T.taiat]], ['P4', "=== cap.statementHash.toLowerCase() /*PAZA:cap-hash*/)) return", `=== cap.statementHash.toLowerCase() /*PAZA:cap-hash*/) && ${NU}) return`, [T.capMan]], ['P5', "try { return JSON.parse(l); } catch { return { necitibil: true }; }", `try { return JSON.parse(l); } catch (e) { if (${NU}) return { necitibil: true }; throw e; }`, [T.necitibil]], ['P6', 'if (!eDigest(attested)) {', `if (!eDigest(attested) && ${NU}) {`, [T.digest]], ['P7', 'if (!(ok /*PAZA:cap-semnatura*/)) return', `if (!(ok /*PAZA:cap-semnatura*/) && ${NU}) return`, [T.semnStricata]], ['P8', 'if (!(asteptat !== null && idCheie(asteptat) === id /*PAZA:cap-semnatar*/)) return', `if (!(asteptat !== null && idCheie(asteptat) === id /*PAZA:cap-semnatar*/) && ${NU}) return`, [T.altaCheie]], ['P9', 'if (!(!semnatarPem /*PAZA:cap-nesemnat*/)) return', `if (!(!semnatarPem /*PAZA:cap-nesemnat*/) && ${NU}) return`, [T.nesemnat]], ['P10', "if (!(!!pub && pub.asymmetricKeyType === 'ml-dsa-65' /*PAZA:cap-schema*/)) return", `if (!(!!pub && pub.asymmetricKeyType === 'ml-dsa-65' /*PAZA:cap-schema*/) && ${NU}) return`, [T.cheieClasica]], ]; for (const [id, a, b, tinte] of P) { let t = planta(nou, a, b); // P1 cere ca `cuLacat` sa poata fi reasignat: declaratia de functie devine o variabila if (t && id === 'P1') t = planta(t, 'function cuLacat(p, fn) {', 'let cuLacat = function (p, fn) {'); if (!t) { stricate++; total++; console.log(` STRICAT ${id}: ancora nu apare exact o data`); continue; } caz(id, t, tinte); } // ---- B15: adaptoarele pe furnizori de cloud --------------------------------------------------------------------------------------- // scurgerea de nume -> valori: ancora e chiar apelul care reduce lista la nume, inlocuit cu lista intreaga (perechi nume/valoare) const SCURGERI = { 'aws-ecs': ['numeDin([...(cd?.environment || []), ...(cd?.secrets || []), ...(over?.environment || [])] /*NUME:aws-ecs*/)', '[...(cd?.environment || []), ...(over?.environment || [])]'], 'aws-ec2': ["numeDin(i.Tags, 'Key' /*NUME:aws-ec2*/)", '(i.Tags || [])'], 'aws-lambda': ['Object.keys(f.Environment?.Variables || {}).sort() /*NUME:aws-lambda*/', '(f.Environment?.Variables || {})'], 'azure-vm': ['tagKeys: Object.keys(v.tags || {}).sort() /*NUME:azure-vm*/,', 'tagKeys: v.tags || {}, customData: v.osProfile?.customData || null,'], 'azure-container': ['numeDin(c.environmentVariables /*NUME:azure-container*/)', '(c.environmentVariables || [])'], 'azure-containerapp': ['numeDin(c.env /*NUME:azure-containerapp*/)', '(c.env || [])'], 'azure-webapp': ['numeDin(sc?.appSettings /*NUME:azure-webapp*/)', '(sc?.appSettings || [])'], 'gcp-compute': ["numeDin(i.metadata?.items, 'key' /*NUME:gcp-compute*/)", '(i.metadata?.items || [])'], 'gcp-run': ['numeDin(c.env /*NUME:gcp-run*/)', '(c.env || [])'], }; const FARA_VERIFICATOR = { AERE_VERIFY_PROOF: path.join(AICI, 'nu-exista-verify-proof.mjs') }; for (const src of Object.keys(SCURGERI)) { const PC = [ [`PR-${src}`, `/*RULEAZA:${src}*/))`, `/*RULEAZA:${src}*/) && ${NU})`, [`${src}: incarcatura OPRITA`]], [`PN-${src}`, SCURGERI[src][0], SCURGERI[src][1], [`${src}: CONFIDENTIALITATE`]], [`PF-${src}`, `/*FORMA:${src}*/))`, `/*FORMA:${src}*/) && ${NU})`, [`${src}: CONTROL: export de alt CLI`]], ]; for (const [id, a, b, tinte] of PC) { let t = planta(nou, a, b); // ECS filtreaza pe doua niveluri (task si container), si un task STOPPED are si containerele STOPPED: filtrul scos inseamna // amandoua (masurat 2026-10-18: cu numai nivelul de task scos, plantarea era mascata de cel de container si SCAPA) if (t && id === 'PR-aws-ecs') t = planta(t, '/*RULEAZA:aws-ecs-container*/))', `/*RULEAZA:aws-ecs-container*/) && ${NU})`); if (!t) { stricate++; total++; console.log(` STRICAT ${id}: ancora nu apare exact o data`); continue; } caz(id, t, tinte, PROBA_CLOUD, FARA_VERIFICATOR); } } const ramase = fs.readdirSync(AICI).filter((f) => f.startsWith('.plantat-sidecar-')); if (sha(SRC) !== inainte || ramase.length) { stricate++; console.log(' STRICAT originalul s-a schimbat sau au ramas copii: ' + ramase.join(',')); } console.log(`\ncontrolul negativ al sidecar-ului: prinse ${prinse}/${total}, stricate ${stricate}`); process.exitCode = stricate ? 2 : prinse === total ? 0 : 1;