Skip to content

VNX-515: Covert Storage Channel: secret placed in the process environment

Detects Python code that stores a password, secret, token, API key or credential in its own process environment (os.environ[...] =, os.putenv, os.environ.setdefault/update), where every child process inherits it and other processes of the same user can read it.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-515
Severity Low
SARIF level note
Kind sast
Languages Python
CWE CWE-515
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:L/AP:A/AL:A/IC:N/FC:LT/RP:N/RL:A/AV:I/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L
Tags covert-storage-channel environment secrets cwe-515
Source internal/sast/rules/vnx-515.rego

Overview

VNX-515 maps to CWE-515: Covert Storage Channel. A covert storage channel moves information through shared storage that was never meant to carry it. Most such channels (file metadata, timing, lock files) cannot be recognised line by line. The one ordinary application code creates routinely is the process environment: a secret written to os.environ is inherited by every child process the program starts and can be read by other processes of the same user (and root) through /proc/<pid>/environ, ps e and crash dumps.

The rule reports os.environ[KEY] = value, os.putenv(KEY, value), os.environ.setdefault(KEY, value) and os.environ.update({KEY: value}) / update(KEY=value) (also via from os import environ) when the key names a password, secret, token, API key, private or access key or credential, or when the key is a variable and the value is a variable named as one.

What Gets Flagged

password = getpass.getpass("DB password: ")
os.environ["PGPASSWORD"] = password          # FLAGGED
os.putenv("AWS_SECRET_ACCESS_KEY", secret)    # FLAGGED
os.environ.update({"VENDOR_API_KEY": api_key})  # FLAGGED

What Is Not Flagged

env = os.environ.copy()
env["PGPASSWORD"] = password                  # a copy for one child process
subprocess.run(["pg_dump", "app"], env=env)
os.environ["GITHUB_TOKEN"] = os.environ.get("GH_TOKEN", "")  # re-export
os.environ["SECRET_KEY_FILE"] = "/run/secrets/key"            # a path, a constant
os.environ["TOKENIZERS_PARALLELISM"] = str(flag)               # not a token

Reads, deletes and pops, constants, comments, strings and test code are not reported.

Remediation

  1. Hand the secret only to the process that needs it: an env= copy for that one subprocess, its standard input, or an owner-only (0600) file.
  2. Remove secrets from os.environ once they are no longer needed, and do not start unrelated children while they are set.
  3. Prefer a secret manager or a file descriptor over environment variables for long-lived credentials.

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-515
// nosec vnx-515

Or record an ignore rule: vulnetix ignore add --rule vnx-515 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-515.rego, cli/internal/sast/docs/vnx-515.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.