claude-mem 安全策略解析:命令注入防护、隐私标签体系与本地数据边界
【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem
claude-mem 是一个常驻后台的记忆增强组件:hooks 拦截 Agent 会话、worker 子进程压缩上下文、SQLite/Chroma 落盘存储、本地 HTTP 服务对外提供检索。正因为它"会执行系统命令、会管理进程、会读写本地数据、还会把上下文交给外部模型 API",仓库根目录的 SECURITY.md 才成为理解其工程安全设计的核心文档。本文基于该文档逐项展开,并结合源码印证每一项安全措施的真实实现位置——读完后你将了解 claude-mem 如何系统性防御命令注入、如何用双标签体系做内容隐私控制、哪些数据会离开本机、以及发现漏洞后的标准报告流程。
支持版本与漏洞报告流程
SECURITY.md 开篇即明确了支持策略:只有最新发布版本接收安全更新,报告漏洞前应先升级到最新版:
| 版本 | 是否受支持 |
|---|---|
| latest | 支持 |
| older | 不支持 |
报告漏洞的规范流程(摘自原文档):
- 不要创建公开的 GitHub issue、PR 或 discussion;
- 通过邮件alex@cmem.ai报告,或使用 GitHub Security 标签页下的 "Report a vulnerability" 按钮发起私有安全通告(private security advisory);
- 报告中应包含复现步骤、影响评估、受影响版本,如有建议修复方案一并附上。
范围界定:该策略覆盖claude-mem插件及其捆绑组件——hooks、worker 服务、SQLite/Chroma 同步、viewer UI、搜索/规划类 skills。上游依赖(upstream dependencies)的问题应直接报告给对应项目,但也可以同步抄送给 claude-mem 维护者。官方承诺对有效报告48 小时内确认,并力争在下个版本中交付修复。
对于非漏洞类的安全疑问,文档建议先阅读安全关键文件中的代码注释,或发起 GitHub Discussion(而非 Issue);敏感问题可直接邮件联系。
命令注入防护:安全策略的核心防线
claude-mem 为 git 操作和进程管理需要执行系统命令,这是它最主要的攻击面。SECURITY.md 给出的防御三支柱是:数组化参数(array-based arguments)、显式shell: false、输入白名单校验。
安全调用模式
文档给出的标准范式:
// ✅ 安全:数组化参数 + 前置校验 if (!isValidBranchName(userInput)) { throw new Error('Invalid input'); } spawnSync('git', ['checkout', userInput], { shell: false }); // ❌ 危险:字符串插值交给 shell 解释 execSync(`git checkout ${userInput}`);核心逻辑在于:只要参数以数组形式传递且shell: false,用户输入就只会作为单个 argv 参数到达 git 进程,$(...)、; rm -rf、换行符等 shell 元字符没有任何解释机会。
仓库中的实际实现
文档描述的模式在当前代码库中有明确的落地证据:
- src/shared/spawn.ts 是全项目的 spawn 封装层。
spawnHidden()统一注入windowsHide: true,而buildSpawnSyncInvocation()处理了 Windows 下.cmd/.batshim 的参数引号转义(quoteWindowsCmdArgument()),保证含空格的路径在cmd /s /c下不被二次解析——这是"数组化参数"原则在跨平台场景下的完整体现,而不是简单一句shell: false就能覆盖的。 - src/shared/path-utils.ts 提供
normalizePath()(反斜杠归一、压缩连续斜杠)与isDirectChild()(判断文件是否为某目录的直接子项),是"文件路径用path.join()拼接、防止路径穿越"这条规范的具体工具函数。 - src/shared/worktree.ts 中
detectWorktree()解析.git文件时只用statSync/readFileSync+ 严格正则^gitdir:\s*(.+)$提取信息,读取失败统一降级为NOT_A_WORKTREE并告警,全程不产生任何子进程——展示了"能不执行命令就不执行命令"的纵深设计。
CI 层面的纪律检查:spawn 环境隔离
除了"命令如何执行",仓库还防"子进程拿到什么环境"。scripts/check-spawn-env-discipline.cjs 是一个纯 CJS 的静态检查脚本(无需编译步骤即可在 tsc 之前运行):它扫描src/下所有.ts文件,凡是spawn/spawnSync/spawnHidden调用窗口内出现env:且引用了process.env的地方,必须同时出现sanitizeEnv(,否则在file:line级别报错并以非零码退出。脚本头部注释说明了动机:把裸的process.env传给子进程,会让宿主 CLI 的环境变量(如CLAUDE_CODE_EFFORT_LEVEL)和游离的 Anthropic 凭据(如ANTHROPIC_BASE_URL)泄漏进 worker 子进程——这对应了 CHANGELOG 中记录的 #2357、#2375 两起真实事故。该规则还有配套测试 tests/env-isolation.test.ts 复用其findViolations逻辑。
CHANGELOG 中的安全加固记录
CHANGELOG.md 保留了多条与安全直接相关的变更轨迹,印证了"命令注入是本项目长期重点"的事实:
- "Security fix: replaced
execSyncwithexecFileSyncto prevent command injection in file path handling"; - "Shell injection in sync-marketplace: Replaced
execSyncwithspawnSyncfor rsync calls to eliminate command injection via gitignore patterns (#1138)"; - "Fixed PowerShell command injection vulnerability in worker-utils.ts";
- "Security enhancement - Switched from execSync to spawnSync with array arguments to prevent command injection"。
安全审计历史:2025-12-16 命令注入漏洞(Issue #354)
SECURITY.md 记录了最近一次正式审计的结论:
- 严重级别:CRITICAL;状态:RESOLVED;共发现并修复3 处漏洞;
- 修复内容:
- 在
BranchManager.ts中将字符串插值替换为数组化参数; - 新增
isValidBranchName()校验函数; - 移除
bun-path.ts中不必要的 shell 使用; - 建立完整的安全测试套件。
- 在
需要说明的是:以当前仓库结构看,BranchManager.ts已不在src/目录树中(该修复属于历史版本的结构),但isValidBranchName所代表的"分支名白名单校验"这一契约被完整保留在了安全规范里。
输入校验规则:白名单与严格正则
SECURITY.md 为三类高频输入给出了明确的校验基线:
| 输入类型 | 校验规则 |
|---|---|
| 分支名 | 必须匹配/^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/,且不允许包含..(防路径穿越) |
| 端口号 | 必须为纯数字,且落在 1024–65535 区间(低于 1024 的保留端口一律拒绝) |
| 文件路径 | 一律经path.join()归并,防止目录穿越 |
这套规则与源码现状互相印证:
- 端口纪律体现在 src/npx-cli/cmem-memory-credentials.ts:默认 host-observer 端口
37777经parsePort()解析并带默认值回退(parsePort(...) ?? 37777),而非直接透传字符串。 - 路径归并体现在 src/shared/paths.ts:数据目录下的所有状态文件(如
DB_PATH = join(DATA_DIR, 'claude-mem.db'))都通过path.join统一构造,杜绝手工字符串拼接。
进程管理与进程边界
SECURITY.md 在 Process Management 一节列出三条:
- PID 文件保护:进程 ID 存放在用户数据目录(
~/.claude-mem/)下,与数据同域管理; - 端口校验:worker 端口在绑定前完成合法性校验;
- 健康检查:处理请求前先确认 worker 健康状态。
从源码结构看,这些约束有明确的承载点:worker 的 HTTP 中间件 src/services/worker/http/middleware.ts 会校验请求来源,只放行http://localhost:/http://127.0.0.1:前缀的 Origin 以及127.0.0.1/::ffff:127.0.0.1来源的客户端 IP——即使 worker 端口因配置失误暴露,中间件层仍把访问面收敛到回环地址。进程树管理则集中在 src/supervisor/(process-registry.ts、health-checker.ts、shutdown.ts),配套的 src/shared/kill-process-tree.ts 处理跨平台的进程组终止,并有 tests/shared/ 下的一整套跨平台与 PID 复用回归测试。
隐私控制:双标签体系与 hook 层剥离
SECURITY.md 定义了内容隐私的双标签机制:
<private>content</private>——用户级隐私:命中内容直接不被存储;<claude-mem-context>content</claude-mem-context>——系统级标签:防止 claude-mem 自己注入的上下文在下一轮被再次记忆(防止递归存储)。
关键在最后一句:"标签在 hook 层剥离,数据到达 worker/数据库之前就已处理"。
实际实现在 src/utils/tag-stripping.ts,且比文档描述覆盖更广——TAG_NAMES白名单实际包含 5 类标签:
const TAG_NAMES = [ 'private', 'claude-mem-context', 'system_instruction', 'system-instruction', 'persisted-output', 'system-reminder', ] as const;实现要点:
STRIP_REGEX用<(name)\b[^>]*>[\s\S]*?</\1>的反向引用匹配成对标签,g标志支持一次剥离多个;stripTags()返回剥离后的文本与每种标签的剥离计数(counts),供上层观测"这次剥离了多少<private>块";- 设置了
MAX_TAG_COUNT = 100的防御阈值:单次输入剥离标签超过 100 个时记录告警(防异常超大注入刷爆日志); - 另有
isInternalProtocolPayload()专门识别"整段都是<task-notification>协议负载"的输入(上限 256 KiB),避免协议消息被当作用户内容入库。
该模块有对应的测试覆盖(如 tests/utils/tag-stripping.test.ts),保证标签剥离行为不随重构漂移。
数据存储边界:什么在本机,什么会离开本机
SECURITY.md 的 Data Storage 一节给出了明确的本机数据清单:
| 数据 | 位置 |
|---|---|
| SQLite 数据库 | ~/.claude-mem/claude-mem.db |
| 向量存储(Chroma) | ~/.claude-mem/chroma/ |
| 日志 | ~/.claude-mem/logs/ |
| 配置 | ~/.claude-mem/settings.json |
原文明确承诺:claude-mem 自身不上传上述任何状态文件,也不自行采集遥测;所有状态文件(数据库、向量库、日志、配置、supervisor 与 PID 文件)均写在本地用户目录下。
但文档同时给出了一段非常重要的诚实边界说明:claude-mem 按设计会调用上游模型提供商与可选集成来完成工作,因此 observation/transcript/prompt 内容可能通过这些通道离开本机:
- Claude Agent SDK(默认的摘要/观察路径):把 prompt 与 transcript 上下文发送到 Anthropic API;
- 替代提供商(
gemini、openrouter):配置后,同样的上下文改发往对应提供商; - Chroma MCP /
chroma-mcp:启用时通过配置的 embedding 后端计算向量,该后端依用户配置可能是远程 API(对应实现见 src/services/sync/ChromaMcpManager.ts,其默认 host 为127.0.0.1,且子进程 spawn 显式shell: false); - OAuth / keychain 读取:spawn 时从平台原生凭据存储读取 Claude Code OAuth token,token 仅注入 worker 子进程、不由 claude-mem 本身传输(读取实现见 src/shared/oauth-token.ts);
- GitHub Releases / npm registry:版本检查与自更新流程从公共 registry 拉取元数据。
由此文档给出的操作建议是:在发送敏感内容前,审阅~/.claude-mem/settings.json与~/.claude-mem/.env中的 provider/Chroma 配置,并对确需保护的内容使用<private>...</private>标签将其排除在本地存储之外。
权限要求
SECURITY.md 声明的运行时权限面:
- 文件系统:读写
~/.claude-mem/与~/.claude/plugins/; - 网络:localhost 上的 HTTP 服务(默认端口 37777);
- 进程管理:spawn worker 子进程、管理 PID。
明确不需要 root/administrator 提权。端口默认值与源码一致:src/npx-cli/cmem-memory-credentials.ts 定义HOST_OBSERVER_DEFAULT_PORT = '37777',且监听一律绑定127.0.0.1;服务器运行时侧 src/server/runtime/ServerService.ts 同样以DEFAULT_SERVER_HOST = '127.0.0.1'为默认宿主。
安全默认值
- Worker Host:默认绑定
127.0.0.1(仅本机可达); - Worker Port:用户可配置,绑定前校验 1024–65535 区间;
- 日志级别:默认 INFO,日志中不落敏感数据;
- 隐私标签:入库前自动剥离
<private>等隐私内容(即上文 tag-stripping 流水线)。
依赖审计与更新策略
文档给出的依赖治理机制:每次发版前执行npm audit;Dependabot 开启自动接收安全更新;关键依赖每季度人工复审。
更新策略方面:安全补丁在发现后尽快发布;用户应保持 claude-mem 在最新版本、关注 releases 中的安全公告,并查阅 CHANGELOG.md 中与安全相关的变更记录(如前文列出的 execSync→spawnSync、PowerShell 注入修复等条目)。
贡献者安全清单:可直接落地的评审基线
SECURITY.md 末尾为贡献者提供了三条可操作的规范,值得单独摘录,因为它们把"文档承诺"变成了"可评审的验收标准"。
添加命令执行时的铁律:
// ❌ 绝不 execSync(`command ${userInput}`); spawn('command', [...], { shell: true }); // ✅ 始终 spawnSync('command', [userInput], { shell: false });处理用户输入的原则:白名单优先于黑名单;用严格正则做格式校验;做类型检查;数值做区间校验;字符串做长度限制。
PR 提交前评审清单:
- 无带字符串插值/模板字符串的
execSync - 涉及用户输入时不出现
shell: true - 所有 spawn/spawnSync 调用使用数组参数
- 所有用户可控参数都有输入校验
- 针对新攻击面补充了安全测试
- 代码遵循上述安全模式
这份清单与仓库已有的工程化保障(check-spawn-env-discipline.cjs的 env 纪律检查、tag-stripping 测试、kill-process-tree 测试矩阵)共同构成了"规范 → 静态检查 → 自动化测试"的三层防线。
小结
SECURITY.md 不是一份泛泛的安全声明,而是一份与代码库强对应的工程合同:命令执行侧以"数组参数 +shell: false+ 白名单校验"为铁律(src/shared/spawn.ts、src/shared/path-utils.ts 与 CHANGELOG 中的多次注入修复互为证据);数据侧明确划出"本机状态文件绝不上传"与"模型 API 通道内容外发"两条边界;隐私侧用<private>/<claude-mem-context>双标签在 hook 层完成入库前剥离(src/utils/tag-stripping.ts);边界侧以回环地址绑定与端口区间校验收敛网络面。对使用者,最重要的行动项是两条:保持最新版本、在发送敏感内容前审阅 provider 配置并使用<private>标签;对贡献者,则按贡献者清单在评审前逐项自查。
【免费下载链接】claude-memPersistent Context Across Sessions for Every Agent – Captures everything your agent does during sessions, compresses it with AI, and injects relevant context back into future sessions. Works with Claude Code, OpenClaw, Codex, Gemini, Hermes, Copilot, OpenCode + More项目地址: https://gitcode.com/GitHub_Trending/cl/claude-mem
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考