aere-cloud-sdk/go/control-negativ.mjs
Aere Network 0fe1828f5b 1.7.0: identity and compliance verification in all five clients (identityVerify / complianceCheck; Go: IdentityVerify / ComplianceCheck with the presentation as its JSON bytes)
POST /v1/identity/verify: an AERE Identity presentation judged by the published command line -> VALID / PARTIAL / INVALID with every
row, the claims shown and the subject when no row failed, and the command that reproduces the verdict.
POST /v1/compliance/check: your policy judged on a presentation -> COMPLIANT / NOT_COMPLIANT with the reasons and, with record, the
decision as an AIP-23 envelope without personal data.
In Go the presentation, keys, lists and policy travel as json.RawMessage: a map or float64 would change numbers above 2^53 and the
canonical text the signatures cover. The .NET negative control now builds clean for each planted defect (an incremental build could
run the tests against the previous planted library). A negative control for the JavaScript and Python routes is added.

Tests: JavaScript 13, Python 14, Go ok, Java 10/10 (and the Java 8 API gate); negative controls Go 9/9, Java 10/10, .NET 12/12,
JavaScript and Python 4/4.
2026-09-30 12:10:59 +03:00

61 lines
4.6 KiB
JavaScript

#!/usr/bin/env node
// Controlul negativ al probelor clientului Go: fiecare paznic se strica intr-o COPIE a pachetului (sursa din depozit nu se
// atinge), `go test` ruleaza pe copie si proba tinta TREBUIE sa pice; o plantare care nu se aplica sau nu compileaza e STRICAT,
// nu rosu. La urma, pachetul neatins trebuie sa treaca intreg.
// node aerenew/sdk/go/control-negativ.mjs
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 FISIERE = ['go.mod', 'aerecloud.go', 'aerecloud_test.go'];
const sursa = fs.readFileSync(path.join(AICI, 'aerecloud.go'), 'utf8');
const PLANTARI = [
['exportul jsonl declarat verificat fara hash', 'ok := e.Digest != "" && e.Digest == sha256Hex(r.body)', 'ok := true', 'TestExportulZilei'],
['piatra zilei sterse acceptata fara comparatie', 'Matches: local == t.Expired.Digest,', 'Matches: true,', 'TestExportulPastrat'],
['ziua tinuta acceptata fara comparatie', 'Matches: local == d}', 'Matches: true}', 'TestExportulPastrat'],
['cheia trimisa si fara attest', 'return c.jsonReq(ctx, "POST", "/pq/readiness", b, attest)', 'return c.jsonReq(ctx, "POST", "/pq/readiness", b, true)', 'TestReadinessCheiaNumaiLaAttest'],
['redirectarile urmate (cheia ar pleca spre alta gazda)', 'CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }', 'CheckRedirect: nil', 'TestRedirectareaNuEUrmata'],
['un 429 intors ca nepotrivire', 'return nil, &Error{Status: r.status, Body: r.body, Path: "/account/audit"}\n\t}\n\td := sha256Hex(r.body)',
'return &KeptCheck{Day: day, State: "held", Local: local}, nil\n\t}\n\td := sha256Hex(r.body)', 'TestExportulPastrat'],
// 2026-09-29: plicul trecut printr-un map de Go (cheile sortate = alt text canonic, un plic valid ar iesi INVALID la Trust API)
['Verify trece plicul printr-un map', 'return c.jsonReq(ctx, "POST", p, envelope, true)',
'var m map[string]any\n\t_ = json.Unmarshal(envelope, &m)\n\treturn c.jsonReq(ctx, "POST", p, m, true)', 'TestVerifyPastreazaOrdinea'],
// 1.7.0: prezentarea trecuta printr-un map (numerele mari devin float64) si ruta de conformitate trimisa la identitate
['IdentityVerify trece prezentarea printr-un map', 'return c.jsonReq(ctx, "POST", "/identity/verify", req, true)',
'var m map[string]any\n\t_ = json.Unmarshal(req.Presentation, &m)\n\tb, _ := json.Marshal(m)\n\treq.Presentation = b\n\treturn c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'TestIdentityVerifyPastreazaOctetii'],
['ComplianceCheck pe ruta de identitate', 'return c.jsonReq(ctx, "POST", "/compliance/check", req, true)', 'return c.jsonReq(ctx, "POST", "/identity/verify", req, true)', 'TestIdentityVerifyPastreazaOctetii'],
];
function ruleaza(src) {
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-go-cn-'));
try {
for (const f of FISIERE) fs.copyFileSync(path.join(AICI, f), path.join(d, f));
fs.writeFileSync(path.join(d, 'aerecloud.go'), src);
const r = spawnSync('go', ['test', '-count=1', '-v', './...'], { cwd: d, encoding: 'utf8' });
const out = (r.stdout || '') + (r.stderr || '');
if (/\[build failed\]|cannot use|undefined:|syntax error/.test(out)) return { stricat: 'nu compileaza: ' + out.split('\n').find((l) => /error|undefined|cannot/.test(l)) };
const picate = [...out.matchAll(/--- FAIL: (\w+)/g)].map((m) => m[1]);
const trecute = [...out.matchAll(/--- PASS: (\w+)/g)].map((m) => m[1]);
if (!picate.length && !trecute.length) return { stricat: 'probele nu au rulat' };
return { picate, trecute };
} finally { fs.rmSync(d, { recursive: true, force: true }); }
}
let rele = 0;
for (const [nume, vechi, nou, tinta] of PLANTARI) {
if (sursa.split(vechi).length !== 2) { console.log(` STRICAT ${nume}: ancora apare de ${sursa.split(vechi).length - 1} ori`); rele++; continue; }
const r = ruleaza(sursa.replace(vechi, nou));
if (r.stricat) { console.log(` STRICAT ${nume}: ${r.stricat}`); rele++; continue; }
if (r.picate.includes(tinta)) console.log(` ROSU cum trebuia ${nume} (pica ${r.picate.join(', ')})`);
else { console.log(` CONTROL CAZUT ${nume}: ${tinta} a ramas verde`); rele++; }
}
const m = ruleaza(sursa);
if (m.stricat || m.picate.length) { console.log(' MARTORUL nu e verde: ' + JSON.stringify(m)); rele++; }
else console.log(` martor: sursa neatinsa, ${m.trecute.length} probe verzi`);
console.log(rele ? `CONTROL NEGATIV: ${rele} probleme` : `CONTROL NEGATIV TRECUT (${PLANTARI.length} din ${PLANTARI.length} rosii, martorul verde)`);
process.exitCode = rele ? 1 : 0;