Files
webui4frpc/scripts/criteria/config-startup.sh
JianFeeeee ed62fd97a4 test(criteria): 坏 JSON / 未知键分支加超时兜底
判据要求「文件存在但不合法时非零退出」。若实现退化成只记日志继续启动,
进程会一直运行而不是报错——原写法会把判据自己挂死,现在用 timeout 兜底
并把 exit 124 明确判为失败(静默忽略),保证反向变异能被咬住。
2026-10-23 09:14:32 +08:00

95 lines
4.3 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 非法 -> 响亮报错退出 ----
# 用 timeout 兜底:若实现退化成"只记日志继续启动",进程会一直跑而不是报错,
# 这里必须把它判成失败,不能把自己挂死。
printf '{"addr":' > "$WORK/bad.json"
timeout 10 "$BIN" -config "$WORK/bad.json" > "$WORK/bad.log" 2>&1
rc=$?
[ "$rc" -eq 124 ] && fail "配置文件 JSON 非法时进程没有退出(静默忽略),判据要求响亮报错"
[ "$rc" -eq 0 ] && fail "配置文件 JSON 非法时应非零退出,实际正常退出"
grep -qi 'invalid JSON' "$WORK/bad.log" || { cat "$WORK/bad.log"; fail "非法 JSON 的报错信息不明确"; }
# ---- 5. 未知键(拼写错误)-> 报错退出,不得静默回落到默认口令 ----
printf '{"passwd":"x"}' > "$WORK/typo.json"
timeout 10 "$BIN" -config "$WORK/typo.json" > "$WORK/typo.log" 2>&1
rc=$?
[ "$rc" -eq 124 ] && fail "未知键被静默忽略(进程继续运行),拼错的 password 会回落到默认值"
[ "$rc" -eq 0 ] && 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: 配置文件启动与口令不泄漏的判据全部通过"