← Back to Cybersecurity News Center
Severity
MEDIUM
CVSS
5.0
Priority
0.623
×
Tip
Pick your view
Analyst for full detail, Executive for the short version.
Analyst
Executive
Executive Summary
GitHub has made staged publishing generally available for npm, introducing a mandatory 2FA-authenticated human approval gate before any package version becomes installable, including releases from automated CI/CD pipelines. This closes two persistent supply chain attack vectors: unauthorized automated publishing via compromised CI credentials and non-registry source substitution attacks. The controls arrive as threat group TeamPCP actively poisons open-source packages at scale, signaling that the npm ecosystem has crossed from theoretical risk to actively exploited territory.
Impact Assessment
CISA KEV Status
Not listed
Threat Severity
MEDIUM
Medium severity — monitor and assess
Actor Attribution
HIGH
TeamPCP
TTP Sophistication
HIGH
5 MITRE ATT&CK techniques identified
Detection Difficulty
HIGH
Multiple evasion techniques observed
Target Scope
INFO
npm registry, GitHub, npm CLI 11.15.0+
Are You Exposed?
⚠
Your industry is targeted by TeamPCP → Heightened risk
⚠
You use products/services from npm registry → Assess exposure
⚠
5 attack techniques identified — review your detection coverage for these TTPs
✓
Your EDR/XDR detects the listed IOCs and TTPs → Reduced risk
✓
You have incident response procedures for this threat type → Prepared
Assessment estimated from severity rating and threat indicators
Business Context
Any organization that builds software using JavaScript or Node.js — or that ships products incorporating npm-sourced packages — faces direct exposure if their publishing pipelines and dependency configurations are not updated. A successful supply chain compromise via npm can embed malicious code into products shipped to customers, triggering breach notification obligations, customer trust erosion, and potential liability depending on the sector and jurisdiction. The active presence of TeamPCP conducting package poisoning campaigns at scale elevates this from a theoretical risk to a credible, near-term operational threat that warrants prioritized engineering and security team attention.
You Are Affected If
Your organization publishes packages to the npm registry and has not yet enabled staged publishing on those packages
Your CI/CD pipelines hold npm publish tokens that can push package versions without a human approval gate
Your projects consume npm dependencies via non-registry sources: git URLs, local file paths, or tarball URLs not subject to registry integrity checks
Your software supply chain includes transitive npm dependencies where you do not control or audit the upstream maintainer's publish security posture
Your development environment uses npm CLI versions below 11.15.0, where the new install source restriction flags are unavailable
Board Talking Points
An active threat group is poisoning open-source software packages that our development teams may use, and the primary protection is a new security control that requires deliberate opt-in from our engineering organization.
Engineering and AppSec teams should be directed to enable staged publishing for any packages we maintain and upgrade npm CLI tooling to version 11.15.0 or later within the next sprint cycle.
Without action, a compromised CI/CD credential or a poisoned third-party dependency could introduce malicious code into our products without any human review — reaching customers before detection.
Technical Analysis
GitHub's staged publishing feature addresses a structural weakness in the npm publishing pipeline that has existed since CI/CD automation became standard practice: once a pipeline held a valid publish token, it could push any package version to the registry without human review.
This gap maps directly to MITRE T1195.001 (Supply Chain Compromise: Compromise Software Dependencies and Development Tools) and T1554 (Compromise Host Software Binary).
Attackers targeting this path, whether through compromised CI credentials (T1078 , Valid Accounts) or a compromised third-party integration (T1199 , Trusted Relationship), could publish malicious versions that downstream consumers would install automatically.
The staged publishing gate requires a 2FA-authenticated human to approve any release before it becomes installable. This breaks the fully automated attack chain. Even if an attacker obtains CI/CD credentials, they cannot complete a malicious publish without triggering the approval workflow and requiring human action, which either exposes the attempt or blocks it entirely. This maps to CWE-287 (Improper Authentication in publishing workflows) and addresses T1553.002 (Subvert Trust Controls: Code Signing) by ensuring that publication itself becomes a trust checkpoint, not just the artifact.
The three new install source flags in npm CLI 11.15.0+ address a separate but related vector: dependency confusion and source substitution attacks that exploit non-registry install paths. When developers specify dependencies via git URLs, file paths, or tarball URLs, there has been no native enforcement mechanism to restrict or audit those sources. The new flags give teams explicit, policy-enforceable control over which non-registry sources are permitted, directly reducing the CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) and CWE-494 (Download of Code Without Integrity Check) attack surface.
The timing matters. TeamPCP has been observed conducting active, large-scale open-source package poisoning campaigns, demonstrating that supply chain compromise via the npm ecosystem is no longer a proof-of-concept scenario. Development teams that rely on npm packages, directly or transitively, face a concrete, operationally active threat. These new controls are meaningful mitigations, but they require adoption: staged publishing must be enabled by package maintainers, and the install source flags must be configured in project tooling. Neither is automatic.
Action Checklist IR ENRICHED
Triage Priority:
URGENT
Escalate to immediate priority and engage AppSec leadership if `npm audit`, lock file diff analysis, or registry metadata review reveals any dependency in a production build resolved to an unexpected version or integrity hash mismatch consistent with TeamPCP substitution activity, or if any npm publish token with scope over a publicly-consumed package is found to have been used outside a known, approved pipeline run.
1
Step 1: Assess exposure, inventory all npm packages your organization publishes and all npm dependencies consumed directly or transitively in production builds; determine whether any published packages have staged publishing available and not yet enabled
IR Detail
Preparation
NIST 800-61r3 §2 — Preparation: establishing IR capability and understanding the environment before an incident occurs
NIST CM-8 (System Component Inventory)
NIST RA-3 (Risk Assessment)
CIS 1.1 (Establish and Maintain Detailed Enterprise Asset Inventory)
CIS 2.1 (Establish and Maintain a Software Inventory)
Compensating Control
Run `npm ls --all --json > dep-tree.json` in each project root to capture the full transitive dependency graph. For published packages, run `npm access list packages <org>` to enumerate all packages your org owns and cross-reference against the npm staged publishing opt-in list at registry.npmjs.org. Use `jq` to parse and diff lock files: `jq '.dependencies | keys' package-lock.json` to surface non-registry sources (look for 'git+', 'file:', 'http' prefixes in version strings).
Preserve Evidence
Before inventorying, snapshot current state for baseline comparison: export `package-lock.json` and `npm-shrinkwrap.json` from all production build environments, capture `npm config list --json` output per pipeline runner to document current registry and source configurations, and record the current resolved version of each package as published in the npm registry to detect future drift.
2
Step 2: Enable staged publishing, for any npm packages your organization maintains, enable the staged publishing feature in npm/GitHub settings to enforce the 2FA-authenticated human approval gate before any version becomes installable (addresses NIST CM-3: Configuration Change Control and NIST SI-7: Software, Firmware, and Information Integrity)
IR Detail
Containment
NIST 800-61r3 §3.3 — Containment Strategy: implementing controls to prevent further exploitation of the identified attack vector
NIST CM-3 (Configuration Change Control)
NIST SI-7 (Software, Firmware, and Information Integrity)
NIST CM-4 (Impact Analyses)
CIS 4.6 (Securely Manage Enterprise Assets and Software)
CIS 6.3 (Require MFA for Externally-Exposed Applications)
Compensating Control
For packages not yet eligible for staged publishing, enforce branch protection rules in GitHub requiring a signed review before any workflow that calls `npm publish` can execute. Add a pre-publish gate using a GitHub Actions workflow with `environment: production` and required reviewers set, so the publish job pauses at the environment approval step — this manually replicates the staged publishing gate using free GitHub features. Document the approval in a GitHub issue or PR comment for audit trail.
Preserve Evidence
Before enabling staged publishing, capture: the npm publish token currently associated with each CI/CD pipeline (`npm token list` with a token that has read:org scope), the GitHub Actions workflow YAML files that contain `npm publish` invocations to establish what automated publishing paths currently exist, and the npm audit log for each package via `npm audit --json` to record the pre-gate baseline of published versions and their integrity hashes.
3
Step 3: Upgrade npm CLI and configure install source flags, update to npm CLI 11.15.0+ and configure the new install source restriction flags to disallow or explicitly permit non-registry sources (git URLs, file paths, tarball URLs) in project and CI/CD configurations (addresses CIS 7.3: Perform Automated Operating System Patch Management and CIS 2.2: Ensure Authorized Software is Currently Supported)
IR Detail
Eradication
NIST 800-61r3 §3.4 — Eradication: removing the conditions that enabled the threat vector, specifically eliminating non-registry source substitution as an installation path
NIST SI-2 (Flaw Remediation)
NIST CM-7 (Least Functionality)
CIS 7.3 (Perform Automated Operating System Patch Management)
CIS 7.4 (Perform Automated Application Patch Management)
CIS 2.2 (Ensure Authorized Software is Currently Supported)
Compensating Control
For CI/CD environments where npm CLI upgrade requires change control approval, add a pre-install lint step using `grep -rE '(git\+|file:|https?://)' package.json package-lock.json` in each pipeline to fail the build if non-registry sources are detected. Add `.npmrc` entries `save-exact=true` and configure `registry=https://registry.npmjs.org` explicitly to prevent registry substitution. For offline or air-gapped environments, pin to a verified Verdaccio proxy registry instance and block outbound npm registry traffic to all other endpoints at the firewall.
Preserve Evidence
Before upgrading, document: the current npm CLI version on all build runners (`npm --version` per runner), any `.npmrc` files present in project roots and user home directories that may specify alternate registries or allow git/file sources, and a grep of all `package-lock.json` files for `resolved` fields containing non-`https://registry.npmjs.org` URLs — these are the exact artifacts TeamPCP-style poisoning would produce to redirect resolution to attacker-controlled sources.
4
Step 4: Audit CI/CD pipeline credentials, rotate npm publish tokens and audit which pipelines, service accounts, and third-party integrations hold publish access; apply least-privilege scoping so credentials cannot publish without the staged approval gate (addresses NIST AC-6: Least Privilege, D3-CRO: Credential Rotation, and CIS 5.4: Restrict Administrator Privileges to Dedicated Administrator Accounts)
IR Detail
Containment
NIST 800-61r3 §3.3 — Containment Strategy: revoking and replacing credentials that represent active publish-path attack surface before confirmed misuse occurs
NIST AC-6 (Least Privilege)
NIST AC-2 (Account Management)
NIST IA-5 (Authenticator Management)
CIS 5.1 (Establish and Maintain an Inventory of Accounts)
CIS 5.4 (Restrict Administrator Privileges to Dedicated Administrator Accounts)
CIS 6.2 (Establish an Access Revoking Process)
Compensating Control
Enumerate all active npm automation tokens using `npm token list --json` (requires owner-level token) and document each token's CIDR allowlist, creation date, and associated pipeline. Revoke any token with publish scope that is not bound to a specific CIDR range or that predates the staged publishing rollout. Replace with granular read-only tokens for install workflows and separate scoped publish tokens restricted to the approval-gated workflow. For GitHub Actions, audit repository secrets and organization secrets for any `NPM_TOKEN` entries using the GitHub CLI: `gh secret list --repo <org>/<repo>`.
Preserve Evidence
Before rotating, preserve: the full output of `npm token list --json` for each package scope as a timestamped record of pre-rotation token state, the GitHub Actions audit log entries (available under Organization Settings > Audit Log, filter by `action:secrets.*` and `action:workflows.*`) showing which workflows accessed publish tokens, and any npm access log entries from the registry showing recent publish events per package — this establishes whether any unauthorized publish occurred before the gate was in place.
5
Step 5: Update threat model for TeamPCP activity, incorporate TeamPCP's active package poisoning campaigns into your threat register against T1195.001, T1554, and T1199; establish detection baselines for unexpected package versions appearing in dependency lock files or build outputs
IR Detail
Detection & Analysis
NIST 800-61r3 §3.2 — Detection and Analysis: integrating current threat actor intelligence into detection baselines to identify indicators of TeamPCP compromise before build artifacts reach production
NIST RA-3 (Risk Assessment)
NIST SI-4 (System Monitoring)
NIST PM-16 (Threat Awareness Program)
CIS 7.1 (Establish and Maintain a Vulnerability Management Process)
CIS 7.2 (Establish and Maintain a Remediation Process)
Compensating Control
Create a YARA rule or bash script to diff `package-lock.json` before and after each CI run, alerting on any change to `resolved`, `integrity` (sha512 hash), or `version` fields for existing dependencies — TeamPCP poisoning would alter these values when a malicious version substitutes a legitimate one. Subscribe to the Socket.dev free tier or `npm audit` RSS feeds for packages in your dependency tree. For MITRE T1195.001 (Compromise Software Supply Chain) detection, configure a GitHub Actions workflow to run `npm audit --json` and compare resolved package hashes against the previous lock file snapshot on every pull request.
Preserve Evidence
Capture for threat modeling baseline: the current `integrity` hashes (sha512) for all top-level and critical transitive dependencies from `package-lock.json` as your known-good state, any npm security advisory history for packages in your dependency tree via `npm audit --json > audit-baseline.json`, and the current `node_modules/.package-lock.json` (the internal lock file used by the npm CLI) which records the actual resolved versions installed — divergence between this and the project-level lock file is a TeamPCP-relevant indicator.
6
Step 6: Communicate findings, brief engineering leadership and AppSec teams on the specific exposure: if your organization publishes npm packages or consumes packages from non-registry sources, the risk is concrete and the mitigations require active opt-in, not passive updates
IR Detail
Post-Incident
NIST 800-61r3 §4 — Post-Incident Activity: translating threat intelligence and control gaps into organizational awareness and documented risk decisions before the next TeamPCP campaign wave
NIST IR-4 (Incident Handling)
NIST PM-15 (Security and Privacy Groups and Associations)
NIST AT-2 (Literacy Training and Awareness)
CIS 7.2 (Establish and Maintain a Remediation Process)
Compensating Control
Produce a one-page exposure brief using the inventory output from Step 1: list each npm package your org publishes, its current staged publishing status (enabled/not enabled), and the count of dependencies resolved from non-registry sources. Frame the risk as concrete: 'TeamPCP has actively poisoned packages on the npm registry; our packages X, Y, Z are currently publishable by any pipeline holding token T without human approval.' Distribute via your existing incident ticketing system (Jira, GitHub Issues) with severity tagging so it enters the engineering backlog rather than email.
Preserve Evidence
Before the briefing, assemble supporting evidence: the token audit output from Step 4 showing publish-capable credentials, the lock file diff report from Step 5 showing any non-registry resolved sources currently in production dependencies, and any historical npm advisory matches from `npm audit` to demonstrate that packages in your dependency tree have been previously targeted — this grounds the briefing in organizational-specific data rather than abstract threat reporting.
7
Step 7: Monitor for follow-up disclosures, track npm security advisories, GitHub changelog updates for staged publishing scope, and threat intelligence on TeamPCP campaign activity for new indicators or expansion of targeted packages
IR Detail
Post-Incident
NIST 800-61r3 §4 — Post-Incident Activity: establishing continuous monitoring and intelligence feeds to detect scope expansion of TeamPCP campaigns and staged publishing feature changes that affect control effectiveness
NIST SI-5 (Security Alerts, Advisories, and Directives)
NIST CA-7 (Continuous Monitoring)
NIST AU-6 (Audit Record Review, Analysis, and Reporting)
CIS 7.1 (Establish and Maintain a Vulnerability Management Process)
CIS 8.2 (Collect Audit Logs)
Compensating Control
Configure a GitHub Actions scheduled workflow (cron: daily) that runs `npm audit --json` against your dependency tree and posts a summary to a Slack webhook or GitHub issue, flagging any new advisories for packages in your lock file. Subscribe to the npm security advisories RSS feed at `https://github.com/advisories` filtered by `ecosystem:npm`. For TeamPCP-specific tracking, monitor the Socket.dev blog, CISA Known Exploited Vulnerabilities catalog, and the OpenSSF package-analysis project feeds — all free. Set a calendar-based review of `package-lock.json` integrity hashes every sprint cycle as a lightweight compensating control for teams without automated SCA tooling.
Preserve Evidence
Establish persistent collection of: npm registry metadata for your critical dependencies via `npm view <package> --json` on a scheduled basis to detect unexpected new versions published between your approved upgrade cycles, GitHub Actions workflow run logs for any pipeline that calls `npm install` or `npm ci` — these logs contain the resolved package versions and will show if a dependency resolved to an unexpected version mid-campaign, and the npm access audit log for your organization's published packages (available via npm website under package settings) to detect unauthorized publish attempts that were blocked by staged publishing.
Recovery Guidance
After enabling staged publishing and rotating publish tokens, verify recovery by confirming that a test publish attempt from the CI/CD pipeline is correctly intercepted and held in staged state pending 2FA-authenticated human approval — do not consider the control active until this end-to-end test passes. Monitor `package-lock.json` integrity hashes across all production build environments for a minimum of 30 days post-remediation, comparing each build's resolved hashes against the known-good baseline captured in Step 5, since TeamPCP campaigns may have pre-staged malicious versions that surface on the next `npm install` cycle. Retain the pre-rotation token audit output and lock file snapshots as forensic baseline records in case a downstream incident requires establishing when a potential compromise window opened or closed.
Key Forensic Artifacts
package-lock.json and npm-shrinkwrap.json from all production build environments: the `resolved` URL and `integrity` (sha512) fields for each dependency are the primary indicators of TeamPCP-style substitution — a compromised package will show a changed sha512 hash or a `resolved` URL pointing to a non-registry source
node_modules/.package-lock.json (npm internal lock file written during install): records the exact versions and sources actually installed during the last `npm ci` or `npm install` run, which may diverge from the committed project-level lock file if a supply chain attack modified the registry-resolved version between commits
CI/CD pipeline execution logs containing npm install/ci output: capture stdout from build runners showing 'added X packages' lines and any 'npm warn' or 'npm error' messages referencing unexpected package sources, versions, or integrity failures — these are the real-time indicators of a TeamPCP poisoning event during a build
npm token audit log from registry.npmjs.org: access via `npm token list --json` and the npm website audit log under each package's settings — records every publish event with timestamp, token ID, and source IP, enabling reconstruction of whether any unauthorized publish occurred against your org's packages before staged publishing was enabled
GitHub Actions audit log entries filtered for secrets access and workflow dispatch events: available under Organization > Settings > Audit Log, exportable via GitHub API; shows which workflows accessed `NPM_TOKEN` secrets and when, establishing the publish credential access timeline relevant to assessing pre-remediation exposure
Detection Guidance
Focus detection on the software supply chain pipeline, not just runtime endpoints.
Key areas to monitor:
**npm Publish Anomalies (AU-2: Event Logging; AU-6: Audit Record Review):** Review npm audit logs for any package publish events originating from CI/CD service accounts outside of expected release windows.
Unexpected version increments, especially patch-level bumps from automated pipelines, should trigger review.
If staged publishing is enabled, alert on any publish workflow that does not complete the human approval step within a defined time window.
**Dependency Lock File Changes (CIS 8.2: Collect Audit Logs):** Hunt for unexpected changes to package-lock.json or yarn.lock files in source control, particularly changes that alter resolved URLs, integrity hashes, or introduce non-registry sources (git:// or http:// prefixes in dependency entries). These changes should be reviewed against an approved change record.
**Non-Registry Install Sources:** With npm CLI 11.15.0+ flags available, audit existing projects for any dependencies currently resolved from git URLs, file paths, or tarball URLs. Flag any that are not explicitly approved. Pre-upgrade, search codebase and CI configurations for install commands that include --save with non-registry source patterns.
**CI/CD Credential Anomalies (NIST AC-2: Account Management; D3-LAM: Local Account Monitoring):** Monitor for npm token usage outside of expected pipeline runs, off-hours publish attempts, token use from unexpected IP ranges, or token use following a pipeline credential rotation event. Alert on any publish token used without a corresponding pipeline execution log.
**Package Integrity Verification (D3-FMBV: File Magic Byte Verification; D3-SFA: System File Analysis):** In build environments, verify package integrity hashes against lock file records before installation completes. Mismatches between expected and actual hashes are a strong indicator of substitution. Integrate Subresource Integrity (SRI) or equivalent hash verification into your CI/CD pipeline.
**TeamPCP Indicators:** Until confirmed IOCs are published, monitor threat intelligence feeds (MISP, Recorded Future, vendor threat reports) for TeamPCP-attributed package names and hashes. Flag new or updated packages in your dependency tree with sudden popularity increases or maintainer account changes.
Indicators of Compromise (1)
Export as
Splunk SPL
KQL
Elastic
Copy All (1)
1 tool
Platform Playbooks
Microsoft Sentinel / Defender
CrowdStrike Falcon
AWS Security
🔒
Microsoft 365 E3
3 log sources
Basic identity + audit. No endpoint advanced hunting. Defender for Endpoint requires separate P1/P2 license.
🛡
Microsoft 365 E5
18 log sources
Full Defender suite: Endpoint P2, Identity, Office 365 P2, Cloud App Security. Advanced hunting across all workloads.
🔍
E5 + Sentinel
27 log sources
All E5 tables + SIEM data (CEF, Syslog, Windows Security Events, Threat Intelligence). Analytics rules, playbooks, workbooks.
Hard indicator (direct match)
Contextual (behavioral query)
Shared platform (review required)
IOC Detection Queries (1)
Known attack tool — NOT a legitimate system binary. Any execution is suspicious.
KQL Query Preview
Read-only — detection query only
// Threat: npm Staged Publishing and Install Source Controls Close Two Persistent Supply Ch
// Attack tool: Pending — refer to threat intelligence sources tracking TeamPCP for published indicators
// Context: TeamPCP has been observed actively poisoning open-source npm packages at scale; specific package names, registry accounts, and payload hashes attributed to this campaign were not published in the avai
DeviceProcessEvents
| where Timestamp > ago(30d)
| where FileName =~ "Pending — refer to threat intelligence sources tracking TeamPCP for published indicators"
or ProcessCommandLine has "Pending — refer to threat intelligence sources tracking TeamPCP for published indicators"
or InitiatingProcessCommandLine has "Pending — refer to threat intelligence sources tracking TeamPCP for published indicators"
| project Timestamp, DeviceName, FileName, FolderPath,
ProcessCommandLine, AccountName, AccountDomain,
InitiatingProcessFileName, InitiatingProcessCommandLine
| sort by Timestamp desc
MITRE ATT&CK Hunting Queries (2)
Sentinel rule: Sign-ins from unusual locations
KQL Query Preview
Read-only — detection query only
SigninLogs
| where TimeGenerated > ago(7d)
| where ResultType == 0
| summarize Locations = make_set(Location), LoginCount = count(), DistinctIPs = dcount(IPAddress) by UserPrincipalName
| where array_length(Locations) > 3 or DistinctIPs > 5
| sort by DistinctIPs desc
Sentinel rule: Supply chain / cross-tenant access
KQL Query Preview
Read-only — detection query only
SigninLogs
| where TimeGenerated > ago(7d)
| where HomeTenantId != ResourceTenantId
| project TimeGenerated, UserPrincipalName, AppDisplayName, IPAddress, Location, HomeTenantId, ResourceTenantId
| sort by TimeGenerated desc
No actionable IOCs for CrowdStrike import (benign/contextual indicators excluded).
No hard IOCs available for AWS detection queries (contextual/benign indicators excluded).
Compliance Framework Mappings
T1554
T1078
T1195.001
T1553.002
T1199
AC-2
AC-6
IA-2
IA-5
IA-8
SI-7
+3
6.3
6.4
6.5
2.5
2.6
14.2
+1
MITRE ATT&CK Mapping
T1554
Compromise Host Software Binary
persistence
T1078
Valid Accounts
defense-evasion
T1195.001
Compromise Software Dependencies and Development Tools
initial-access
T1199
Trusted Relationship
initial-access
Free Template
ISO 42001 Audit Preparation & Evidence Guide
Professional guide for AI governance teams. Aligned with ISO 42001. $30.
Download Guide →
Guidance Disclaimer
The analysis, framework mappings, and incident response recommendations in this intelligence
item are derived from established industry standards including NIST SP 800-61, NIST SP 800-53,
CIS Controls v8, MITRE ATT&CK, and other recognized frameworks. This content is provided
as supplemental intelligence guidance only and does not constitute professional incident response
services. Organizations should adapt all recommendations to their specific environment, risk
tolerance, and regulatory requirements. This material is not a substitute for your organization's
official incident response plan, legal counsel, or qualified security practitioners.
View All Intelligence →