Skip to content

SuperMarioYL/skillsig

Repository files navigation

English | 简体中文

skillsig banner

CI Release License Go Claude Code Skill

skillsig 是给 Claude Code Skill 做签名 + 权限漂移检测的命令行验证器。claude skills update 把更广的权限静悄悄拉进来之前,先在 CI 里把它拦住。

目录


为什么需要它

ComposioHQ/awesome-claude-skillssickn33/antigravity-awesome-skills 两份清单合计 10 万星、1 494+ 个 Skill,每天还以约 280 颗星的速度往上涨。 @affaan-meverything-claude-code 就是其中一份还在持续增长的合集。每一个 Skill 都是一段挂在 Claude Code 上的 prompt + 工具授权 + 文件系统/网络范围;claude skills update 会在用户下一次开会话时 默默拉回最新内容。2026 年 5 月 Ars Technica 报道的 jqwik prompt-injection 事件 已经把这种攻击落实到生产:一个被信任的开发包带毒上线, 让 AI 编码代理删用户的应用输出。

今天没有任何机制能回答:

  • 我团队装的 Skill 里,哪些声明了对 ~/ 的写入?
  • 上周我审过的那份 manifest,是不是就是刚才被拉下来的那份?
  • 某个 Skill 在版本 0.3.0 之后,悄悄多加了一行 Bash(rm -rf …),CI 能拦下来吗?

skillsig 就是来回答这三个问题的:声明范围的 YAML manifest,加上 Sigstore keyless 签名, 再加上 ~/.skillsig/lock.yaml 跨版本权限漂移检测。

架构

Skill 目录(SKILL.md 的 allowed-tools 加 SKILLSIG.yaml manifest)先被解析,scope 引擎把声明范围与实际授权、锁文件基线做 diff,verifier 校验 Sigstore 或 ed25519 签名 bundle,最后报告打印 TRUSTED / UNSIGNED / SCOPE-DRIFTED 表并卡住 CI

每个 Skill 目录有两份输入:SKILL.md(frontmatter 里是实际生效allowed-tools 授权),以及 SKILLSIG.yaml声明意图范围的 manifest,四个轴:model · tools · fs_write · network_egress)。parser 把两者规范化,scope 引擎把声明白名单和实际 授权做 diff(带 Tool(prefix*) 通配语义),再和 ~/.skillsig/lock.yaml 基线比对以捕获 跨版本漂移,verifier 校验 ed25519 或 Sigstore keyless 签名 bundle。整个流程全程本地、 不联网,最终落到一张三色报告表;带 --ci 时一旦出现范围扩张就返回非零退出码,让 CI 直接 fail,而不是事后才让人发现。

60 秒上手

# 1) 装 (macOS:Homebrew tap 即将上线;先用 go install)
go install github.com/SuperMarioYL/skillsig/cmd/skillsig@latest

# 2) 立刻看到它能干嘛(仓库自带 jqwik 复现 fixture)
git clone https://github.com/SuperMarioYL/skillsig && cd skillsig
skillsig verify ./testdata/skills/

