#!/bin/bash # Controlul negativ al probelor gateway-ului PQ (proba-pq-gateway.mjs). Paznicii din pq-gateway.mjs se ORBESC pe rand, # intr-o COPIE a dosarului (originalul nu se atinge), si proba tinta TREBUIE sa iasa ROSIE pe numele ei. Suita trebuie # sa fi RULAT intreaga, altfel verdictul e STRICAT, nu rosu. La urma suita neatinsa trebuie sa fie verde. # bash aerenew/cloud-gateway/pq-gateway/proba-pq-gateway-control-negativ.sh # Iesire: un rand pe paznic, apoi "CONTROL NEGATIV: N treceri, M esecuri"; cod nenul la orice esec sau STRICAT. set -u AICI="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" W=$(mktemp -d); trap 'rm -rf "$W"' EXIT # probele (s) folosesc autoritatea de certificare PQ de langa gateway; copia nu o are, deci calea ei vine din mediu export AERE_PQ_PKI="$(cd "$AICI/../pq-pki" && pwd)/pki.mjs" if command -v cygpath >/dev/null 2>&1; then AERE_PQ_PKI="$(cygpath -w "$AERE_PQ_PKI")"; fi for f in pq-gateway.mjs proba-pq-gateway.mjs pq-gateway.example.json; do cp "$AICI/$f" "$W/$f" cmp -s "$AICI/$f" "$W/$f" || { echo "STRICAT: copia lui $f difera de original"; exit 3; } done PROBE=$(grep -c "^await test(" "$AICI/proba-pq-gateway.mjs") [ "$PROBE" -gt 0 ] || { echo "STRICAT: nu am gasit nicio proba in proba-pq-gateway.mjs"; exit 3; } cale_nod() { if command -v cygpath >/dev/null 2>&1; then cygpath -w "$1"; else echo "$1"; fi; } treceri=0; esecuri=0 # $1 vechi, $2 nou. Ancora trebuie sa apara EXACT o data, altfel plantarea e un esec al controlului, nu un verde. planteaza() { AERE_VECHI="$1" AERE_NOU="$2" node -e " const fs = require('fs'); const p = process.argv[1]; const t = fs.readFileSync(p, 'utf8'); const a = process.env.AERE_VECHI; const n = t.split(a).length - 1; if (n !== 1) { console.log(' ancora apare de ' + n + ' ori'); process.exitCode = 3; } else fs.writeFileSync(p, t.split(a).join(process.env.AERE_NOU));" "$(cale_nod "$W/pq-gateway.mjs")" } # $1 descrierea paznicului orbit, $2 eticheta probei tinta, de ex. (g) masoara() { if cmp -s "$AICI/pq-gateway.mjs" "$W/pq-gateway.mjs"; then echo " $1: STRICAT (plantarea nu a schimbat nimic)"; esecuri=$((esecuri+1)); return fi if ! cmp -s "$AICI/proba-pq-gateway.mjs" "$W/proba-pq-gateway.mjs"; then echo " $1: STRICAT (proba din copie difera de original)"; esecuri=$((esecuri+1)); return fi ( cd "$W" && node proba-pq-gateway.mjs > "$W/out.txt" 2>&1 ) local cod=$? local rulate; rulate=$(grep -c "^ OK \|^ ESEC \|^ -- " "$W/out.txt") local tinta; tinta=$(grep "^ ESEC " "$W/out.txt" | grep -cF "ESEC $2 ") local rosii; rosii=$(grep -c "^ ESEC " "$W/out.txt") if [ "$rulate" -ne "$PROBE" ]; then echo " $1: STRICAT (au rulat $rulate probe din $PROBE, cod $cod)"; tail -3 "$W/out.txt" | cut -c1-220; esecuri=$((esecuri+1)) elif [ "$cod" -eq 0 ]; then echo " $1: CONTROL NEGATIV CAZUT (suita a iesit 0)"; esecuri=$((esecuri+1)) elif [ "$tinta" -ge 1 ]; then echo " $1: ROSU cum trebuia ($rosii esecuri in suita, intre ele $2)" grep "^ ESEC " "$W/out.txt" | grep -F "ESEC $2 " | cut -c1-330 | sed 's/^/ /' treceri=$((treceri+1)) else echo " $1: CONTROL NEGATIV CAZUT (proba $2 a ramas verde; au picat: $(grep '^ ESEC ' "$W/out.txt" | cut -c8-12 | tr '\n' ' '))"; esecuri=$((esecuri+1)) fi cp "$AICI/pq-gateway.mjs" "$W/pq-gateway.mjs" cmp -s "$AICI/pq-gateway.mjs" "$W/pq-gateway.mjs" || { echo "STRICAT: copia nu s-a putut reface"; exit 3; } } control() { # $1 descriere, $2 tinta, $3 ancora, $4 inlocuire if planteaza "$3" "$4"; then masoara "$1" "$2"; else echo " $1: STRICAT (plantarea a esuat)"; esecuri=$((esecuri+1)); fi } control "autentificarea raportata mereu post-quantum" "(s2)" \ "const autentificare = lant.length && lant.every(pq) ? 'post-quantum'" \ "const autentificare = lant.length && (lant.every(pq) || Boolean(Number('1'))) ? 'post-quantum'" control "REQUIRE_PQ_AUTH ignorat" "(s3)" \ " if (cfg.requirePqAuth && lantCert.autentificare !== 'post-quantum') {" \ " if (cfg.requirePqAuth && lantCert.autentificare !== 'post-quantum' && Boolean(Number('0'))) {" control "numai cheile lantului judecate, nu si semnaturile" "(s3)" \ "const pq = (c) => /^ML-DSA-/.test(c.publicKeyAlgorithm) && /^ML-DSA-/.test(c.signatureAlgorithm);" \ "const pq = (c) => /^ML-DSA-/.test(c.publicKeyAlgorithm);" control "X-Real-IP al clientului crezut (revizuirea 2026-09-25, A8)" "(e3)" \ "'x-forwarded-host', 'x-real-ip', 'forwarded'" \ "'x-forwarded-host', 'forwarded'" control "X-Client-* si X-SSL-* ale clientului trec la upstream (A8)" "(e3)" \ "PREFIXE_IDENTITATE.some((p) => n.startsWith(p))" \ "PREFIXE_IDENTITATE.some((p) => n.startsWith(p) && Boolean(Number('0')))" control "refuzul pe algoritmul de semnatura numarat ca other (A9)" "(g3)" \ "if (c === 'ERR_SSL_NO_SUITABLE_SIGNATURE_ALGORITHM') return 'no shared signature algorithm';" \ "if (c === 'ERR_SSL_NO_SUITABLE_SIGNATURE_ALGORITHM' && Boolean(Number('0'))) return 'no shared signature algorithm';" control "hybrid-only ofera si X25519" "(g)" \ "'hybrid-only': 'X25519MLKEM768'," "'hybrid-only': 'X25519MLKEM768:X25519'," control "motivul refuzului nu mai e numarat (totul 'other')" "(g)" \ "export function motivRefuz(e) {" "export function motivRefuz(e) { if (!process.env.AERE_NU) return 'other';" control "minVersion TLSv1.3 scos (ramane implicitul Node, TLSv1.2)" "(h)" \ "minVersion: 'TLSv1.3'," "" control "antetele hop-by-hop nescoase" "(e)" \ "if (HOP.has(n) || n.startsWith('proxy-') || numite.has(n)) continue;" "if (Boolean(process.env.AERE_NU)) continue;" control "numele enumerate in Connection nescoase" "(e)" \ "if (HOP.has(n) || n.startsWith('proxy-') || numite.has(n)) continue;" "if (HOP.has(n) || n.startsWith('proxy-')) continue;" control "X-Forwarded-For al clientului crezut" "(e)" \ "if (cfg.trustForwarded) xffVechi =" "if (!process.env.AERE_NU) xffVechi =" control "x-aere-pq-gateway nepus (cel falsificat de client trece)" "(e)" \ "out.push('x-aere-pq-gateway', cfg.mode);" "if (Boolean(process.env.AERE_NU)) out.push('x-aere-pq-gateway', cfg.mode);" control "cheia privata inclusa in stare" "(f)" \ "certificate: { sha256: x509.fingerprint256," "privateKey: cheiePem.toString('utf8'), certificate: { sha256: x509.fingerprint256," control "starea trimisa la upstream in loc sa fie servita" "(f)" \ "if (esteCaleaStarii(req.url)) {" "if (Boolean(process.env.AERE_NU) && esteCaleaStarii(req.url)) {" control "upstream cazut: niciun raspuns (agatare)" "(i)" \ "raspundeJson(req, res, cod, corpEroare(cod, e), true);" "if (Boolean(process.env.AERE_NU)) raspundeJson(req, res, cod, corpEroare(cod, e), true);" control "timeout raportat ca 502" "(i2)" \ "const cod = e && e.aereTimeout ? 504 : 502;" "const cod = 502;" control "hybrid-preferred pretinde hibrid garantat" "(j)" \ "hybridKeyExchangeOnEveryConnection: false," "hybridKeyExchangeOnEveryConnection: true," control "hybrid-preferred ca lista plata (fara tuplu, fara HelloRetryRequest)" "(j2)" \ "'hybrid-preferred': 'X25519MLKEM768/X25519:P-256'," "'hybrid-preferred': 'X25519MLKEM768:X25519:P-256'," control "corpul bufferat intreg inainte de trimitere" "(k)" \ "req.pipe(upReq);" "(async () => { const b = []; for await (const x of req) b.push(x); upReq.end(Buffer.concat(b)); })().catch(() => upReq.destroy());" control "tunelul upgrade numai intr-un sens" "(d)" \ "upSock.pipe(socket);" "" control "upgrade refuzat de upstream trimis fara Connection: close" "(d2)" \ "linii.push('Connection: close');" "" control "upgrade catre upstream cazut: niciun raspuns pe socket" "(i3)" \ "if (!socket.destroyed) scrieBrut(socket, cod, corpEroare(cod, e));" "if (Boolean(process.env.AERE_NU)) scrieBrut(socket, cod, corpEroare(cod, e));" control "drumul catre upstream https fara preferinta hibrida" "(q)" \ "ecdhCurve: GRUPURI['hybrid-preferred'], ...(caSus ?" "ecdhCurve: 'X25519', ...(caSus ?" control "upgrade catre upstream https fara CA-ul configurat" "(d3)" \ "ecdhCurve: GRUPURI['hybrid-preferred'], ...caUpgrade }" "ecdhCurve: GRUPURI['hybrid-preferred'] }" control "plecarea clientului numarata ca 502 (cerere)" "(s)" \ "if (res.headersSent || res.destroyed) { res.destroy(); return; }" "" control "plecarea clientului numarata ca 502 (upgrade)" "(s)" \ "if (socket.destroyed) return; //" "if (Boolean(process.env.AERE_NU)) return; //" control "raspunsul care curge taiat de cronometrul pornit la 'finish'" "(k2)" \ "if (raspunsPrimit) return;" "if (Boolean(process.env.AERE_NU)) return;" control "adresa ::ffff: trimisa neschimbata in X-Forwarded-For" "(e2)" \ "return s.toLowerCase().startsWith('::ffff:') && net.isIPv4(s.slice(7)) ? s.slice(7) : s;" "return s;" control "certificatul upstream-ului https nu se mai verifica" "(q2)" \ "new https.Agent({ keepAlive: true," "new https.Agent({ rejectUnauthorized: false, keepAlive: true," control "fara cronometru de conectare la upstream" "(r)" \ "tConectare = setTimeout(() => upReq.destroy(expirat('connect')), cfg.connectTimeoutMs);" "tConectare = null;" control "oprirea taie cererile in curs" "(l)" \ "server.closeIdleConnections();" "server.closeAllConnections();" ( cd "$AICI" && node proba-pq-gateway.mjs > "$W/final.txt" 2>&1 ) codf=$? rulatef=$(grep -c "^ OK \|^ ESEC \|^ -- " "$W/final.txt") okf=$(grep -c "^ OK " "$W/final.txt") if [ "$codf" -eq 0 ] && [ "$rulatef" -eq "$PROBE" ] && [ "$okf" -eq "$PROBE" ]; then echo " suita neatinsa: VERDE ($(tail -1 "$W/final.txt"))"; treceri=$((treceri+1)) else echo " suita neatinsa: NU e verde (cod $codf, $okf din $PROBE OK, rulate $rulatef)"; grep "^ ESEC \|^ -- \|STRICAT" "$W/final.txt" | cut -c1-220; esecuri=$((esecuri+1)) fi echo "CONTROL NEGATIV: $treceri treceri, $esecuri esecuri" [ "$esecuri" -eq 0 ] || exit 1