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:

  1. Pwn Request: exploit a pull_request_target trigger to run fork-sourced code with base repo permissions
  2. Cache poisoning: pre-populate a build cache with malicious binaries that the trusted workflow restores and executes
  3. 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

StageWhat to MonitorLog Source
Pwn Request triggerpull_request_target runs on fork PRsGitHub Audit Log
Cache poisoningCross-context cache restoresGitHub Audit Log / Workflow logs
OIDC token useOIDC token issuance + cloud API calls from unexpected IPGitHub Token API / Cloud provider logs
Credential sweepProcess accessing credential pathsEndpoint telemetry
Package publicationPublish events from unexpected sourcesnpm / 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-tag changes 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_target with pull_request for 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 subject conditions 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