git-tag-guardian
v1.0.0
Published
Zero-dependency supply chain defense for Node.js/Bun — detects git tag rewrite attacks, postinstall backdoors, SHA drift, tarball tampering and unpinned GitHub Actions
Maintainers
Readme
git-tag-guardian
Zero-dependency supply chain defense for Node.js and Bun projects.
Detects and blocks Git Tag Rewrite attacks, postinstall backdoors, SHA drift in lockfiles, tarball tampering, and unpinned GitHub Actions — before they compromise your CI/CD or production.
Built by CanisterWorm / TeamPCP as a practical countermeasure to real-world tag rewrite exploitation techniques.
The Problem
Git tags are mutable. Anyone with push access to a repository can silently repoint a tag to a completely different commit — including one from a fork that contains a backdoor. npm, GitHub Actions, and Dockerfiles that reference dependencies by tag will then pull the malicious code without any warning.
Attack chain:
1. Attacker forks your dependency
2. Adds malicious code in the fork (postinstall exfiltration, modified source, etc.)
3. Pushes to fork — commit lands in shared GitHub Object Store
4. Rewrites the tag in upstream: PATCH /git/refs/tags/v1.0.0 { sha: "malicious", force: true }
5. GitHub regenerates .tar.gz automatically
6. Next `npm install` pulls compromised code
7. postinstall runs silently — steals env vars, SSH keys, tokensThis tool detects every stage of that chain.
Install
# As a dev dependency
npm install --save-dev git-tag-guardian
# Or run directly
npx git-tag-guardian auditNo external dependencies. Works on Node.js >= 18 and Bun.
Quick Start
# 1. Full audit of current project
npx git-tag-guardian audit
# 2. Create a baseline (records current SHA hashes)
npx git-tag-guardian baseline
# 3. Install git hooks (pre-commit + post-merge)
npx git-tag-guardian hooks-install
# 4. Run in CI (exits 1 on critical/high findings)
npx git-tag-guardian audit --ci5 Security Checks
1. SHA Pinning
Detects git dependencies in package.json that reference tags or branches instead of full 40-character SHA hashes. Tags can be rewritten; SHA hashes cannot.
[HIGH] Dependency "logger" references tag "v1.0.0" instead of SHA
fix: Replace with full SHA commit hash2. Postinstall Hook Detection
Scans every package in node_modules/ for lifecycle scripts (preinstall, install, postinstall, prepare, etc.) and analyzes their content for 15+ suspicious patterns:
| Pattern | Risk |
|---------|------|
| curl / wget / fetch() to external URLs | Data exfiltration |
| process.env access | Credential theft |
| .ssh/ access | SSH key theft |
| .bashrc / .zshrc modification | Persistent backdoor |
| .npmrc modification | Registry hijacking |
| child_process / exec / eval | Arbitrary code execution |
| Buffer.from(..., 'base64') | Obfuscation |
| os.userInfo() / os.hostname() | Reconnaissance |
| dns.resolve / dns.lookup | DNS exfiltration |
Packages with 3+ suspicious patterns are flagged as CRITICAL.
3. Tag Integrity (GitHub API)
Queries the GitHub API to verify that tag SHA matches the baseline. Detects tag rewrite attacks in real-time. Also checks whether the commit belongs to the default branch or is a fork commit (strong indicator of attack).
[CRIT] TAG REWRITE DETECTED: owner/repo#v1.0.0 SHA changed!
baseline SHA: 1fadba8...
remote SHA: 16e4324... (from fork!)Requires GITHUB_TOKEN environment variable.
4. Tarball Integrity
Downloads .tar.gz archives from GitHub for tag-based dependencies and compares SHA256 hashes against the baseline. GitHub regenerates tarballs dynamically after tag rewrite — same URL, different content.
[CRIT] TARBALL CHANGED: "logger" .tar.gz hash changed!
baseline: 475308ac...
current: 4204c3e4...5. GitHub Actions Pinning
Scans .github/workflows/*.yml for uses: directives that reference actions by tag instead of SHA. An action tag rewrite gives attackers access to GITHUB_TOKEN, all secrets, and build artifacts.
[HIGH] Action "third-party/action@v2" uses tag instead of SHA!
fix: uses: third-party/action@<40-char-sha> # v2Baseline System
The baseline records known-good SHA hashes for dependencies, tags, and tarballs. Subsequent audits compare current state against the baseline to detect drift.
# Create initial baseline
npx git-tag-guardian baseline
# Commit it
git add .guardian-baseline.json
git commit -m "Add supply chain baseline"The baseline file (.guardian-baseline.json) should be committed to your repository so every developer and CI runner uses the same reference.
GitHub Actions Workflow
A ready-to-use CI workflow is included at .github/workflows/supply-chain-guard.yml:
- Runs on every PR that touches
package.json/package-lock.json - Runs on push to
main/master - Scheduled daily cron for tag rewrite detection between commits
- Posts findings as PR comments on failure
- All actions in the workflow itself are SHA-pinned
# Key: install with --ignore-scripts to prevent postinstall execution
- run: npm ci --ignore-scripts
- run: npx git-tag-guardian audit --ciGit Hooks
npx git-tag-guardian hooks-installInstalls two hooks:
- pre-commit — blocks commits that introduce SHA drift or new lifecycle hooks
- post-merge — automatically checks dependencies after
git pull
Configuration
Create .guardian.yml in your project root (optional — sensible defaults are used otherwise):
checks:
shaPinning: true
postinstallHooks: true
tagIntegrity: true # requires GITHUB_TOKEN
tarballIntegrity: true
actionsPinning: true
# Packages allowed to have lifecycle scripts
allowedLifecyclePackages:
- electron
- sharp
- better-sqlite3
# Actions owners trusted (severity: low instead of high)
trustedActions:
- actions/*
# Baseline file name
baselineFile: .guardian-baseline.json
# Minimum severity to fail CI
failOnSeverity: highCLI Reference
COMMANDS:
audit Full project audit (all 5 checks)
baseline Create/update SHA baseline
verify Quick check (SHA drift + postinstall only)
hooks-install Install git hooks (pre-commit, post-merge)
help Show help
FLAGS:
--ci CI mode (exit 1 on critical/high findings)
--cwd <path> Project root (default: cwd)
--token <tok> GitHub PAT for API checks (or GITHUB_TOKEN env)
--output <file> Save report as JSON
--no-color Disable colorsIntegration with AI Coding Agents
This project ships companion skills for Claude Code and OpenCode that automatically run supply chain checks when the agent installs npm packages:
- claude-supply-chain-skill — Claude Code / Anthropic CLI skill
- opencode-supply-chain-skill — OpenCode agent skill
These skills hook into the agent's npm install / bun install workflow and block installation if critical findings are detected.
How It Works (Technical)
Git Tag Rewrite Attack
Upstream repo: owner/logger
refs/tags/v1.0.0 → commit abc123 (clean)
Attacker fork: attacker/logger
branch: malicious → commit xyz789 (backdoor)
API call (with push access):
PATCH /repos/owner/logger/git/refs/tags/v1.0.0
{ "sha": "xyz789", "force": true }
Result:
refs/tags/v1.0.0 → commit xyz789 (backdoor!)
.tar.gz regenerated with malicious content
npm install pulls backdoor automaticallyWhy SHA Pinning Stops It
SHA-256 commit hashes are cryptographically unique. A rewritten tag points to a different SHA — but if your package-lock.json or baseline records the original SHA, the drift is immediately detectable.
Severity Levels
| Severity | Meaning | CI Impact |
|----------|---------|-----------|
| critical | Active attack indicator (SHA drift, tag rewrite, dangerous hooks) | Fail |
| high | Missing protection (tag-based deps, unpinned actions) | Fail |
| medium | Informational risk (short SHA, missing baseline) | Pass |
| low | Best practice suggestion | Pass |
| info | Informational only | Pass |
License
MIT
CanisterWorm / TeamPCP — Practical security tooling for the Node.js ecosystem.
