| name | pentest-cicd |
| description | CI/CD red team methodology — GitHub Actions, GitLab CI, Jenkins pipeline security analysis, secret leak, workflow injection advisory. Triggers on CI/CD pentest, GitHub Actions security, GitLab CI, Jenkins, pipeline injection, workflow injection, secret leak, OIDC abuse, runner takeover, supply chain. |
| license | MIT |
| compatibility | Works with Claude Code |
| allowed-tools | Read Write Edit Bash Grep |
| metadata | {"author":"badi","homepage":"https://github.com/fatihkan/badi-skills/tree/main/skills/pentest-cicd","badi-version":">=1.24.0","category":"pentest","scope":"advisory","inspired-by":"0xSteph/pentest-ai-agents cicd-redteam"} |
pentest-cicd
CI/CD pipeline attack surface advisory. Assumes repository access within an authorized engagement.
Triggers
- "CI/CD pentest"
- "GitHub Actions vulnerability"
- "GitLab CI test"
- "Jenkins security"
- "workflow injection"
- "OIDC token abuse"
- "supply chain pentest"
Attack Surface Map
| Component | Common Vulnerability |
|---|
| Source repo | Secret leak (env, .pem, .key) |
| Workflow file | Injection via PR title, comment, branch name |
| Runner | Self-hosted runner takeover |
| Secret store | Environment variable leak in logs |
| OIDC trust | Privilege leak to the cloud provider |
| Action marketplace | Compromised 3rd party action |
| Artifact | Exfiltrate or tamper with the build artifact |
| Cache | Cache poisoning (the next build picks it up) |
GitHub Actions Specific
Common Injection Pattern
- run: echo "Processing PR: ${{ github.event.pull_request.title }}"
- run: echo "Processing PR: $TITLE"
env:
TITLE: ${{ github.event.pull_request.title }}
pull_request_target Vulnerability
on: pull_request_target
jobs:
test:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- run: npm install
Detect:
pull_request_target + checkout head.sha = HIGH risk
pull_request_target + secret usage = HIGH risk
Self-Hosted Runner Takeover
- Public repo + self-hosted runner: anyone can register job
- Fix: organization-level allowlist, ephemeral runner (single job)
OIDC Trust Misconfig
{
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:sub": "repo:org/repo:*"
}
}
}
Fix: :ref:refs/heads/main for a specific branch, :environment:production for a specific env.
GitLab CI Specific
script:
- echo "Branch: ${CI_COMMIT_BRANCH}"
CI_JOB_TOKEN abuse: cross-project pipeline trigger
before_script global override
- Compliance pipeline by-pass (mr-only checks bypass with detached pipeline)
Jenkins Specific
curl https://jenkins.example.com/api/json
curl https://jenkins.example.com/script
- Script Approval bypass
- Pipeline shared library compromise (npm/git supply chain)
- Credentials plugin: master key + decryption
Secret Leak Detection
trufflehog git https://github.com/<org>/<repo> --only-verified
gitleaks detect --source . --report-path report.json
detect-secrets scan --all-files > .secrets.baseline
Workflow File Audit
Skill audit command (recursive scan):
grep -rE "pull_request_target|github.event.(issue|pull_request).body|github.event.(issue|pull_request).title|github.head_ref|github.event.workflow_run" .github/workflows/
Output Template
## CI/CD Pentest — <repo>
### Findings
- [CRITICAL] .github/workflows/lint.yml — pull_request_target + checkout head.sha
-> Fork PR malicious code reaches production secrets
- [HIGH] AWS OIDC trust "repo:org/*:*" — admin role from any branch
- [MEDIUM] gitleaks: 3 expired AWS keys in git history
- [LOW] Self-hosted runner on a public repo (missing org allowlist)
### Recommendations
- pull_request_target only for read-only workflows (test, lint) + script env-isolated
- Make the OIDC sub claim specific
- Truffle/gitleaks pre-commit hook
- Runner ephemeral mode (actions/runner --ephemeral)
Out-of-Scope
- Production deploy disruption (stays outside the engagement)
- Supply chain attack attempt against 3rd party action repos
- DoS pipeline (exceeding limits)