JSPM

git-tag-guardian

1.0.0
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 11
  • Score
    100M100P100Q62009F
  • License MIT

Zero-dependency supply chain defense for Node.js/Bun — detects git tag rewrite attacks, postinstall backdoors, SHA drift, tarball tampering and unpinned GitHub Actions

Package Exports

  • git-tag-guardian
  • git-tag-guardian/src/index.js

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (git-tag-guardian) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

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, tokens

This 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 audit

No 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 --ci

5 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 hash

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

Baseline 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 --ci

Git Hooks

npx git-tag-guardian hooks-install

Installs 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: high

CLI 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 colors

Integration 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:

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 automatically

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