RudrenduPaul/ShimGuard

GitHub: RudrenduPaul/ShimGuard

ShimGuard 是一个 CLI 工具,用于验证 GitHub 上标记为「已修复」而关闭的 issue 所引用的 PR 是否真正被合并,帮助开发者和安全团队避免信任虚假的修复状态。

Stars: 0 | Forks: 0

# ShimGuard 在你信任 issue 跟踪系统之前,验证那些被标记为“已修复”而关闭的 GitHub issue 是否真的合并了修复代码。 [![CI](https://static.pigsec.cn/wp-content/uploads/repos/cas/ad/ad5834178f7599af9fdda11629d49cae07f2997beec49821b2920eff5bfd50e7.svg)](https://github.com/RudrenduPaul/ShimGuard/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE) [![npm](https://img.shields.io/npm/v/shimguard-cli)](https://www.npmjs.com/package/shimguard-cli) ``` npx shimguard-cli verify sybil-solutions/codex-shim --issues 38,41,42,43,45,46 ``` 针对真实的 `sybil-solutions/codex-shim` 仓库(1000+ 星标)运行的那条单行命令发现了 6 个不匹配(MISMATCH)结果:6 个安全问题,每一个都以“在 PR #52 中修复”的评论被关闭,但实际上 PR #52 从未被合并。直到今天,易受攻击的代码仍然存在于 `main` 分支中。阅读这些已关闭 issue 的人根本不会知道。 ## 为什么会有这个项目 在阅读 issue 跟踪系统时,你信任两个信号:issue 的 `state`(开启或关闭)以及维护者的关闭评论(“在 #N 中修复”)。这两个信号都没有被 GitHub 本身与实际情况进行过验证。维护者可能会引用一个从未合并的 PR 来关闭 issue;自动化机器人可能会在 PR 描述中出现 "fixes #N" 关键字但 PR 尚未正式提交时就将其关闭;或者修复代码可能会在 issue 已经被关闭后遭到回退。这些情况中的任何一种都会导致跟踪系统对一个仍然存在的 bug 显示“已修复”。 ShimGuard 检查的是人类粗略浏览 issue 时不会留意的一点:跟踪系统引用为修复的 PR 是否确实显示 `merged: true`?这是一个小型的、机械的且明确的检查,不是启发式方法,也不是猜测。 ## 安装 ShimGuard 提供了两个独立且同等重要的软件包——选择适合你工具链的一个,或者两者都安装。两者之间没有被互相弃用;它们都通过相同的 GitHub REST API 执行相同的“已关闭 issue 引用了‘在 PR #N 中修复’,那么 #N 真的合并了吗?”检查。 ``` # npm -- JavaScript/TypeScript CLI + 库 npm install -g shimguard-cli # 或者无需安装直接运行一次 npx shimguard-cli verify / --issues # PyPI -- Python CLI + 库(真正的 port,而不是围绕 Node binary 的 wrapper) pip install shimguard-cli ``` npm CLI 需要 Node.js 18 或更高版本(使用内置的 `fetch` API)。 Python 包的 CLI 入口点也是 `shimguard`(例如 `shimguard verify sybil-solutions/codex-shim --issues 45,46`);有关针对 Python 的详细步骤说明,请参阅 [`python/README.md`](./python/README.md) 和 [docs/getting-started.md](./docs/getting-started.md),有关每个发行版的版本历史,请参阅 [CHANGELOG.md](./CHANGELOG.md)。 ![从 npm 安装 shimguard-cli 并针对真实的 sybil-solutions/codex-shim 仓库运行其第一条 verify 命令,报告了 2 个不匹配(MISMATCH)结果](https://raw.githubusercontent.com/RudrenduPaul/ShimGuard/main/docs/demo.gif) ## 快速开始 ``` shimguard verify sybil-solutions/codex-shim --issues 38,41,42,43,45,46 ``` ``` ShimGuard v0.1 -- Tracker Verification: sybil-solutions/codex-shim [MISMATCH] Issue #45 "_resolve_api_key silently falls back to Cursor API key for any model with an empty api_key, forwarding it to arbitrary upstream URLs" https://github.com/sybil-solutions/codex-shim/issues/45 Cited fix: PR #52 (open, not merged) Issue is closed and cites PR #52 as the fix, but that PR is open and was never merged. Summary: 6 MISMATCH, 0 MATCH, 0 UNVERIFIED (6 checked) ``` 发现任何不匹配(MISMATCH)时退出码为 `1`(可用于拦截 CI),当每个被检查 issue 声称的修复确实已合并时为 `0`,遇到使用或网络错误时为 `2`。 ### 可选:验证代码,而不仅仅是合并状态 为了进行更严格的检查,你可以让 ShimGuard 指向 issue 中指出的、包含易受攻击代码的具体文件和模式: ``` cat > patterns.json <<'EOF' { "45": { "path": "codex_shim/settings.py", "pattern": "cursor_key_fallback" } } EOF shimguard verify sybil-solutions/codex-shim --issues 45 --patterns patterns.json ``` 如果 PR 已合并但引用的模式在 `HEAD` 的文件中仍然存在,ShimGuard 依然会报告 `MISMATCH`:已合并的 PR 并不能保证特定的易受攻击代码行已被实际移除。 **信任边界:** `pattern` 会被编译为 JavaScript 的 `RegExp`,并且 `path` 会被验证以确保其保留在目标 repo 内(截至 0.1.3 版本,禁止使用 `..` 遍历到不同的 repo 或 API endpoint)。请仅将 `--patterns` 指向你自己编写或审查过的文件。请参阅 [SECURITY.md](./SECURITY.md)。 ## CLI 参考 ``` Usage: shimguard [options] [command] Verify that GitHub issues closed as "fixed" actually have a merged fix. Catches security issues marked fixed whose PR was never merged. Options: -V, --version output the version number -h, --help display help for command Commands: verify [options] Check whether closed issues in a repo actually have a merged fix help [command] display help for command ``` ``` Usage: shimguard verify [options] Check whether closed issues in a repo actually have a merged fix Arguments: repo target repo as /, e.g. sybil-solutions/codex-shim Options: --issues comma-separated issue numbers to check, e.g. 38,41,42 --patterns JSON file mapping issue number -> {path, pattern} for an optional code-pattern check --token GitHub token for higher API rate limits (defaults to $GITHUB_TOKEN) --format output format: text or json (default: "text") -h, --help display help for command ``` `--format json` 的输出是稳定的,专为脚本和 AI 代理直接解析而设计: ``` { "repo": "sybil-solutions/codex-shim", "checked": 1, "summary": { "mismatch": 1, "match": 0, "unverified": 0 }, "results": [ { "issue": { "number": 45, "title": "...", "state": "closed", "htmlUrl": "..." }, "citedPullRequest": { "number": 52, "state": "open", "merged": false, "htmlUrl": "..." }, "patternCheck": null, "verdict": "MISMATCH", "reason": "Issue is closed and cites PR #52 as the fix, but that PR is open and was never merged." } ] } ``` ![针对真实的 sybil-solutions/codex-shim 仓库运行带有 --format json 参数的 shimguard verify 命令,打印出 issue 45 的结构化 JSON 结论](https://static.pigsec.cn/wp-content/uploads/repos/cas/03/03ac569fe63b3e757a78b15992c1ea4378f2f59e39e55da88ebbb1c288502a7f.gif) ## 库 API ShimGuard 的验证逻辑也可以直接导入使用: ``` import { TrackerVerifier, RestGitHubClient, RegexPatternMatcher } from "shimguard-cli"; const client = new RestGitHubClient(process.env.GITHUB_TOKEN); const verifier = new TrackerVerifier(client, new RegexPatternMatcher(client)); const result = await verifier.verify({ owner: "sybil-solutions", repo: "codex-shim", number: 45 }); console.log(result.verdict); // "MISMATCH" ``` `TrackerVerifier` 接受任何 `GitHubClient` 和一个可选的 `PatternMatcher`(见 `src/types.ts`、`src/pattern-matcher.ts`),它们都是接口(interface),因此未来的本地配置扫描器或不同的代码托管平台可以在不更改验证器本身的情况下直接接入。 Python 包暴露了相同的结构: ``` from shimguard import TrackerVerifier, RestGitHubClient, RegexPatternMatcher, IssueRef client = RestGitHubClient() # or RestGitHubClient(token=os.environ["GITHUB_TOKEN"]) verifier = TrackerVerifier(client, RegexPatternMatcher(client)) result = verifier.verify(IssueRef(owner="sybil-solutions", repo="codex-shim", number=45)) print(result.verdict) # "MISMATCH" ``` ## 对比 没有现有的开源工具会检查“此 issue 跟踪系统声称在 PR #N 中修复,那么 #N 真的合并了吗”。在编写本项目之前,通过搜索 issue 修复验证工具、补丁验证工具和安全公告修复工具验证了这一说法。最接近的同类工具解决的是不同的问题: | 工具 | 它实际检查的内容 | 读取 issue 跟踪系统 / PR 合并状态? | |---|---|---| | **ShimGuard** | GitHub issue 引用的“在 PR #N 中修复”声明是否与 PR #N 的真实合并状态匹配(以及,可选地,引用的代码模式是否已从 `HEAD` 中消失) | 是的,这就是检查的全部内容 | | [gitleaks](https://github.com/gitleaks/gitleaks) / [trufflehog](https://github.com/trufflesecurity/trufflehog) | 提交到源码中的机密信息(API keys、tokens) | 否,扫描文件内容,而不是跟踪系统状态 | | [trivy](https://github.com/aquasecurity/trivy) / [grype](https://github.com/anchore/grype) / [osv-scanner](https://github.com/google/osv-scanner) | 你的依赖树中的已知 CVE | 否,扫描依赖清单/lockfile,而不是跟踪系统状态 | | [Vanir](https://github.com/google/vanir) (Google) | 已知 CVE 的代码签名是否仍然存在于目标源码树中 | 否,从 CVE 追踪到代码,不涉及 GitHub issue 跟踪系统 | | [VFCFinder](https://github.com/s3c2/vfcfinder) (NC State, ASIACCS 2024) | 为*尚未*关联修复的公告寻找可能的修复提交 | 方向相反:寻找缺失的引用,而不是验证现有的引用 | ShimGuard 填补的空白是真实的,而非理论上的。[wow-actions/auto-close-fixed-issues](https://github.com/wow-actions/auto-close-fixed-issues),一个被其他 repo 使用的 GitHub Action,是在 PR 的 `closed` 事件而不是 `merged` 事件时关闭 issue:机器人可以在 PR 关闭的那一刻将 issue 标记为“已修复”,无论它是否真的被合并。GitHub 原生的 `Closes #N` 关键字链接只会在真正合并到默认分支时自动关闭 issue,因此这种特定的故障模式来自于手动维护者评论和第三方自动化,而不是 GitHub 自己的默认行为,这正是为什么事后没有任何机制能捕捉到它的原因。 ## 什么是 ShimGuard,它为什么存在 ShimGuard 是一个 CLI 工具和 npm 库,它通过检查 PR 真实的合并状态(以及可选地检查易受攻击的代码模式是否仍存在于 `HEAD` 中),来验证 GitHub issue 声称的修复(“在 PR #N 中修复”)是否属实。它的存在是因为用一个引用了未合并 PR 的评论来关闭 issue 是一种真实存在的、已被观察到的故障模式,而不是假设:`sybil-solutions/codex-shim`,一个拥有 1000+ 星标的项目,截至撰稿时已有 6 个安全问题是以这种方式关闭的,每一个都引用了同一个未合并的 PR #52。 ShimGuard 不会扫描源代码中的机密信息(相关需求请见 `gitleaks`、`trufflehog`),也不会对依赖项进行常规的漏洞扫描(相关需求请见 `osv-scanner`、`trivy`、`grype`)。它只验证一个具体且狭窄的声明:跟踪系统的“已修复”状态是否符合事实。 ## 常见问题 **ShimGuard 会修改我的仓库或目标仓库吗?** 不会。它只发起只读的 GitHub API 请求(issue、评论、pull request,以及可选的文件内容)。它永远不会进行写入、评论或更改任何内容。 **它需要 GitHub token 吗?** 对于偶尔使用不需要。未经身份验证的请求是可以工作的,但受限于 GitHub 的标准速率限制(60 次请求/小时)。设置 `GITHUB_TOKEN` 或传入 `--token` 可获得更高的已验证限制(5,000 次请求/小时),这在 CI 中非常有用。 **什么算作“引用的修复”?** ShimGuard 会在 issue 正文及其评论中寻找诸如 "Fixed in PR #52"、"fixed by #101" 或 "resolved in #20" 之类的短语,并提取引用的 PR 编号。如果没有找到这样的短语,结果将是 `UNVERIFIED`,而不是 `MATCH` 或 `MISMATCH`:ShimGuard 绝不进行猜测。 **我可以在 CI 中使用它吗?** 可以。当发现任何不匹配(MISMATCH)时,`shimguard verify` 会以退出码 `1` 退出,因此 CI 步骤可以直接据此进行拦截。`--format json` 会为机器人或仪表板提供一份稳定、易于解析的报告。 **既然它不扫描 BYOK shim 配置,为什么叫“ShimGuard”?** 这个想法早期被设定为一个更广泛的、针对 BYOK(自带密钥)模型 shim 的本地配置安全扫描器。该范围后来被缩小到一个更尖锐、更无懈可击的切入点:根据实际合并的代码验证跟踪系统的声明,这就是 v0.1 版本发布的内容。这个名字反映了该项目最初的起源案例(`sybil-solutions/codex-shim`,一个 BYOK 模型 shim),而不是当前版本所不具备的范围。 **ShimGuard 可以在 Windows、macOS 和 Linux 上运行吗?** npm CLI 需要 Node.js 18 或更高版本(为了使用内置的 `fetch` API),只要能运行 Node 的地方它就能运行,包括 Windows、macOS 和 Linux。PyPI 包需要 Python 3.9 或更高版本,并在其自身的分类器中将自己列为操作系统无关(OS Independent)。这两个包都没有原生或编译型的依赖项。 **ShimGuard 与 gitleaks 或 trufflehog 有什么区别?** Gitleaks 和 trufflehog 会扫描文件内容,寻找意外提交到 repo 中的机密信息,如 API keys 和 tokens。ShimGuard 根本不扫描文件内容来寻找机密——它检查的是 GitHub 的 issue 和 pull-request API,寻找一种特定的不匹配情况:一个以“在 PR #N 中修复”关闭的 issue,但其 PR #N 实际上从未被合并。这两类工具捕捉的是不同的故障模式,可以在同一个 pipeline 中运行而不会发生重叠;有关在编写一行代码之前检查过的所有相关工具的完整列表,请参阅上面的“对比”部分。 **为什么 `shimguard --version` 打印的数字与已安装的软件包不同?** 在 npm 包 `0.1.3` 版本中,运行 `shimguard --version` 会报告 `0.1.2`——因为在 `0.1.3` 的安全修复版本中,未能在 `src/cli.ts` 中更新传递给 `commander` 的版本字符串。这只是一个显示上的不匹配,不影响 `verify` 的行为。要检查实际安装的版本,请阅读该包自己的 `package.json` 或运行 `npm view shimguard-cli version`。 **ShimGuard 会自动扫描 repo 中所有已关闭的 issue 吗?** 不会。你需要使用 `--issues 38,41,42` 传入要检查的具体 issue 编号;ShimGuard 目前不会自行遍历 repo 完整的已关闭 issue 历史记录来寻找引用的修复声明。对于大型 repo,请挑选出你关心的 issue,例如那些被标记为 `security` 或与发布里程碑相关联的 issue。 **我可以在商业产品或付费的 CI pipeline 中使用 ShimGuard 吗?** 可以。ShimGuard 采用 MIT 许可(见 [LICENSE](./LICENSE)),允许商业使用、修改和再分发,包括在付费产品或内部工具中使用,无需支付版税,也没有必须将你自己的代码开源的要求。唯一的条件是保留 MIT 声明。 ## 安全 请参阅 [SECURITY.md](./SECURITY.md)。 ## 许可证 MIT,请参阅 [LICENSE](./LICENSE)。
标签:LNA, MITM代理, SOC Prime, TypeScript, 安全插件, 开发工具, 暗色界面, 逆向工具