Skip to content

Commit 511fcac

Browse files
committed
Improves logging of environment variables
Fixes: #3542
1 parent f39abe3 commit 511fcac

File tree

3 files changed

+55
-22
lines changed

3 files changed

+55
-22
lines changed

docs/changelog/3542.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improves logging of environment variables by sorting them by key and redacting
2+
the values for the ones that are likely to contain secrets.

src/tox/tox_env/api.py

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -31,23 +31,23 @@
3131
from tox.tox_env.installer import Installer
3232

3333
LOGGER = logging.getLogger(__name__)
34-
SECRET_ENV_VAR_REGEX = re.compile(
35-
r"""(?ix) # case-insensitive, verbose mode
36-
^\s* # optional leading whitespace
37-
(?P<key> # capture group: key
38-
(?:\w*(_)?)
39-
(?:
40-
(SECRET|TOKEN|KEY|PASSWORD|PWD|CRED|PRIVATE|AUTH|API)
41-
)
42-
(?:\w*) # allow variable prefixes/suffixes
43-
)\s*=\s* # equal sign with optional spaces
44-
(?P<value> # capture group: value
45-
(['"])? # optional opening quote
46-
([A-Za-z0-9\-_]{12,}) # suspicious value (long, alphanumeric)
47-
\1? # optional closing quote matching opening
48-
)
49-
"""
50-
)
34+
# Based on original gitleaks rule named generic-api-key
35+
# See: https://github.com/gitleaks/gitleaks/blob/master/config/gitleaks.toml#L587
36+
secret_keywords = [
37+
"access",
38+
"api",
39+
"auth",
40+
"client",
41+
"cred",
42+
"key",
43+
"passwd",
44+
"password",
45+
"private",
46+
"pwd",
47+
"secret",
48+
"token",
49+
]
50+
SECRET_ENV_VAR_REGEX = re.compile(".*(" + "|".join(secret_keywords) + ").*", re.IGNORECASE)
5151

5252

5353
def redact_value(name: str, value: str) -> str:
@@ -485,8 +485,20 @@ def _write_execute_log(env_name: str, log_file: Path, request: ExecuteRequest, s
485485
with log_file.open("wt", encoding="utf-8") as file:
486486
file.write(f"name: {env_name}\n")
487487
file.write(f"run_id: {request.run_id}\n")
488+
redacted = False
489+
msg = ""
488490
for env_key, env_value in sorted(request.env.items()):
489-
file.write(f"env {env_key}: {redact_value(name=env_key, value=env_value)}\n")
491+
redacted_value = redact_value(name=env_key, value=env_value)
492+
if redacted_value != env_value:
493+
redacted = True
494+
msg += f"env {env_key}: {redacted_value}\n"
495+
if redacted:
496+
msg = (
497+
"notice: Same values of the environment variables with names containing "
498+
+ ", ".join(secret_keywords)
499+
+ " were redacted with '*' to prevent potential leak of secrets.\n"
500+
+ msg
501+
)
490502
for meta_key, meta_value in status.metadata.items():
491503
file.write(f"metadata {meta_key}: {meta_value}\n")
492504
file.write(f"cwd: {request.cwd}\n")

tests/tox_env/test_api.py

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,28 @@ def test_setenv_section_substitution(tox_project: ToxProjectCreator) -> None:
3939

4040

4141
@pytest.mark.parametrize(
42-
("key", "value", "expected"), [pytest.param("FOO", "bar", "bar"), pytest.param("GITHUB_TOKEN", "foo", "***")]
42+
("key", "do_redact"),
43+
[
44+
pytest.param("SOME_KEY", True, id="key"),
45+
pytest.param("API_FOO", True, id="api"),
46+
pytest.param("AUTH", True, id="auth"),
47+
pytest.param("CLIENT", True, id="client"),
48+
pytest.param("DB_PASSWORD", True, id="password"),
49+
pytest.param("FOO", False, id="foo"),
50+
pytest.param("GITHUB_TOKEN", True, id="token"),
51+
pytest.param("NORMAL_VAR", False, id="other"),
52+
pytest.param("S_PASSWD", True, id="passwd"),
53+
pytest.param("SECRET", True, id="secret"),
54+
pytest.param("SOME_ACCESS", True, id="access"),
55+
pytest.param("MY_CRED", True, id="cred"),
56+
pytest.param("MY_PRIVATE", True, id="private"),
57+
pytest.param("MY_PWD", True, id="pwd"),
58+
],
4359
)
44-
def test_redact(key: str, value: str, expected: str) -> None:
60+
def test_redact(key: str, do_redact: bool) -> None:
4561
"""Ensures that redact_value works as expected."""
46-
result = redact_value(key, value)
47-
assert result == expected
62+
result = redact_value(key, "foo")
63+
if do_redact:
64+
assert result == "***"
65+
else:
66+
assert result == "foo"

0 commit comments

Comments
 (0)