# 3) 给自己的 Skill 生成一份 manifest
cd ~/.claude/skills/my-skill
skillsig init
$EDITOR SKILLSIG.yaml          # 收紧 declares.fs_write / declares.network_egress
skillsig sign                  # ed25519 dev 模式;--keyless 走 Sigstore Fulcio
skillsig verify ./testdata/skills/ 的样例输出
SKILL                                       VERDICT        DETAILS
-----                                       -------        -------
skillsig-examples/jqwik-style-bad           SCOPE-DRIFTED  undeclared grant(s): Bash(rm -rf ~/.claude/*)
skillsig-examples/safe-skill                TRUSTED        scope matches declared manifest (sidecar)
scope-mismatch                              UNSIGNED       no skillsig manifest (sidecar or SKILLSIG.yaml)

3 skill(s): 1 trusted, 1 unsigned, 1 scope-drifted

演示

skillsig 演示 — verify, init, sign, diff

录屏走的就是 happy path:verify 仓库自带的 fixture(jqwik 被标红 SCOPE-DRIFTED)、 用 dev ed25519 后端 init + sign 一个 Skill,再 diff 两个版本,抓出悄悄新增的 Bash(rm -rf …) 授权。GIF 由 CI 从 docs/demo.tapevhs 渲染——本地可用 vhs docs/demo.tape 重新生成。

核心概念:skillsig manifest

skillsig 引入的新名词是 manifest:在 SKILL.md 里嵌入一段 fenced YAML 边栏, 或者写到同目录的 SKILLSIG.yaml。它声明四件事 —— model × tools × fs_write × network_egress

skillsig: v1
skill_id: skillsig-examples/safe-skill
version: 0.1.0
declares:
  model: claude-opus-4-7        # 作者声明的目标模型(SKILL.md 里没有这个字段)
  tools:                         # 与 SKILL.md `allowed-tools` 一一比对
    - Read
    - Edit
    - Bash(git status*)
    - Bash(git diff*)
  fs_write:                      # 仅工作区,不允许 $HOME
    - "${WORKSPACE}/**"
  network_egress: []             # 空 = 不联网
attestation:
  sigstore_bundle: ./skillsig.bundle

skillsig verify 把 manifest 的 declares.tools 当作白名单,扫描 SKILL.md 真正生效的 allowed-tools:凡是 actuals 里没在白名单中出现的条目,就是这次扫描标红 SCOPE-DRIFTED 的原因 —— 这正是 jqwik 那条 Bash(rm -rf ~/.claude/*) 走过来的路径。

对比:skillsig vs 既有方案

维度 skillsig awesome-list 人工 review Sigstore / SLSA 单独用 平台自带(推测中)
列出 1 494+ 个 Skill 的权限差异 partial
(model, tools, fs_write, net) 这个 tuple 做声明 partial
update 之后检测权限漂移
Sigstore keyless 签名 ✓(m2 上线) partial
跨宿主(Cursor / Codex / Gemini / Antigravity) ✓ 格式可移植 partial
中国大陆可用 ✓(CI 内签名 + 离线 verify) partial(Fulcio 联通问题)

说句公道话:awesome-claude-skillsantigravity-awesome-skills发现 这一层做得比 skillsig 完整得多;skillsig 不替代它们,而是补上后续的 信任 这一层。

配置一览

skillsig verify 默认零配置,下面这几项可按需打开:

配置 类型 默认 含义
SKILLSIG_HOME env $HOME/.skillsig 锁文件 / 临时凭据存放目录
--ci flag false 出现 UNSIGNED 或 SCOPE-DRIFTED(含跨版本锁漂移)时退出码非零,CI 用
--trust flag false 把当前所有 TRUSTED 的 Skill 范围写进 ~/.skillsig/lock.yaml,作为后续漂移检测的基线(首次信任一份 Skill 语料时跑一次)。已相对锁基线漂移的 skill 不会被写入——它判 SCOPE-DRIFTED 且保留旧基线,需 --force-trust 才重设
--force-trust flag false 配合 --trust:把已 SCOPE-DRIFTED 的 skill 也重设为基线(用变宽后的范围覆盖旧的)。默认关闭,避免 --trust 静默把范围升级洗白进锁
--json flag false 输出机器可读的 JSON 报告(verify / diff 都支持),方便 CI 用 jq 解析
--sarif string "" 额外写一份 SARIF 2.1.0 报告到该路径(- 表示标准输出,此时 SARIF 是 stdout 上唯一文档),供 GitHub 代码扫描在 PR 上行内标注漂移
--no-color flag false 关闭 ANSI 上色,方便 diff
attestation.sigstore_bundle yaml ./skillsig.bundle manifest 指向的签名 bundle 路径
~/.skillsig/lock.yaml yaml 自动 每个 skill_id 上一次 TRUSTED 时的范围基线(由 --trust 写入;verify / verify --ci 都会比对它来抓跨版本漂移)

发布到 CI

GitHub Actions:

- name: verify installed skills have not drifted
  run: |
    go install github.com/SuperMarioYL/skillsig/cmd/skillsig@latest
    skillsig verify --ci ./skills/

GitLab CI:

verify-skills:
  image: golang:1.24
  script:
    - go install github.com/SuperMarioYL/skillsig/cmd/skillsig@latest
    - skillsig verify --ci ./skills/

先在可信状态跑一次 skillsig verify --trust ./skills/ 把当前范围写进锁文件作基线, 之后每次 verify --ci 都会拿当前 manifest 和锁基线比对——某个 Skill 在版本更新后悄悄 拓宽了 fs_write / network_egress / tools(即便 SKILL.md 的 allowed-tools 仍在 声明范围内),就会被判 SCOPE-DRIFTED 并 fail,无需再单独跑 diff

--ci 让任何一行 UNSIGNEDSCOPE-DRIFTED(含上面的跨版本锁漂移)都直接 fail。配合 --no-color 就能拿到稳定 diff 的纯文本输出。需要在流水线里按结果分支时,用 --json 拿结构化输出:

# 顶层 .drift 为 true 即代表有 UNSIGNED / SCOPE-DRIFTED 行
skillsig verify --json ./skills/ | jq -e '.drift == false'

verify --json 给出 per-skill 数组、汇总计数和顶层 drift 布尔(语义同 --ci); diff --json 给出 escalation 布尔加上越权的具体授权项。

需要让漂移直接显示在 PR 的代码视图里,用 --sarif 产出 SARIF 2.1.0 报告,再交给 GitHub 代码扫描的 upload-sarif 动作。每个 SCOPE-DRIFTED 是 error、UNSIGNED 是 warning,干净的 PR 不产生任何标注:

- run: skillsig verify --ci --sarif skillsig.sarif ./skills/
  continue-on-error: true   # 让 upload 步骤总能跑到
- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: skillsig.sarif

路线图

  • m1 — manifest schema + parser + skillsig verify 3-color 表 + jqwik fixture 复现
  • m2skillsig sign:ed25519 dev 模式 + Sigstore keyless OIDC 占位(接 sigstore-go)
  • m3skillsig diff old/ new/ + ~/.skillsig/lock.yaml 跨版本漂移(v0.2.0 修正 glob 感知 + 加 --json
  • v0.3.0 加固 — glob 覆盖加上 segment 边界(堵住 api.github.com* 误盖 api.github.com.attacker.net* / ** 混淆),并加 verify --sarif 让 GitHub 代码扫描在 PR 上行内标注漂移
  • v0.4.0 — 让跨版本锁漂移真正在 verify --ci(与 SARIF 标注)里生效:此前 verify 走的是版本内检查,从不构造 Scanner,跨版本漂移只有 diff 能抓——现在 verify 直接走锁感知扫描,并新增 verify --trust 写基线;同时修掉 --sarif - 把表格 / JSON 与 SARIF 拼在 stdout 上导致无法解析的问题
  • v0.5.0 — 补上最后一条没做边界的轴:tools 轴。此前 Bash(git status*) 用裸前缀匹配,会把形近的兄弟命令 Bash(git statuscheckout --force) 也当成已覆盖(它只是文本前缀相同、其实是另一个子命令),于是重签的 skill 换个同前缀子命令就能在 diffverify --ci 里静默逃过 SCOPE-DRIFTED。现在通配符做参数 token 边界判断(额外文本必须另起一个以空格分隔的 token),与 fs_write / network_egress/. 边界一致——git status -s / --porcelain 这类细化仍算覆盖,形近兄弟命令则判为增长。四条声明轴(model × tools × fs_write × network_egress)至此对“变宽”有了统一定义
  • v0.6.0 — 两个校验正确性修复。① verify 真正校验签名 bundle 了:此前 runVerify 从不调用 verifier 包——verifier.VerifySkill(规范化 manifest、定位 skillsig.bundle、验 ed25519 签名)虽已实现却零调用者,于是一个范围合规但没有 bundle、或 bundle 与 manifest 不匹配的 skill 照样判 TRUSTED。攻击者篡改 manifest 覆盖恶意授权、再不带(或带无效)bundle,在冷启动(尚无锁基线)时就能让 verify --ciTRUSTED——正是 skillsig 要防的供应链向量。现在 verify 把签名校验并入每一行:缺 bundle 的把 TRUSTED 降为 UNSIGNED,bundle 验不过(被篡改/伪造)的降为 SCOPE-DRIFTED(并让 --ci 失败),keyless bundle 标为待验,TRUSTED 现在要求签名有效。② verify --trust 不再静默把漂移的范围重设为基线ScanAndTrust 此前只跑版本内检查,就把当前(可能已变宽的)声明写进锁,于是一个悄悄变宽范围的 skill 被重设为新基线并打印 TRUSTED,把漂移永久抹掉。现在 --trust 先跑跨版本锁漂移检查:漂移的 skill 判 SCOPE-DRIFTED 且保留旧基线(后续 plain verify 仍能抓到),要故意重设漂移基线得显式加 verify --trust --force-trust
  • v0.7.0 — 补上 v0.6.0 两处修复遗漏的收口缺口:v0.6.0 让 verify 展示(以及 --ci 失败)时的 TRUSTED 要求签名有效,但 verify --trust写入路径仍只凭范围检查决定往 ~/.skillsig/lock.yaml 里钉什么——而范围检查对签名一无所知。于是一个有 manifest、范围合规但没有 bundle 的 skill 展示为 UNSIGNED,其声明范围却被静默钉进锁,违背了 "只钉你真正背书的范围" 契约,并重新打开一条洗白通道(等这个从未背书的 skill 日后补上任意有效 bundle,它变宽的范围恰好匹配预先钉好的基线,读出来就是干干净净的 TRUSTED、零漂移)。现在 verify --trust 按签名裁决门控写入:SIGNED / KEYLESS-PENDING 的 skill——正是 verify 展示为 TRUSTED 的那一批,NO-BUNDLEBAD-SIGNATURE 一律不钉(clean-TRUSTED--force-trust 两条路径都如此)
  • 托管档skillsig.cloud 托管镜像 + 团队策略 + 飞书/Slack/微信群 webhook 告警
  • runtime hook — 在 host CLI 加载 Skill 之前把声明范围当成沙箱配置应用

之后的计划见 CHANGELOG.md

付费版(v0.2)

OSS 这一半永远免费,verify 永远免费。但 v0.1 README 把付费路径写在显眼处,是因为 靠卖 skillsig.cloud 托管镜像 + 告警这一档来养仓库的迭代速度。

档位 价格 内容
个人 / OSS ¥0 skillsig verify / init / sign / diff、自托管签名、社区支持
团队 ¥99/月(≤10 席) skillsig.cloud 镜像、团队策略 YAML、微信群 / Slack webhook、订阅 awesome-list 漂移告警
企业 ¥999/月 SSO、本地化部署、审计日志、专属 SLA、签名身份审计

国际定价:$14/$140 USD。微信支付 + 支付宝 + Stripe 全部接,账号在 skillsig.cloud 开放白名单(v0.1 启动期为内测)。想列入早期客户可以 开 Issue 打招呼

贡献 & 许可

  • 想交个 fixture?把 SKILL.md 放到 testdata/skills/ 下,开 PR 即可。
  • 想给 awesome-list 加 signed: 字段?我们正在做样例 PR,欢迎一起。
  • License: MIT,永远是 MIT,没有 CLA。

Share this

skillsig — 给 Claude Code Skill 做签名 + 权限漂移检测的命令行验证器。
jqwik 事件之后,Skill 装得越多越要把权限关进 manifest。
github.com/SuperMarioYL/skillsig

About

Signed manifest + cross-version scope-drift detector for Claude Code skills

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages