pbl_evidence/tests/s3_evidence_chain.sh
agent.develop 9c76329a56 [S3] live DB harness 端到端 + 删 trigger 复验 + 重放幂等取证脚本
- tests/s3_evidence_chain.sh: 新增 [7] 步,由原始日志程序化装配归属 markdown
- tests/gen_s3_log.py: 取证日志装配器(切片锚点为段落标题,杜绝人工转抄差异)
- 配套探针 s3_trigger_probe/s3_sql_probe/s3_replay_idempotency/
  s3_clock_skew_probe/s3_db_url/s3_clock_compare 已随本链验证
- 取证结果:RUN A/B 均 LIVE DB HARNESS ALL PASS + RC=0(RUN B 在 DROP
  BEFORE INSERT trigger 后执行,SHOW TRIGGERS 0→1→0→0),重放 3 次
  count(*) 恒为 2、id 长度恒 32、无重复主键,CHAIN_RC=0
2026-09-23 01:51:59 +08:00

148 lines
6.5 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.

#!/usr/bin/env bash
# S3 取证链:真实沙箱库 live DB harness 端到端 + 删 trigger 复验 + collector 重放幂等
# 一次原子执行(外部把 stdout+stderr 整体重定向到 chain log),避免沙箱库被并发进程
# DROP 造成竞态,也保证「日志内时间」与「日志文件本身」同源,不可事后拼接。
#
# 顺序:
# [1] 环境 + 时钟基线(date 与文件系统 mtime 两时钟源偏移实测)
# [2] RUN A:python3 tests/m5a_live_db_harness.py --keep(必做项2,grep ALL PASS 门禁)
# [3-0] RUN A 后 trigger 现状(0 条:harness 已不含 BEFORE INSERT 适配)
# [3-1] 按历史 DDL(git 80156fd) 复原适配 trigger → [3-2] drop 前 SHOW TRIGGERS
# [3-3] DROP 语句 → [3-4] drop 后 SHOW TRIGGERS(0 条)
# [3-5] RUN B:无该 trigger 状态下重跑 harness(必做项3,grep ALL PASS 门禁)
# [4] collector 重放幂等(必做项4,脚本自带 exit 1 判定)
# [4b] 重放后 id/行数复核 + trigger 终态
# [5] 清理沙箱库(DROP DATABASE 仅沙箱 schema,凭据不落盘)
# [6] 产物文件系统实测时间(ls -l --time-style=full-iso + stat + 同进程成对打印)
# [7] 由原始日志程序化装配归属 markdown(tests/gen_s3_log.py),保证「原样输出」零转抄差异
#
# 任一步失败立即终止(set -euo pipefail + 显式 grep 门禁),最终退出码写进日志末行。
# 路径全部由脚本自身位置推导,无绝对路径硬编码;OUT 目录可用 S3_OUT 覆盖。
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")" # modules/pbl_evidence
WS="$(dirname "$(dirname "$REPO_ROOT")")" # 机构工作空间
OUT="${S3_OUT:-$WS/projects/pbls/docs/02-develop/evidence/s3}"
CHAIN_LOG="${S3_CHAIN_LOG:-$OUT/s3_chain.log}"
mkdir -p "$OUT"
cd "$REPO_ROOT"
ts() { date '+%Y-%m-%d %H:%M:%S %z'; }
now_ns() { date '+%s.%N'; }
gate() { # gate <日志文件> <必须命中的行> —— 命不中就 exit 1,杜绝「崩了也往下跑」
local f="$1" pat="$2"
if grep -q "$pat" "$f"; then
echo "[GATE] PASS 在 $f 中命中 '$pat'"
else
echo "[GATE] FAIL $f 未命中 '$pat' —— 取证链终止"
exit 1
fi
}
export OUT REPO_ROOT WS CHAIN_LOG
echo "########## S3 EVIDENCE CHAIN START $(ts) (epoch=$(now_ns)) ##########"
echo "[path] REPO_ROOT=$REPO_ROOT"
echo "[path] OUT=$OUT"
echo "[path] CHAIN_LOG=$CHAIN_LOG"
echo "[env] python3 = $(command -v python3) ($(python3 -V 2>&1))"
echo "[env] 凭据唯一事实源 = projects/pbls/env/test.json 的 db.sandbox(scope=sandbox_only,口令不打印)"
echo "[env] 沙箱连接串(脱敏)= $(python3 tests/s3_db_url.py)"
echo
echo "########## [1] 时钟基线:date/time() 与文件系统 mtime 两时钟源偏移实测 ($(ts)) ##########"
python3 tests/s3_clock_skew_probe.py --samples 5 --dir "$OUT"
echo
echo "########## [2] RUN A: python3 tests/m5a_live_db_harness.py --keep ($(ts)) ##########"
T0=$(now_ns)
set +e
python3 tests/m5a_live_db_harness.py --keep > "$OUT/runA.log" 2>&1
RC_A=$?
set -e
T1=$(now_ns)
echo "\$ python3 tests/m5a_live_db_harness.py --keep"
echo "RC=$RC_A 耗时=$(awk -v a="$T0" -v b="$T1" 'BEGIN{printf "%.3f", b-a}')s ($(ts))"
cat "$OUT/runA.log"
[ "$RC_A" -eq 0 ] || { echo "[GATE] FAIL RUN A RC=$RC_A"; exit 1; }
gate "$OUT/runA.log" "LIVE DB HARNESS ALL PASS"
echo
echo "########## [3-0] RUN A 之后沙箱库 trigger 现状(harness 已不再建适配 trigger)($(ts)) ##########"
python3 tests/s3_trigger_probe.py show
echo
echo "########## [3-1] 复原「适配补丁」时代的 BEFORE INSERT trigger(历史版本 git 80156fd 所建)##########"
python3 tests/s3_trigger_probe.py create
echo
echo "########## [3-2] drop 前 SHOW TRIGGERS ($(ts)) ##########"
python3 tests/s3_trigger_probe.py show
echo
echo "########## [3-3] DROP 语句执行 ($(ts)) ##########"
python3 tests/s3_trigger_probe.py drop
echo
echo "########## [3-4] drop 后 SHOW TRIGGERS ($(ts)) ##########"
python3 tests/s3_trigger_probe.py show
echo
echo "########## [3-5] RUN B: 无该 trigger 状态下重跑 harness ($(ts)) ##########"
T2=$(now_ns)
set +e
python3 tests/m5a_live_db_harness.py --keep > "$OUT/runB.log" 2>&1
RC_B=$?
set -e
T3=$(now_ns)
echo "\$ python3 tests/m5a_live_db_harness.py --keep # trigger 已 DROP"
echo "RC=$RC_B 耗时=$(awk -v a="$T2" -v b="$T3" 'BEGIN{printf "%.3f", b-a}')s ($(ts))"
cat "$OUT/runB.log"
[ "$RC_B" -eq 0 ] || { echo "[GATE] FAIL RUN B RC=$RC_B"; exit 1; }
gate "$OUT/runB.log" "LIVE DB HARNESS ALL PASS"
echo "########## RUN B 结束后再次确认 trigger(harness 自建库、全程无 trigger)##########"
python3 tests/s3_trigger_probe.py show
echo
echo "########## [4] collector 重放幂等(同一批事件重放 3 次)($(ts)) ##########"
set +e
python3 tests/s3_replay_idempotency.py --replays 3 > "$OUT/replay.log" 2>&1
RC_R=$?
set -e
echo "\$ python3 tests/s3_replay_idempotency.py --replays 3"
echo "RC=$RC_R"
cat "$OUT/replay.log"
[ "$RC_R" -eq 0 ] || { echo "[GATE] FAIL replay RC=$RC_R"; exit 1; }
gate "$OUT/replay.log" "REPLAY IDEMPOTENT OK"
echo
echo "########## [4b] 重放后 id/行数复核 + trigger 终态 ($(ts)) ##########"
python3 tests/s3_sql_probe.py \
"SELECT COUNT(*) AS evidence_rows FROM pbl_evidence" \
"SELECT COUNT(*) AS event_rows FROM pbl_runtime_event" \
"SELECT id, LENGTH(id) AS id_len, tenant_id, source_event_id, evidence_type FROM pbl_evidence ORDER BY id" \
"SHOW TRIGGERS"
echo
echo "########## [5] 清理沙箱库(DROP DATABASE 仅沙箱 schema,凭据不落盘)($(ts)) ##########"
python3 tests/s3_sql_probe.py --drop-sandbox
echo
echo "########## [6] 产物文件系统实测时间(与日志内 date 对照,排除拼接/事后改写)##########"
echo "[6] 本行 date = $(ts) (epoch=$(now_ns))"
ls -l --time-style=full-iso "$OUT"
echo "--- stat 逐文件(size + mtime)---"
stat -c '%n size=%s mtime=%y' "$OUT/runA.log" "$OUT/runB.log" "$OUT/replay.log" "$CHAIN_LOG"
echo "--- 同一 Python 进程内成对打印 time() 与各文件 mtime(消除跨调用干扰,直接暴露两时钟源偏移)---"
python3 tests/s3_clock_compare.py
echo
echo "########## [7] 装配归属日志(程序化切片,杜绝人工转抄差异)($(ts)) ##########"
DOC="${S3_DOC:-$WS/projects/pbls/docs/02-develop/live-harness-${S3_TASK_KEY:-unknown}.md}"
python3 tests/gen_s3_log.py --out "$DOC" --dir "$OUT" --repo "$REPO_ROOT"
echo "[7] 归属日志 = $DOC ($(stat -c 'size=%s mtime=%y' "$DOC"))"
echo "########## S3 EVIDENCE CHAIN END $(ts) (epoch=$(now_ns)) ##########"
echo "CHAIN_RC=0"