Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow for redacting of environment table values #431

Merged
merged 7 commits into from Jan 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/changelog.rst
Expand Up @@ -17,6 +17,10 @@ Version History

* Thanks to `@gnikonorov <https://github.com/gnikonorov>`_ for the PR

* Implement :code:`environment_table_redact_list` to allow for redaction of environment table values. (`#233 <https://github.com/pytest-dev/pytest-html/issues/233>`_)

* Thanks to `@fenchu <https://github.com/fenchu>`_ for reporting and `@gnikonorov <https://github.com/gnikonorov>`_ for the PR

3.1.1 (2020-12-13)
~~~~~~~~~~~~~~~~~~

Expand Down
16 changes: 14 additions & 2 deletions docs/user_guide.rst
Expand Up @@ -80,8 +80,20 @@ Note that in the above example `@pytest.hookimpl(tryfirst=True)`_ is important,
If this line is omitted, then the *Environment* table will **not** be updated since the :code:`pytest_sessionfinish` of the plugins will execute first,
and thus not pick up your change.

The generated table will be sorted alphabetically unless the metadata is a
:code:`collections.OrderedDict`.
The generated table will be sorted alphabetically unless the metadata is a :code:`collections.OrderedDict`.

It is possible to redact variables from the environment table. Redacted variables will have their names displayed, but their values grayed out.
This can be achieved by setting :code:`environment_table_redact_list` in your INI configuration file (e.g.: :code:`pytest.ini`).
:code:`environment_table_redact_list` is a :code:`linelist` of regexes. Any environment table variable that matches a regex in this list has its value redacted.

For example, the following will redact all environment table variables that match the regexes :code:`^foo$`, :code:`.*redact.*`, or :code:`bar`:

.. code-block:: ini

[pytest]
environment_table_redact_list = ^foo$
.*redact.*
bar

Additional summary information
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
13 changes: 13 additions & 0 deletions src/pytest_html/html_report.py
Expand Up @@ -2,6 +2,7 @@
import datetime
import json
import os
import re
import time
from collections import defaultdict
from collections import OrderedDict
Expand Down Expand Up @@ -226,6 +227,10 @@ def _generate_environment(self, config):

for key in keys:
value = metadata[key]
if self._is_redactable_environment_variable(key, config):
black_box_ascii_value = 0x2593
value = "".join(chr(black_box_ascii_value) for char in str(value))

if isinstance(value, str) and value.startswith("http"):
value = html.a(value, href=value, target="_blank")
elif isinstance(value, (list, tuple, set)):
Expand All @@ -239,6 +244,14 @@ def _generate_environment(self, config):
environment.append(html.table(rows, id="environment"))
return environment

def _is_redactable_environment_variable(self, environment_variable, config):
redactable_regexes = config.getini("environment_table_redact_list")
for redactable_regex in redactable_regexes:
if re.match(redactable_regex, environment_variable):
return True

return False

def _save_report(self, report_content):
dir_name = os.path.dirname(self.logfile)
assets_dir = os.path.join(dir_name, "assets")
Expand Down
6 changes: 6 additions & 0 deletions src/pytest_html/plugin.py
Expand Up @@ -53,6 +53,12 @@ def pytest_addoption(parser):
help="set the maximum filename length for assets "
"attached to the html report.",
)
parser.addini(
"environment_table_redact_list",
type="linelist",
help="A list of regexes corresponding to environment "
"table variables whose values should be redacted from the report",
)


def pytest_configure(config):
Expand Down
55 changes: 55 additions & 0 deletions testing/test_pytest_html.py
Expand Up @@ -1209,3 +1209,58 @@ def test_show_capture_no():
assert extra_log_div_regex.search(html) is not None
else:
assert extra_log_div_regex.search(html) is None

def test_environment_table_redact_list(self, testdir):
testdir.makeini(
"""
[pytest]
environment_table_redact_list = ^foo$
.*redact.*
bar
"""
)

testdir.makeconftest(
"""
def pytest_configure(config):
config._metadata["foo"] = "will not appear a"
config._metadata["afoo"] = "will appear"
config._metadata["foos"] = "will appear"
config._metadata["redact"] = "will not appear ab"
config._metadata["will_redact"] = "will not appear abc"
config._metadata["redacted_item"] = "will not appear abcd"
config._metadata["unrelated_item"] = "will appear"
config._metadata["bar"] = "will not appear abcde"
config._metadata["bars"] = "will not appear abcdef"
"""
)

testdir.makepyfile(
"""
def test_pass():
assert True
"""
)

result, html = run(testdir)
assert result.ret == 0
assert_results(html)

black_box_ascii_value = 0x2593
expected_environment_values = {
"foo": "".join(chr(black_box_ascii_value) for value in range(17)),
"afoo": "will appear",
"foos": "will appear",
"redact": "".join(chr(black_box_ascii_value) for value in range(18)),
"will_redact": "".join(chr(black_box_ascii_value) for value in range(19)),
"redacted_item": "".join(chr(black_box_ascii_value) for value in range(20)),
"unrelated_item": "will appear",
"bar": "".join(chr(black_box_ascii_value) for value in range(21)),
"bars": "".join(chr(black_box_ascii_value) for value in range(22)),
}
for variable in expected_environment_values:
variable_value = expected_environment_values[variable]
variable_value_regex = re.compile(
f"<tr>\n.*<td>{variable}</td>\n.*<td>{variable_value}</td></tr>"
)
assert variable_value_regex.search(html) is not None