145 lines
9.5 KiB
Bash
145 lines
9.5 KiB
Bash
#!/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
|