修复认证挑战与可信代理安全边界(07-17-auth-challenge-trusted-proxy-hardening)

这个提交包含在:
2026-07-17 14:24:58 +08:00
父节点 bbb5c89825
当前提交 6ddedf1e0e
共修改 31 个文件,包含 3801 行新增和 419 行删除
+132 -14
查看文件
@@ -17,15 +17,25 @@ QA_DB_ADMIN_DB="${QA_DB_ADMIN_DB:-postgres}"
QA_DB_CLIENT="${QA_DB_CLIENT:-host}"
QA_DB_DOCKER_CONTAINER="${QA_DB_DOCKER_CONTAINER:-hyb-oa-trialrun-db}"
QA_DOCKER_USE_SG="${QA_DOCKER_USE_SG:-0}"
QA_DB_PREFIX="${QA_DB_PREFIX:-hyb_oa_webgate}"
if [[ "$MODE" == "auth-security-local" ]]; then
DEFAULT_QA_DB_PREFIX="hyb_oa_auth"
else
DEFAULT_QA_DB_PREFIX="hyb_oa_webgate"
fi
QA_DB_PREFIX="${QA_DB_PREFIX:-$DEFAULT_QA_DB_PREFIX}"
QA_KEEP_DB="${QA_KEEP_DB:-0}"
QA_SERVER_LOG="${QA_SERVER_LOG:-/tmp/hyb-oa-web-gates-server.log}"
QA_SERVER_PID_FILE="${QA_SERVER_PID_FILE:-/tmp/hyb-oa-web-gates-server.pid}"
QA_RUN_ID="$(date +%Y%m%d%H%M%S)_$$_$RANDOM"
QA_SERVER_LOG="${QA_SERVER_LOG:-/tmp/hyb-oa-web-gates-server-${QA_RUN_ID}.log}"
QA_SERVER_PID_FILE="${QA_SERVER_PID_FILE:-/tmp/hyb-oa-web-gates-server-${QA_RUN_ID}.pid}"
QA_SERVER_ENTRY="src/index.js"
QA_READY_TIMEOUT_SECONDS="${QA_READY_TIMEOUT_SECONDS:-90}"
QA_BASE_URL=""
QA_DB_NAME="${QA_DB_NAME:-${QA_DB_PREFIX}_$(date +%Y%m%d%H%M%S)_$RANDOM}"
QA_DB_CREATED=0
QA_SERVER_STARTED=0
QA_SERVER_OWNED=0
DATABASE_URL="postgresql://${QA_DB_USER}:${QA_DB_PASSWORD}@${QA_DB_HOST}:${QA_DB_PORT}/${QA_DB_NAME}?schema=public"
TEST_FIELD_ENCRYPTION_KEY_DEFAULT="${FIELD_ENCRYPTION_KEY:-MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=}"
TEST_FIELD_ENCRYPTION_KEY_DEFAULT="MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY="
qa_require_cmd() {
command -v "$1" >/dev/null 2>&1 || {
@@ -36,7 +46,7 @@ qa_require_cmd() {
qa_usage() {
cat <<USAGE
Usage: bash scripts/qa/run-web-gates.sh <smoke-local|full-local|business-flow-local|project-wide-visual-local|qg6-visual-matrix-local|r70-assist-local|r106-visual-local|r108-mobile-local|r109-mobile-local|r110-mobile-local|smoke-live>
Usage: bash scripts/qa/run-web-gates.sh <smoke-local|full-local|auth-security-local|business-flow-local|project-wide-visual-local|qg6-visual-matrix-local|r70-assist-local|r106-visual-local|r108-mobile-local|r109-mobile-local|r110-mobile-local|smoke-live>
可选环境变量:
QA_PORT / QA_HOST
@@ -213,17 +223,32 @@ qa_dropdb() {
return 0
fi
qa_psql -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${QA_DB_NAME}' AND pid <> pg_backend_pid();" >/dev/null || true
if ! qa_psql -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${QA_DB_NAME}' AND pid <> pg_backend_pid();" >/dev/null; then
echo "[ERROR] 临时数据库连接清理失败" >&2
return 1
fi
if [[ "$QA_DB_CLIENT" == "docker" ]]; then
qa_docker exec -e "PGPASSWORD=$QA_DB_PASSWORD" "$QA_DB_DOCKER_CONTAINER" \
dropdb --if-exists -U "$QA_DB_USER" "$QA_DB_NAME" >/dev/null || true
if ! qa_docker exec -e "PGPASSWORD=$QA_DB_PASSWORD" "$QA_DB_DOCKER_CONTAINER" \
dropdb --if-exists -U "$QA_DB_USER" "$QA_DB_NAME" >/dev/null; then
echo "[ERROR] 临时数据库删除失败" >&2
return 1
fi
QA_DB_CREATED=0
return
fi
PGPASSWORD="$QA_DB_PASSWORD" dropdb --if-exists -h "$QA_DB_HOST" -p "$QA_DB_PORT" -U "$QA_DB_USER" "$QA_DB_NAME" >/dev/null || true
if ! PGPASSWORD="$QA_DB_PASSWORD" dropdb --if-exists -h "$QA_DB_HOST" -p "$QA_DB_PORT" -U "$QA_DB_USER" "$QA_DB_NAME" >/dev/null; then
echo "[ERROR] 临时数据库删除失败" >&2
return 1
fi
QA_DB_CREATED=0
}
qa_cleanup_server() {
if [[ "$QA_SERVER_STARTED" != "1" || "$QA_SERVER_OWNED" != "1" ]]; then
return 0
fi
if [[ -f "$QA_SERVER_PID_FILE" ]]; then
local pid
pid="$(cat "$QA_SERVER_PID_FILE")"
@@ -242,6 +267,8 @@ qa_cleanup_server() {
fi
rm -f "$QA_SERVER_PID_FILE"
fi
QA_SERVER_STARTED=0
QA_SERVER_OWNED=0
if qa_wait_port_free 15; then
return 0
@@ -251,10 +278,23 @@ qa_cleanup_server() {
}
qa_cleanup() {
qa_cleanup_server
if [[ -n "${QA_DB_NAME:-}" && "$MODE" != "smoke-live" ]]; then
qa_dropdb
local original_status=$?
local cleanup_status=0
trap - EXIT
if ! qa_cleanup_server; then
cleanup_status=1
fi
if [[ "$QA_DB_CREATED" == "1" && "$MODE" != "smoke-live" ]]; then
if ! qa_dropdb; then
cleanup_status=1
fi
fi
if [[ "$original_status" != "0" ]]; then
exit "$original_status"
fi
exit "$cleanup_status"
}
qa_export_app_env() {
@@ -267,6 +307,7 @@ qa_export_app_env() {
export BASE_URL="$QA_BASE_URL"
export DATABASE_URL="$DATABASE_URL"
export JWT_SECRET="${JWT_SECRET:-qa_web_gates_secret_key}"
export AUTH_CODE_PEPPER="${AUTH_CODE_PEPPER:-qa_auth_code_pepper_with_at_least_32_bytes}"
export OCR_PROVIDER="${OCR_PROVIDER:-mock}"
export OCR_MAX_ATTEMPTS="${OCR_MAX_ATTEMPTS:-3}"
export OCR_POLL_CRON="${OCR_POLL_CRON:-*/5 * * * * *}"
@@ -290,7 +331,64 @@ qa_export_app_env() {
export ERP_SYNC_ENABLED="${ERP_SYNC_ENABLED:-true}"
export PAYMENT_BATCH_ENABLED="${PAYMENT_BATCH_ENABLED:-true}"
export IM_NOTIFIER_ENABLED="true"
export FIELD_ENCRYPTION_KEY="$TEST_FIELD_ENCRYPTION_KEY_DEFAULT"
export FIELD_ENCRYPTION_KEY="${FIELD_ENCRYPTION_KEY:-$TEST_FIELD_ENCRYPTION_KEY_DEFAULT}"
}
qa_run_auth_security_local() {
case "$QA_HOST" in
127.0.0.1|localhost|::1) ;;
*)
echo "[ERROR] auth-security-local 只允许 loopback API" >&2
exit 1
;;
esac
case "$QA_DB_HOST" in
127.0.0.1|localhost|::1) ;;
*)
echo "[ERROR] auth-security-local 只允许 loopback PostgreSQL" >&2
exit 1
;;
esac
if [[ ! "$QA_DB_NAME" =~ ^hyb_oa_auth_[A-Za-z0-9_]+$ ]]; then
echo "[ERROR] auth-security-local 数据库名必须使用 hyb_oa_auth_ 前缀" >&2
exit 1
fi
qa_select_port
export JOBS_ENABLED=false
export RATE_LIMIT_ENABLED=false
export DEV_SHOW_PASSWORD_RESET_CODE=false
unset JWT_SECRET AUTH_CODE_PEPPER FIELD_ENCRYPTION_KEY
qa_export_app_env
export EMAIL_ENABLED=false
export SMTP_HOST=""
export SMTP_PORT=""
export SMTP_SECURE=false
export SMTP_USER=""
export SMTP_PASS=""
export IM_NOTIFIER_ENABLED=false
export WECOM_ENABLED=false
export WECOM_AGENT_SECRET=""
export OIDC_ENABLED=false
export OIDC_CLIENT_SECRET=""
unset JWT_SECRET AUTH_CODE_PEPPER FIELD_ENCRYPTION_KEY
JWT_SECRET="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("hex"))')"
AUTH_CODE_PEPPER="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("hex"))')"
FIELD_ENCRYPTION_KEY="$(node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("base64"))')"
export JWT_SECRET AUTH_CODE_PEPPER FIELD_ENCRYPTION_KEY
export TRUST_PROXY_HOPS=0
export SERVE_CLIENT_DIST=false
export RUN_AUTH_SECURITY_REGRESSION=1
export AUTH_SECURITY_DATABASE_CONFIRM="$QA_DB_NAME"
QA_SERVER_ENTRY="src/scripts/start-auth-security-test-server.js"
qa_prepare_db
qa_start_server
echo ">>> 执行认证安全与真实 PostgreSQL 并发回归"
(
cd "$SERVER_DIR"
RUN_AUTH_POSTGRES_CONCURRENCY=1 ./scripts/regression-auth-security.sh
)
}
qa_prepare_db() {
@@ -303,6 +401,7 @@ qa_prepare_db() {
echo ">>> 创建临时数据库: $QA_DB_NAME"
qa_createdb
QA_DB_CREATED=1
echo ">>> 执行 Prisma migrate"
(
@@ -318,6 +417,10 @@ qa_prepare_db() {
}
qa_start_server() {
if [[ "$QA_SERVER_STARTED" == "1" || "$QA_SERVER_OWNED" == "1" ]]; then
echo "[ERROR] 本轮 server 已启动,拒绝重复 spawn" >&2
exit 1
fi
if ! qa_wait_port_free 15; then
echo "[ERROR] 端口 ${QA_PORT} 已被占用,请改用 QA_PORT=<port> 后重试。" >&2
exit 1
@@ -328,9 +431,15 @@ qa_start_server() {
echo ">>> 启动本地 server: $QA_BASE_URL"
(
cd "$SERVER_DIR"
nohup node src/index.js > "$QA_SERVER_LOG" 2>&1 &
nohup node "$QA_SERVER_ENTRY" > "$QA_SERVER_LOG" 2>&1 &
echo $! > "$QA_SERVER_PID_FILE"
)
if [[ ! -s "$QA_SERVER_PID_FILE" ]]; then
echo "[ERROR] 未取得本轮 server PID" >&2
exit 1
fi
QA_SERVER_STARTED=1
QA_SERVER_OWNED=1
qa_wait_ready "$QA_READY_TIMEOUT_SECONDS" || {
echo "[ERROR] server 未就绪,最近日志如下:" >&2
@@ -607,6 +716,15 @@ case "$MODE" in
trap qa_cleanup EXIT
qa_run_full_local
;;
auth-security-local)
qa_require_cmd curl
qa_require_cmd jq
qa_require_db_client
qa_require_cmd node
qa_require_cmd npm
trap qa_cleanup EXIT
qa_run_auth_security_local
;;
business-flow-local)
qa_require_cmd curl
qa_require_db_client