Findings security-review P1/P2 (2026-07-03): - headere de securitate pe toate raspunsurile (nosniff, X-Frame-Options, Referrer-Policy, HSTS doar pe HTTPS) + teste - body-cap global 10MB ca middleware ASGI pur (413 inainte de parserul multipart/JSON; verificarea per-endpoint ramane strat 2) - imagine Docker non-root (uid 10001), port 8010 aliniat, loguri pe volumul /data - fail-fast la boot cu rar_env=prod fara AUTOPASS_REQUIRE_API_KEY sau AUTOPASS_SESSION_SECRET - compose: env-uri critice obligatorii (:?) ca api/worker sa nu diverga tacit; FORWARDED_ALLOW_IPS ca rate-limit-ul sa vada IP-ul real dupa Traefik - signup fara PII in stdout: log_event in loc de print cu email (idem notify degradat) - ratelimit: sterge cheile fara timestamp-uri valide (crestere monotona a memoriei pe IP-uri reale) - backup criptat SQLite (backup online API, gpg AES256) + verificare restore + docs/backup.md Suita completa verde: 1557 passed, 1 skipped (live). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
113 lines
3.1 KiB
Bash
Executable File
113 lines
3.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Verificare de restaurare pentru backup-urile SQLite criptate (T2/P0-4).
|
|
#
|
|
# Un backup neverificat nu e backup: decripteaza + dezarhiveaza (intr-un fisier temporar,
|
|
# NU suprascrie baza vie) ultimul backup (sau cel indicat explicit) si ruleaza
|
|
# PRAGMA integrity_check + un SELECT count(*) FROM submissions. Iese cu cod != 0 daca
|
|
# ceva esueaza sau daca integritatea nu e "ok".
|
|
#
|
|
# Utilizare:
|
|
# AUTOPASS_BACKUP_PASSPHRASE_FILE=/run/secrets/backup_pass tools/restore_check.sh
|
|
# tools/restore_check.sh /data/backups/autopass-20260706-120000.db.gz.gpg
|
|
|
|
set -euo pipefail
|
|
|
|
BACKUP_DIR="${AUTOPASS_BACKUP_DIR:-/data/backups}"
|
|
|
|
log() { echo "[restore_check] $*"; }
|
|
err() { echo "[restore_check] EROARE: $*" >&2; }
|
|
|
|
CLEANUP_PATHS=()
|
|
cleanup() {
|
|
local p
|
|
for p in "${CLEANUP_PATHS[@]:-}"; do
|
|
[[ -n "$p" ]] && rm -rf "$p"
|
|
done
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
if [[ -n "${AUTOPASS_BACKUP_PASSPHRASE_FILE:-}" ]]; then
|
|
if [[ ! -f "$AUTOPASS_BACKUP_PASSPHRASE_FILE" ]]; then
|
|
err "AUTOPASS_BACKUP_PASSPHRASE_FILE indica un fisier inexistent: $AUTOPASS_BACKUP_PASSPHRASE_FILE"
|
|
exit 1
|
|
fi
|
|
PASSPHRASE_FILE="$AUTOPASS_BACKUP_PASSPHRASE_FILE"
|
|
elif [[ -n "${AUTOPASS_BACKUP_PASSPHRASE:-}" ]]; then
|
|
PASSPHRASE_FILE="$(mktemp)"
|
|
chmod 600 "$PASSPHRASE_FILE"
|
|
CLEANUP_PATHS+=("$PASSPHRASE_FILE")
|
|
printf '%s' "$AUTOPASS_BACKUP_PASSPHRASE" > "$PASSPHRASE_FILE"
|
|
else
|
|
err "lipseste parola de decriptare."
|
|
err "seteaza AUTOPASS_BACKUP_PASSPHRASE_FILE (recomandat) sau AUTOPASS_BACKUP_PASSPHRASE."
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${1:-}" != "" ]]; then
|
|
TARGET="$1"
|
|
else
|
|
TARGET="$(ls -1t "$BACKUP_DIR"/autopass-*.db.gz.gpg 2>/dev/null | head -n1 || true)"
|
|
fi
|
|
|
|
if [[ -z "$TARGET" || ! -f "$TARGET" ]]; then
|
|
err "niciun backup gasit de verificat (director: $BACKUP_DIR)"
|
|
exit 1
|
|
fi
|
|
|
|
log "verific: $TARGET"
|
|
|
|
TMP_DIR="$(mktemp -d)"
|
|
CLEANUP_PATHS+=("$TMP_DIR")
|
|
GZ_FILE="$TMP_DIR/snapshot.db.gz"
|
|
DB_FILE="$TMP_DIR/snapshot.db"
|
|
|
|
log "decriptez (gpg)"
|
|
gpg --batch --yes --decrypt --passphrase-file "$PASSPHRASE_FILE" \
|
|
--output "$GZ_FILE" "$TARGET"
|
|
|
|
log "dezarhivez (gzip)"
|
|
gzip -d "$GZ_FILE"
|
|
|
|
if [[ ! -s "$DB_FILE" ]]; then
|
|
err "fisierul restaurat e gol sau lipseste: $DB_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
log "PRAGMA integrity_check + count(submissions)"
|
|
set +e
|
|
RESULT="$(python3 - "$DB_FILE" <<'PYEOF'
|
|
import sqlite3
|
|
import sys
|
|
|
|
conn = sqlite3.connect(sys.argv[1])
|
|
try:
|
|
integrity = conn.execute("PRAGMA integrity_check").fetchone()[0]
|
|
try:
|
|
count = conn.execute("SELECT count(*) FROM submissions").fetchone()[0]
|
|
except sqlite3.OperationalError as exc:
|
|
print(f"integrity={integrity}")
|
|
print(f"eroare_count_submissions={exc}")
|
|
sys.exit(1)
|
|
print(f"integrity={integrity}")
|
|
print(f"count_submissions={count}")
|
|
finally:
|
|
conn.close()
|
|
PYEOF
|
|
)"
|
|
STATUS=$?
|
|
set -e
|
|
|
|
echo "$RESULT"
|
|
|
|
if [[ $STATUS -ne 0 ]]; then
|
|
err "verificare esuata (vezi mesajul de mai sus)"
|
|
exit 1
|
|
fi
|
|
|
|
if ! grep -q "^integrity=ok$" <<<"$RESULT"; then
|
|
err "PRAGMA integrity_check NU a raportat 'ok'"
|
|
exit 1
|
|
fi
|
|
|
|
log "backup valid: restaurabil, integritate ok."
|