Merge origin/main into codex/btw-side-question

合入 main 的 norma v0.3.7、会话分组与流量证据更新,保留 /btw 快照和输入布局。同步自动放行、关闭流量绑定的测试断言,并等待归档测试夹具的旁路后台退出。
This commit is contained in:
ji ruo committed 2026-09-11 00:54:51 +08:00
commit 2930c4b915
94 files changed
+9737 -1241

No files matched your search

+71 -1
View File
@@ -1024,6 +1024,11 @@ CREATE INDEX IF NOT EXISTS idx_intercept_pending_status ON intercept_pending(sta
CREATE INDEX IF NOT EXISTS idx_intercept_pending_task ON intercept_pending(task_id, created_at DESC);
-- 补旧库:reason 列(已发版,加列要带 IF NOT EXISTS)。
ALTER TABLE intercept_pending ADD COLUMN IF NOT EXISTS reason TEXT NOT NULL DEFAULT '';
-- Detail payloads are lazy-loaded; NULL preserves the meaning of legacy history.
ALTER TABLE intercept_pending ADD COLUMN IF NOT EXISTS audit JSONB;
ALTER TABLE intercept_pending ADD COLUMN IF NOT EXISTS decision_source TEXT NOT NULL DEFAULT '';
UPDATE intercept_pending SET decision_source=CASE WHEN rule_id IS NOT NULL THEN 'rule'
WHEN reason LIKE '[模型]%' THEN 'model' ELSE 'unknown' END WHERE decision_source='';
-- =====================================================================
-- L. 漏洞发现持久化
@@ -1041,7 +1046,7 @@ CREATE TABLE IF NOT EXISTS findings (
evidence TEXT NOT NULL DEFAULT '',
worker TEXT NOT NULL DEFAULT '',
asset_ids JSONB NOT NULL DEFAULT '[]',
-- 处置状态:pending 待处理 / in_progress 处理中 / confirmed 已确认 / resolved 已处理 /
-- 处置状态:pending 待处理 / in_progress 处理中 / confirmed 已确认 / resolved 已处理 / fixed 已修复 /
-- false_positive 误报 / ignored 忽略 / duplicate 重复 / risk_accepted 风险接受。
-- 取值不加 CHECK:旧库靠下面的 ALTER 补列,CHECK 无法回填,统一由 server 侧白名单校验。
status TEXT NOT NULL DEFAULT 'pending',
@@ -1058,6 +1063,71 @@ CREATE INDEX IF NOT EXISTS idx_findings_status ON findings(status, created_at DE
-- 「按资产」视图靠 asset_ids @> '[<id>]' 反查发现,没有这个 GIN 索引就是全表扫。
CREATE INDEX IF NOT EXISTS idx_findings_asset_ids ON findings USING GIN(asset_ids jsonb_path_ops);
-- 手动复测属于独立会话;结论与原漏洞处置状态分开保存。
CREATE TABLE IF NOT EXISTS finding_retests (
id BIGSERIAL PRIMARY KEY,
finding_id BIGINT NOT NULL REFERENCES findings(id) ON DELETE CASCADE,
conversation_id BIGINT UNIQUE REFERENCES conversations(id) ON DELETE SET NULL,
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','running','completed','failed','stopped')),
verdict TEXT NOT NULL DEFAULT '' CHECK (verdict IN ('','reproduced','fixed','inconclusive')),
notes TEXT NOT NULL DEFAULT '',
snapshot JSONB NOT NULL,
summary TEXT NOT NULL DEFAULT '',
evidence TEXT NOT NULL DEFAULT '',
error TEXT NOT NULL DEFAULT '',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
started_at TIMESTAMPTZ,
finished_at TIMESTAMPTZ
);
CREATE INDEX IF NOT EXISTS idx_finding_retests_history ON finding_retests(finding_id, id DESC);
CREATE UNIQUE INDEX IF NOT EXISTS idx_finding_retests_active ON finding_retests(finding_id)
WHERE status IN ('pending','running');
-- 删除会话保留复测记录,同时解除尚未结束的复测占用。
CREATE OR REPLACE FUNCTION stop_deleted_conversation_retest() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
UPDATE finding_retests SET status='stopped', error='复测会话已删除', finished_at=now()
WHERE conversation_id=OLD.id AND status IN ('pending','running');
RETURN OLD;
END;
$$;
DROP TRIGGER IF EXISTS trg_conversation_retest_delete ON conversations;
CREATE TRIGGER trg_conversation_retest_delete BEFORE DELETE ON conversations
FOR EACH ROW EXECUTE FUNCTION stop_deleted_conversation_retest();
ALTER TABLE findings ADD COLUMN IF NOT EXISTS evidence_version BIGINT NOT NULL DEFAULT 0;
ALTER TABLE findings ADD COLUMN IF NOT EXISTS report_evidence_version BIGINT NOT NULL DEFAULT 0;
CREATE TABLE IF NOT EXISTS traffic_evidence_snapshots (
id TEXT PRIMARY KEY,
source_traffic_id TEXT NOT NULL,
captured_at BIGINT NOT NULL,
url TEXT NOT NULL,
method TEXT NOT NULL,
status INTEGER NOT NULL,
content_type TEXT NOT NULL DEFAULT '',
req_head TEXT NOT NULL,
resp_head TEXT NOT NULL,
req_hash TEXT NOT NULL,
resp_hash TEXT NOT NULL,
req_len BIGINT NOT NULL,
resp_len BIGINT NOT NULL,
unreferenced_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE IF NOT EXISTS finding_traffic_bindings (
id BIGSERIAL PRIMARY KEY,
finding_id BIGINT NOT NULL REFERENCES findings(id) ON DELETE CASCADE,
snapshot_id TEXT NOT NULL REFERENCES traffic_evidence_snapshots(id),
role TEXT NOT NULL DEFAULT 'supporting',
note TEXT NOT NULL DEFAULT '',
position INTEGER NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(finding_id, snapshot_id)
);
CREATE INDEX IF NOT EXISTS idx_finding_traffic_order ON finding_traffic_bindings(finding_id, position, id);
CREATE INDEX IF NOT EXISTS idx_finding_traffic_snapshot ON finding_traffic_bindings(snapshot_id);
-- =====================================================================
-- M. 后端日志持久化
-- =====================================================================