From agent-infra-security
Triages compromised GitHub Actions supply chain attacks with interactive checklists, full runbooks, or shell scripts for exposure checks, log audits, secret rotation, and remediation.
npx claudepluginhub makash/agent-infra-security --plugin agent-infra-securityThis skill uses the workspace's default tool permissions.
Help developers and security teams triage, investigate, contain, and recover from a compromised GitHub Action where tags have been overwritten with malicious code.
Finds exploitable vulnerabilities in GitHub Actions workflows with concrete exploitation scenarios for external attackers. Reviews .github/workflows/*.yml, composite actions, and related configs.
Reviews GitHub Actions workflows for exploitation vulnerabilities by external attackers like pwn requests, expression injection, credential theft, and supply chain attacks with concrete PoC scenarios.
Hardens GitHub Actions workflows against supply chain attacks, credential theft, and privilege escalation by pinning actions to SHA digests, minimizing GITHUB_TOKEN permissions, preventing script injection, and adding reviewer gates.
Share bugs, ideas, or general feedback.
Help developers and security teams triage, investigate, contain, and recover from a compromised GitHub Action where tags have been overwritten with malicious code.
This skill produces one of three outputs depending on what the user asks for:
check_gha_compromise.sh script that automates detection, reports findings, and prompts before any remediation action.If the user doesn't specify which format, default to the interactive triage checklist. If the user says something like "just give me everything" or "runbook", produce the full markdown document. If they say "script" or "automate", generate the shell script.
Before producing any output, collect the following from the user. If they've already provided some of this in the conversation, don't re-ask.
Required:
aquasecurity/trivy-action)Helpful but not required (use defaults or skip if the user doesn't know):
references/ioc-patterns.md).Every output format follows these six phases in order. The depth and format change based on the output type, but the sequence is always the same.
The goal is to determine whether any repositories in the organization reference the compromised action in their workflow files.
Commands to guide the user through:
Search local workflow files for references to the affected action:
grep -rn "ACTION_NAME" .github/workflows/
Check if references use mutable tags (vulnerable) vs pinned SHAs (likely safe):
# Mutable tag references (VULNERABLE)
grep -rn "uses: ACTION_NAME@v" .github/workflows/
grep -rn "uses: ACTION_NAME@main" .github/workflows/
# SHA-pinned references (likely safe — verify the SHA is not the malicious commit)
grep -rn "uses: ACTION_NAME@[a-f0-9]\{40\}" .github/workflows/
Use gh CLI to search across the entire org:
gh search code "ACTION_NAME" --owner ORG --json repository,path
List all repos using the action and classify each reference as tag-based (vulnerable) or SHA-pinned (verify SHA):
gh search code "uses: ACTION_NAME" --owner ORG --json repository,path,textMatch
If Phase 1 found the action in any workflow, determine whether those workflows actually ran during the attack window.
List workflow runs during the attack window:
gh api "/repos/ORG/REPO/actions/runs?per_page=100&created=START..END" \
--jq '.workflow_runs[] | "\(.id)|\(.created_at)|\(.conclusion)"'
Download and search workflow logs for the action reference:
gh run view RUN_ID --log 2>/dev/null | grep -i "ACTION_NAME"
Check if the run used a tag-based reference (vulnerable) or SHA-pinned (safe):
gh api "/repos/ORG/REPO/actions/runs/RUN_ID/jobs" \
--jq '.jobs[].steps[] | select(.name | test("ACTION_NAME"; "i")) | .name'
Classify each run:
If Phase 2 confirmed runs during the attack window with tag-based references, hunt for evidence that the payload executed.
Search workflow logs for known exfiltration patterns:
# Download logs for a specific run
gh run view RUN_ID --log > /tmp/run_RUN_ID.log
# Search for IOC patterns
grep -i "base64" /tmp/run_RUN_ID.log
grep -iE "curl|wget" /tmp/run_RUN_ID.log | grep -v "github.com\|githubusercontent"
grep -i "tpcp" /tmp/run_RUN_ID.log
grep -iE "proc/(self|[0-9]+)/(mem|environ)" /tmp/run_RUN_ID.log
Check for exfiltration dead drop repos:
gh api "/orgs/ORG/repos" --jq '.[] | select(.name | test("tpcp|docs-tpcp")) | .full_name'
Search for repos created during the attack window that match dead drop patterns:
gh api "/orgs/ORG/repos?sort=created&direction=desc&per_page=20" \
--jq '.[] | "\(.name) \(.created_at)"'
Check for known C2 domain references in logs:
# For Trivy compromise
grep -i "aquasecurtiy\|scan\.aquasecurtiy" /tmp/run_*.log
grep -i "45\.148\.10\.212" /tmp/run_*.log
grep -i "icp0\.io" /tmp/run_*.log
# For KICS compromise
grep -i "checkmarx\.zone\|83\.142\.209\.11" /tmp/run_*.log
For self-hosted runners: check for persistence artifacts:
# systemd services
find ~/.config/systemd/user/ -name "*.service" -mtime -7 2>/dev/null
find /etc/systemd/system/ -name "internal-monitor.service" 2>/dev/null
# Known Trivy persistence paths
ls -la ~/.config/sysmon/sysmon.py 2>/dev/null
ls -la ~/.local/share/pgmon/service.py 2>/dev/null
ls -la /var/lib/svc_internal/runner.py 2>/dev/null
ls -la /var/lib/pgmon/pgmon.py 2>/dev/null
Pin affected action references to known-safe commit SHAs: Replace all tag-based references with SHA-pinned references in workflow files:
# BEFORE (vulnerable)
- uses: aquasecurity/trivy-action@v1
# AFTER (safe)
- uses: aquasecurity/trivy-action@57a97c7e7821a5776cebc9bb87c984fa69cba8f1
Cancel any in-flight workflow runs using the compromised action:
gh api "/repos/ORG/REPO/actions/runs?status=in_progress" \
--jq '.workflow_runs[].id' | while read run_id; do
gh api -X POST "/repos/ORG/REPO/actions/runs/$run_id/cancel"
done
Clear GitHub Actions cache:
gh api -X DELETE "/repos/ORG/REPO/actions/caches"
Disable affected workflows temporarily if needed:
gh workflow disable WORKFLOW_NAME -R ORG/REPO
For self-hosted runners: isolate and rebuild. Do not simply clean the runner. Rebuild from a fresh image. Self-hosted runners retain state between jobs, making persistence far more likely.
Hand off to the credential-exfiltration-response skill for systematic rotation.
Before handing off, scope what secrets were accessible to the compromised workflow:
# Repository-level secrets
gh api "/repos/ORG/REPO/actions/secrets" --jq '.secrets[].name'
# Organization-level secrets
gh api "/orgs/ORG/actions/secrets" --jq '.secrets[].name'
# Environment-level secrets
gh api "/repos/ORG/REPO/environments" --jq '.environments[].name' | while read env; do
echo "--- $env ---"
gh api "/repos/ORG/REPO/environments/$env/secrets" --jq '.secrets[].name' 2>/dev/null
done
GITHUB_TOKEN note: GITHUB_TOKEN is inherently scoped to the workflow run, but review its permissions. If contents: write or packages: write was granted, the attacker could have pushed code or packages. Check for dead drop repos:
gh api "/orgs/ORG/repos?sort=created&direction=desc&per_page=20" \
--jq '.[] | "\(.name) \(.created_at) \(.pushed_at)"'
Tell the credential-exfiltration-response skill:
The credential skill will walk through detection of abuse via audit trails, rotation for each credential class, and verification that old credentials are truly invalidated (including provider-specific delays like AWS STS sessions surviving key deletion for up to 36 hours).
Pin ALL GitHub Actions to full commit SHAs, never mutable tags:
# BAD — mutable tag, can be overwritten
- uses: actions/checkout@v4
# GOOD — immutable SHA
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
Use GitHub's Actions allow-list to restrict which actions can run:
Enable Dependabot for GitHub Actions version updates:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
Use StepSecurity Harden-Runner for runtime monitoring:
- uses: step-security/harden-runner@v2
with:
egress-policy: audit # or block
Review GITHUB_TOKEN permissions — use minimum required:
permissions:
contents: read
# Only add write permissions for steps that truly need them
Separate CI secrets: Don't give build workflows access to deploy secrets. Use GitHub Environments with protection rules to gate access to production credentials.
Use OpenID Connect (OIDC) instead of long-lived credentials for cloud deployments:
permissions:
id-token: write
contents: read
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/deploy
aws-region: us-east-1
Walk the user through one phase at a time. After each phase, ask what they found before proceeding to the next. Adapt the remaining phases based on their answers. For example, if Phase 1 shows they don't use the action, stop and tell them they're clear — don't walk through IOC hunting.
Structure each phase as:
Produce a markdown document with all six phases, all commands pre-filled with the specific action name, org, attack window, IOC domains, and persistence paths from the advisory. Include a summary header with the incident metadata (action, compromised tags, attack window, IOC domains). This is meant to be shared with a team, so write it to be self-contained — someone reading it for the first time should understand what happened and what to do.
Save this as a .md file using the create_file tool.
Generate a bash script called check_gha_compromise.sh that:
read -p before any destructive action (cache clearing, workflow disabling)--dry-run flag that skips all prompts and just reportsRead scripts/check_gha_compromise.sh for the template. Customize it with the specific action details from the user's context.
Save this using the create_file tool and make it executable.
When producing the full incident response runbook or interactive checklist, include this template at the end so the user can document their findings.
contents: write, the attacker could push code. If it had packages: write, the attacker could publish packages. Review the permissions block in each affected workflow.credential-exfiltration-response skill for systematic rotation — it handles the full detect/rotate/verify lifecycle.