170 lines
7.4 KiB
Bash
Executable File
170 lines
7.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 账号报告采集器回归:使用假 SSH/MySQL 输出,不连接真实内网主机。
|
||
set -euo pipefail
|
||
|
||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||
SCRIPT="$ROOT_DIR/deploy/collect-dogfood-account-report.sh"
|
||
TMP_DIR="$(mktemp -d)"
|
||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||
|
||
fail() {
|
||
printf 'TEST_FAIL %s\n' "$*" >&2
|
||
exit 1
|
||
}
|
||
|
||
openssl genpkey -algorithm ED25519 -out "$TMP_DIR/private.pem" >/dev/null 2>&1
|
||
openssl pkey -in "$TMP_DIR/private.pem" -pubout -out "$TMP_DIR/public.pem" >/dev/null 2>&1
|
||
|
||
{
|
||
for index in $(seq 1 10); do
|
||
printf 'ACCOUNT\t%d\tdogfoodc%02d\tcreator\tACTIVE\t%d\tCLAIMED\t6849315\t%d\t%d\n' \
|
||
"$((1000 + index))" "$index" "$((2000 + index))" "$((4000 + index))" "$((5000 + index))"
|
||
done
|
||
for index in $(seq 1 30); do
|
||
printf 'ACCOUNT\t%d\tdogfoodp%02d\tplayer\tACTIVE\t%d\tCLAIMED\t6849315\t%d\t%d\n' \
|
||
"$((1100 + index))" "$index" "$((2100 + index))" "$((4100 + index))" "$((5100 + index))"
|
||
done
|
||
for index in $(seq 1 12); do
|
||
printf 'RESERVE\t%d\t%d\t%d\tFREE\t6849315\t\t\t\t\n' \
|
||
"$((3000 + index))" "$((4200 + index))" "$((5200 + index))"
|
||
done
|
||
} >"$TMP_DIR/mysql.tsv"
|
||
|
||
{
|
||
for index in $(seq 1 10); do
|
||
printf 'TOKEN|%d|neice_%03d|%d|1|0|-1|6849315\n' \
|
||
"$((4000 + index))" "$index" "$((5000 + index))"
|
||
done
|
||
for index in $(seq 1 30); do
|
||
printf 'TOKEN|%d|neice_%03d|%d|1|0|-1|6849315\n' \
|
||
"$((4100 + index))" "$((10 + index))" "$((5100 + index))"
|
||
done
|
||
for index in $(seq 1 12); do
|
||
printf 'TOKEN|%d|neice_%03d|%d|1|0|-1|6849315\n' \
|
||
"$((4200 + index))" "$((40 + index))" "$((5200 + index))"
|
||
done
|
||
} >"$TMP_DIR/newapi.tsv"
|
||
|
||
cat >"$TMP_DIR/fake-ssh" <<'SH'
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
printf '%s\n' "$*" >"$DOGFOOD_TEST_SSH_ARGS"
|
||
cat >"$DOGFOOD_TEST_SQL_CAPTURE"
|
||
cat "$DOGFOOD_TEST_MYSQL_ROWS"
|
||
SH
|
||
chmod +x "$TMP_DIR/fake-ssh"
|
||
|
||
cat >"$TMP_DIR/fake-docker" <<'SH'
|
||
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
printf '%s\n' "$*" >"$DOGFOOD_TEST_DOCKER_ARGS"
|
||
cat >"$DOGFOOD_TEST_PG_SQL_CAPTURE"
|
||
cat "$DOGFOOD_TEST_NEWAPI_ROWS"
|
||
SH
|
||
chmod +x "$TMP_DIR/fake-docker"
|
||
|
||
export DOGFOOD_COLLECTOR_TEST_MODE=1
|
||
export DOGFOOD_COLLECTOR_TEST_SSH_BIN="$TMP_DIR/fake-ssh"
|
||
export DOGFOOD_COLLECTOR_TEST_SIGNER_KEY="$TMP_DIR/private.pem"
|
||
export DOGFOOD_COLLECTOR_TEST_OPENSSL_BIN="$(command -v openssl)"
|
||
export DOGFOOD_COLLECTOR_TEST_DOCKER_BIN="$TMP_DIR/fake-docker"
|
||
export DOGFOOD_TEST_SSH_ARGS="$TMP_DIR/ssh-args"
|
||
export DOGFOOD_TEST_SQL_CAPTURE="$TMP_DIR/mysql.sql"
|
||
export DOGFOOD_TEST_MYSQL_ROWS="$TMP_DIR/mysql.tsv"
|
||
export DOGFOOD_TEST_DOCKER_ARGS="$TMP_DIR/docker-args"
|
||
export DOGFOOD_TEST_PG_SQL_CAPTURE="$TMP_DIR/newapi.sql"
|
||
export DOGFOOD_TEST_NEWAPI_ROWS="$TMP_DIR/newapi.tsv"
|
||
|
||
report="$TMP_DIR/account-report.json"
|
||
"$SCRIPT" \
|
||
--activation-run-id activation-20260723T010203Z \
|
||
--rc-commit 1111111111111111111111111111111111111111 \
|
||
--output "$report"
|
||
|
||
[ -f "$report" ] && [ -f "$report.sig" ] || fail '没有生成报告及 detached signature'
|
||
[ "$(stat -f '%Lp' "$report" 2>/dev/null || stat -c '%a' "$report")" = 600 ] || fail '报告权限不是 0600'
|
||
[ "$(stat -f '%Lp' "$report.sig" 2>/dev/null || stat -c '%a' "$report.sig")" = 600 ] || fail '签名权限不是 0600'
|
||
openssl pkeyutl -verify -pubin -inkey "$TMP_DIR/public.pem" -rawin \
|
||
-in "$report" -sigfile "$report.sig" >/dev/null 2>&1 || fail 'detached signature 无法验证'
|
||
grep -q 'root@100.64.0.7' "$TMP_DIR/ssh-args" || fail 'SSH 目标不是固定 mini-desktop'
|
||
grep -q '^START TRANSACTION READ ONLY;' "$TMP_DIR/mysql.sql" || fail 'MySQL 查询未进入服务端只读事务'
|
||
grep -q 'FROM game_player p' "$TMP_DIR/mysql.sql" || fail '固定账号查询缺失'
|
||
grep -q 'FROM newapi_quota_pool q' "$TMP_DIR/mysql.sql" || fail '固定额度池查询缺失'
|
||
if grep -Eqi 'newapi_token_key|password|access_token|refresh_token' "$TMP_DIR/mysql.sql"; then
|
||
fail '固定查询选择了敏感列'
|
||
fi
|
||
grep -q 'infra-postgres' "$TMP_DIR/docker-args" || fail 'new-api PostgreSQL 容器不是固定值'
|
||
grep -q 'new-api' "$TMP_DIR/docker-args" || fail 'new-api PostgreSQL 数据库不是固定值'
|
||
grep -q '^BEGIN TRANSACTION READ ONLY;' "$TMP_DIR/newapi.sql" || fail 'new-api 查询未进入只读事务'
|
||
grep -q 'FROM users u' "$TMP_DIR/newapi.sql" || fail 'new-api users 固定查询缺失'
|
||
grep -q 'JOIN tokens t' "$TMP_DIR/newapi.sql" || fail 'new-api tokens 固定查询缺失'
|
||
if grep -Eqi 'SELECT[^;]*(key|access_token|password)' "$TMP_DIR/newapi.sql"; then
|
||
fail 'new-api 固定查询选择了敏感凭据'
|
||
fi
|
||
grep -q "SIGNER_PRIVATE_KEY='/root/dogfood-r1-signer/r1-ed25519-private.pem'" "$SCRIPT" || \
|
||
fail '生产 signer 私钥路径不是固定值'
|
||
grep -q 'realpath.*signer_key' "$SCRIPT" || fail '生产 signer 私钥未校验 canonical 路径'
|
||
grep -q "stat -c '%u'.*signer_key" "$SCRIPT" || fail '生产 signer 私钥未校验 root 所有权'
|
||
|
||
python3 - "$report" <<'PY'
|
||
import json
|
||
import sys
|
||
|
||
report = json.load(open(sys.argv[1], encoding="utf-8"))
|
||
assert report["schema"] == "dogfood-account-database-report/2"
|
||
assert report["issuer"] == "r1-mini-infra"
|
||
assert report["queryId"] == "dogfood-account-readiness-v2"
|
||
assert report["querySchema"] == "dogfood-account-readiness-row/2"
|
||
assert report["rcCommit"] == "1" * 40
|
||
assert len(report["accounts"]) == 40
|
||
assert len(report["reserves"]) == 12
|
||
for item in report["accounts"] + report["reserves"]:
|
||
assert item["newapiUsername"].startswith("neice_")
|
||
assert item["tokenStatus"] == "ENABLED"
|
||
assert item["tokenRemainQuota"] > 0
|
||
assert item["tokenUnlimitedQuota"] is False
|
||
assert item["tokenExpiredTime"] == -1
|
||
serialized = json.dumps(report)
|
||
for secret_name in ("tokenKey", "password", "accessToken", "refreshToken"):
|
||
assert secret_name not in serialized
|
||
PY
|
||
|
||
# 精确规模是签发前置条件,39 个 CLAIMED 账号必须失败且不得留下半成品。
|
||
head -n 51 "$TMP_DIR/mysql.tsv" >"$TMP_DIR/incomplete.tsv"
|
||
export DOGFOOD_TEST_MYSQL_ROWS="$TMP_DIR/incomplete.tsv"
|
||
if "$SCRIPT" --activation-run-id activation-20260723T010203Z \
|
||
--rc-commit 1111111111111111111111111111111111111111 \
|
||
--output "$TMP_DIR/incomplete.json" >/dev/null 2>&1; then
|
||
fail '不完整 40+12 数据仍被签发'
|
||
fi
|
||
[ ! -e "$TMP_DIR/incomplete.json" ] && [ ! -e "$TMP_DIR/incomplete.json.sig" ] || fail '失败后遗留半成品'
|
||
|
||
# new-api 权威状态异常、ID 关联漂移或一名用户匹配多行时都不得签发。
|
||
for mutation in disabled zero id-drift multi-row; do
|
||
case "$mutation" in
|
||
disabled)
|
||
awk -F'|' 'BEGIN{OFS="|"} NR==1{$5=0} {print}' "$TMP_DIR/newapi.tsv" >"$TMP_DIR/newapi-$mutation.tsv"
|
||
;;
|
||
zero)
|
||
awk -F'|' 'BEGIN{OFS="|"} NR==1{$8=0} {print}' "$TMP_DIR/newapi.tsv" >"$TMP_DIR/newapi-$mutation.tsv"
|
||
;;
|
||
id-drift)
|
||
awk -F'|' 'BEGIN{OFS="|"} NR==1{$4=999999} {print}' "$TMP_DIR/newapi.tsv" >"$TMP_DIR/newapi-$mutation.tsv"
|
||
;;
|
||
multi-row)
|
||
cp "$TMP_DIR/newapi.tsv" "$TMP_DIR/newapi-$mutation.tsv"
|
||
head -n 1 "$TMP_DIR/newapi.tsv" >>"$TMP_DIR/newapi-$mutation.tsv"
|
||
;;
|
||
esac
|
||
export DOGFOOD_TEST_NEWAPI_ROWS="$TMP_DIR/newapi-$mutation.tsv"
|
||
if "$SCRIPT" --activation-run-id activation-20260723T010203Z \
|
||
--rc-commit 1111111111111111111111111111111111111111 \
|
||
--output "$TMP_DIR/$mutation.json" >/dev/null 2>&1; then
|
||
fail "new-api 异常仍被签发 mutation=$mutation"
|
||
fi
|
||
[ ! -e "$TMP_DIR/$mutation.json" ] && [ ! -e "$TMP_DIR/$mutation.json.sig" ] || \
|
||
fail "new-api 异常后遗留半成品 mutation=$mutation"
|
||
done
|
||
|
||
printf 'TEST_PASS 账号报告固定 SSH/MySQL、40+12、签名、权限与脱敏校验通过\n'
|