Safeguard
Developer Tools•

How do you stop a CLI printing secrets into CI logs?

Redact inside the CLI first, then treat CI masking as a backstop. GitHub add-mask only works if the exact value is registered before it is printed.

Safeguard Security Team
4 min read

Redact secrets inside your CLI before anything is written, and treat the CI platform's masking as a backstop. GitHub Actions masks a value only after it is registered with add-mask, and redaction relies on matching the exact value. Anything transformed, split or printed earlier can slip through.

What masking does

GitHub's workflow commands documentation says that ::add-mask::VALUE stops a string or variable from being printed in the log. Each masked word is replaced with asterisks. It adds that you must register the secret before outputting it in logs or using it in other workflow commands, and that masking is per value per job.

The security hardening guide adds a limit that affects tool authors. Redaction largely relies on finding an exact match for the specific secret value, so structured data such as a JSON blob stored as a secret can cause redaction to fail. It also says that if an unredacted secret reaches a log, you should delete the log and rotate the secret.

Four ways a CLI defeats masking

  1. Printing before registering. Your tool starts, logs its configuration, and only later does the workflow mask the value.
  2. Transforming the value. Base64, URL encoding, truncation or splitting across lines produces a different string that does not match.
  3. Structured secrets. A token embedded in a larger JSON secret is not matched as its own value.
  4. Derived values. A signed URL built from a token contains the token in a new form.

What to build into the CLI

  1. Keep a single logging function. Every message passes through it.
  2. Maintain a list of values to hide, filled when credentials are loaded. Replace each occurrence with a fixed marker.
  3. Redact common transformed forms of each secret, at least the encoded form your own code produces.
  4. Never print raw request or response headers. Print an allowlist of safe header names instead.
  5. Hide query string values in URLs you log.
  6. Make verbose mode safe. Debug output is where secrets usually leak, because people turn it on in a hurry during an incident.

Ask the CI platform for help too

If your tool produces a value at run time, such as a short-lived token, the workflow can register it. GitHub documents masking a value generated in a job, then passing it on through an output file instead of printing it. If your CLI has a mode for CI, consider printing a mask command line for any credential it creates, in the format the platform expects.

Test it

Write a test that sets a fake token, runs the tool in its noisiest mode, and searches the captured output for the token and for its encoded forms. Run it in your own release pipeline.

If a leak happens

Delete the affected logs, rotate the secret, and check where else the value could have gone, such as uploaded artifacts and error reports. Say so in your release notes if your tool caused it.

Quick check

Does masking remove a secret that was already printed?

No. Register the value before it can appear. If a secret reached a log, delete the log and rotate the secret, as GitHub's guidance says.

Why did masking miss my token?

Likely the printed string differs from the registered value, for example encoded or part of a larger structured secret. Masking relies on exact matches.

Safeguard offers a free scan of your dependencies at gold.safeguard.sh/scan, useful for seeing what third-party code sits in your CLI.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.