Files
webui4frpc/scripts/criteria/config-startup.sh
JianFeeeee f4d4e3cd52 test(criteria): 新增「配置文件启动 + 口令不泄漏」判据脚本
scripts/criteria/config-startup.sh 一条命令跑完正向判据:
构建与全量测试、优先级回归测试、配置文件启动后端到端验证
(/proc/<pid>/cmdline 与启动日志均不得出现明文口令)、
非法 JSON 必须非零退出、未知键必须被拒绝、文件缺失必须回落纯 flag、
交付的单元与示例配置不得含 -password。

退出码 0 = 通过,可直接在 CI / 复核里跑。
2026-10-23 09:13:29 +08:00

89 lines
3.9 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# 判据:webui4frpc 支持从配置文件启动,且口令不泄漏。
#
# 覆盖三件事:
# 1. go build ./... 与 go test ./... 全绿(含配置文件优先级的回归测试);
# 2. 优先级 显式 flag > 配置文件 > 内置默认值 —— 未显式传的 flag 不得用
# 默认值压过配置文件;
# 3. 用配置文件(file: 引用口令)启动后,明文口令既不出现在进程命令行,
# 也不出现在启动日志;坏 JSON 必须非零退出,文件缺失必须能正常启动。
#
# 退出码 0 = 通过。
set -u
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT" || { echo "FAIL: 无法进入仓库根 $ROOT"; exit 1; }
WORK="$(mktemp -d)"
PIDS=()
cleanup() { for p in "${PIDS[@]:-}"; do kill "$p" 2>/dev/null; done; wait 2>/dev/null; rm -rf "$WORK"; }
trap cleanup EXIT
fail() { echo "FAIL: $*"; exit 1; }
# ---- 1. 构建与全量测试 ----
go build ./... || fail "go build ./..."
go test ./... > "$WORK/test.log" 2>&1 || { cat "$WORK/test.log"; fail "go test ./..."; }
# ---- 2. 优先级回归测试必须存在且通过 ----
# 这是整个改动最容易做错的一点:Go 的 flag 默认值会无条件覆盖配置文件。
go test ./internal/appconfig/ -count=1 \
-run 'TestResolveFileBeatsFlagDefaults|TestResolveExplicitFlagBeatsFile|TestLoadRejectsUnknownKey|TestSummaryRedactsSecrets' \
> "$WORK/appconfig.log" 2>&1 || { cat "$WORK/appconfig.log"; fail "appconfig 优先级/脱敏回归测试未通过"; }
# ---- 3. 端到端:配置文件启动,口令不落到命令行与日志 ----
BIN="$WORK/w4f"
go build -o "$BIN" ./cmd/webui4frpc || fail "构建二进制失败"
SECRET="CRIT_SECRET_${RANDOM}_9f3a"
printf '%s\n' "$SECRET" > "$WORK/pw"
chmod 600 "$WORK/pw"
python3 -c '
import json, sys
t = sys.argv[1]
json.dump({"addr": "127.0.0.1:17699", "user": "crituser",
"password": "file:" + t + "/pw", "workdir": t + "/data"},
open(t + "/cfg.json", "w"))
' "$WORK"
# 注意:-config 只是路径,口令来自 file: 引用,命令行本身不含秘密。
"$BIN" -config "$WORK/cfg.json" > "$WORK/run.log" 2>&1 &
P=$!
PIDS+=("$P")
sleep 3
kill -0 "$P" 2>/dev/null || { cat "$WORK/run.log"; fail "进程未能从配置文件启动"; }
if tr '\0' ' ' < "/proc/$P/cmdline" | grep -q -- "$SECRET"; then
fail "明文口令出现在进程命令行(ps 可见)"
fi
if grep -q -- "$SECRET" "$WORK/run.log"; then
cat "$WORK/run.log"
fail "明文口令出现在启动日志"
fi
kill "$P" 2>/dev/null; wait "$P" 2>/dev/null
# ---- 4. 文件存在但 JSON 非法 -> 响亮报错退出 ----
printf '{"addr":' > "$WORK/bad.json"
"$BIN" -config "$WORK/bad.json" > "$WORK/bad.log" 2>&1 \
&& fail "配置文件 JSON 非法时应非零退出,实际正常退出"
grep -qi 'invalid JSON' "$WORK/bad.log" || { cat "$WORK/bad.log"; fail "非法 JSON 的报错信息不明确"; }
# ---- 5. 未知键(拼写错误)-> 报错退出,不得静默回落到默认口令 ----
printf '{"passwd":"x"}' > "$WORK/typo.json"
"$BIN" -config "$WORK/typo.json" > "$WORK/typo.log" 2>&1 \
&& fail "未知键应被拒绝(否则拼错的 password 会静默使用默认值)"
# ---- 6. 配置文件缺失 -> 不算错误,回落到纯 flag 行为 ----
"$BIN" -config "$WORK/none.json" -addr 127.0.0.1:17698 -workdir "$WORK/d2" > "$WORK/miss.log" 2>&1 &
P2=$!
PIDS+=("$P2")
sleep 3
kill -0 "$P2" 2>/dev/null || { cat "$WORK/miss.log"; fail "配置文件缺失时应回落到纯 flag 行为并正常启动"; }
kill "$P2" 2>/dev/null; wait "$P2" 2>/dev/null
# ---- 7. 交付的单元/示例配置不得携带明文口令 ----
if grep -rn -- '-password' packaging/common/webui4frpc.service \
packaging/common/config.json scripts/release.sh 2>/dev/null \
| grep -v 'never as -password'; then
fail "打包产物(单元/示例配置/分发模板)仍含命令行口令"
fi
echo "OK: 配置文件启动与口令不泄漏的判据全部通过"