Why CI/CD Is the New Credential Store
The Mini Shai-Hulud supply chain campaign that compromised TanStack, @antv, and the Nx Console VS Code extension in May 2026 turned GitHub Actions into an attack surface in a way security teams weren’t monitoring. The attacker didn’t steal npm credentials from a vault or breach an identity provider — they ran attacker-controlled code inside a trusted Actions workflow that had legitimate access to short-lived OIDC tokens, which they then used to publish malicious package versions under the target organisation’s identity.
The three-step attack chain:
- Pwn Request: exploit a
pull_request_targettrigger to run fork-sourced code with base repo permissions - Cache poisoning: pre-populate a build cache with malicious binaries that the trusted workflow restores and executes
- OIDC token extraction: extract the workflow’s short-lived GitHub OIDC token directly from the Actions runner process memory to authenticate as the legitimate publisher
Most SIEM configurations have no visibility into any of these steps. This guide covers what to instrument and what to alert on.
Detection Surface Overview
| Stage | What to Monitor | Log Source |
|---|---|---|
| Pwn Request trigger | pull_request_target runs on fork PRs | GitHub Audit Log |
| Cache poisoning | Cross-context cache restores | GitHub Audit Log / Workflow logs |
| OIDC token use | OIDC token issuance + cloud API calls from unexpected IP | GitHub Token API / Cloud provider logs |
| Credential sweep | Process accessing credential paths | Endpoint telemetry |
| Package publication | Publish events from unexpected sources | npm / registry audit |
GitHub Audit Log: Key Events
GitHub’s organisation-level audit log is the primary visibility source. Enable audit log streaming to your SIEM via the GitHub Audit Log Streaming API.
Events to watch:
# OIDC token issued from non-standard workflow
repo.create_actions_oidc_token
# Workflow triggered from fork via pull_request_target
workflows.completed (filter: head_branch from fork, trigger=pull_request_target)
# Actions cache restored across repository fork boundary
actions.cache_restored
Sigma rule — OIDC token issuance for external repositories:
title: GitHub Actions OIDC Token Issued for External Fork Workflow
id: 7e4f2b1c-3a8d-4e2f-b5c9-1d3e7a2f4b8c
status: experimental
description: Detects OIDC token issuance in workflows triggered by fork pull requests
via pull_request_target -- indicates potential Pwn Request exploitation
author: SOC Analyst Hub
date: 2026/05/28
logsource:
product: github
service: audit
detection:
selection:
action: 'repo.create_actions_oidc_token'
filter_internal:
# Adjust to match your org's internal repos
actor_location.country_code|exists: true
condition: selection
timeframe: 5m
falsepositives:
- Legitimate OIDC token generation from trusted internal workflows
level: medium
tags:
- attack.credential_access
- attack.t1552.001
Detecting Anomalous Cloud API Calls Post-OIDC
When a stolen OIDC token is used to authenticate against AWS, Azure, or GCP from an unexpected source IP, the cloud provider logs the call — but teams rarely correlate those logs back to GitHub Actions workflow activity.
AWS CloudTrail rule — OIDC-sourced API call from non-Actions IP:
title: AWS OIDC Credential Use from Non-GitHub-Actions IP
id: 9a3b5c7d-2e1f-4a6b-8c9d-3f5e7a1b4c2d
status: experimental
description: Detects AWS API calls authenticated via GitHub Actions OIDC token from
source IPs outside known GitHub Actions runner CIDR ranges
author: SOC Analyst Hub
date: 2026/05/28
logsource:
product: aws
service: cloudtrail
detection:
selection:
eventSource: '*'
userIdentity.type: 'WebIdentityUser'
userIdentity.principalId|contains: 'token.actions.githubusercontent.com'
filter_known_ips:
# GitHub Actions hosted runner IPs -- update from https://api.github.com/meta
sourceIPAddress|cidr:
- '192.30.252.0/22'
- '185.199.108.0/22'
- '140.82.112.0/20'
- '143.55.64.0/20'
condition: selection and not filter_known_ips
falsepositives:
- Self-hosted runners with non-standard IPs (add to allowlist)
level: high
tags:
- attack.credential_access
- attack.t1552.001
- attack.initial_access
- attack.t1195.002
Detecting the Credential Sweep on Endpoints
If malicious package code runs in a developer’s environment (via a compromised npm package’s postinstall script), it will attempt to read credential files. The Mini Shai-Hulud payload targeted 130+ file paths. The most consistent detectable pattern is node processes reading credential files outside expected application paths.
Sigma rule — Node.js postinstall accessing credential paths:
title: Node.js Process Reading Cloud or Developer Credential Files
id: 2c4d6e8f-1a3b-5c7d-9e2f-4a6b8c1d3e5f
status: experimental
description: Detects node/npm processes accessing credential files that supply chain
malware typically sweeps -- triggers on postinstall scripts or spawned child processes
author: SOC Analyst Hub
date: 2026/05/28
logsource:
category: file_access
product: linux
detection:
selection_process:
Image|endswith:
- '/node'
- '/npm'
- '/npx'
- '/bun'
selection_paths:
TargetFilename|contains:
- '/.aws/credentials'
- '/.aws/config'
- '/.kube/config'
- '/.npmrc'
- '/.ssh/id_'
- '/.config/claude'
- '/.gitconfig'
- '/VAULT_TOKEN'
- '/.env'
filter_expected:
# npm/node legitimately reads .npmrc -- filter on scope
TargetFilename|endswith: '/.npmrc'
CommandLine|contains: 'npm install'
condition: selection_process and selection_paths and not filter_expected
falsepositives:
- Legitimate package tools that read these paths during normal operation
- Add process allowlists for known-good tooling
level: high
tags:
- attack.credential_access
- attack.t1552.001
- attack.t1195.002
KQL: Azure Sentinel — npm Process Credential File Access
For environments forwarding endpoint telemetry to Sentinel via MDE:
DeviceFileEvents
| where InitiatingProcessFileName in ("node", "npm", "npx", "bun")
| where FolderPath has_any (
"/.aws/credentials",
"/.kube/config",
"/.npmrc",
"/.ssh/id_rsa",
"/.ssh/id_ed25519",
"/.config/claude",
"/.gitconfig"
)
| where InitiatingProcessCommandLine !contains "npm install --global"
| project Timestamp, DeviceName, InitiatingProcessCommandLine, FolderPath, FileName, InitiatingProcessParentFileName
| order by Timestamp desc
npm Registry Anomaly Detection
If your organisation publishes packages to npm, monitor the npm Audit Log API for publish events. Unexpected publish events from unfamiliar IP addresses or at unusual times are a strong signal of compromised publishing credentials.
Key signals in npm audit log:
- Publish event from IP not matching known CI runner IPs
dist-tagchanges on existing packages- Package versions published outside normal release cadence
- New collaborator additions to existing packages
Many organisations don’t monitor npm publish events at all. For any package with significant downstream usage, setting up publish-event alerting is a high-ROI detection control.
Hardening Recommendations
Detections should be paired with these preventive controls:
- Replace
pull_request_targetwithpull_requestfor workflows that don’t require elevated permissions — the latter does not grant base-repo secrets or OIDC tokens to fork-sourced code - Scope OIDC token claims using
subjectconditions in your cloud OIDC trust policy to restrict which repos and branches can assume a given role - Separate publishing credentials from build credentials — the ability to publish a package should be isolated from the normal CI build context
- Pin Actions to full commit SHAs rather than floating tags to prevent cache poisoning via tag hijacking
- Deploy StepSecurity harden-runner or equivalent to audit all network egress from runners and alert on unexpected external connections during build steps