Every artifact's SHA-256 and size, the SBOM's digest, the git commit and tree of the source, a hybrid signature (classical + ML-DSA, both required) and, when notarized, a first-seen time on Aere Network covered by the validators' post-quantum certificate. verify recomputes everything from the files; --rebuild-from repeats an npm pack build from a clone the verifier chose; the SBOM is re-derived from the committed package-lock.json or, new in 1.4.0, from the committed go.mod and go.sum; --signer requires the signing keys you expect (1.4.0); a developer credential binds the keys to a person, judged against a trust root you choose. The builder's declared date can only accuse, never acquit (1.4.0). Includes the GitHub Action and the hybrid signature library it uses. Laid out as in the development repository (tools/proof-of-software/, sdk-pq-sign/, sdk/) so that nothing is rewritten for publication. Tests and negative controls measured on 2026-09-29 are listed in README.md.
47 lines
3.4 KiB
JavaScript
47 lines
3.4 KiB
JavaScript
// Mecanismul comun al controalelor negative ale Proof of Software 1.3.0: o COPIE (pos.mjs + suita + sdk-pq-sign + sdk, intr-un dosar
|
|
// temporar; depozitul nu se atinge), un paznic scos in pos.mjs-ul copiei, suita rulata pe copie. Martorul (copia neatinsa) trebuie
|
|
// verde; fiecare plantare trebuie sa inroseasca EXACT proba numita, cu suita chiar rulata (rezumatul ei exista). O ancora care nu
|
|
// apare exact o data e un esec al controlului (STRICAT), nu o linie informativa.
|
|
import fs from 'node:fs';
|
|
import os from 'node:os';
|
|
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 RAD = path.resolve(AICI, '..', '..', '..');
|
|
|
|
export function controleaza(suita, plantari) {
|
|
function copie() {
|
|
const t = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-pos-cn-'));
|
|
for (const d of ['sdk-pq-sign', 'sdk']) fs.cpSync(path.join(RAD, d), path.join(t, d), { recursive: true });
|
|
fs.mkdirSync(path.join(t, 'tools', 'proof-of-software', 'test'), { recursive: true });
|
|
fs.copyFileSync(path.join(RAD, 'tools', 'proof-of-software', 'pos.mjs'), path.join(t, 'tools', 'proof-of-software', 'pos.mjs'));
|
|
fs.copyFileSync(path.join(AICI, suita), path.join(t, 'tools', 'proof-of-software', 'test', suita));
|
|
// 1.4.0: fixturile suitelor (dosarele fixturi-*, de ex. go.mod/go.sum reale) merg si ele in copie
|
|
for (const d of fs.readdirSync(AICI).filter((n) => n.startsWith('fixturi') && fs.statSync(path.join(AICI, n)).isDirectory())) {
|
|
fs.cpSync(path.join(AICI, d), path.join(t, 'tools', 'proof-of-software', 'test', d), { recursive: true });
|
|
}
|
|
return t;
|
|
}
|
|
function ruleaza(t) {
|
|
const r = spawnSync(process.execPath, [path.join(t, 'tools', 'proof-of-software', 'test', suita)], { encoding: 'utf8', timeout: 400000 });
|
|
const out = (r.stdout || '') + (r.stderr || '');
|
|
return { cod: r.status, rulat: /\d+ treceri, \d+ esecuri/.test(out), picate: out.split('\n').filter((l) => l.startsWith(' ESEC')) };
|
|
}
|
|
let rele = 0;
|
|
const t0 = copie(); const m = ruleaza(t0); fs.rmSync(t0, { recursive: true, force: true });
|
|
if (m.cod === 0 && m.rulat && !m.picate.length) console.log(' OK martorul: copia neatinsa verde'); else { rele++; console.log(` STRICAT martorul nu e verde (cod ${m.cod}, ${m.picate.length} esecuri)`); }
|
|
for (const [nume, din, inl, tinta] of plantari) {
|
|
const t = copie(); const f = path.join(t, 'tools', 'proof-of-software', 'pos.mjs'); const src = fs.readFileSync(f, 'utf8');
|
|
if (src.split(din).length !== 2) { rele++; console.log(` STRICAT ${nume}: ancora apare de ${src.split(din).length - 1} ori`); fs.rmSync(t, { recursive: true, force: true }); continue; }
|
|
fs.writeFileSync(f, src.replace(din, inl));
|
|
const r = ruleaza(t); fs.rmSync(t, { recursive: true, force: true });
|
|
if (!r.rulat) { rele++; console.log(` STRICAT ${nume}: suita nu a ajuns la rezumat (cod ${r.cod})`); continue; }
|
|
if (r.picate.some((l) => l.includes(tinta))) console.log(` ROSU cum trebuia ${nume} (proba '${tinta}' pica)`);
|
|
else { rele++; console.log(` CONTROL CAZUT ${nume}: proba '${tinta}' a ramas verde (${r.picate.length} esecuri altundeva)`); }
|
|
}
|
|
console.log(rele ? `CONTROL NEGATIV: ${rele} probleme` : `DOVEDIT: martorul verde, ${plantari.length} din ${plantari.length} paznici scosi -> proba lor rosie`);
|
|
return rele ? 1 : 0;
|
|
}
|