89 lines
9.6 KiB
JavaScript
89 lines
9.6 KiB
JavaScript
// Controlul negativ al probelor agentilor (politica, registrul, aprobarea si revocarea, linia de comanda): fiecare paznic se strica intr-o COPIE a
|
|
// dosarului, proba lui ruleaza pe copie si TREBUIE sa iasa rosie, cu probele chiar rulate (rezumatul lor exista); pe copia neatinsa,
|
|
// verde. O plantare al carei tipar nu apare exact o data e un esec al controlului, nu o linie informativa. Trei stari: o proba care nu
|
|
// ajunge la rezumat e STRICAT, si se numara esec (rosul ei nu masoara nimic).
|
|
// 2026-09-29 (B-17): acopera si paznicii noi - argumentele in aprobare, hash-ul politicii recalculat, antedatarea (la scriere si fata
|
|
// de ancore), ancora altei ramuri, sesiunea derivata, dovada de echivocare, reluarea registrului, campul necunoscut, activul, actionHash canonic.
|
|
// node control-negativ-aprobare.mjs iesire 0 = martorii verzi si toate plantarile rosii
|
|
// Proba politicii foloseste verificatorul AIP-23 (AERE_VERIFY_PROOF sau ../aere-proof-protocol/verify.mjs); fara el, martorul ei iese
|
|
// cu 2 (partea AIP-23 NEMASURATA) si se accepta ca martor numai asa, cu zero RAU.
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { spawn } from 'node:child_process';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const AICI = path.dirname(fileURLToPath(import.meta.url));
|
|
const DEV_VERIFY = path.resolve(AICI, '..', 'aere-proof-protocol', 'verify.mjs');
|
|
const VERIFY = process.env.AERE_VERIFY_PROOF || (fs.existsSync(DEV_VERIFY) ? DEV_VERIFY : '');
|
|
const P = { pol: 'proba-agent-policy.mjs', reg: 'proba-agent-ledger.mjs', apr: 'proba-agent-aprobare.mjs', cli: 'proba-agent-cli.mjs' };
|
|
const PLANTARI = [
|
|
// [nume, fisier, tipar, inlocuire, proba]
|
|
['nonce-ul unei aprobari nu mai e unic', 'agent-ledger.mjs', "if (ap && folosite.has(String(ap.nonce))) { respinse.push('nonce already used'); continue; }", '', P.apr],
|
|
['un aprobator nenumit in politica numara', 'agent-aprobare.mjs', 'if (!policy.approval.approvers.includes(humanId)) return', 'if (false) return', P.apr],
|
|
['semnatura aprobarii nu se mai verifica', 'agent-aprobare.mjs', "if (!sig) return { ok: false, error: 'the approval signature does not verify' };", '', P.apr],
|
|
['aprobarea devine scutire de limita', 'agent-policy.mjs', 'if (!d.allowed || !needsApproval(policy, action)) return d;', 'if (needsApproval(policy, action) && (ctx.approvers || []).length >= policy.approval.threshold) return { allowed: true, reason: "approved (exempt)" }; if (!d.allowed || !needsApproval(policy, action)) return d;', P.apr],
|
|
['verificatorul ignora revocarile date separat', 'agent-ledger.mjs', 'let revokedAt = ext.revokedAt;', 'let revokedAt = null;', P.apr],
|
|
['revocarea nu mai cere proprietarul', 'agent-aprobare.mjs', "if (humanIdFromKey(k) !== policy.owner) return { ok: false, error: 'the revocation is not signed by the owner named in the policy' };", '', P.apr],
|
|
['aprobarea nu mai e legata de continutul actiunii', 'agent-aprobare.mjs', 'if (ap.actionHash !== actionHash(action)) return', 'if (false) return', P.apr],
|
|
['argumentele uneltei nu mai intra in continutul aprobat', 'agent-aprobare.mjs', 'if (a.args !== undefined) c.argsHash = sha(canonical(a.args));', '', P.apr],
|
|
['verificatorul crede hash-ul dat in loc sa il recalculeze', 'agent-ledger.mjs', 'if (!(hashFixat == null || String(hashFixat).toLowerCase() === policyHash /*PAZA:politica*/)) return', 'if (hashFixat != null) policyHash = String(hashFixat).toLowerCase(); if (false) return', P.reg],
|
|
['registrul accepta la scriere o intrare antedatata', 'agent-ledger.mjs', 'if (Number(at) < acum - TOLERANTA_S) throw', 'if (false) throw', P.reg],
|
|
['ancora nu mai margineste timpul de jos', 'agent-ledger.mjs', 'if (!(!(i > a.seq && atI < a.at - tol) /*PAZA:timp*/)) return', 'if (false) return', P.reg],
|
|
['ancora nu mai trebuie sa numeasca o intrare a registrului', 'agent-ledger.mjs', 'if (!(!!ledger.entries[s] && !!a && ledger.entries[s].hash === a.hash /*PAZA:ancora*/)) return', 'if (false) return', P.reg],
|
|
['verificatorul nu mai cere sesiunea registrului in intrare', 'agent-ledger.mjs', ' && b.session === ledger.session /*PAZA:corp*/)) return', ' /*PAZA:corp*/)) return', P.reg],
|
|
['sesiunea registrului nu mai trebuie sa fie cea derivata', 'agent-ledger.mjs', 'if (!(ledger.session === sessionId(ledger.agentId, policyHash) /*PAZA:sesiune*/)) return', 'if (false) return', P.reg],
|
|
['dovada de echivocare fara semnaturile agentului', 'agent-ledger.mjs', "if (!intrareSemnata(e, pub)) return { ok: false, error: 'an entry is not signed by the agent' };", '', P.reg],
|
|
['un registru atins se poate relua', 'agent-ledger.mjs', 'if (!v.ok) throw new Error(`agent-ledger: the ledger to resume does not verify', 'if (false) throw new Error(`agent-ledger: the ledger to resume does not verify', P.reg],
|
|
['reluarea uita nonce-urile folosite', 'agent-ledger.mjs', 'for (const e of entries) for (const n of nonceuri(e.body.approvals)) folosite.add(n);', '', P.apr],
|
|
['reluarea uita revocarea', 'agent-ledger.mjs', 'JSON.parse(JSON.stringify(ledger.entries)), v.revocations.revokedAt);', 'JSON.parse(JSON.stringify(ledger.entries)), null);', P.apr],
|
|
['linia de comanda suprascrie o cheie existenta', 'agent-cli.mjs', 'if (fs.existsSync(cheie)) throw new Folosire(', 'if (false) throw new Folosire(', P.cli],
|
|
['linia de comanda lasa pe oricine sa aprobe', 'agent-cli.mjs', 'if (!policy.approval || !policy.approval.approvers.includes(human.humanId)) throw', 'if (false) throw', P.cli],
|
|
['verify din linia de comanda uita hash-ul numit in fisierul politicii', 'agent-cli.mjs', "const pinned = get('--policy-hash') ?? j.policyHash;", "const pinned = get('--policy-hash');", P.cli],
|
|
['record deschide mereu un registru nou in loc sa il reia', 'agent-cli.mjs', 'const L = fs.existsSync(lf) ? resumeLedger(', 'const L = false ? resumeLedger(', P.cli],
|
|
['o plata din alt portofel decat cel numit trece', 'agent-policy.mjs', 'if (policy.wallet && String(action.from', 'if (false && String(action.from', P.pol],
|
|
['politica cu un camp necunoscut e primita', 'agent-policy.mjs', 'if (necunoscute.length) throw', 'if (false) throw', P.pol],
|
|
['o plata in alt activ trece pe langa limita', 'agent-policy.mjs', 'if (action.asset != null && String(action.asset) !== policy.spend.asset) return', 'if (false) return', P.pol],
|
|
['actionHash-ul plicului nu mai e canonic', 'agent-policy.mjs', "actionHash: sha256(Buffer.from(canonical(action), 'utf8')),", "actionHash: sha256(Buffer.from(JSON.stringify(action), 'utf8')),", P.pol],
|
|
];
|
|
const FISIERE = ['agent-policy.mjs', 'agent-ledger.mjs', 'agent-aprobare.mjs', 'agent-cli.mjs', P.pol, P.reg, P.apr, P.cli];
|
|
function copie() { const t = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-b2-ctl-')); for (const f of FISIERE) fs.copyFileSync(path.join(AICI, f), path.join(t, f)); return t; }
|
|
// 2026-09-29: probele ruleaza cate PARALEL deodata (asincron), ca tot controlul sa incapa in termenul rulatorului comun
|
|
const PARALEL = 4;
|
|
function ruleaza(t, proba) {
|
|
const env = { ...process.env }; if (VERIFY) env.AERE_VERIFY_PROOF = VERIFY; else delete env.AERE_VERIFY_PROOF;
|
|
return new Promise((resolve) => {
|
|
const c = spawn(process.execPath, [path.join(t, proba)], { env }); let out = '';
|
|
const ceas = setTimeout(() => c.kill(), 240000);
|
|
c.stdout.on('data', (x) => { out += x; }); c.stderr.on('data', (x) => { out += x; });
|
|
c.on('close', (cod) => { clearTimeout(ceas); const m = /agent-(policy|ledger|aprobare|cli): (\d+)\/(\d+)/.exec(out);
|
|
resolve({ cod, rulat: !!m, rele: (out.match(/^\s*(RAU\s|\[RAU)/gm) || []).length }); });
|
|
});
|
|
}
|
|
async function inGrup(lucrari) { const rez = new Array(lucrari.length); let i = 0;
|
|
await Promise.all(Array.from({ length: PARALEL }, async () => { while (i < lucrari.length) { const k = i++; rez[k] = await lucrari[k](); } })); return rez; }
|
|
let esecuri = 0;
|
|
const martori = await inGrup(Object.values(P).map((proba) => async () => { const t0 = copie(); try { return [proba, await ruleaza(t0, proba)]; } finally { fs.rmSync(t0, { recursive: true, force: true }); } }));
|
|
for (const [proba, m0] of martori) {
|
|
const verde = m0.rulat && m0.rele === 0 && (m0.cod === 0 || (m0.cod === 2 && proba === P.pol && !VERIFY));
|
|
if (verde) console.log(` OK martorul ${proba}: copia neatinsa verde${m0.cod === 2 ? ' (fara verificatorul AIP-23: partea lui NEMASURATA)' : ''}`);
|
|
else { esecuri++; console.log(` RAU martorul ${proba} nu e verde (cod ${m0.cod}, ${m0.rulat ? m0.rele + ' RAU' : 'nu a ajuns la rezumat'})`); }
|
|
}
|
|
const linii = await inGrup(PLANTARI.map((pl) => async () => {
|
|
const [nume, f, din, inl, proba] = pl; let t = null;
|
|
try {
|
|
// o plantare scrisa gresit (fisier, tipar) e un esec al controlului, nu o cadere a lui
|
|
if (!FISIERE.includes(f) || typeof din !== 'string' || !proba) return [false, ` RAU ${nume}: plantarea e scrisa gresit (fisier ${f}, proba ${proba})`];
|
|
t = copie(); const fp = path.join(t, f); const src = fs.readFileSync(fp, 'utf8');
|
|
if (src.split(din).length !== 2) return [false, ` RAU ${nume}: tiparul nu apare exact o data in ${f}`];
|
|
fs.writeFileSync(fp, src.replace(din, inl));
|
|
const r = await ruleaza(t, proba);
|
|
if (r.rulat && r.cod !== 0 && r.rele > 0) return [true, ` OK ${nume}: ${proba} ROSIE (${r.rele} RAU)`];
|
|
return [false, ` RAU ${nume}: ${r.rulat ? `${proba} a ramas verde` : `${proba} nu a ajuns la rezumat (STRICAT)`} (cod ${r.cod})`];
|
|
} catch (e) { return [false, ` RAU ${nume}: controlul a cazut pe ea (${String(e.message).slice(0, 80)})`]; }
|
|
finally { if (t) fs.rmSync(t, { recursive: true, force: true }); }
|
|
}));
|
|
for (const [bun, l] of linii) { console.log(l); if (!bun) esecuri++; }
|
|
console.log(esecuri ? `RAU: ${esecuri} esecuri ale controlului` : `DOVEDIT: martorii verzi, ${PLANTARI.length} din ${PLANTARI.length} plantari rosii`);
|
|
process.exitCode = esecuri ? 1 : 0;
|