diff --git a/.workbuddy/memory/2026-10-21.md b/.workbuddy/memory/2026-10-21.md index 491ce8c..fe895a2 100644 --- a/.workbuddy/memory/2026-10-21.md +++ b/.workbuddy/memory/2026-10-21.md @@ -154,3 +154,44 @@ - ⚠️ 库名带连字符要用双引号:`CREATE DATABASE "tamabox-cli-test"`。 - ⚠️ Edit 工具改 CRLF 文件会保持 CRLF,但**改写时我打的字会转码损坏**(出现过「实测指事/程定/不图」)→ 改完必须逐行读回复核。 - 仓库内统一存 LF,工作区 CRLF 由 `core.autocrlf=true` 自动转换 —— 提交时 warning 属正常,不是行尾被改。 + +--- + +## 配置键名静默失效的根治(commit d300167,已 push) + +### 现场故障 +用户配置 `honeypot_retention_days <= 30`,日志报「蜜罐自动清理未启用:honeypot_retention_days <= 0」。 + +### 根因(探针实测 ini v1.67.0,不是读源码猜的) +1. **键名多一个 t**(retention vs retain)→ ini 对未知键**静默忽略**,保留期成 0。 +2. **`<= 30` 这种写法**让 ini 把键名解析成 `"honeypot_retention_days <"`(带尾随比较符), + 实测输出:`key="honeypot_retention_days <" value="30"` —— 更匹配不上。 +3. **我之前那条警告文案里写的也是错键名** `honeypot_retention_days`, + 等于在误导运维往错的键上配 —— 教训:警告文案里的键名必须与代码读的键名逐字一致。 + +### 实测到的 ini 行为(重要,写 ini 相关代码前必读) +- `MapTo` 在**键缺失或值非法时不覆盖**目标字段 → 可以安全预置缺省值。 +- 键名**大小写敏感**(`Honeypot_Auto_Clean` 不生效)。 +- `HasKey` 能区分「缺失」与「显式 0」。 +- 段不存在时 `MapTo` 返回 nil 且不修改目标。 +- bool 的解析是「有键才赋值」→ **未配置与显式 false 无法用零值区分**,必须靠 HasKey。 + +### 修复三层 +- `internal/conf/keynorm.go`(新):键名归一化(修剪空白、剥尾随 `<` `>` `=`、别名表纠错)。 + ⚠️ 归一化结果必须命中该段**合法键名白名单**才采用 —— 不认识的键不猜,交给未知键告警。 +- `internal/conf/unknown.go`(新):启动逐段比对键名,未知键 WARN + 给出最接近的合法键名。 + 合法键名由**反射从结构体 ini tag 得出**(单一事实来源,新增配置项自动纳入)。 +- `internal/conf/honeypot_defaults.go`(新):蜜罐缺省值语义修正 + (保留期缺省 30、auto_clean 缺省 true 用 HasKey 判、max_rows 缺省 200000、threshold 缺省 1)。 +- 归一化必须在 `MapTo` **之前**做 —— 等 MapTo 跑完,错拼的键早被丢掉了。 + +### 顺带清理 +`app.sample.ini` 里两条死配置:`app.title`(代码从未读,站点标题走 i18n `site.title`)、 +`server.xsrf_expire`(无对应字段)。留着会让新检测对官方模板误报。 + +### 踩坑 +- ⚠️ **Python 脚本里写 Go struct tag 的反引号** → bash 命令替换报错。 + 改用 Edit 工具逐处改(Edit 对 CRLF 文件保持 CRLF,但**我打的字可能转码损坏** → 改完逐行读回复核)。 +- ⚠️ `gofmt -l` 对 CRLF 文件一律报警(gofmt 要求 LF)—— 那是既有状态,不是我的改动。 +- ⚠️ 我自己的测试预期写错过两次(别名生效后的期望值、static_version 不是 ini 键)。 + **测试失败要先怀疑自己的预期**,别急着改产品代码。 diff --git a/.workbuddy/memory/topics/misc.md b/.workbuddy/memory/topics/misc.md index b8afd43..a3e3531 100644 --- a/.workbuddy/memory/topics/misc.md +++ b/.workbuddy/memory/topics/misc.md @@ -37,3 +37,18 @@ `grep | cat -A` 不显示 `^M` 极易误判 → Python 替换用 `\r\n` 锚点,写后统计 `b'\r\n'` 复核。 - ⚠️ Go 里 `time.Duration(60)` = 60 **纳秒**,测试写裸数字会让封禁/冷却瞬间过期 → 一律 `60 * time.Minute`。 - ⚠️ 测 `IsBanned` → 403 必须注册中间件(`f.Use(autobanMiddleware(ab, hp))`),只注册路由+NotFound 会被绕过。 + +## 配置(ini)相关约定 + +- ⚠️ **ini 对未知键静默忽略** → 键名写错 = 配置不生效且无任何现场线索。 + 已加两道防线(`internal/conf/keynorm.go` + `unknown.go`): + 键名归一化(自动纠正常见误拼)+ 未知键启动告警(附最接近的合法键名)。 +- ⚠️ **键名必须与代码逐字一致**,包括警告文案里提到的键名 —— 写错等于误导运维。 +- ⚠️ `MapTo` 在**键缺失/值非法时不覆盖**目标字段 → 可安全预置缺省值。 +- ⚠️ **bool 的 ini 解析是「有键才赋值」** → 「未配置」与「显式 false」无法用零值区分, + 必须用 `HasKey` 判定。缺省为 true 的开关尤其要小心(否则未配置 = 关闭)。 +- 归一化必须在 `MapTo` **之前**执行,否则错拼的键已被丢弃。 +- 合法键名白名单由**反射从结构体 ini tag 得出**,是单一事实来源;新增配置项自动纳入。 +- `TestSampleIniHasNoUnknownKeys` 回归检测:官方样例配置零误报。 + 改结构体字段名时必须同步 `conf/app.sample.ini`,否则这个测试会失败。 +- app.sample.ini 里不要留「代码中从未读取」的死配置(`title` / `xsrf_expire` 已清理)。