#!/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: 配置文件启动与口令不泄漏的判据全部通过"