Skip to content

PG 文本/JSONB 列的 NUL(0x00) 净化未统一(skill_content + JSONB 列同类暴露面) #451

Description

@ncw1992120

背景

PR #427(PostgreSQL 一等公民)review 中,@rootdeng 指出 mate_skill_file.content 定义为 TEXT、当 skill 文件含 NUL 字符(0x00)时在 PostgreSQL 上插入会报 invalid byte sequence for encoding "UTF8": 0x00

根因:PG 的 text/jsonb 列不接受 0x00,而 MySQL(utf8mb4 TEXT)和 H2(CLOB)能存——所以这类问题在默认 H2/MySQL 上从不暴露,只有 PG 升一等公民后才会撞上。

该 review 指出的那一列已在 PR #427(commit 70b69a7)修复:在 SkillFileService.applyBundleFiles 写库前剥除 NUL,并让两条安装 fetcher(Zip/Git)一致地跳过二进制文件。但这只是同一类问题的一个实例,本 issue 跟踪剩余暴露面,统一收口。

剩余暴露面

1. mate_skill.skill_content(SKILL.md)

  • 同样是 PG text 列,内容来自相同的 fetcher。
  • Zip 路径的 SKILL.md 已被 ZipSkillFetcher.isLikelyBinary 覆盖(在同一个解压循环里过二进制检测)。
  • Git 路径的 SKILL.md 仍走单独的 GitSkillFetcher 第 ~82 行 Files.readString(skillMd),不经过 isLikelyBinary,理论上同病。NUL(0x00)是合法 UTF-8 字节,readString 会原样保留 → PG 上 skill_content 插入失败。
  • 风险:markdown 几乎不会含 NUL,实际触发概率低,但属于同类缺口。

2. TEXT→JSONB 的 JSON 列(PR #427 引入)

建议方案

统一在「外部内容进入 PG 文本/JSONB 列」的入口处净化 NUL,而非逐列打补丁:

  • skill_content:给 GitSkillFetcher 的 SKILL.md 读取路径补上和 bundle 文件一致的二进制跳过 / NUL 净化。
  • JSONB 列:在 JSON 序列化写库的入口(或 JacksonTypeHandler 路径 / 写库前的统一钩子)剥除 NUL,并补一条 Testcontainers PG 用例覆盖「JSON 值含 NUL」。
  • 可考虑抽一个共享的 stripNul(String) 工具,复用 PR feat(db): 把 PostgreSQL 做成一等公民(独立迁移树 + JSONB + 集成测试) #427SkillFileService 里的实现。

验证

关联

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions