DeepSeek Harness 插件

JohnXu22786/secret-guard

Star 数 ★ 0 分类 工具与能力 收录于 2026-08-16

拦截 agent 对敏感文件(.env、凭据、密钥材料)的读写,对工具结果中泄露的机密形状内容做掩码兜底,记录审计日志,并提供永不输出原始值的 sg_* 安全检查工具。

安装

# GitHub 源码(首次需按提示配置 allowBuilds 构建授权后重试)

dsh plugin --profile web add github:JohnXu22786/secret-guard

装任何插件都等于在你的机器上跑第三方代码,权限和你本人一样大——能读你的文件、用你的凭据、访问网络,工具审批管不到它。GitHub 来源的插件还会在安装时执行构建脚本。请只安装可信来源,并尽量锁定 commit(github:owner/repo#sha)。

README

English

secret-guard

面向 DeepSeek Harness(dsh)的安全插件:在 agent 的文件工具执行之前拦截对敏感文件的读写(.env、credentials、密钥文件等),防止 API 密钥等机密泄漏到对话上下文;并对工具结果做内容掩码兜底,即使有内容绕过了拦截也会被清洗。

  • 零构建:纯 TypeScript 源码加载(dsh 用 Node 原生 strip-only 类型剥离加载 .ts,因此源码不得使用参数属性等 strip 不支持的语法——本仓库已遵守,并有 npm run smoke:strip 冒烟检查);自包含,运行时依赖仅 @deepseek-ai/dsh-tools(工具定义)与 schemastery(配置校验)。
  • 拦截层:tools/pre-execute 瀑布事件(在工具体执行前短路)。
  • 兜底层:tools/post-execute 瀑布事件(对结果内容做形状识别掩码)。
  • 配套 sg_* 安全检查工具:只返回键名、行号、形状、布尔值与 HMAC 指纹,永不返回原始值
  • 审计日志:JSONL 追加式、按大小轮转;规则文件热加载(自动轮询 + 手动 sg_reload)。

目录结构

secret-guard/
  package.json         # dsh.bundle.patch 声明;main 指向源码
  cordis.patch.yml     # bundle 补丁层(插件行:id / name / config)
  src/
    index.ts           # 插件入口:name / inject / Config / apply
    config.ts          # 配置 schema(schemastery)+ 校验与默认值
    policy.ts          # 规则引擎:路径归一化、glob 编译、默认规则表
    gate.ts            # tools/pre-execute 拦截监听器
    scrub.ts           # tools/post-execute 内容掩码监听器
    inspect.ts         # dotenv 解析、值形状分类、sg_* 安全工具
    fingerprint.ts     # HMAC-SHA256 封印密钥(seal key)与指纹
    journal.ts         # JSONL 审计日志 + 轮转
    watch.ts           # 规则文件热加载轮询器
  tests/               # node:test + tsx,无需编译即可运行
  README.md
  LICENSE

在 DSH 中安装

dsh plugin --profile demo add github:JohnXu22786/secret-guard

安装后默认配置即生效。卸载:

dsh plugin --profile demo remove dsh-secret-guard

安装与接入(dsh 如何加载它)

插件遵循 dsh 的 Cordis 插件约定:一个导出 name / inject / Config / apply(ctx, config) 的 ESM 模块(入口 src/index.ts),由 cordis.patch.yml 作为 bundle 层插入插件树。注册的副作用(事件监听、工具注册、文件轮询)都在 apply 返回的清理函数与 Cordis 上下文中可逆卸载。

# 从本目录安装到 web profile(等效于 pnpm 链接 + bundle 层加载)
dsh plugin --profile web add .

# 或 headless profile
dsh plugin --profile headless add .

安装后默认配置即生效。检查插件树:

dsh --profile web --dump-config | grep -A 6 secret-guard

本地开发(不安装)

未安装到 profile 时,patch 行里的包名无法从 profile 目录解析,需要用绝对路径 overlay 指向源码入口。注意 Windows 上 Node 的 ESM 不认盘符路径(D:/… 会被当成协议),必须加 file:/// 前缀:

# dev-overlay.yml
- insert:
    - id: secret-guard-dev
      name: 'file:///D:/path/to/secret-guard/src/index.ts'
      config:
        maskResults: true
dsh --profile headless --patch D:/path/to/secret-guard/dev-overlay.yml "请列出当前目录"

环境要求

  • dsh ≥ 0.1.0-rc.6(@deepseek-ai/dsh-tools 类型即 0.1.0-rc.6 的接口面)
  • Node.js ≥ 22.19(或 ≥ 24)
  • peer 依赖:@deepseek-ai/cordis@deepseek-ai/dsh-tools@deepseek-ai/dsh-llm(仅类型)、schemastery(宿主 profile 需提供,dsh plugin add 会自动安装 peer)

插件接口一览

接口 说明
清单 package.jsondsh.bundle.patchcordis.patch.yml
入口 src/index.tsname='secret-guard'inject=['tools']apply(ctx, config)
拦截事件 消费 tools/pre-execute(拒绝则返回 {kind:'deny', reason},短路瀑布)
掩码事件 消费 tools/post-execute(返回替换内容后的 accept 决策)
工具 注册 sg_keys / sg_scan / sg_fingerprint / sg_probe / sg_status / sg_reload
配置 插件行的 config 字段(见下),由 schemastery Config 校验

配置

配置写在 cordis.patch.yml 的插件行 config 下,或通过 profile 的 cordis.patch.yml 补丁覆盖:

- insert:
    - id: secret-guard
      name: 'dsh-secret-guard'
      config:
        # 自定义规则(在默认规则之前求值,先匹配者胜)
        rules:
          - id: my-prod-creds
            match: '**/prod-secrets.yml'
            effect: block            # block | block-read | block-write | allow
            reason: '生产凭据,禁止读写'
        # 放行名单(在任何规则之前检查;同样支持 glob)
        allow:
          - 'tests/fixtures/.env'
          - '**/sandbox.env'
        # 参与拦截的工具(参数含 file_path / path 的文件类工具;默认含 read_image)
        gateTools: [read, write, edit, glob, grep, read_image]
        # grep 的 pattern 命中敏感关键词(env/credential/password…)时也拦截
        guardSearchPatterns: true
        # 结果内容掩码兜底
        maskResults: true
        # 封印密钥(HMAC):优先读环境变量,其次本地文件(自动创建,0600)
        sealKey:
          env: SECRET_GUARD_SEAL_KEY
          path: .secret-guard/seal.key
        # 审计日志
        audit:
          enabled: true
          dir: .secret-guard/logs
          maxBytes: 1048576   # 单文件超过即轮转
          keep: 5             # 保留的轮转文件数
        # 外部规则文件(JSON,可热加载):{ "rules": [...], "allow": [...] }
        rulesFile: .secret-guard/rules.json
        watchRules: true      # 自动轮询热加载(约 400ms 周期)

相对路径(sealKey.pathaudit.dirrulesFile)均相对 dsh 的启动工作目录解析。

规则语法与默认规则表

规则 match 使用类似 .gitignore 的 glob:

  • 包含 / 的模式锚定完整路径**/.aws/credentials 匹配任意深度);**/ 前缀可匹配零层目录;
  • 不含 / 的模式只匹配文件名.env 匹配任意目录下的 .env);
  • ** 跨目录段,* 单段内任意,? 单字符;
  • 匹配不区分大小写(对拦截型规则更安全)。

求值顺序:allow 放行名单 → 自定义 rules(按声明顺序)→ 内置默认规则(首条命中即止)。放行名单与规则中的 allow 效果一致,但放行名单永远最先检查。

匹配目标:规则与放行名单都作用于归一化路径——正斜杠、去掉盘符与前导 /、折叠 ..a/../bb)。因此模式请按相对形式书写(如 tests/fixtures/.env),不要带盘符(C:\…);按绝对路径写放行名单会静默失效。

内置默认规则(id 即日志中的 rule 字段):

id 匹配 效果 说明
guard-vault **/.secret-guard/** block 插件自身存储(封印密钥、审计日志)
env-example ~ env-default .env.example / .env.sample / .env.template / .env.dist / .env.default allow 安全的示例文件
env-file .env block 可能含真实机密
env-variant .env.* block 环境特定机密文件
env-suffixed *.env block-read 非标准命名(如 api.env
aws-credentials **/.aws/credentials block
git-credentials **/.git-credentials block
netrc **/.netrc block
npmrc-auth **/.npmrc block-read 含 registry 令牌
pypirc **/.pypirc block-read 含 registry 令牌
credential-files *credential* block 凭据存储
ssh-rsa / ssh-ed25519 / ssh-ecdsa / ssh-dsa id_rsa block-read SSH 私钥(写入放行,支持密钥生成流程)
key-ext-* *.pem *.key *.ppk *.p12 *.pfx *.jks *.keystore *.kdbx block-read 私钥/密钥库材料

效果语义:block 读与写都拦;block-read 只拦读取类工具(read/read_image/glob/grep),写放行;block-write 反之;allow 全放行。

安全检查工具(sg_*

这些工具故意可以读取被拦截的文件——这正是它们的存在意义——但它们只返回元信息,任何输出路径都不会包含原始值。

工具 用途 返回
sg_keys 列出 dotenv 文件的键 键名、行号、是否为空、值形状标签
sg_scan 值形状扫描 每个键的形状(empty/bool/numeric/jwt/url/hex/base64/opaque)与长度
sg_fingerprint 单个键的确定性指纹 HMAC-SHA256 前 16 位十六进制(同一封印密钥下稳定)
sg_probe 对单个键做布尔提问 is-set / is-empty / starts-with / ends-with / contains / matches(正则,在独立 worker 线程中执行并限时,防病态正则卡死宿主) / equals(常数时间比较,不交换明文) 的布尔结果
sg_status 查看当前策略 规则数、放行名单、拦截工具、掩码/审计开关;check 参数可对任意路径做试分类
sg_reload 立即重读外部规则文件 重载结果(规则数/放行数/错误信息)

用法示例(agent 视角):

sg_status { check: ".env" }        # -> block (rule 'env-file')
sg_keys { file: ".env" }           # 只列出键名
sg_probe { file: ".env", key: "DB_PASSWORD", op: "equals", value: "候选值" }   # true/false
sg_fingerprint { file: ".env", key: "DB_PASSWORD" }   # 9f2c… 稳定指纹

内容掩码兜底(scrub)

即使拦截被绕过(例如通过 bash 执行 cat .env、MCP 工具、未列入 gateTools 的工具),tools/post-execute 仍会对结果文本做形状识别,将疑似机密替换为 [redacted:<类型>:<长度>],并追加一行摘要说明清洗数量。

能力边界(重要):掩码是文本形状匹配,不是安全边界。把内容变换后输出(cat .env | base64rev、拆行拼接等)可以击穿全部形状规则;同理,符号链接、.. 等路径别名属于字符串分类的已知限制(见「安全注意事项」)。它只用于拦住"模型直接把机密原样读进上下文"这一最常见路径,不能替代拦截层。

识别形状(刻意保守,避免误伤哈希、UUID、普通长字符串):

  • PEM 私钥块(-----BEGIN … PRIVATE KEY-----
  • JWT(eyJ… 三段式)
  • Bearer 令牌(Bearer <16+ 字符>
  • 知名密钥前缀:ghp_/gho_/ghu_/ghs_/ghr_sk-AKIA…xox[baprs]-
  • 带明文的连接串(scheme://user:pass@host,要求存在 :密码@
  • key=value 赋值(键名含 password/passwd/secret/token/api_key/access_key/client_secret/private_key/auth_key,值 ≥ 12 字符且不是 <占位符>/xxx/example 等文档占位)

刻意不掩码:裸 base64/十六进制长串(git 提交哈希、UUID 等误伤率高);仅当它们出现在上述上下文中才处理。

审计日志

所有拦截(block)、掩码(mask)、规则重载(reload)、错误(error)都会写入 audit.dir/events.jsonl(JSONL,追加式),超过 maxBytes 自动轮转为 events.1.jsonlevents.2.jsonl…,最多保留 keep 份。

日志契约:永不记录值——条目只含时间戳、事件类型、工具名、路径、规则 id、效果、形状计数、消息。日志路径本身也在默认规则 guard-vault 的保护范围内。

规则热加载

配置 rulesFile 指向一个 JSON 文件({ "rules": [...], "allow": [...] },结构与配置中的同名字段一致):

  • watchRules: true 时每 400ms 轮询一次,文件变化(mtime/大小)后去抖 300ms 自动重载;
  • 或随时调用 sg_reload 立即重载;
  • 重载成功/失败都会写审计日志并在控制台提示;失败的加载不会破坏当前生效的规则(保留旧引擎)。

实现说明:轮询而非 fs.watch,是因为 Windows 上删除被监视目录会泄漏事件循环的退出资格(平台缺陷),且规则文件常被编辑器以重命名方式替换;轮询跨平台行为一致且无泄漏(定时器已 unref)。

安全注意事项

  • 封印密钥(seal key)是 HMAC 指纹的根密钥:不要提交 .secret-guard/seal.key,建议加入 .gitignore;换机或重置后指纹会变化(属预期行为)。
  • 默认把 sealKey.path / audit.dir 留在 .secret-guard/(内置规则 guard-vault 会保护它们)。若改到别处,请自行配置等价拦截规则,否则 agent 可能读到密钥文件或审计日志。
  • 默认规则刻意偏保守(宁可误拦),误拦时用 allow 放行名单精确放行,或 sg_status {check: …} 先验证分类结果。
  • 本插件不迁移、不加密、不移动任何机密文件;它只阻止"把机密读进模型上下文"这一件事。
  • 已知限制(设计取舍):路径分类是纯字符串匹配,不解析符号链接目标,.. 折叠只覆盖字符串层;通过 bash 等执行类工具读取敏感文件不受拦截层约束(掩码兜底尽力清洗输出)。sg_probe 的布尔提问是"值预言机"——理论上可用多轮 contains/equals 查询重构出值,它是为方便 agent 安全核对而设计,不应用于不可信上下文。

开发与测试

npm install        # 仅 devDependencies(tsx / typescript / cordis 运行时 / schemastery)
npm test           # node --import tsx --test tests/*.test.ts(71 个用例,约 3 秒)+ strip-only 加载冒烟
npm run typecheck  # tsc --noEmit

测试覆盖:规则引擎(归一化/glob/默认表/放行名单/自定义规则优先级)、dotenv 解析、指纹与封印密钥、掩码模式与误伤控制、拦截决策矩阵(含搜索工具与关键词守卫)、审计轮转、以及用真实 Cordis 上下文驱动瀑布事件的集成测试(含热加载端到端)。

许可

本项目以 MIT 许可协议发布。

内容来自项目 README(GitHub)↗

链接

同类插件

查看整个分类 →