Supply-Chain Lockdown — claude-jacked

Status:significant exposure
Date:2026-05-20
Branch:master
Mode:audit --paranoid
Repo:jackneil/claude-jacked

Executive Summary

claude-jacked is published to PyPI, used as a developer-tool dependency, and ships behavioral rules + hooks that Claude Code installs into users' shells. That blast radius makes the publish pipeline the highest-value target in this repo. The pipeline currently passes core dependency-integrity checks (uv.lock has SHA-256 hashes; pip-audit reports zero vulnerabilities; PyPI Trusted Publishers eliminates long-lived PYPI_API_TOKEN exposure), but four high-risk patterns remain:

  1. Publisher action pypa/gh-action-pypi-publish is tag-pinned, not SHA-pinned — a tag-mutation attack (tj-actions class) would publish poisoned wheels to PyPI under claude-jacked's name.
  2. No step-security/harden-runner egress filtering — a compromised dep installed during build could exfil to anywhere unobserved.
  3. No persist-credentials: false on checkout — GITHUB_TOKEN persists in .git/config and the artifact upload step uploads dist/ only (low actual risk) but the pattern is still wrong.
  4. Workflow-level id-token: write is overly broad — should be on the publish job only, not the build job.

None of these have been exploited here; all are pre-emptive. The audit also flags a number of monitoring gaps (Dependabot, SBOM, pre-commit secret scanning, in-CI workflow lint) that have not caused harm but would catch future regressions. /lockdown fix can apply most of these as low-risk auto-fixes.

Score & Posture

27
Significant exposure
Publisher pipeline has multiple un-hardened controls. Lockfile + dep CVE state is clean; CI hardening is the gap.
BandScorePosture
Hardened90–100Gold-standard supply-chain hygiene.
Solid baseline70–89Most controls present; minor gaps.
Critical gaps50–69Important controls missing; address before next release.
Significant exposure0–49Hardening is the next blocker. ← you are here

Category Breakdown

Per-category status — where to focus, vs. the overall number above.

CategoryStatusNotes
Lockfile integrityokuv.lock committed with SHA-256 on 68 packages; uv lock --check reproduces clean
Dependency CVEsokpip-audit via OSV: no known vulnerabilities; osv-scanner not installed (would be 2nd opinion)
Malware / typosquatwarnSocket CLI not installed; no install-script heuristic findings on this Python-only repo
CI/Actions hardeningdanger5 unpinned actions (1 critical: PyPI publisher), excessive workflow permissions, missing persist-credentials, no Harden-Runner. Primary problem area.
Secrets hygieneok.gitignore covers all common secret patterns; no hardcoded secrets in tree; gitleaks not installed (would be CI gate)
Provenance & signingwarnPyPI Trusted Publishers (OIDC) active — long-lived tokens eliminated. No SBOM generated per release. No PEP 740 attestations.
Container hardeningn/aNo Dockerfile
Pre-commit + ScorecardwarnNo .pre-commit-config.yaml; OpenSSF Scorecard not running

Ecosystem Inventory

EcosystemDetectedScannerResult
Python (uv)pyproject.toml, uv.lockpip-audit 2.x via OSVno known CVEs
Python (uv) — lockfile integrityuv.lock (68 packages)uv lock --check + hash counthashes present
GitHub Actions.github/workflows/publish.ymlzizmor 1.24.15 errors, 1 warning
Secrets in git.gitignore coveragegrep heuristic (gitleaks missing).gitignore covers all common patterns
Node.jsnot present
Docker / containernot present
OpenSSF Scorecardscorecard CLI missingnot measured
Socket (malware/typosquat)socket CLI missingnot measured
OSV-Scanner (second opinion)osv-scanner missingnot measured

Findings

