Safeguard
Developer Tools•

Should a CLI take a secret as a flag, env var or stdin?

Prefer reading secrets from standard input or a credential store. Flags land in shell history and process lists. Here is how the three options compare.

Safeguard Security Team
3 min read

Prefer reading a secret from standard input, or from a credential store, over a command line flag. Arguments are visible to other processes and often saved in shell history. An environment variable is better than a flag but still readable by other code the same user runs.

Where each option leaks

MethodExampleCommon leak points
Flagtool login --token abc123Shell history, process listings, CI command echo, screen recordings
Environment variableTOOL_TOKEN=abc123 tool runChild processes inherit it, crash dumps, debug output that prints the environment
Standard inputtool login --with-token < fileFewer places, but the source file or pipe still matters
Credential storePrompt once, store in keychainDepends on the store's access controls

Flags are the weakest because the secret becomes part of the command text. Many systems let any user on the machine list running commands with their arguments, and shells commonly keep a history file.

What to build

  1. Do not offer a token flag, or mark it deprecated. If users insist, accept it but print a warning that shell history will keep it.
  2. Read from stdin with an explicit option. A flag like --with-token that reads one line from standard input makes the safe path obvious.
  3. Support a prompt on a terminal. Turn off echo while the user types.
  4. Support an environment variable for CI only. Name it in the docs as the unattended option and warn about child processes.
  5. Offer a file path option with a permission check. If the file is readable by others, refuse or warn.
  6. Support a credential helper so interactive users never type the secret again. See the post on where to store a CLI token.

Scrub what you print

Even when input is safe, output can leak. Debug logging that dumps request headers prints the Authorization header. Error messages that echo the command line print flags. Write a small redaction function and run all log lines through it. Test it with a fake token and grep the output.

On CI, remember that masking is a backstop only. GitHub's documentation says to register a value with add-mask before it is printed, and that masking depends on matching the exact value. If your CLI transforms a secret before printing, such as encoding or splitting it, the mask may not match. Do not rely on masking to clean up output you control.

A note on documentation

Examples are copied. If your README shows --token with a realistic value, users will paste that form into scripts. Show stdin or environment variable examples, with obvious placeholders.

Quick check

Is an environment variable a safe place for a production secret?

It is better than a flag, but anything that runs as the same user or inherits the environment can read it. Prefer short-lived tokens and a credential store where you can.

Will CI log masking catch a leaked token?

Only if the exact value was registered first. Treat masking as a safety net and keep secrets out of output in the first place.

For a view of the third-party code your CLI includes, Safeguard's free scan at gold.safeguard.sh/scan produces a dependency inventory.

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.