# 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.

Band | Score | Posture
--- | --- | ---
Hardened | 90–100 | Gold-standard supply-chain hygiene.
Solid baseline | 70–89 | Most controls present; minor gaps.
Critical gaps | 50–69 | Important controls missing; address before next release.
Significant exposure | 0–49 | Hardening is the next blocker. ← you are here

## Category Breakdown

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

Category | Status | Notes
--- | --- | ---
Lockfile integrity | ok | `uv.lock` committed with SHA-256 on 68 packages; `uv lock --check` reproduces clean
Dependency CVEs | ok | `pip-audit` via OSV: no known vulnerabilities; `osv-scanner` not installed (would be 2nd opinion)
Malware / typosquat | warn | Socket CLI not installed; no install-script heuristic findings on this Python-only repo
CI/Actions hardening | danger | 5 unpinned actions (1 critical: PyPI publisher), excessive workflow permissions, missing persist-credentials, no Harden-Runner. Primary problem area.
Secrets hygiene | ok | `.gitignore` covers all common secret patterns; no hardcoded secrets in tree; gitleaks not installed (would be CI gate)
Provenance & signing | warn | PyPI Trusted Publishers (OIDC) active — long-lived tokens eliminated. No SBOM generated per release. No PEP 740 attestations.
Container hardening | n/a | No Dockerfile
Pre-commit + Scorecard | warn | No `.pre-commit-config.yaml`; OpenSSF Scorecard not running

## Ecosystem Inventory

Ecosystem | Detected | Scanner | Result
--- | --- | --- | ---
Python (uv) | `pyproject.toml`, `uv.lock` | `pip-audit` 2.x via OSV | no known CVEs
Python (uv) — lockfile integrity | `uv.lock` (68 packages) | `uv lock --check` + hash count | hashes present
GitHub Actions | `.github/workflows/publish.yml` | `zizmor` 1.24.1 | 5 errors, 1 warning
Secrets in git | `.gitignore` coverage | grep heuristic (gitleaks missing) | .gitignore covers all common patterns
Node.js | — | — | not present
Docker / container | — | — | not present
OpenSSF Scorecard | — | scorecard CLI missing | not measured
Socket (malware/typosquat) | — | socket CLI missing | not measured
OSV-Scanner (second opinion) | — | osv-scanner missing | not measured

## Findings

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`
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`
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`
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
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`
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`
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`
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`
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`
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
What's already good:

- `uv.lock` committed with SHA-256 hashes on every package (68 packages, all hashed)
- `pip-audit` reports zero known CVEs in the dep tree
- PyPI Trusted Publishers configured (`id-token: write` + no long-lived `PYPI_API_TOKEN` secret)
- `permissions:` block exists on the publish workflow (just needs to move `id-token` to a single job)
- `.gitignore` covers `.env`, `*.pem`, `*.key`, `.npmrc`, `.pypirc`, `service-account*`, OAuth token files, SQLite DBs
- Pre-built shims for credential-file leakage in `.gitignore` (rare; well done)
- No hardcoded secrets found in current tree

## Remediation Plan

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

### Auto-fixable (run `/lockdown fix`)

- SCSC-001 — pin `pypa/gh-action-pypi-publish` to SHA
- SCSC-002 — pin all `actions/*` uses to SHAs
- SCSC-003 — move `id-token: write` to publish job only
- SCSC-004 — add `step-security/harden-runner` (audit mode) to both jobs
- SCSC-005 — add `persist-credentials: false` to `actions/checkout`
- SCSC-006 — add CycloneDX SBOM generation to release build
- SCSC-007 — create `.github/dependabot.yml` with 7-day cooldown
- SCSC-008 — create `.pre-commit-config.yaml` (gitleaks, detect-secrets, actionlint, zizmor)

### Run `/lockdown baseline`

- SCSC-009 — generate `.github/workflows/lockdown.yml` (CI scanner workflow)

### User installs locally

- SCSC-010 — install `osv-scanner`, `gitleaks`, `socket`, `scorecard`, `trivy`

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

Applicability: claude-jacked itself does not process PHI — it's a developer tool. But the user maintains other repos that do, and this mapping is the canonical reference for those projects. Each row notes whether the corresponding control is currently met in claude-jacked.

Safeguard | § | Supply-chain control | Status here
--- | --- | --- | ---
Unique user ID | 164.312(a)(1) | Per-maintainer PyPI accounts; FIDO2 2FA | manual verify
Encryption at rest | 164.312(a)(2)(iv) | Secret-scanning, .gitignore coverage | covered
Audit Controls | 164.312(b) | Sigstore Rekor transparency log via cosign / SLSA provenance | no SLSA / cosign yet
Integrity Controls | 164.312(c)(1) | Lockfile hash pinning, cosign signature verify at deploy, SBOM attestation | lockfile ok; no SBOM/cosign
Authentication | 164.312(d) | Trusted Publishers OIDC (no long-lived tokens), MFA | Trusted Publishers active
Transmission Security | 164.312(e)(1) | TLS-only registries, signed manifests | PyPI TLS-only
Risk Analysis | 164.308(a)(1)(ii)(A) | Continuous CVE scanning in CI, quarterly Scorecard run | no in-CI scanners yet
Workforce termination | 164.308(a)(3)(ii)(C) | Off-boarding checklist (GitHub/npm/PyPI/cloud revocation) | runbook needed
Activity review | 164.308(a)(1)(ii)(D) | Harden-Runner anomaly alerts to SIEM, monitor cosign verification failures | no harden-runner yet
Incident procedures | 164.308(a)(6) | IR runbook for malicious-dep + credential compromise | runbook needed
Contingency plan | 164.308(a)(7) | Internal mirror / vendored deps; SBOM for blast-radius queries | no SBOM, no mirror
Periodic evaluation | 164.308(a)(8) | Annual third-party pen-test + SLSA self-attestation | internal 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

- tj-actions CVE-2025-30066 — NVD, Wiz analysis
- CISA alert (Shai-Hulud, Sept 2025) — cisa.gov
- PyPI Shai-Hulud impact — blog.pypi.org
- PyPI Trusted Publishers + PEP 740 attestations — blog.pypi.org
- StepSecurity Harden-Runner — github
- zizmor — github
- SHA-pinning GitHub policy GA — github changelog
- SLSA L3 with GitHub Artifact Attestations — github blog
- OpenSSF Scorecard — scorecard.dev
- HIPAA Security Rule Technical Safeguards — ecfr 164.312
- xz-utils backdoor timeline (CVE-2024-3094) — research.swtch.com

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