CRITICAL SCSC-001 Publisher action pypa/gh-action-pypi-publish@release/v1 is not SHA-pinned
Location
.github/workflows/publish.yml:51
Evidence
uses: pypa/gh-action-pypi-publish@release/v1
Risk
An attacker who compromises the PyPA repo (or its maintainer's GitHub credentials) can force-push release/v1 to a malicious commit. Every consumer using this tag would then run attacker code with OIDC id-token permission and direct PyPI publish authority — they could ship a poisoned claude-jacked wheel to PyPI without your knowledge. This is exactly the tj-actions CVE-2025-30066 attack pattern, applied to your release pipeline.
Fix
Pin to the 40-char commit SHA with the version in a comment. Look up the current SHA: gh api repos/pypa/gh-action-pypi-publish/git/refs/tags/v1.13.0. Replace with: uses: pypa/gh-action-pypi-publish@<sha> # v1.13.0. Set up Dependabot or Renovate with a 7-day cooldown to keep the SHA fresh while still gaining tag-mutation immunity.
Auto-fixable
true/lockdown fix can pin via pinact run --min-age 7
HIGH SCSC-002 First-party Actions (actions/*) not SHA-pinned
Location
.github/workflows/publish.yml:15, 18, 31, 45
Evidence
uses: actions/checkout@v4
uses: actions/setup-python@v5
uses: actions/upload-artifact@v4
uses: actions/download-artifact@v4
Risk
GitHub-owned actions are lower-risk than third-party but not zero-risk. SHA-pinning is the paranoid-mode standard recommended by GitHub's own org-level "Require actions to be pinned to a full-length commit SHA" policy (GA Aug 2025). Healthcare orgs handling PHI should pin everything.
Fix
Pin each to a 40-char SHA with a version comment. pinact run will do all four in one pass.
Auto-fixable
true
HIGH SCSC-003 Workflow-level id-token: write is overly broad
Location
.github/workflows/publish.yml:7–9
Evidence
permissions:
  contents: read
  id-token: write
Risk
The build job does not need id-token: write — only the publish job does. Granting OIDC token-mint capability to a job that runs python -m build means a malicious dep can mint an OIDC token and use it to authenticate to the cloud / PyPI itself. zizmor flagged this as excessive-permissions with high confidence.
Fix
Move id-token: write to the publish job only:
permissions: { contents: read }   # workflow default

jobs:
  build:
    permissions: { contents: read }
    ...
  publish:
    permissions: { id-token: write, contents: read }
    ...
Auto-fixable
true
HIGH SCSC-004 No step-security/harden-runner egress filtering
Location
.github/workflows/publish.yml — both jobs
Risk
A compromised transitive dep installed during python -m build can exfil to any destination — there is no eBPF egress monitor / blocker on the runner. Harden-Runner would have detected the tj-actions, axios, and Shai-Hulud incidents at exfil time.
Fix
Add as first step of both jobs:
- uses: step-security/harden-runner@<sha> # v2.17.0
  with:
    egress-policy: audit
    disable-sudo: true
Run in audit mode for a week to discover legitimate endpoints, then flip to block with an explicit allowed-endpoints: list (PyPI mirrors, GitHub API only).
Auto-fixable
true — audit mode is safe to add unconditionally
HIGH SCSC-005 actions/checkout missing persist-credentials: false
Location
.github/workflows/publish.yml:15
Risk
Without persist-credentials: false, the GITHUB_TOKEN remains in .git/config. Any later step that uploads the workspace as an artifact (or invokes a third-party action that does so) leaks the token. zizmor labels this artipacked. In this specific workflow only dist/ is uploaded, so direct leak risk is low — but the pattern should be eliminated regardless.
Fix
- uses: actions/checkout@<sha> # v4.2.2
  with:
    persist-credentials: false
Auto-fixable
true
MEDIUM SCSC-006 No SBOM generated per release
Location
.github/workflows/publish.yml — no cyclonedx / syft / spdx step
Risk
Without an SBOM attached to each PyPI release, downstream consumers can't quickly determine whether they're exposed to a newly disclosed CVE in a transitive dep without re-running their own SCA tools. NIST SSDF and the CISA Secure-by-Design pledge both ask for SBOM publication.
Fix
Add a CycloneDX SBOM step to the build job:
- name: Generate SBOM
  run: |
    uv tool install cyclonedx-bom
    cyclonedx-py environment -o sbom.cdx.json
- uses: actions/upload-artifact@<sha>
  with: { name: sbom, path: sbom.cdx.json }
Attach to the GitHub Release alongside wheels.
Auto-fixable
true
MEDIUM SCSC-007 No Dependabot configuration (with cooldown)
Location
.github/dependabot.yml — missing
Risk
Dep upgrades for security patches are applied ad-hoc rather than via automated PRs. Without a cooldown, you also have no defense against smash-and-grab attacks (axios 1.14.1, Shai-Hulud) — the bad version would be the first one Dependabot proposes within the hour of publish.
Fix
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "pip"
    directory: "/"
    schedule: { interval: "weekly" }
    cooldown: { default-days: 7, semver-patch-days: 3 }
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule: { interval: "weekly" }
    cooldown: { default-days: 7 }
Auto-fixable
true
MEDIUM SCSC-008 No .pre-commit-config.yaml for secret detection
Location
repo root — missing
Risk
Secrets that get committed accidentally are detected at PR time only (or, more often, after release when someone scrapes git history). A pre-commit hook stops them at the workstation. pre-commit is already installed in uv tool list — only the config is missing.
Fix
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.21.2
    hooks: [{ id: gitleaks }]
  - repo: https://github.com/Yelp/detect-secrets
    rev: v1.5.0
    hooks: [{ id: detect-secrets, args: ['--baseline', '.secrets.baseline'] }]
  - repo: https://github.com/rhysd/actionlint
    rev: v1.7.7
    hooks: [{ id: actionlint }]
  - repo: https://github.com/zizmorcore/zizmor-pre-commit
    rev: v1.25.2
    hooks: [{ id: zizmor }]
Then: pre-commit install + detect-secrets scan > .secrets.baseline.
Auto-fixable
true
MEDIUM SCSC-009 No CI workflow runs pip-audit, osv-scanner, zizmor on PRs
Location
.github/workflows/ — only publish.yml exists, triggered on release; no PR/push workflow
Risk
A new vulnerable dep can be merged to master without any automated scanner objecting. The only enforcement happens at release time, which is too late if a security regression made it past code review.
Fix
Generate .github/workflows/lockdown.yml via /lockdown baseline — runs on every PR + nightly cron and fails on CRITICAL findings.
Auto-fixable
true/lockdown baseline
LOW SCSC-010 Local scanner tools missing (limits future audit coverage)
Tools
osv-scanner, gitleaks, socket, scorecard, trivy
Risk
This audit had to fall back to grep heuristics for several phases. A repeat run with these installed will catch more.
Fix
brew install osv-scanner gitleaks trivy
curl -fsSL https://socket.dev/install.sh | sh
go install github.com/ossf/scorecard/v5@latest
Auto-fixable
false — user installs locally

Remediation Plan

Run /lockdown fix to apply the auto-fixable items below interactively. Each becomes its own commit.

Auto-fixable (run /lockdown fix)

Run /lockdown baseline

User installs locally

Estimated post-fix score: ~92 (Hardened) — assumes all auto-fixable items applied and baseline workflow added.

Manual Verification (require gh admin scope or human eyes)

  1. GitHub org → Settings → Actions → General: "Workflow permissions" set to Read repository contents and packages permissions. Uncheck "Allow GitHub Actions to create and approve pull requests."
  2. GitHub org → Settings → Actions → General → Policies: enable "Require actions to be pinned to a full-length commit SHA".
  3. GitHub org → Code security: enable Secret Scanning + Push Protection.
  4. Repo → Settings → Rules → Rulesets for master: require signed commits, PR with 1 reviewer, dismiss stale, required status checks (add the new lockdown workflow once it's running), block force-push, linear history.
  5. Repo → Settings → Environments: create a pypi environment with required reviewers. Move the OIDC trust scope onto the environment, not the repo.
  6. PyPI maintainer accounts: confirm FIDO2 hardware-key 2FA (not TOTP) on all owners. Revoke any long-lived API tokens still present.
  7. GitHub maintainer accounts: confirm WebAuthn 2FA enforced org-wide. Audit PATs and shorten lifetimes to ≤ 7 days.

HIPAA Mapping

Safeguard§Supply-chain controlStatus here
Unique user ID164.312(a)(1)Per-maintainer PyPI accounts; FIDO2 2FAmanual verify
Encryption at rest164.312(a)(2)(iv)Secret-scanning, .gitignore coveragecovered
Audit Controls164.312(b)Sigstore Rekor transparency log via cosign / SLSA provenanceno SLSA / cosign yet
Integrity Controls164.312(c)(1)Lockfile hash pinning, cosign signature verify at deploy, SBOM attestationlockfile ok; no SBOM/cosign
Authentication164.312(d)Trusted Publishers OIDC (no long-lived tokens), MFATrusted Publishers active
Transmission Security164.312(e)(1)TLS-only registries, signed manifestsPyPI TLS-only
Risk Analysis164.308(a)(1)(ii)(A)Continuous CVE scanning in CI, quarterly Scorecard runno in-CI scanners yet
Workforce termination164.308(a)(3)(ii)(C)Off-boarding checklist (GitHub/npm/PyPI/cloud revocation)runbook needed
Activity review164.308(a)(1)(ii)(D)Harden-Runner anomaly alerts to SIEM, monitor cosign verification failuresno harden-runner yet
Incident procedures164.308(a)(6)IR runbook for malicious-dep + credential compromiserunbook needed
Contingency plan164.308(a)(7)Internal mirror / vendored deps; SBOM for blast-radius queriesno SBOM, no mirror
Periodic evaluation164.308(a)(8)Annual third-party pen-test + SLSA self-attestationinternal only today

4 of 12 HIPAA-relevant safeguards have full supply-chain coverage in this repo; 4 are partial; 4 are missing. Applying the auto-fix plan above moves Risk Analysis, Activity review, and Integrity Controls to "covered" — a 7/12 baseline. The remaining gaps require organizational process (runbooks, off-boarding, external audit), not code changes.

Sources


Generated by /lockdown · 2026-05-20 · re-run with /lockdown audit