test(redaction): assert the coarse behaviour the source actually has
This asserted that _redact_sensitive recurses into a dict under a
sensitive key — redacting auth.token while leaving auth.type readable.
The source replaces the whole value the moment the KEY matches, so
redacted["config"]["auth"] is a string and indexing ["token"] into it
raises TypeError. Source and test arrived in the same commit, so this
was never drift: it was a disagreement nobody settled.
Settled in favour of the source. Fine-grained redaction has to know
which sub-keys carry a secret, which is a guess about the shape of data
nobody has inspected; matching on the key cannot be wrong that way. Real
configs here are {"auth": {"type": "bearer", "token": "${SOME_KEY}"}},
and the cost of guessing wrong is a credential in a log, which no later
fix undoes. The price is readability, and it is paid deliberately.
Adds a second assertion that the secret appears nowhere in the output by
any path, which is the property actually worth protecting.
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -498,7 +498,24 @@ class TestRestApiExecutor:
|
||||
assert redacted["normal_field"] == "visible"
|
||||
|
||||
def test_redact_sensitive_nested(self):
|
||||
"""Test redaction in nested structures."""
|
||||
"""A dict under a sensitive key is redacted whole, not recursed into.
|
||||
|
||||
This asserted fine-grained recursion — that `auth.token` was replaced
|
||||
while `auth`'s other keys stayed readable — and had never passed. The
|
||||
source redacts the entire value the moment the KEY matches, so
|
||||
`redacted["config"]["auth"]` is the string, and indexing `["token"]`
|
||||
into it raises TypeError.
|
||||
|
||||
Settled in favour of the source. Fine-grained redaction has to know
|
||||
which sub-keys carry the secret, which is a guess about the shape of
|
||||
data nobody has inspected; redacting on the key cannot be wrong that
|
||||
way. Real configs here look like
|
||||
{"auth": {"type": "bearer", "token": "${SOME_API_KEY}"}}, and the cost
|
||||
of guessing wrong is a credential in a log, which no later fix undoes.
|
||||
|
||||
The price is readability: a reader learns that auth was present, not
|
||||
that it was bearer. That is the trade being made deliberately.
|
||||
"""
|
||||
data = {
|
||||
"config": {
|
||||
"database": "mydb",
|
||||
@@ -513,7 +530,10 @@ class TestRestApiExecutor:
|
||||
|
||||
assert redacted["config"]["database"] == "mydb"
|
||||
assert redacted["config"]["password"] == "***REDACTED***"
|
||||
assert redacted["config"]["auth"]["token"] == "***REDACTED***"
|
||||
# The whole sub-dict, not a recursed copy of it.
|
||||
assert redacted["config"]["auth"] == "***REDACTED***"
|
||||
# And the secret is nowhere in the output, by any path.
|
||||
assert "bearer123" not in str(redacted)
|
||||
|
||||
# Helper Function Tests
|
||||
|
||||
|
||||
Reference in New Issue
Block a user