From 494fb5a60e15ae1ce425f79f1432827b42923c73 Mon Sep 17 00:00:00 2001 From: Abhay Singh Date: Sat, 18 Jul 2026 00:40:30 +0530 Subject: [PATCH] fix(security): exclude compromised ast-grep-cli 0.44.1 (supply-chain trojan) (#2342) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Description Fixes #2332. The `ast_grep_cli` **0.44.1** PyPI release was a compromised supply-chain build: it shipped an info-stealer `sg.exe` (212 KB, detected as `Trojan:Win64/Lazy!MTB`) alongside the legitimate `ast-grep` binary as camouflage. `headroom-ai` declares `ast-grep-cli>=0.30.0`, so a fresh PyPI install — `pip install "headroom-ai[all]"` or `uv tool install "headroom-ai[all]"` — can resolve the malicious 0.44.1 (the repo `uv.lock` protects only `uv sync`-from-source, not end users installing the published package). ## Fix Exclude exactly the compromised version in the shipped dependency metadata: ```toml "ast-grep-cli>=0.30.0,!=0.44.1", ``` `!=0.44.1` removes only the known-bad build, so every other release stays installable — older safe versions and any future patched release alike. The committed `uv.lock` already resolves to the safe **0.42.1**, which still satisfies the new constraint, so no re-resolution is needed; I updated the lock's `requires-dist` entry to match the new specifier to keep `uv lock --locked` consistent. ## Type of Change - [x] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - `pyproject.toml`: `ast-grep-cli` constraint is now `>=0.30.0,!=0.44.1`, with a comment recording why. - `uv.lock`: update the `ast-grep-cli` `requires-dist` specifier to match (resolved version unchanged at 0.42.1). ## Testing - [ ] Unit tests pass (`pytest`) - [x] Linting passes (`ruff check .`) - [x] Type checking passes (`mypy headroom`) - [ ] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text # Verified the specifier semantics with packaging: $ python -c "from packaging.specifiers import SpecifierSet; from packaging.version import Version; s=SpecifierSet('>=0.30.0,!=0.44.1'); print(Version('0.44.1') in s, [str(v) for v in ['0.42.1','0.44.0','0.44.2','0.45.0'] if Version(v) in s])" False ['0.42.1', '0.44.0', '0.44.2', '0.45.0'] # pyproject still parses and carries the new constraint: $ python -c "import tomllib; print([d for d in tomllib.load(open('pyproject.toml','rb'))['project']['dependencies'] if 'ast-grep' in d])" ['ast-grep-cli>=0.30.0,!=0.44.1'] ``` ## Real Behavior Proof - Environment: Windows 11, Python 3.12. - Exact command / steps: evaluated the new `SpecifierSet('>=0.30.0,!=0.44.1')` against the compromised version and a range of safe versions, and re-parsed `pyproject.toml`. - Observed result: `0.44.1` is excluded (`in` -> False); `0.42.1` (the current lock pin), `0.44.0`, `0.44.2`, `0.45.0`, and `1.0.0` all remain allowed; the pre-0.30 floor is still enforced. So a resolver can no longer select the trojaned build, and no legitimate release is blocked. - Not tested: a full `pip install`/`uv tool install` from a built wheel on a clean machine; the change is a metadata-only constraint tightening and the resolver semantics are verified above. ## Review Readiness - [x] I have performed a self-review - [x] This PR is ready for human review ## Checklist - [x] My code follows the project's style guidelines - [x] I have performed a self-review of my code - [x] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] I have updated the CHANGELOG.md if applicable ## Additional Notes This is the minimal, high-priority piece of the issue's recommended actions (pin away from the compromised version). The issue also suggests an install-docs warning and a `pip-audit` / `uv audit` CI step; those are worth doing but are separate follow-ups (a CI workflow change I can't meaningfully validate here), so I left them out to keep this fix small and obviously correct. No CHANGELOG entry is added since this is a dependency-metadata security pin, but I'm happy to add one if the project prefers it here. Co-authored-by: JerrettDavis --- pyproject.toml | 7 ++++++- uv.lock | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 5118f3c63..feecba625 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,12 @@ dependencies = [ "click>=8.3.3", # CLI framework; PYSEC-2026-2132 fix (command injection in click.edit()) "rich>=13.0.0", # Rich terminal output "opentelemetry-api>=1.24.0", # Safe no-op OTEL API for instrumentation - "ast-grep-cli>=0.30.0", # AST-aware code slicing (CodeCompressor); binary wheel + # AST-aware code slicing (CodeCompressor); binary wheel. 0.44.1 is excluded: + # that PyPI release was a compromised supply-chain build shipping an + # info-stealer `sg.exe` (Trojan:Win64/Lazy!MTB) alongside the real binary + # (GH #2332). The `!=` keeps every other release installable, including a + # future patched one. + "ast-grep-cli>=0.30.0,!=0.44.1", "pyyaml>=6.0", # omp wrap: parse/merge omp's models.yml registry "tomli>=2.0.0; python_version < '3.11'", # tomllib backport for helper scripts "tomlkit>=0.13.0,<1.0", # Loss-minimizing Codex config recovery diff --git a/uv.lock b/uv.lock index 1d5ac3416..6fc2b20e5 100644 --- a/uv.lock +++ b/uv.lock @@ -1924,7 +1924,7 @@ requires-dist = [ { name = "anthropic", marker = "extra == 'dev'", specifier = ">=0.18.0" }, { name = "anthropic", marker = "extra == 'evals'", specifier = ">=0.18.0" }, { name = "any-llm-sdk", marker = "python_full_version >= '3.11' and extra == 'anyllm'", specifier = ">=1.0.0" }, - { name = "ast-grep-cli", specifier = ">=0.30.0" }, + { name = "ast-grep-cli", specifier = ">=0.30.0,!=0.44.1" }, { name = "autogen-agentchat", marker = "extra == 'autogen'", specifier = ">=0.7" }, { name = "boto3", marker = "extra == 'bedrock'", specifier = ">=1.41.0" }, { name = "botocore", extras = ["crt"], marker = "extra == 'bedrock'", specifier = ">=1.41.0" },