from __future__ import annotations import unittest from govoplan_core.security.redaction import contains_plain_secret, redact_secret_values class SecurityRedactionTests(unittest.TestCase): def test_structured_payload_redaction_covers_nested_container_shapes(self) -> None: payload = { "request": { "Authorization": "Bearer must-not-leak", "headers": [{"x-api-key": "must-not-leak"}], }, "records": ( {"clientSecret": "must-not-leak", "status": "ok"}, {"credential_ref": "credential-1"}, ), } redacted = redact_secret_values(payload) self.assertEqual("", redacted["request"]["Authorization"]) # type: ignore[index] self.assertEqual("", redacted["request"]["headers"][0]["x-api-key"]) # type: ignore[index] self.assertEqual("", redacted["records"][0]["clientSecret"]) # type: ignore[index] self.assertEqual("", redacted["records"][1]["credential_ref"]) # type: ignore[index] self.assertNotIn("must-not-leak", repr(redacted)) def test_plain_secret_detection_descends_into_lists_and_tuples(self) -> None: self.assertTrue(contains_plain_secret([{"metadata": ({"private-key": "secret"},)}])) self.assertFalse(contains_plain_secret([{"credential_ref": "credential-1"}])) if __name__ == "__main__": unittest.main()