#!/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'