Skip to main content
170 npm Packages Compromised via GitHub ActionsIncident
4 min readFor Security Engineers

170 npm Packages Compromised via GitHub Actions

What Happened

On June 18, 2024, GitHub announced a security update to actions/checkout v7 to prevent workflows from executing unreviewed code from forked pull requests. This change addresses a recent attack that compromised 170 npm packages, including the TanStack Router ecosystem, through a "pwn request" exploit.

The attack exploited workflows configured with pull_request_target or workflow_run events. These triggers executed with write permissions to the base repository, even without code review. Attackers submitted malicious pull requests to open-source projects, and the automated workflows executed their code with elevated privileges, allowing them to exfiltrate secrets and publish compromised packages.

The hacking group TeamPCP showed how a single misconfigured workflow file could impact an entire package ecosystem. Once they compromised one package's CI/CD pipeline, they used those credentials to publish malicious updates, which then spread to dependent packages.

Timeline

Before June 18: Workflows using pull_request_target or workflow_run with actions/checkout executed code from fork PRs without explicit review, even when that code modified the workflow itself or accessed repository secrets.

June 18: GitHub announced actions/checkout v7 with new security defaults. The updated action now blocks execution when it detects unreviewed fork PR code in dangerous event contexts.

July 16: GitHub backported these security defaults to all supported major versions of actions/checkout (v2, v3, v4). Any workflow using Dependabot or similar automated update tools received the protection automatically.

Post-July 16: Organizations that pinned to specific SHA commits or disabled automated updates remained vulnerable unless they manually updated their workflows.

Which Controls Failed or Were Missing

The TanStack Router compromise exposed three control failures:

Insufficient input validation on untrusted code execution. Workflows treated fork PR code as semi-trusted because it came through GitHub's interface. No validation checked whether the PR author had repository write access before granting the workflow write permissions.

Missing least-privilege enforcement. Workflows used pull_request_target to access secrets for operations like publishing packages but didn't restrict which code paths could access those secrets. The event trigger gave blanket write access rather than scoping permissions to specific, reviewed code paths.

No separation between build and publish operations. A single workflow both built artifacts from untrusted code and published them with repository credentials. When the untrusted code compromised the build environment, it inherited publish permissions.

What the Relevant Standards Require

NIST 800-53, Control CM-3 (Configuration Change Control) requires that proposed changes to information systems undergo review and approval before implementation. The pwn request attack violated this by executing code changes before any review occurred. Your CI/CD pipeline must enforce that workflow modifications require approval from someone with write access before those workflows execute.

ISO/IEC 27001:2022, Control 8.32 (Change Management) mandates that changes to information processing facilities and systems follow formal change management procedures. Running unreviewed fork code violates this control because it allows changes to your build environment without documented approval. You need technical controls that prevent workflow execution until a maintainer explicitly approves the PR.

OWASP ASVS v4.0.3, Requirement 14.1.3 states that build pipelines must verify the integrity of build inputs. Executing arbitrary code from forks fails this requirement. Your checkout process must verify that the code being built matches reviewed, approved commits—not unvetted submissions from external contributors.

PCI DSS v4.0.1, Requirement 6.2.4 requires that security vulnerabilities are identified and addressed. If your workflows process or access cardholder data environments, allowing unreviewed code execution creates a direct path for attackers to compromise that data. You must implement controls that prevent untrusted code from accessing production secrets or deployment credentials.

Lessons and Action Items for Your Team

Audit your workflow triggers immediately. Run this search across your repositories:

grep -r "pull_request_target\|workflow_run" .github/workflows/

Any workflow using these triggers needs review. If the workflow checks out PR code with actions/checkout and has access to secrets, you have the same vulnerability that compromised TanStack Router.

Update actions/checkout to v4 or later. If you're on v2 or v3, the July 16 backport protects you only if you accept automated updates. Pin to a recent SHA after July 16, or better yet, move to v4 and enable Dependabot to track future security updates.

Separate build and publish workflows. Create one workflow that builds and tests PR code with no secrets access. Create a second workflow that only runs on merged commits to your main branch, which handles publishing. This separation ensures untrusted code never executes in an environment with deployment credentials.

Implement explicit approval gates. For workflows that must run on fork PRs (like preview deployments), use GitHub's environment protection rules to require manual approval before the workflow accesses secrets. This adds a human verification step between PR submission and credential exposure.

Review your secret scope. Many workflows have access to organization or repository secrets they don't need. Use environment-specific secrets instead of repository-wide secrets, and scope each workflow to the minimum set of credentials required for its specific function. If a test workflow doesn't publish packages, it shouldn't have npm tokens.

Document your workflow security model. Create a policy that defines which event triggers are allowed for different types of workflows. Make it explicit: workflows with pull_request_target cannot check out PR code. Workflows that check out PR code cannot access secrets. Workflows that publish artifacts only run on protected branches.

The shift to secure-by-default in GitHub Actions transfers responsibility from individual developers to the platform. Your job changes from "remember to configure this securely" to "verify the defaults protect you and understand when you need to opt out." Ensure these defaults are in place to maintain a robust security posture.

Topics:Incident

You Might Also Like