# Secrets leaked in CI logs

Credentials in remote URLs or env vars that failing CI tools echo into public logs

_Area: Software supply chain and CI/CD. Seen in 3 disclosed reports; one confirmed bounty: $2,000; latest 2026-09-23. Updated 2026-10-23._

## How it works
A secret is placed where a tool will echo it back. The assumption that breaks is "quiet means nothing is printed": a command that suppresses output on success still prints its full argument list, including a credential-bearing URL, when it fails. The same holds for any address or link a product hands out as "private": if a token is embedded in it, the token's real scope is whatever the server accepts, not what the UI label says.

## Where it shows up
- Remote URLs of the form `https://<token>@github.com/...` or `@gitlab.com` passed to `git push`/`clone`, especially with `-q` or `--quiet`.
- CI steps that print environment, config, or command lines on failure (set -x, verbose flags, error dumps).
- "Private" email addresses, share links, or webhook URLs containing a long token prefix.
- Public CI artifacts (e.g. uploaded checkout dirs or linter logs) that capture persisted tokens [E1](https://bugbountylibrary.com/r/blog%3Aunit42-paloaltonetworks-com-github-repo-artifacts-leak-tokens.md).

## How to review
For each CI step, ask: if this command fails, what does it print? The invariant is that no credential ever appears in a command line, URL, or env var that a failing tool can echo. Check that tokens are injected via credential helpers or masked variables, that remote URLs carry no embedded secret, and that logs and artifacts are treated as public. For handed-out addresses/links, verify that the token's server-side scope matches the scope the UI claims, and that sender identity is verified [E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-gitlab-email-push-to-main.md).

## Variants seen
- Long-lived account-wide token embedded in a "project-scoped" incoming email address, with no sender verification [E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-gitlab-email-push-to-main.md).
- Personal access token in a remote URL printed by a failed `git push -q` into public build logs [E4](https://bugbountylibrary.com/r/h1%3A215625.md).
- Tokens persisted into `.git` by checkout defaults and captured in public workflow artifacts [E1](https://bugbountylibrary.com/r/blog%3Aunit42-paloaltonetworks-com-github-repo-artifacts-leak-tokens.md).
- Secret masking defeated by reversible transforms (double base64, chunking) before exfiltration [E2](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-github-attacks-pat-control-plane.md).

## Why fixes fail
Quiet flags and UI labels are treated as controls; neither limits what a failure prints or what a token can reach [E3](https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-gitlab-email-push-to-main.md)[E4](https://bugbountylibrary.com/r/h1%3A215625.md). Secret masking only blocks the literal string, so any reversible transform defeats it [E2](https://bugbountylibrary.com/r/blog%3Awiz-io-blog-github-attacks-pat-control-plane.md).

## Impact and severity
E3 gave push-to-main, CI/CD execution, and IP-allowlist bypass account-wide. E4 gave push/admin over all reachable public repos. E1 exposed job tokens mid-run; E2 enabled cloud lateral movement. Severity is high because tokens are long-lived and broad; it worsens when logs and artifacts are public, tokens are account-wide, and nothing rotates them.

## Evidence
- E1: https://bugbountylibrary.com/r/blog%3Aunit42-paloaltonetworks-com-github-repo-artifacts-leak-tokens.md
- E2: https://bugbountylibrary.com/r/blog%3Awiz-io-blog-github-attacks-pat-control-plane.md
- E3: https://bugbountylibrary.com/r/blog%3Aaikido-dev-blog-gitlab-email-push-to-main.md
- E4: https://bugbountylibrary.com/r/h1%3A215625.md
