How to Find Secrets Leaked in Jenkins Build Logs

By the OzenOps team. Published .

Jenkins keeps every build's console output, and anyone who can read a job can read its logs. A token printed once can sit there for months. This guide shows how secrets end up in logs, how to search the builds you already have, and what to do when you find one.

How secrets end up in build logs

The Credentials Binding plugin replaces a bound secret with **** when it is printed, and also tries to catch the escaped forms Bourne shell, Bash, Almquist shell and Windows batch print. Jenkins calls that second part best effort. Masking still misses secrets in several common cases, which the Jenkins post on the limits of credentials masking describes:

  • Changed values: a secret that is Base64-encoded, URL-encoded or escaped in forms Jenkins does not recognize (anything beyond its best-effort shell rules) no longer matches the original text, so it is printed as is.
  • Secrets that were never bound: a token hard-coded in a script, read from a file or fetched from a vault by your own code isn't known to Jenkins, so nothing masks it.
  • Tools that print their own settings: debug flags, verbose HTTP clients and commands that dump the environment can print credentials. set +x stops the shell echoing commands, not a tool printing its own output.
  • Files: a secret written into an archived artifact or a test report isn't in the console at all, so masking never sees it.

The same Credentials Binding page also warns against Groovy string interpolation (double quotes around a secret in a sh step), which hands the value to the shell in a less safe way.

Step 1: Decide what you are searching

Write down which jobs, which builds and which time range you will cover before you start. Jenkins deletes old builds under each job's discard settings, some teams send Pipeline logs to external storage, and logs may also live in backups or a log platform. A search is only as good as the list of places it covered, so keep that list with your results.

Step 2: Check one build by hand

Open a build's Console Output and its plain-text view, and search for the names of your secrets' environment variables, Authorization, token, password and the prefixes your providers use. This tells you what a leak looks like in your logs before you scan many builds. Colored output, wrapped lines and JSON escaping can split a value, so don't treat a quick text search as proof the build is clean.

Step 3: Scan past builds with an established scanner

Use a maintained secret scanner rather than writing your own patterns:

  • TruffleHog has a jenkins source that reads build logs through the Jenkins API: trufflehog jenkins --url https://jenkins.example.com, with the user and API token in the JENKINS_USERNAME and JENKINS_PASSWORD environment variables, loaded from your secret store rather than typed on the command line, where shell history and process listings would keep them. By default it tries the credentials it finds against their providers to tell you which are live. Don't filter to verified results only: an unverified match can still be a real secret. Check that it reached every folder and multibranch job you listed in step 1.
  • Gitleaks scans files you have already exported, for example console text you downloaded into one directory: gitleaks dir --redact -r report.json ./exported-logs. Exporting first keeps collection and scanning separate, so you can check which builds you actually got.

Scanner output and exported logs contain the secrets you are looking for. Keep them somewhere restricted and delete them when the incident is closed. --redact applies to the Gitleaks example only: TruffleHog has no redaction option and prints the secrets it finds, so never send its output to a CI log or any other shared or persistent log. Don't paste matches into tickets or chat.

Step 4: Read the results carefully

  • A match is a candidate, not proof. Example keys in docs, test fixtures and dummy values match the same patterns. Confirm each one with whoever owns the credential.
  • No match is not proof either. Separate "scanned and clean" from "couldn't read", "skipped" and "deleted before you looked". Builds you couldn't scan stay on the list.

Step 5: Respond to a real leak

  1. Revoke or rotate the credential first. Deleting the build doesn't make the secret safe: anyone who read or downloaded the log still has it.
  2. Check whether it was used. Look at the provider's access logs for the period it was exposed.
  3. Keep the evidence you need (job, build number, time, who could read it) in a restricted place before you delete anything.
  4. Then clean up every copy: the build itself, exported logs, scanner reports, backups and any log platform that received the output.
  5. Fix the cause in the job so the next build doesn't print it again.

Step 6: Stop new leaks

  • Bind secrets with Credentials Binding (withCredentials) instead of hard-coding them, and pass them to sh steps in single quotes.
  • Turn off debug and verbose flags in steps that run with credentials.
  • Keep secrets out of archived artifacts and test reports.
  • Limit who can read job logs to the people who need them.
  • Test your checks with fake values that look like real secrets, never with a working credential.

Where Credentials Guardian fits

The Credentials Guardian plugin for Jenkins scans console output as a build writes it, which catches new leaks rather than old ones. What it does and doesn't do today:

  • Freestyle and other classic jobs only, not Pipeline. The scan uses Jenkins' console log filter, and Pipeline builds don't apply global console log filters. Pipeline builds are not scanned.
  • No scan of past builds. Use the steps above for the logs you already have.
  • What it looks for: 19 built-in patterns (AWS, GitHub, GitLab, Slack, Google, npm, Docker Hub, SendGrid, Twilio, Stripe, Mailgun and PagerDuty keys, private key headers and generic API keys, tokens and passwords) plus your own regular expressions. It matches patterns; it doesn't check whether a key is live.
  • What happens on a match: the original output stays in the log, and the plugin adds a "[Credentials Guardian] WARNING: potential secret leak detected" line naming the pattern and line number. Matches in its alerts are masked, keeping at most the first and last four characters. When the build ends it sends one alert to your notification destinations.
  • Failing the build: Fail Build on Leak is off by default. When it is on, the plugin marks the build as failed; steps that already ran have already run.
  • Limits: each build's scan stops after 5 MB of output, 15 seconds of matching or 500 findings, and only the first 10,000 characters of a line are checked. With Fail Build on Leak on, a scan that stops early fails the build too.
  • When it runs: only while the plugin, Enable Leak Detection and a valid license are all on.

The settings are in the Leak Detection and Notifications sections of the docs, and the license in Licensing.

Next steps

Read the full Credentials Guardian documentation for every setting, or see what the plugin includes and start a free trial on the Credentials Guardian product page.