trading-system-docs/tolaria/c5-sync-service/auth3a-sensitivity.sh
Red Queen a11c1bbe53 AUTH.3A: C5 Caller Auth Integration (SAVE/DELETE Credential, fail-closed, Tests, Sensitivity, Gap-Doku)
- rq_c5c.py: TolariaClient save_token/delete_token DI, _require_token fail-closed,
  write()/delete() senden Bearer (SAVE/DELETE), read()/list() ohne Credential
- rq_c5_cli.py: _delete_executor liest nur DELETE-Credential (Least Privilege)
- test_c5c.py: write()-Tests injizieren synthetisches SAVE-Token
- test_c5_auth3a.py: isolierte AUTH.3A-Testsuite (19 Tests, Fake/Mock Tolaria)
- auth3a-sensitivity.sh: 8 Sensitivitaets-Mutationen (A-H) -> ROT
- AUTH3A_HUMAN_APPROVAL_AUTHENTICITY_GAP.md: Gap dokumentiert (OPEN, nicht repariert)

Keine echten Tokens. Keine ENV-Mutation. Kein Deployment. Keine produktive Auth-Aktivierung.
2026-08-27 08:42:26 +00:00

133 lines
4.6 KiB
Bash

#!/usr/bin/env bash
# AUTH.3A PHASE 11 — Test-Sensitivity: Beweise ROT bei gebrochener Auth-Semantik.
#
# Mutiert rq_c5c.py temporaer (A-H) und prueft, dass die AUTH.3A-Testsuite
# ROT wird. Keine hardcodierten PASS-Werte. Keine Produktion.
#
# A. fehlendes Token sendet Request trotzdem
# B. Authorization Header entfernt
# C. SAVE Caller laedt DELETE Token
# D. DeleteExecutor laedt SAVE Token
# E. Delete ohne Human Approval erlaubt
# F. Token wird geloggt
# G. Retry verliert Authorization
# H. Replay faellt auf unauthenticated zurueck
set -u
cd "$(dirname "$0")"
TARGET="rq_c5c.py"
TARGET2="rq_c5_delete.py"
TEST="test_c5_auth3a.py"
PY="python3 -m unittest ${TEST%.py}"
fail=0
run_mutation() {
local name="$1"; shift
local expect_red="$1"; shift
# Mutation anwenden
"$@" || { echo "MUTATION-APPLY-FAIL: $name"; fail=1; return; }
# Tests ausfuehren
local out
out=$($PY 2>&1)
local rc=$?
# Mutation zurueckrollen (aus .bak, NICHT git checkout — AUTH.3A-Aenderungen sind uncommitted)
cp "$TARGET.bak" "$TARGET"
cp "$TARGET2.bak" "$TARGET2"
if [ "$expect_red" = "RED" ]; then
if [ $rc -ne 0 ]; then
echo "OK [$name] -> ROT (rc=$rc)"
else
echo "FAIL[$name] -> GRUEN (sollte ROT sein)"
fail=1
fi
else
if [ $rc -eq 0 ]; then
echo "OK [$name] -> GRUEN"
else
echo "FAIL[$name] -> ROT (sollte GRUEN sein)"
fail=1
fi
fi
}
cp "$TARGET" "$TARGET.bak"
cp "$TARGET2" "$TARGET2.bak"
# A. fehlendes Token sendet Request trotzdem (entferne _require_token in write)
run_mutation "A-fehlendes-token-sendet-trotzdem" RED \
python3 - <<'EOF'
import re
p="rq_c5c.py"
s=open(p).read()
# write(): _require_token entfernen -> sendet ohne Token
s=s.replace("token = self._require_token(self.save_token, \"SAVE\")","token = self.save_token")
open(p,"w").write(s)
EOF
# B. Authorization Header entfernt (kein auth_token an _post)
run_mutation "B-auth-header-entfernt" RED \
python3 - <<'EOF'
p="rq_c5c.py"
s=open(p).read()
s=s.replace("if auth_token is not None:\n headers[\"Authorization\"] = f\"Bearer {auth_token}\"","pass")
open(p,"w").write(s)
EOF
# C. SAVE Caller laedt DELETE Token (write nutzt delete_token)
run_mutation "C-save-laedt-delete-token" RED \
python3 - <<'EOF'
p="rq_c5c.py"
s=open(p).read()
s=s.replace("token = self._require_token(self.save_token, \"SAVE\")","token = self._require_token(self.delete_token, \"SAVE\")")
open(p,"w").write(s)
EOF
# D. DeleteExecutor laedt SAVE Token (delete nutzt save_token)
run_mutation "D-delete-laedt-save-token" RED \
python3 - <<'EOF'
p="rq_c5c.py"
s=open(p).read()
s=s.replace("token = self._require_token(self.delete_token, \"DELETE\")","token = self._require_token(self.save_token, \"DELETE\")")
open(p,"w").write(s)
EOF
# E. Delete ohne Human Approval erlaubt (entferne _validate_approval in execute)
run_mutation "E-delete-ohne-approval-erlaubt" RED \
python3 - <<'EOF'
p="rq_c5_delete.py"
s=open(p).read()
s=s.replace("approval = self._validate_approval(commit_sha, change)","approval = {'approval_id': 'x', 'workflow_commit': commit_sha, 'object_id': change.get('object_id'), 'path': change.get('path_before') or change.get('path_after')}")
open(p,"w").write(s)
EOF
# F. Token wird geloggt (repr enthaelt Token)
run_mutation "F-token-geloggt" RED \
python3 - <<'EOF'
p="rq_c5c.py"
s=open(p).read()
# Fuege eine __repr__ ein, die den save_token exponiert (nach self.delete_token = delete_token)
s=s.replace(" self.save_token = save_token\n self.delete_token = delete_token",
" self.save_token = save_token\n self.delete_token = delete_token\n\n def __repr__(self):\n return f\"TolariaClient(save_token={self.save_token})\"")
open(p,"w").write(s)
EOF
# G. Retry verliert Authorization (write sendet kein Token beim 2. Aufruf)
run_mutation "G-retry-verliert-auth" RED \
python3 - <<'EOF'
p="rq_c5c.py"
s=open(p).read()
s=s.replace("token = self._require_token(self.save_token, \"SAVE\")","token = self._require_token(self.save_token, \"SAVE\") if not hasattr(self,'_retried') else None\n self._retried = True")
open(p,"w").write(s)
EOF
# H. Replay faellt auf unauthenticated zurueck (delete ohne Token)
run_mutation "H-replay-unauthenticated" RED \
python3 - <<'EOF'
p="rq_c5c.py"
s=open(p).read()
s=s.replace("token = self._require_token(self.delete_token, \"DELETE\")","token = None")
open(p,"w").write(s)
EOF
rm -f "$TARGET.bak" "$TARGET2.bak"
echo "=== SENSITIVITY RESULT: $([ $fail -eq 0 ] && echo ALL-RED || echo FAILURES) ==="
exit $fail