From f4d4e3cd5234d1d61e4c87a58b875820613a3b36 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Fri, 23 Oct 2026 09:13:29 +0800 Subject: [PATCH] =?UTF-8?q?test(criteria):=20=E6=96=B0=E5=A2=9E=E3=80=8C?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E5=90=AF=E5=8A=A8=20+=20?= =?UTF-8?q?=E5=8F=A3=E4=BB=A4=E4=B8=8D=E6=B3=84=E6=BC=8F=E3=80=8D=E5=88=A4?= =?UTF-8?q?=E6=8D=AE=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/criteria/config-startup.sh 一条命令跑完正向判据: 构建与全量测试、优先级回归测试、配置文件启动后端到端验证 (/proc//cmdline 与启动日志均不得出现明文口令)、 非法 JSON 必须非零退出、未知键必须被拒绝、文件缺失必须回落纯 flag、 交付的单元与示例配置不得含 -password。 退出码 0 = 通过,可直接在 CI / 复核里跑。 --- scripts/criteria/config-startup.sh | 88 ++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100755 scripts/criteria/config-startup.sh diff --git a/scripts/criteria/config-startup.sh b/scripts/criteria/config-startup.sh new file mode 100755 index 0000000..2aa8bf6 --- /dev/null +++ b/scripts/criteria/config-startup.sh @@ -0,0 +1,88 @@ +#!/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: 配置文件启动与口令不泄漏的判据全部通过"