devsecops
supply-chain-security
github-actions
kubernetes
python
ci-cd

TeamPCP Poisoned The Security ToolsIn Your CI/CD Pipeline

When Trivy, KICS, and LiteLLM sit inside trusted delivery paths, compromising them is not opportunistic. It is efficient.

The March 2026 TeamPCP campaign did not just hit application dependencies. It moved through the security and developer tooling layer itself: Trivy, Checkmarx KICS, and LiteLLM release paths. This post breaks down what appears verified, what remains reported attribution, and the controls that would have cut the chain early.

Last verified against available public sources on March 28, 2026.

Zara visual explaining the TeamPCP CI/CD and package supply chain compromise

Spark Mode: This Was the Strategy

The uncomfortable part is not that a package got poisoned. Packages get poisoned. The uncomfortable part is which packages and which workflow surfaces were chosen.

Trivy and KICS are security-adjacent CI/CD components. LiteLLM sits in a fast-growing AI integration layer. Those are exactly the places where you find broad credentials, release automation, Kubernetes reach, and teams that trust the tool by default.

The Reported Five-Day Chain

Late February to early March 2026

The Trivy compromise created the opening

Public incident reporting and postmortem coverage indicate Aqua Security’s Trivy project was abused through GitHub Actions workflow weaknesses, leading to stolen credentials and malicious downstream publishing activity.

March 23, 2026

Checkmarx KICS GitHub Actions were reportedly retargeted

Follow-on reporting tied the KICS GitHub Action compromise to credentials harvested earlier in the campaign, extending the blast radius from one trusted security tool to another.

March 24, 2026

LiteLLM versions 1.82.7 and 1.82.8 were published to PyPI with malicious code

Endor Labs research, cited in multiple reports, found the malicious code was not present in the upstream repository and that version 1.82.8 additionally dropped a .pth-based persistence path that executed on Python startup.

What Looks Verified

LiteLLM versions 1.82.7 and 1.82.8 on PyPI contained malicious code not present in the upstream GitHub repository.

The payload harvested credentials and secrets, attempted Kubernetes lateral movement, and installed persistent host-level components.

GitHub’s own guidance still says pinning third-party actions to a full-length commit SHA is the only way to treat an action reference as immutable.

Teams that only removed the poisoned package without rotating secrets were underreacting to the actual blast radius.

What Needs Careful Wording

Exact counts of moved tags, the precise PAT origin point, and some ecosystem-to-ecosystem linkages are reported in follow-on analysis, but not every detail is backed by a vendor-published primary incident report.

Attribution to TeamPCP is strong in research and media coverage, but where direct vendor confirmation is unavailable, it should be described as reported or linked by researchers, not as courtroom-grade proof.

Public reporting connects the Trivy compromise to the LiteLLM release poisoning chain. That linkage is credible and repeated, but still should be framed as investigative attribution.

Under The Hood: Three Payload Goals

Stage 1: Credential harvest

Researchers reported theft targets including SSH keys, cloud tokens, Kubernetes secrets, crypto wallets, CI/CD secrets, and .env material. This is not just package compromise. It is operator environment compromise.

Stage 2: Kubernetes lateral movement

The reported behavior included deploying privileged pods broadly across clusters to gain node-level leverage. If your affected workload had enough rights to create privileged pods, the attacker did not need another bug.

Stage 3: Persistence

Reports describe a persistent systemd backdoor and filesystem artifacts such as `~/.config/sysmon/`. That means “pip uninstall” is not incident response. It is cosmetic cleanup.

Blueprint Mode: Break The Chain At Step One

GitHub’s own security guidance is unambiguous here: pin third-party actions to a full-length commit SHA. Not a major version tag. Not `main`. Not `latest`. A full SHA.

Why? Because tags move. The attacker only needs repository write access once. If your workflow follows a mutable tag, you just subscribed your pipeline to the attacker’s release process.

github-actions-pinning.ymlyaml
# Unsafe: mutable tag
- uses: aquasecurity/trivy-action@v0.33.1

# Safer: immutable commit SHA
- uses: aquasecurity/trivy-action@d0c8b7f5c8f5d7c99c9f1b9d8b0d5e2b6f4a1c77

# Organization policy:
# Require all third-party actions to be pinned to full-length SHAs

Audit Right Now

Immediate triage commands

teampcp-triage.shbash
# 1. Find mutable third-party action references
rg -n "uses:\s+[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+@(v|main|master|latest|[0-9]+\.)" .github/workflows

# 2. Check whether LiteLLM poisoned versions were installed
python -m pip show litellm || true
python -m pip freeze | rg '^litellm=='

# 3. Search for likely persistence artifacts on Linux runners/workstations
find ~/.config -maxdepth 3 -type f 2>/dev/null | rg 'sysmon|systemd|pg_state|pglog' || true
systemctl --user list-units --type=service 2>/dev/null | rg 'sysmon|pg' || true

# 4. Look for suspicious cluster-wide privileged pod creation patterns
kubectl get pods -A -o json | jq -r '
  .items[]
  | select(any(.spec.containers[]?; .securityContext.privileged == true))
  | [.metadata.namespace, .metadata.name] | @tsv
'

# 5. Hunt for the attacker pod naming pattern mentioned in reporting
kubectl get pods -A | rg 'node-setup-' || true

If You Ran It, Rotate More Than The Package

Rotate SSH keys used on any machine that installed the affected packages or ran the affected actions.

Rotate cloud credentials, kubeconfigs, and every API key stored in `.env` files on those systems.

Invalidate CI/CD tokens and GitHub credentials tied to release pipelines, package publishing, or bot automation.

Treat self-hosted runners as potentially burned if they executed poisoned workflows or packages.

Rebuild from known-good images where persistence is possible. Do not trust in-place cleanup alone.

What This Means For AI Stacks

LiteLLM matters because it is not a niche package. It sits in the request path between apps, models, gateways, and agent frameworks. That makes transitive compromise realistic.

If an AI platform team says “we did not install that package directly,” that is not a reassuring sentence. It is the definition of supply-chain exposure.

What This Means For Platform Teams

Stop thinking of scanners, release actions, package publishers, and extension registries as support tooling. They are production control planes with nicer branding.

The controls belong in the same risk tier as cluster admin, package signing, registry trust, and workload identity.

Bottom Line

The attack worked because teams trusted the tools inside the pipeline more than they trusted the pipeline itself. Raise the bar: immutable action references, short-lived credentials, runner isolation, and package compromise playbooks that assume persistence instead of hoping for cleanup.

Read More TNTM Analysis

Sources