M63(M60 审计 P0-1/P0-2)。作用域 ≠ 敏感度:`scope='user'` 是共享域,里 面却装着住址(花溪 64 条 active)/私称(叶酱 63)/作息(早睡 38),于是受限车 道(群聊/陌生人)把共享域整锅注入上下文 —— 2026-10-29 群 531082168 的住址泄漏 即此路径。本片给 memory_items 加 `sensitive` 列,把「这条归谁」(scope)与 「这条能不能外说」(sensitive)拆成两根正交的轴。 - 迁移 0028:加列(INTEGER NOT NULL DEFAULT 0)+ **同迁移幂等回填**(精确短语 白名单 花溪/叶酱/早睡,`AND sensitive=0`,不改 updated_at),down 只撤结构; memory 包内 `ensureSensitiveSchema` 同形前滚(任意 *sql.DB 自举)。 - 受限判据:新增 `MemoryItem.SharedVisible()` = 共享域 且 !sensitive,成为 restricted 侧唯一起点;`loop.FilterSharedVisible` 与复用它的 knowledge 工具 自动跟随。主人车道不过这道判据,照常读到全部记忆(sensitive 不是权限系统)。 - 提取器:prompt 追加 sensitive 位(住址/私人称呼/家人/健康/作息与行程 → true, 口味/兴趣/项目约定 → false,含正反例);产出侧与落库侧共用 `resolveSensitive`(模型的类目判断 ∨ 已实证短语白名单),只加标不撤标。 - 回填入口 `BackfillSensitive(ctx, db, dryRun)`:幂等、可 dry-run、返回候选池 前后规模与逐短语计数(生产快照实测 3116 → 2948,标 725 行)。 - 读路径并到 15 列(scanItem + 7 处 SELECT);store.go 拆分 store_insert.go 守住 500 行上限。 验收:go build/vet/test 全绿(memory + loop 单包复跑绿);删实现三处变异分别打红 (sensitiveColumn 恒 0 / SharedVisible 忽略 Sensitive / Extractor 跳过白名单); 生产库快照上跑 0028 后 Go 侧回填 marked=0(迁移 SQL 与 privateFacts 等价)。
18 lines
1.3 KiB
SQL
18 lines
1.3 KiB
SQL
-- 0028 的回滚:只撤结构(删列),**不改写任何一行的其它数据**。
|
|
--
|
|
-- 回滚的代价(如实登记):`sensitive` 的标记值全部丢失 —— 所有行回到「非敏感」,
|
|
-- 于是受限车道(群聊 / 陌生人)会重新把主人的私事捞进候选池,即 2026-10-29 的
|
|
-- 泄漏面重新打开。这是**预期**的回滚代价,不是 bug:本列的值无法从其它列派生
|
|
-- (判据是内容白名单 + 提取器判断,见 up 文件),因此回滚必须显式承认它丢信息。
|
|
--
|
|
-- 回滚**不许**顺手把那些私事行改成别的形态(删行、改 scope、改 status):
|
|
-- - 删行 / 改 scope 是一次 schema 回滚**改数据**,且不可逆(内容就没了);
|
|
-- - 改 status 会把在册记忆藏起来,读侧闸看起来「干净」,但数据其实还在 ——
|
|
-- 那正是本片要消灭的「靠不可见假装安全」。
|
|
-- 要恢复保护就重跑 up(列以默认 0 重建)或执行 memory 包的 BackfillSensitive
|
|
-- (幂等、可 dry-run,见 internal/runtime/memory/sensitive_backfill.go)。
|
|
--
|
|
-- SQLite 的 DROP COLUMN 要求该列不被索引/视图/触发器引用:本列没有索引(见 up),
|
|
-- 因此无需先删索引(0026 的 down 因为带索引才以 DROP INDEX 开头)。
|
|
ALTER TABLE memory_items DROP COLUMN sensitive;
|