What Happened

In March 2025, attackers compromised the tj-actions/changed-files GitHub Action, used in approximately 23,000 repositories. The malicious code was injected to print secrets (tokens, credentials, API keys) from the runner environment to workflow logs.

Scope

  • 23,000+ repositories used the compromised action
  • Secrets potentially exposed include: GitHub tokens, cloud provider keys, npm tokens, Docker Hub credentials
  • The attack traced back to a compromised Personal Access Token

Attack Timeline

  1. Attacker gains access to maintainer account via stolen/phished PAT
  2. Malicious commits pushed to all version tags (v45, v44, etc.)
  3. Repositories using @v45 (not pinned to SHA) immediately ran malicious code
  4. Secrets leaked to logs; some logs publicly accessible

How to Detect Exposure

# Check if your workflows used the compromised action

grep -r 'tj-actions/changed-files' .github/workflows/

Review workflow run logs for unexpected output

Look for base64-encoded strings or dump-like output in logs

Hardening Your GitHub Actions

  1. Pin all Actions to full commit SHA, not tags:
   # BAD

uses: tj-actions/changed-files@v45

# GOOD

uses: tj-actions/changed-files@a29e8b565651ce417abb5db7164b4a2b5b3b77f4

  1. Use Dependabot or Renovate to auto-update pinned SHAs
  2. Restrict GITHUB_TOKEN permissions in workflow: permissions: contents: read
  3. Use OIDC for cloud authentication instead of stored secrets
  4. Enable secret scanning and push protection in repository settings