diff --git a/haystack/utils/auth.py b/haystack/utils/auth.py index 774492c521c..5682f3559fe 100644 --- a/haystack/utils/auth.py +++ b/haystack/utils/auth.py @@ -162,6 +162,10 @@ def _from_dict(_: dict[str, Any]) -> "Secret": "Cannot deserialize token-based secret. Use an alternative secret type like environment variables." ) + def __repr__(self) -> str: + # Hide the token so it can't leak through print/log/traceback formatting. + return f"TokenSecret(_token=, _type={self._type!r})" + def resolve_value(self) -> Any | None: """Return the token.""" return self._token diff --git a/releasenotes/notes/redact-tokensecret-repr-34c7549247f4b6e4.yaml b/releasenotes/notes/redact-tokensecret-repr-34c7549247f4b6e4.yaml new file mode 100644 index 00000000000..50914758978 --- /dev/null +++ b/releasenotes/notes/redact-tokensecret-repr-34c7549247f4b6e4.yaml @@ -0,0 +1,8 @@ +--- +security: + - | + The ``repr()`` of a ``Secret`` built via ``Secret.from_token(...)`` no longer + includes the raw token. Previously the dataclass-generated repr printed the + token verbatim, so any path that formats a component's locals — exception + tracebacks, ``print()``, Jupyter echo, log aggregators — could leak the API + key. The token is now rendered as ````. diff --git a/test/utils/test_auth.py b/test/utils/test_auth.py index a7b40424ffe..0d7fd36188f 100644 --- a/test/utils/test_auth.py +++ b/test/utils/test_auth.py @@ -36,6 +36,11 @@ def test_token_secret(): with pytest.raises(FrozenInstanceError): secret._type = SecretType.ENV_VAR + secret = Secret.from_token("sk-supersecret-1234567890ABCDEF") + assert "sk-supersecret-1234567890ABCDEF" not in repr(secret) + assert "sk-supersecret-1234567890ABCDEF" not in str(secret) + assert "" in repr(secret) + def test_env_var_secret(): secret = Secret.from_env_var("TEST_ENV_VAR1")