Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,29 @@ def _redact(
for dict_key, subval in item.items()
}
return to_return
# Avoid spending too much effort on pattern-based masking of
# deeply nested non-dict structures.
# Always walk lists/tuples/sets too, mirroring the unconditional dict
# walk above, so a sensitive key wrapped in an iterable is still
# caught at any nesting depth. Self-referential iterables hit Python's
# own recursion limit and are caught by the except clause below, which
# fails closed.
if isinstance(item, (tuple, set)):
# Turn set in to tuple!
return tuple(
self._redact(
subval, name=None, depth=(depth + 1), max_depth=max_depth, replacement=replacement
)
for subval in item
)
if isinstance(item, list):
return [
self._redact(
subval, name=None, depth=(depth + 1), max_depth=max_depth, replacement=replacement
)
for subval in item
]
# The depth cutoff only bounds the work of pattern-based string
# masking below — key-name redaction (dicts and iterables above) is
# unbounded so sensitive keys fail closed at any depth.
if depth > max_depth:
return item
if isinstance(item, Enum):
Expand All @@ -393,21 +414,6 @@ def _redact(
# the structure.
return self.replacer.sub(replacement, str(item))
return item
if isinstance(item, (tuple, set)):
# Turn set in to tuple!
return tuple(
self._redact(
subval, name=None, depth=(depth + 1), max_depth=max_depth, replacement=replacement
)
for subval in item
)
if isinstance(item, list):
return [
self._redact(
subval, name=None, depth=(depth + 1), max_depth=max_depth, replacement=replacement
)
for subval in item
]
return item
# I think this should never happen, but it does not hurt to leave it just in case
# Well. It happened (see https://github.com/apache/airflow/issues/19816#issuecomment-983311373)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,6 +718,20 @@ def test_redact_max_depth(self, val, expected, max_depth):
{"a": {"b": {"c": {"d": {"e": {"api_key": "leaked"}}}}}},
{"a": {"b": {"c": {"d": {"e": {"api_key": "***"}}}}}},
),
# A sensitive key wrapped in a list beyond MAX_RECURSION_DEPTH is
# still redacted: the list is walked unconditionally, mirroring the
# unbounded dict walk above. Here the list sits at depth 6 (one past
# the cutoff), reached through the unbounded dict chain a..f.
(
{"a": {"b": {"c": {"d": {"e": {"f": [{"password": "leaked"}]}}}}}},
{"a": {"b": {"c": {"d": {"e": {"f": [{"password": "***"}]}}}}}},
),
# Same for a tuple-wrapped sensitive key past MAX_RECURSION_DEPTH
# (a set cannot hold a dict, so only list/tuple are exercised here).
(
{"a": {"b": {"c": {"d": {"e": {"f": ({"token": "leaked"},)}}}}}},
{"a": {"b": {"c": {"d": {"e": {"f": ({"token": "***"},)}}}}}},
),
],
)
def test_redact_sensitive_key_past_max_depth(self, val, expected):
Expand Down
Loading