Supply-Chain Lockdown - claude-jacked

Date: 2026-10-29Mode: audit (baseline) Scope: worktree claude-jacked-m11 (branch jack_20261029_deps_ruff_anthropic, a git worktree), compared with master 92f2232 Not evaluated: AI/ML model deserialization; IDE-extension supply chain

Score & Posture

91
Hardened after this branch (master before it: 25, Critical gaps).
Coverage: 9 of 10 applicable categories (90%). Malware is unknown: Socket is not installed; the manual existence and provenance checks below passed.
Category breakdown (after this branch)
CategoryStatusNotes
Lockfile integrityok54/54 registry packages hashed (1353 sha256 entries); uv lock --check clean; CI uses uv run --frozen; cooldown 2026-10-22; no git-source deps
Dependency CVEsokosv-scanner: no issues; pip-audit (osv + pypi): no known vulnerabilities; CI requirement files: no issues
Malware / typosquatunknownSocket missing (install: curl -fsSL https://socket.dev/install.sh | sh). All 54 lock names exist on PyPI; claude-jacked is registered; the two packages added in 90 days (pyobjc-framework-LocalAuthentication, pyobjc-framework-Security) are cleared by canonical source (ronaldoussoren/pyobjc master setup.py names) and are declared by this repo's pyproject.toml. Python only: no install scripts; CI installs --only-binary=:all:.
CI/Actions hardeningok11/11 uses: SHA-pinned with version comments; permissions: read-all on both workflows; persist-credentials: false on both checkouts; harden-runner (audit) in all 3 jobs; zizmor: no findings; actionlint clean; CI tools hash-locked
Secrets hygieneokgitleaks: 1001 commits, no leaks; .gitignore covers .env*, *.pem, *.key, .npmrc, .pypirc, credential files; pre-commit gitleaks
Provenance & signingokPyPI Trusted Publishing (id-token scoped to the publish job), pypa/gh-action-pypi-publish v1.14.2 (PEP 740 attestations on by default), CycloneDX SBOM per release build
Container hardeningn/aNo Dockerfile
Pre-commit + ScorecardwarnPre-commit: gitleaks, actionlint, zizmor. Scorecard on master 5.5/10: see SCSC-010..012

Ecosystem Inventory

EcosystemEvidenceScanners
Python (uv)pyproject.toml, uv.lock (54 packages)osv-scanner, pip-audit, uv lock --check
GitHub Actionstests.yml, publish.yml, dependabot.ymlzizmor 1.24.1, actionlint, OpenSSF Scorecard
Secrets.gitleaks.toml, .pre-commit-config.yamlgitleaks
Node, Dockernoneskipped

Dependabot PRs in scope: #170pr (step-security/harden-runner v2.22.2 dc2769ab after its rebase), #171pr (actions/download-artifact v8.0.2 9000827c), #172pr (actions/upload-artifact v7.0.2 cf430e03). Each pinned SHA matches its upstream release tag (git ref API).

Findings

[cve][CRITICAL] SCSC-001: 139 known advisories in the locked dependency set

Status: fixed

Evidence: osv-scanner on master's uv.lock: pillow 12.2.0 (up to CVSS 9.8, fix 13.0.0), anyio 4.13.0 (CVSS 9.3), certifi 2026.4.22 (CVSS 9.1), aiohttp 3.13.5 (up to 8.7), starlette 1.0.0 (up to 7.5), multidict 6.7.1 (5.3). Scorecard Vulnerabilities: 85 existing.

Risk: An attacker could send crafted input to a vulnerable aiohttp/starlette code path (the service is an HTTP server and an HTTP client), or exploit pillow image decoding, on every install built from this lock.

Fix: Targeted upgrade of the six packages under a cutoff moved to 2026-10-22: aiohttp 3.14.5, anyio 4.14.2, certifi 2026.10.19, multidict 7.1.0, pillow 13.0.0, starlette 1.9.0 (yarl 1.25.1 follows aiohttp). Pillow cap raised to <14. Pillow 13 needs Python 3.11, and Python 3.10 reached end of life in October 2026, so requires-python is now >=3.11. After: osv-scanner 'No issues found'; pip-audit (osv and pypi databases) 'No known vulnerabilities found'.

[lockfile][HIGH] SCSC-002: The exclude-newer cooldown was five months behind

Status: fixed

Evidence: pyproject.toml [tool.uv] exclude-newer = 2026-05-13. The cooldown is meant to delay a new release by about seven days; left in place it froze every security fix out of the lock.

Risk: An attacker could keep exploiting a fixed vulnerability for months, because the build refuses every release newer than the cutoff, including the fix.

Fix: Cutoff moved to 2026-10-22 (seven days before the audit), with a comment that a stale cutoff is its own risk.

[lockfile][HIGH] SCSC-003: starlette had no upper version bound

Status: fixed

Evidence: pyproject.toml: "starlette>=0.48" (16 prod deps, only this one unbounded).

Risk: An attacker who publishes a poisoned starlette 99.0 could land it in any downstream fresh resolve, whatever this repo's lock says.

Fix: "starlette>=0.48,<2".

[actions-hardening][HIGH] SCSC-004: CI tool installs were not hash-pinned

Status: fixed

Evidence: Scorecard Pinned-Dependencies 5/10: pipCommand not pinned by hash at tests.yml:41 (uv) and publish.yml:32-33 (pip, build, hatchling, cyclonedx-bom, twine).

Risk: An attacker who compromises one of those PyPI projects (or a transitive dependency) could run code in the publish job, the job that holds the PyPI OIDC token.

Fix: New .github/requirements/ci-uv.txt and ci-publish.txt (from .in files, uv pip compile --universal --generate-hashes, same cutoff); workflows install with --require-hashes --only-binary=:all:. Rehearsed locally on Python 3.11: hashed install, python -m build, twine check PASSED (wheel and sdist), cyclonedx-py SBOM 78 KB. osv-scanner on both files: no issues.

[actions-hardening][HIGH] SCSC-005: install.sh piped a remote installer to sh

Status: fixed

Evidence: install.sh:13 curl -LsSf https://astral.sh/uv/install.sh | sh (Scorecard downloadThenRun not pinned).

Risk: An attacker who controls or compromises that URL could run any code on the machine of everyone who runs the jacked installer.

Fix: The script no longer runs another installer: it prints the official uv install command and exits, which matches the README's 'install uv first' step (the script already asked the user to re-run it after installing uv).

[lockfile][MEDIUM] SCSC-006: Dependabot never updated uv.lock

Status: fixed

Evidence: .github/dependabot.yml used package-ecosystem "pip" for a uv project; its PRs (#122pr anthropic, #177pr ruff) changed pyproject.toml only, while CI runs uv run --frozen from uv.lock.

Risk: An attacker benefits when security updates are proposed but never reach the locked build: a merged Dependabot PR looks like a fix and changes nothing.

Fix: package-ecosystem "uv" for the project (7-day cooldown kept), plus a pip entry for .github/requirements.

[lockfile][MEDIUM] SCSC-007: An unused runtime dependency (anthropic)

Status: fixed

Evidence: pyproject.toml declared anthropic>=0.40,<1; no file in the repository imports it (jacked calls the API with httpx).

Risk: An attacker who compromises the anthropic SDK or one of its four exclusive transitive dependencies gains code execution in every jacked install, for code jacked never uses.

Fix: Removed; the lock drops anthropic, distro, docstring-parser, jiter and sniffio. #122pr is superseded.

[precommit][MEDIUM] SCSC-008: Lint was not enforced in CI

Status: fixed

Evidence: tests.yml ran pytest only; a lint regression (E702 in tests/unit/api/test_db_off_loop_guard.py) reached master.

Risk: Not an attack path by itself; it removes a guard that catches unsafe patterns before review.

Fix: tests.yml runs uv run --frozen ruff check . before pytest; the rule set is pinned in [tool.ruff.lint] so a ruff upgrade cannot change it (ruff 0.15 and 0.17 both report 0 findings). #177pr is superseded by the pinned rules plus the <0.18 cap.

[scorecard][MEDIUM] SCSC-009: No security policy

Status: fixed

Evidence: Scorecard Security-Policy 0: no SECURITY.md.

Risk: An attacker benefits when a researcher has no private way to report a vulnerability and reports it in public.

Fix: SECURITY.md added (private report route, supported versions, scope, how releases are built).

[scorecard][MEDIUM] SCSC-010: Branch protection is off on master; private vulnerability reporting is off

Status: manual

Evidence: Scorecard Branch-Protection 0; GET /repos/.../branches/master/protection: 404 Branch not protected; private-vulnerability-reporting: enabled=false.

Risk: An attacker with a stolen maintainer token can force-push master without a status check.

Fix: Repository settings (admin): see Manual Verification. Not changed by this audit.

[scorecard][MEDIUM] SCSC-011: No SAST on commits

Status: fixed

Evidence: Scorecard SAST 0: no CodeQL or other SAST tool runs on commits.

Risk: An attacker benefits from an injection or unsafe-deserialization pattern that a SAST pass would have flagged before merge.

Fix: The workflow .github/workflows/codeql.yml runs CodeQL (Python, JavaScript, GitHub Actions) on each pull request, each push to master and each week (#199i).

Note (2026-11-03): The test client now runs on httpx2 (#200i). PyPI holds no PEP 740 attestation for httpx2 2.16.0, httpcore2 2.16.0 or httpx2-jsfetch 1.0, although the publisher (pydantic/httpx2) uses trusted publishing. The lock's hash pins are the only control for those three packages. httpx2-jsfetch installs only on the emscripten platform, so it does not run on a normal install. truststore and idna carry attestations. The next lockdown pass does not need to re-discover this.

[scorecard][LOW] SCSC-012: Fuzzing, CII badge, single-organization contributors

Status: noted

Evidence: Scorecard Fuzzing 0, CII-Best-Practices 0, Contributors 3, Code-Review 0 (single maintainer).

Risk: Low direct risk; these lower the external trust score.

Fix: Noted only.

Remediation Plan

Manual Verification