#!/usr/bin/env bash # test_isolation.sh — тестирует namespace изоляцию: # Пользователь A не видит и не может изменить функции пользователя B. # LIST каждого пользователя возвращает только его функции. set -uo pipefail ROOT_DIR=$(cd "$(dirname "$0")" && pwd) source "${ROOT_DIR}/lib.sh" NS_CLEANUP_SCRIPT="${ROOT_DIR}/cleanup_test_namespaces.sh" RUN_ID=$(mk_run_id) ALICE="test-alice-${RUN_ID}@test.local" BOB="test-bob-${RUN_ID}@test.local" cleanup() { if [ -x "$NS_CLEANUP_SCRIPT" ]; then KUBECTL_DIRECT=1 "$NS_CLEANUP_SCRIPT" "$ALICE" "$BOB" >/dev/null 2>&1 || true fi } trap cleanup EXIT echo "" echo "========================================" echo " test_isolation.sh run=$RUN_ID" echo "========================================" JS_CODE='module.exports = async function() { return { status: 200, body: "ok" }; }' # ── Alice создаёт функцию ───────────────────────────────────────────────────── echo "" echo ">>> Alice создаёт функцию" R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${ALICE}" -H "Content-Type: application/json" \ -d "{\"name\":\"t-alice-fn\",\"language\":\"nodejs\",\"code\":$(printf '%s' "$JS_CODE" | json_escape)}") check_http "$(echo "$R" | tail -1)" "201" "Alice CREATE t-alice-fn → 201" # ── Bob не может получить функцию Alice ────────────────────────────────────── echo "" echo ">>> Bob GET t-alice-fn → 404" R=$(curl -s -w "\n%{http_code}" "${BASE}/functions/t-alice-fn" \ -H "X-Auth-Token: ${BOB}") check_http "$(echo "$R" | tail -1)" "404" "Bob GET t-alice-fn → 404" echo "" echo ">>> Bob DELETE t-alice-fn → 404" R=$(curl -s -w "\n%{http_code}" -X DELETE "${BASE}/functions/t-alice-fn" \ -H "X-Auth-Token: ${BOB}") check_http "$(echo "$R" | tail -1)" "404" "Bob DELETE t-alice-fn → 404" echo "" echo ">>> Bob PUT t-alice-fn/code → 404" R=$(curl -s -w "\n%{http_code}" -X PUT "${BASE}/functions/t-alice-fn/code" \ -H "X-Auth-Token: ${BOB}" -H "Content-Type: application/json" \ -d '{"code":"module.exports = async () => ({ body: \"hacked\" });"}') check_http "$(echo "$R" | tail -1)" "404" "Bob PUT t-alice-fn/code → 404" echo "" echo ">>> Bob INVOKE t-alice-fn → 404" R=$(curl -s -w "\n%{http_code}" -X POST "${BASE}/functions/t-alice-fn/invoke" \ -H "X-Auth-Token: ${BOB}" -H "Content-Type: application/json" -d '{}') check_http "$(echo "$R" | tail -1)" "404" "Bob INVOKE t-alice-fn → 404" # ── Alice убеждается что её функция цела ──────────────────────────────────── echo "" echo ">>> Alice GET t-alice-fn → 200 (не повреждена)" R=$(curl -s -w "\n%{http_code}" "${BASE}/functions/t-alice-fn" \ -H "X-Auth-Token: ${ALICE}") check_http "$(echo "$R" | tail -1)" "200" "Alice GET t-alice-fn → 200" # ── LIST изоляция ──────────────────────────────────────────────────────────── echo "" echo ">>> LIST изоляция: Alice и Bob видят только свои функции" # Bob создаёт свою функцию curl -s -X POST "${BASE}/functions" \ -H "X-Auth-Token: ${BOB}" -H "Content-Type: application/json" \ -d "{\"name\":\"t-bob-fn\",\"language\":\"nodejs\",\"code\":$(printf '%s' "$JS_CODE" | json_escape)}" > /dev/null sleep 1 ALICE_LIST=$(curl -s "${BASE}/functions" -H "X-Auth-Token: ${ALICE}") BOB_LIST=$(curl -s "${BASE}/functions" -H "X-Auth-Token: ${BOB}") # Alice видит t-alice-fn но не t-bob-fn ALICE_COUNT=$(json_len "$ALICE_LIST") ALICE_HAS_OWN=$(printf '%s' "$ALICE_LIST" | python3 -c " import json, sys try: fns = json.load(sys.stdin) print(sum(1 for f in fns if f.get('metadata',{}).get('name','') == 't-alice-fn')) except: print(0)" 2>/dev/null) ALICE_HAS_BOB=$(printf '%s' "$ALICE_LIST" | python3 -c " import json, sys try: fns = json.load(sys.stdin) print(sum(1 for f in fns if f.get('metadata',{}).get('name','') == 't-bob-fn')) except: print(0)" 2>/dev/null) [ "${ALICE_HAS_OWN:-0}" -ge 1 ] && pass "Alice LIST содержит t-alice-fn" || fail "Alice LIST не содержит t-alice-fn (count=$ALICE_COUNT)" [ "${ALICE_HAS_BOB:-0}" -eq 0 ] && pass "Alice LIST не содержит t-bob-fn" || fail "Alice LIST содержит t-bob-fn — УТЕЧКА ДАННЫХ!" # Bob видит t-bob-fn но не t-alice-fn BOB_HAS_OWN=$(printf '%s' "$BOB_LIST" | python3 -c " import json, sys try: fns = json.load(sys.stdin) print(sum(1 for f in fns if f.get('metadata',{}).get('name','') == 't-bob-fn')) except: print(0)" 2>/dev/null) BOB_HAS_ALICE=$(printf '%s' "$BOB_LIST" | python3 -c " import json, sys try: fns = json.load(sys.stdin) print(sum(1 for f in fns if f.get('metadata',{}).get('name','') == 't-alice-fn')) except: print(0)" 2>/dev/null) [ "${BOB_HAS_OWN:-0}" -ge 1 ] && pass "Bob LIST содержит t-bob-fn" || fail "Bob LIST не содержит t-bob-fn" [ "${BOB_HAS_ALICE:-0}" -eq 0 ] && pass "Bob LIST не содержит t-alice-fn" || fail "Bob LIST содержит t-alice-fn — УТЕЧКА ДАННЫХ!" # ── Route изоляция ─────────────────────────────────────────────────────────── echo "" echo ">>> Route изоляция: одинаковое имя → разные routes" ROUTE_A=$(printf '%s' "$ALICE_LIST" | python3 -c " import json, sys try: fns = json.load(sys.stdin) for f in fns: if f.get('metadata',{}).get('name','') == 't-alice-fn': print(f.get('spec',{}).get('relativeurl','')) break except: print('')" 2>/dev/null) ROUTE_B=$(printf '%s' "$BOB_LIST" | python3 -c " import json, sys try: fns = json.load(sys.stdin) for f in fns: if f.get('metadata',{}).get('name','') == 't-bob-fn': print(f.get('spec',{}).get('relativeurl','')) break except: print('')" 2>/dev/null) # Проверяем через GET (возвращает route) ALICE_GET=$(curl -s "${BASE}/functions/t-alice-fn" -H "X-Auth-Token: ${ALICE}") BOB_GET=$(curl -s "${BASE}/functions/t-bob-fn" -H "X-Auth-Token: ${BOB}") ROUTE_A=$(json_field "$ALICE_GET" route) ROUTE_B=$(json_field "$BOB_GET" route) [ "$ROUTE_A" != "$ROUTE_B" ] && [ -n "$ROUTE_A" ] && [ -n "$ROUTE_B" ] \ && pass "Route изоляция: $ROUTE_A ≠ $ROUTE_B" \ || fail "Route коллизия: alice=$ROUTE_A bob=$ROUTE_B" print_summary