Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions haystack/utils/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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=<redacted>, _type={self._type!r})"

def resolve_value(self) -> Any | None:
"""Return the token."""
return self._token
Expand Down
Original file line number Diff line number Diff line change
@@ -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 ``<redacted>``.
5 changes: 5 additions & 0 deletions test/utils/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 "<redacted>" in repr(secret)


def test_env_var_secret():
secret = Secret.from_env_var("TEST_ENV_VAR1")
Expand Down
Loading