SEC016 — environment-dump¶
Severity: warning · Category: Security · Platforms: GitHub Actions, Azure Pipelines
What it checks¶
Flags a step whose script content runs a bare, unfiltered
environment-dump command: printenv or env with no argument naming a
specific variable, cmd.exe's bare set, or PowerShell's Get-ChildItem
Env: (and its gci/dir/ls aliases) or
[Environment]::GetEnvironmentVariables(). Checked wherever a script can
live: GitHub Actions' run:, Azure's script:/bash:/powershell:/
pwsh: shorthand steps, and Azure's task-based equivalents
(task: CmdLine@2, PowerShell@2, Bash@3, AzureCLI@2) via their
inputs.script/inputs.inlineScript.
A command that names a single variable — printenv HOME, env FOO=bar
some-command (using env as a prefix to set one variable for one
invocation, not to dump anything) — is deliberately not flagged; only
the no-argument, dump-everything form is.
Why it matters¶
Both platforms mask known secret values in log output, but that masking
only works for variables the platform actually knows are secret — a
variable added to a variable group without checking "keep this value
secret" in Azure, or a token passed through env: from a context the
platform's masker doesn't scan, prints in plaintext right alongside
everything else. Dumping the entire environment as a debug step is a
common, usually harmless-feeling habit (printenv next to dotnet
--info to sanity-check a build environment), but it means every secret
present on the runner — masked or not — ends up in the log, whether the
step needed it or not. The fix isn't "never debug your environment," it's
"print only what you're actually trying to check."
This is the same reasoning as SEC008
(toJSON(secrets)): dumping everything exposes more than the step
needs, independent of whether masking happens to catch it this time.
Found in practice vetting
AvaloniaUI/Avalonia's real
azure-pipelines.yml, which runs a bare printenv as a debug step in
two of its four CI jobs.
Examples¶
Flagged — dumps everything to sanity-check the environment:
- script: |
dotnet --info
printenv
./build.sh --target Release
- task: CmdLine@2
inputs:
script: |
dotnet --info
printenv
Fixed — check only the variable(s) actually in question:
- script: |
dotnet --info
printenv DOTNET_ROOT
./build.sh --target Release
Suppressing¶
The finding is reported on the step's line:
- run: printenv # vlotpipe: ignore[SEC016]
Reasonable for a genuinely secret-free environment (e.g., a job with no secret variables configured at all) — but that's an easy assumption to get wrong later when a secret gets added to the job and nobody remembers this step is still dumping everything.