aere-cloud-sdk/java/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

86 lines
6.5 KiB
JavaScript

#!/usr/bin/env node
// Controlul negativ al probelor clientului Java (1.5.0; 1.6.1 adauga redirectarea si poarta --release 8): fiecare paznic se strica intr-o COPIE a clasei (sursa din depozit nu se
// atinge), probele se ruleaza pe copie si proba tinta TREBUIE sa pice; o plantare care nu se aplica sau nu compileaza e un ESEC al
// controlului (STRICAT), nu un rosu. La urma, clasa neatinsa trebuie sa treaca intreaga.
// node aerenew/sdk/java/control-negativ.mjs
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
const AICI = path.dirname(fileURLToPath(import.meta.url));
const SRC = path.join(AICI, 'src', 'main', 'java', 'network', 'aere', 'cloud', 'AereCloud.java');
const TEST = path.join(AICI, 'src', 'test', 'java', 'network', 'aere', 'cloud', 'TestAereCloud.java');
const original = fs.readFileSync(SRC, 'utf8');
const PLANTARI = [
['piatra zilei sterse acceptata fara comparatie', 'return new KeptCheck(day, "expired", dig, local, local.equals(dig), cand);',
'return new KeptCheck(day, "expired", dig, local, true, cand);', 'auditVerifyKept'],
['ziua tinuta acceptata fara comparatie', 'return new KeptCheck(day, "held", dig, local, local.equals(dig), null);',
'return new KeptCheck(day, "held", dig, local, true, null);', 'auditVerifyKept'],
['exportul jsonl declarat verificat fara hash', 'Boolean ok = "jsonl".equals(f) ? (digest != null && digest.equals(sha256Hex(r.octeti))) : null;',
'Boolean ok = "jsonl".equals(f) ? Boolean.TRUE : null;', 'auditExport'],
['cheia trimisa si fara attest', 'return req("POST", "/pq/readiness", json(m), attest);',
'return req("POST", "/pq/readiness", json(m), true);', 'readiness fara attest'],
['un 429 intors ca nepotrivire', 'if (r.status < 200 || r.status >= 300) throw new AereCloudException(r.status, r.text(), "/account/audit");\n String dig = sha256Hex(r.octeti);',
'if (r.status < 200 || r.status >= 300) return new KeptCheck(day, "held", null, local, false, null);\n String dig = sha256Hex(r.octeti);', 'auditVerifyKept'],
// 1.6.1: HttpURLConnection urmeaza redirectarile implicit; fara oprire, cheia pleaca spre gazda din Location
['redirectarea urmata (cheia pleaca spre alta gazda)', 'c.setInstanceFollowRedirects(false);', 'c.setInstanceFollowRedirects(true);', '302'],
// 1.7.0: conformitatea trimisa pe ruta identitatii; identitatea fara cheie
['complianceCheck pe ruta identitatii', 'return req("POST", "/compliance/check", requestJson, true);', 'return req("POST", "/identity/verify", requestJson, true);', '1.7.0'],
['identityVerify fara cheie', 'return req("POST", "/identity/verify", requestJson, true);', 'return req("POST", "/identity/verify", requestJson, false);', '1.7.0'],
];
// 1.6.1: poarta Android = clasa compileaza cu `javac --release 8`. Un API de dupa Java 8 trece probele pe JDK 21 si trebuie prins
// numai de poarta; plantarea NU e o proba, e o compilare care TREBUIE sa cada.
const PLANTARI_JAVA8 = [
['InputStream.readAllBytes (Java 9)', 'while ((n = in.read(buf)) != -1) b.write(buf, 0, n);\n return b.toByteArray();', 'return in.readAllBytes();'],
['URLEncoder.encode cu Charset (Java 10)', 'try { return URLEncoder.encode(s, "UTF-8"); }', 'try { if (s == null) throw new java.io.UnsupportedEncodingException(); return URLEncoder.encode(s, StandardCharsets.UTF_8); }'],
];
function compileazaJava8(sursa) {
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-java8-'));
try {
fs.writeFileSync(path.join(d, 'AereCloud.java'), sursa);
execFileSync('javac', ['--release', '8', '-Xlint:-options', '-d', path.join(d, 'out'), path.join(d, 'AereCloud.java')], { stdio: 'pipe' });
return { ok: true };
} catch (e) { return { ok: false, err: String(e.stderr || e.message).split('\n').find((l) => /error/.test(l)) || 'eroare' }; }
finally { fs.rmSync(d, { recursive: true, force: true }); }
}
function ruleaza(sursa) {
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'aere-java-cn-'));
try {
fs.writeFileSync(path.join(d, 'AereCloud.java'), sursa);
try { execFileSync('javac', ['-d', path.join(d, 'out'), path.join(d, 'AereCloud.java'), TEST], { stdio: 'pipe' }); }
catch (e) { return { stricat: 'nu compileaza: ' + String(e.stderr || e.message).split('\n')[0] }; }
let out;
try { out = execFileSync('java', ['-cp', path.join(d, 'out'), 'network.aere.cloud.TestAereCloud'], { encoding: 'utf8' }); }
catch (e) { out = String(e.stdout || ''); }
const m = /(\d+) treceri, (\d+) esecuri/.exec(out);
if (!m) return { stricat: 'probele nu au terminat' };
return { treceri: +m[1], esecuri: +m[2], picate: out.split('\n').filter((l) => l.startsWith(' PICA')) };
} finally { fs.rmSync(d, { recursive: true, force: true }); }
}
let rele = 0;
for (const [nume, vechi, nou, tinta] of PLANTARI) {
if (original.split(vechi).length !== 2) { console.log(` STRICAT ${nume}: ancora apare de ${original.split(vechi).length - 1} ori`); rele++; continue; }
const r = ruleaza(original.replace(vechi, nou));
if (r.stricat) { console.log(` STRICAT ${nume}: ${r.stricat}`); rele++; continue; }
if (r.picate.some((l) => l.includes(tinta))) console.log(` ROSU cum trebuia ${nume} (${r.esecuri} esec, intre ele proba '${tinta}')`);
else { console.log(` CONTROL CAZUT ${nume}: proba '${tinta}' a ramas verde`); rele++; }
}
for (const [nume, vechi, nou] of PLANTARI_JAVA8) {
if (original.split(vechi).length !== 2) { console.log(` STRICAT ${nume}: ancora apare de ${original.split(vechi).length - 1} ori`); rele++; continue; }
const r = compileazaJava8(original.replace(vechi, nou));
if (!r.ok) console.log(` ROSU cum trebuia ${nume}: --release 8 refuza (${r.err.trim().slice(0, 90)})`);
else { console.log(` CONTROL CAZUT ${nume}: --release 8 a compilat`); rele++; }
}
const j8 = compileazaJava8(original);
if (!j8.ok) { console.log(' MARTORUL nu compileaza cu --release 8: ' + j8.err); rele++; } else console.log(' martor: sursa neatinsa compileaza cu --release 8 (API Java 8, deci si Android)');
const martor = ruleaza(original);
if (martor.stricat || martor.esecuri !== 0) { console.log(' MARTORUL nu e verde: ' + JSON.stringify(martor)); rele++; }
else console.log(` martor: sursa neatinsa ${martor.treceri}/${martor.treceri} verde`);
console.log(rele ? `CONTROL NEGATIV: ${rele} probleme` : `CONTROL NEGATIV TRECUT (${PLANTARI.length + PLANTARI_JAVA8.length} din ${PLANTARI.length + PLANTARI_JAVA8.length} rosii, martorul verde si pe --release 8)`);
process.exitCode = rele ? 1 : 0;