Skip to main content

Verify that a GitHub issue closed as "fixed" actually has a merged fix. Catches security issues marked fixed whose PR was never merged.

Project description

shimguard-cli (Python)

Verify that a GitHub issue closed as "fixed" actually has a merged fix, before you trust the tracker.

PyPI version License: MIT Python versions CI npm version

Why this exists

Reading an issue tracker, you trust two signals: the issue's state (open or closed) and the maintainer's closing comment ("fixed in #N"). Neither signal is verified against reality by GitHub itself. A maintainer can close an issue citing a PR that never merged, an automated bot can close on a "fixes #N" keyword in a PR description before that PR lands, or a fix can get reverted after the issue was already closed. ShimGuard checks the one thing a human skimming issues does not: does the PR the tracker cites as the fix actually show merged: true? This package is the Python distribution -- a genuine, independent port, not a wrapper around the Node binary.

Install

pip install shimguard-cli

or with uv:

uv add shimguard-cli

No external binary to fetch: the verification logic ships inside the wheel as pure Python plus one dependency (requests). The complementary JS/TS distribution installs the same way on the npm side: npm install -g shimguard-cli (or npx shimguard-cli verify ... to run it once without installing) -- see the project README for that package. Both are first-class, maintained together; neither is deprecated in favor of the other.

Quickstart

shimguard verify sybil-solutions/codex-shim --issues 45,46

Real output against the real, currently-live sybil-solutions/codex-shim repo (verified directly against the GitHub API while building this port):

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.

[MISMATCH] Issue #46 "Debug request dump writes full conversation bodies to disk"
  https://github.com/sybil-solutions/codex-shim/issues/46
  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: 2 MISMATCH, 0 MATCH, 0 UNVERIFIED (2 checked)

Exit code is 1 when any MISMATCH is found (useful for gating CI), 0 when every checked issue's claimed fix actually merged, 2 on a usage or network error.

Optional: verify the code, not just the merge status

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

If the PR is merged but the cited pattern is still present in the file at HEAD, ShimGuard still reports MISMATCH. Trust boundary: pattern is compiled as a Python regular expression. Only point --patterns at files you wrote or reviewed yourself -- see SECURITY.md.

Or call the library directly (the agent-native path):

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"

How it works

<owner>/<repo> + issue numbers
   -> fetch issue (state, body) + comments
   -> extract "Fixed in PR #N" style reference
   -> fetch PR #N, check merged == true
   -> (optional) fetch file at HEAD, check whether --patterns regex still matches
   -> verdict: MATCH / MISMATCH / UNVERIFIED -> exit code

TrackerVerifier takes any object implementing the GitHubClient protocol and an optional PatternMatcher, both are structural (typing.Protocol) interfaces, so a different code host or a different matching strategy can plug in without changing the verifier itself -- same extension point as the npm package's GitHubClient/PatternMatcher TypeScript interfaces.

CLI reference

usage: shimguard [-h] [--version] {verify} ...

Verify that GitHub issues closed as "fixed" actually have a merged fix.
Catches security issues marked fixed whose PR was never merged.
usage: shimguard verify [-h] --issues ISSUES [--patterns PATTERNS]
                         [--token TOKEN] [--format FORMAT]
                         repo

positional arguments:
  repo                  target repo as <owner>/<repo>, e.g.
                        sybil-solutions/codex-shim

options:
  --issues ISSUES       comma-separated issue numbers to check, e.g.
                        38,41,42
  --patterns PATTERNS   JSON file mapping issue number -> {path, pattern}
                        for an optional code-pattern check
  --token TOKEN         GitHub token for higher API rate limits (defaults
                        to $GITHUB_TOKEN)
  --format FORMAT       output format: "text" or "json" (default: "text")

--format json output is stable and designed for scripts and AI agents to parse directly (field names match the npm CLI's JSON output exactly):

{
  "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."
    }
  ]
}

Fidelity to the npm package

This is a genuine Python reimplementation of the same detection logic in src/verifier.ts and src/pattern-matcher.ts, not a wrapper around the Node binary. One implementation detail differs by necessity: the TypeScript RegexPatternMatcher runs the --patterns regex match in a Node worker thread with a hard timeout so a catastrophic-backtracking regex can be force-terminated; Python's re module has no equivalent thread-level kill switch, so this port runs the match in a subprocess (multiprocessing.Process) instead and terminates that process on the same 2-second deadline -- same guarantee (a hung match cannot block the CLI forever), different mechanism. See SECURITY.md for the full threat model.

Testing

cd python
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pytest

The suite is a full port of the TypeScript vitest suite (fix-reference extraction, the ReDoS guard, tracker-verdict logic, the pattern matcher, CLI argument parsing and output formatting), plus HTTP-mocked tests for the GitHub REST client and one live test against a real, currently-open GitHub issue/PR pair (sybil-solutions/codex-shim#45 / PR #52) that skips cleanly if the network or GitHub's unauthenticated rate limit is unavailable.

Security

ShimGuard's whole purpose is auditing repos you may not fully trust. It only makes read-only GitHub API requests -- it never writes, comments, or mutates anything in the target repo. GitHub tokens are sent only as an Authorization header to api.github.com and never logged. See SECURITY.md for the full policy and the ReDoS-mitigation history this port carries forward from the npm package's v0.1.1/v0.1.2 security fixes.

Contributing

See CONTRIBUTING.md.

License

MIT, see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

shimguard_cli-0.1.2.tar.gz (20.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

shimguard_cli-0.1.2-py3-none-any.whl (17.5 kB view details)

Uploaded Python 3

File details

Details for the file shimguard_cli-0.1.2.tar.gz.

File metadata

  • Download URL: shimguard_cli-0.1.2.tar.gz
  • Upload date:
  • Size: 20.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for shimguard_cli-0.1.2.tar.gz
Algorithm Hash digest
SHA256 5cfea0a6c1a01daf7a4141586eac25e640a6527fc3acd035a3c91c47c3838a8f
MD5 55a4b93c98e9593ba8012461d8080ecc
BLAKE2b-256 a4b890b08db421f2a2d6ae2c9b1f2ea08eac4f680231c39933149cf997183991

See more details on using hashes here.

Provenance

The following attestation bundles were made for shimguard_cli-0.1.2.tar.gz:

Publisher: publish-pypi.yml on RudrenduPaul/ShimGuard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file shimguard_cli-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: shimguard_cli-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 17.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for shimguard_cli-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 4eb96e3dea79cc8865aec15eaaef438f7a86de5599e3109b6f5e74f592181f1c
MD5 bd17506d9eeeb0f50868b8a865a8394a
BLAKE2b-256 b8b70a534812080ee5a7b76b7473766707646a10968ff29d92aec4e671b7515b

See more details on using hashes here.

Provenance

The following attestation bundles were made for shimguard_cli-0.1.2-py3-none-any.whl:

Publisher: publish-pypi.yml on RudrenduPaul/ShimGuard

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page