Skip to content

Commit 3fb8da5

Browse files
committedMay 18, 2023
fix: hide sensitive environment variables with asterisks. #1628
1 parent 48126d4 commit 3fb8da5

File tree

4 files changed

+66
-13
lines changed

4 files changed

+66
-13
lines changed
 

‎CHANGES.rst

+5
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ Unreleased
2828
as invisible functions and coverage.py would warn you if they weren't
2929
completely executed. This no longer happens under Python 3.12.
3030

31+
- Fix: the ``coverage debug sys`` command includes some environment variables
32+
in its output. This could have included sensitive data. Those values are
33+
now hidden with asterisks, closing `issue 1628`_.
34+
3135
.. _issue 1553: https://github.com/nedbat/coveragepy/issues/1553
36+
.. _issue 1628: https://github.com/nedbat/coveragepy/issues/1628
3237

3338

3439
.. scriv-start-here

‎coverage/control.py

+5-10
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,17 @@
2929
from coverage.config import CoverageConfig, read_coverage_config
3030
from coverage.context import should_start_context_test_function, combine_context_switchers
3131
from coverage.data import CoverageData, combine_parallel_data
32-
from coverage.debug import DebugControl, NoDebugging, short_stack, write_formatted_info
32+
from coverage.debug import (
33+
DebugControl, NoDebugging, short_stack, write_formatted_info, relevant_environment_display
34+
)
3335
from coverage.disposition import disposition_debug_msg
3436
from coverage.exceptions import ConfigError, CoverageException, CoverageWarning, PluginError
3537
from coverage.files import PathAliases, abs_file, relative_filename, set_relative_directory
3638
from coverage.html import HtmlReporter
3739
from coverage.inorout import InOrOut
3840
from coverage.jsonreport import JsonReporter
3941
from coverage.lcovreport import LcovReporter
40-
from coverage.misc import bool_or_none, join_regex, human_sorted
42+
from coverage.misc import bool_or_none, join_regex
4143
from coverage.misc import DefaultValue, ensure_dir_for_file, isolate_module
4244
from coverage.multiproc import patch_multiprocessing
4345
from coverage.plugin import FileReporter
@@ -1298,14 +1300,7 @@ def plugin_info(plugins: List[Any]) -> List[str]:
12981300
("pid", os.getpid()),
12991301
("cwd", os.getcwd()),
13001302
("path", sys.path),
1301-
("environment", human_sorted(
1302-
f"{k} = {v}"
1303-
for k, v in os.environ.items()
1304-
if (
1305-
any(slug in k for slug in ("COV", "PY")) or
1306-
(k in ("HOME", "TEMP", "TMP"))
1307-
)
1308-
)),
1303+
("environment", [f"{k} = {v}" for k, v in relevant_environment_display(os.environ)]),
13091304
("command_line", " ".join(getattr(sys, "argv", ["-none-"]))),
13101305
]
13111306

‎coverage/debug.py

+35-2
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,18 @@
1212
import itertools
1313
import os
1414
import pprint
15+
import re
1516
import reprlib
1617
import sys
1718
import types
1819
import _thread
1920

2021
from typing import (
21-
Any, Callable, IO, Iterable, Iterator, Optional, List, Tuple, cast,
22+
cast,
23+
Any, Callable, IO, Iterable, Iterator, Mapping, Optional, List, Tuple,
2224
)
2325

24-
from coverage.misc import isolate_module
26+
from coverage.misc import human_sorted_items, isolate_module
2527
from coverage.types import TWritable
2628

2729
os = isolate_module(os)
@@ -489,3 +491,34 @@ def _clean_stack_line(s: str) -> str: # pragma: debugging
489491
s = s.replace(os.path.dirname(os.__file__) + "/", "")
490492
s = s.replace(sys.prefix + "/", "")
491493
return s
494+
495+
496+
def relevant_environment_display(env: Mapping[str, str]) -> List[Tuple[str, str]]:
497+
"""Filter environment variables for a debug display.
498+
499+
Select variables to display (with COV or PY in the name, or HOME, TEMP, or
500+
TMP), and also cloak sensitive values with asterisks.
501+
502+
Arguments:
503+
env: a dict of environment variable names and values.
504+
505+
Returns:
506+
A list of pairs (name, value) to show.
507+
508+
"""
509+
slugs = {"COV", "PY"}
510+
include = {"HOME", "TEMP", "TMP"}
511+
cloak = {"API", "TOKEN", "KEY", "SECRET", "PASS", "SIGNATURE"}
512+
513+
to_show = []
514+
for name, val in env.items():
515+
keep = False
516+
if name in include:
517+
keep = True
518+
elif any(slug in name for slug in slugs):
519+
keep = True
520+
if keep:
521+
if any(slug in name for slug in cloak):
522+
val = re.sub(r"\w", "*", val)
523+
to_show.append((name, val))
524+
return human_sorted_items(to_show)

‎tests/test_debug.py

+21-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
from coverage import env
2020
from coverage.debug import (
2121
DebugOutputFile,
22-
clipped_repr, filter_text, info_formatter, info_header, short_id, short_stack,
22+
clipped_repr, filter_text, info_formatter, info_header, relevant_environment_display,
23+
short_id, short_stack,
2324
)
2425

2526
from tests.coveragetest import CoverageTest
@@ -297,3 +298,22 @@ def test_short_stack_limit(self) -> None:
297298
def test_short_stack_skip(self) -> None:
298299
stack = f_one(skip=1).splitlines()
299300
assert "f_two" in stack[-1]
301+
302+
303+
def test_relevant_environment_display() -> None:
304+
env_vars = {
305+
"HOME": "my home",
306+
"HOME_DIR": "other place",
307+
"XYZ_NEVER_MIND": "doesn't matter",
308+
"SOME_PYOTHER": "xyz123",
309+
"COVERAGE_THING": "abcd",
310+
"MY_PYPI_TOKEN": "secret.something",
311+
"TMP": "temporary",
312+
}
313+
assert relevant_environment_display(env_vars) == [
314+
("COVERAGE_THING", "abcd"),
315+
("HOME", "my home"),
316+
("MY_PYPI_TOKEN", "******.*********"),
317+
("SOME_PYOTHER", "xyz123"),
318+
("TMP", "temporary"),
319+
]

0 commit comments

Comments
 (0)
Please sign in to comment.