
Security News
Happy Birthday, Shai-Hulud
It has been one year since Shai-Hulud made its first appearance on npm.
git-tag-guardian
Advanced tools
Zero-dependency supply chain defense for Node.js/Bun — detects git tag rewrite attacks, postinstall backdoors, SHA drift, tarball tampering and unpinned GitHub Actions
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.
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, tokens
This tool detects every stage of that chain.
# As a dev dependency
npm install --save-dev git-tag-guardian
# Or run directly
npx git-tag-guardian audit
No external dependencies. Works on Node.js >= 18 and Bun.
# 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 --ci
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 hash
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.
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.
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...
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> # v2
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.
A ready-to-use CI workflow is included at .github/workflows/supply-chain-guard.yml:
package.json / package-lock.jsonmain / master# Key: install with --ignore-scripts to prevent postinstall execution
- run: npm ci --ignore-scripts
- run: npx git-tag-guardian audit --ci
npx git-tag-guardian hooks-install
Installs two hooks:
git pullCreate .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: high
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 colors
This project ships companion skills for Claude Code and OpenCode that automatically run supply chain checks when the agent installs npm packages:
These skills hook into the agent's npm install / bun install workflow and block installation if critical findings are detected.
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 automatically
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 | 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 |
MIT
CanisterWorm / TeamPCP — Practical security tooling for the Node.js ecosystem.
FAQs
Zero-dependency supply chain defense for Node.js/Bun — detects git tag rewrite attacks, postinstall backdoors, SHA drift, tarball tampering and unpinned GitHub Actions
The npm package git-tag-guardian receives a total of 1 weekly downloads. As such, git-tag-guardian popularity was classified as not popular.
We found that git-tag-guardian demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Security News
It has been one year since Shai-Hulud made its first appearance on npm.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.