security(deps): add upper bounds to 5 loose deps + document supply chain policy (#24226)

After the Mini Shai-Hulud supply chain campaign (May 2026) and the litellm
compromise (March 2026), codify the dependency pinning policy that was
established in PRs #2810 and #9801 but never written down for contributors.

Changes:
- pyproject.toml: Add tight upper bounds to the 5 deps that slipped
  through as review escapes from external contributor PRs:
  - hindsight-client>=0.4.22,<0.5 (was >=0.4.22)
  - aiosqlite>=0.20,<0.23 (was >=0.20)
  - asyncpg>=0.29,<0.32 (was >=0.29)
  - alibabacloud-dingtalk>=2.0.0,<3 (was >=2.0.0)
  - youtube-transcript-api>=1.2.0,<2 (was >=1.2.0)

  Pre-1.0 packages get <0.(current_minor+2) — tight enough to block
  hostile minor releases but loose enough to not require bumps every week.

- CONTRIBUTING.md: Add 'Dependency pinning policy' section under Security
  with the full rationale, table of source types + treatments, and examples.

- AGENTS.md: Add concise 'Dependency Pinning Policy' section for AI coding
  agents with the decision table and step-by-step checklist.

- supply-chain-audit.yml: Add dep-bounds job that fails PRs introducing
  PyPI deps without <ceiling upper bounds. Fires on pyproject.toml changes.
  Posts a PR comment with the specific unbounded specs found.

Refs: #2796 #2810 #9801 #24205
This commit is contained in:
Siddharth Balyan 2026-05-15 14:03:08 +05:30 committed by GitHub
parent 681778a0b7
commit 04b1fdaecf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 130 additions and 0 deletions

View file

@ -800,6 +800,47 @@ Hermes has terminal access. Security matters.
If your PR affects security, note it explicitly in the description.
### Dependency pinning policy (supply chain hardening)
After the [litellm supply chain compromise](https://github.com/BerriAI/litellm/issues/24512) in March 2026 and the [Mini Shai-Hulud worm campaign](https://socket.dev/blog/tanstack-npm-packages-compromised-mini-shai-hulud-supply-chain-attack) in May 2026, all dependencies must follow these rules:
| Source type | Required treatment | Rationale |
|---|---|---|
| **PyPI package** | `>=floor,<next_major` | PyPI versions are immutable once published, but new versions can be pushed into your range. A `<next_major` ceiling stops a 1.x install from upgrading to a malicious 2.0.0. |
| **Git URL** (atroposlib, tinker, yc-bench, Baileys) | Full commit SHA | Branches and tags are mutable refs; SHA is content-addressed. |
| **GitHub Actions** | Full commit SHA + version comment | Action tags are mutable refs (e.g. tj-actions/changed-files March 2025). Pin as `uses: owner/action@<sha> # vX.Y.Z` |
| **CI-only pip installs** | `==exact` | Hermetic CI builds; churn is acceptable. |
**Every new PyPI dependency in a PR must have a `<next_major` upper bound.** PRs adding unbounded `>=X.Y.Z` specs will be rejected by reviewers. The `supply-chain-audit.yml` CI workflow also flags dependency manifest changes for manual review.
**How to determine the ceiling:**
- If the package is at version `1.x.y`, use `<2`.
- If the package is at version `0.x.y` (pre-1.0), use `<0.(current_minor + 2)` — e.g. if current is `0.29.x`, use `<0.32`. This gives ~2 minor versions of headroom while keeping the window small enough that a hostile takeover version is unlikely to land inside it.
- Exception: packages with very stable APIs (e.g. `aiohttp-socks`) can use `<1` at reviewer discretion.
**Examples:**
```toml
# ✅ Correct — post-1.0
"openai>=2.21.0,<3"
"pydantic>=2.12.5,<3"
# ✅ Correct — pre-1.0 (tight minor window)
"asyncpg>=0.29,<0.32"
"aiosqlite>=0.20,<0.23"
"hindsight-client>=0.4.22,<0.5"
# ❌ Rejected — no upper bound
"some-package>=1.2.3"
# ❌ Rejected — too tight (blocks legitimate patches)
"some-package==1.2.3"
# ❌ Rejected — too loose for pre-1.0 (allows 80 minor versions)
"some-package>=0.20,<1"
```
**Reference PRs:** #2796 (litellm removal), #2810 (upper bounds pass), #9801 (SHA pinning + supply-chain-audit CI).
---
## Pull Request Process