OpenHands / OpenHands/software-agent-sdk
[Bug]: redact_text_secrets leaves api_key assignments with spaces around = unredacted
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 1.1k
- Forks
- 539
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 137
Description
Is there an existing issue for the same bug?
- I have searched existing issues and this is not a duplicate.
Bug Description
redact_text_secrets fails to redact quoted api_key assignments when whitespace appears around =.
The no-whitespace form is redacted:
redact_text_secrets(
"prefix api_key='secret_value_12345' suffix"
)
# "prefix api_key='<redacted>' suffix"
However, the equivalent single-quoted and double-quoted forms containing spaces are returned unchanged:
redact_text_secrets(
"prefix api_key = 'secret_value_12345' suffix"
)
# "prefix api_key = 'secret_value_12345' suffix"
redact_text_secrets(
'prefix api_key = "secret_value_12345" suffix'
)
# 'prefix api_key = "secret_value_12345" suffix'
The function is intended to redact credentials from string representations that may be used in logs and diagnostics. It is used by command logging and ACP diagnostic paths, so a credential serialized with this ordinary whitespace variation can remain in log-bound text.
Expected Behavior
Quoted api_key assignments should be redacted regardless of optional whitespace around =.
For example:
redact_text_secrets(
"prefix api_key = 'secret_value_12345' suffix"
)
should produce output such as:
prefix api_key = '<redacted>' suffix
The exact surrounding whitespace may be preserved or normalized, but the original secret value must not remain in the result.
Actual Behavior
Running the reproduction with:
uv run pytest \
tests/sdk/utils/test_redact_assignment_whitespace.py \
-q
produces assertion failures for both quote styles because the original secret remains in the returned string.
Observed values:
single quoted:
"prefix api_key = 'secret_value_12345' suffix"
double quoted:
'prefix api_key = "secret_value_12345" suffix'
Both values still contain secret_value_12345.
Steps to Reproduce
-
Check out release
v1.44.1at commit9d143aac35c2dcec9cbb046ff9f35ac5eb072f6a. -
Set up the development environment:
make build
- Create
tests/sdk/utils/test_redact_assignment_whitespace.py:
import pytest
from openhands.sdk.utils.redact import redact_text_secrets
@pytest.mark.parametrize("quote", ["'", '"'])
def test_redacts_api_key_assignment_with_whitespace(quote):
secret = "secret_value_12345"
input_text = (
f"prefix api_key = {quote}{secret}{quote} suffix"
)
result = redact_text_secrets(input_text)
assert secret not in result
assert "<redacted>" in result
- Run:
uv run pytest \
tests/sdk/utils/test_redact_assignment_whitespace.py \
-q
- Observe that both parameterized cases fail because
redact_text_secretsreturns the original input without removing the secret.
Acceptance Criteria
- Single-quoted
api_keyassignments are redacted when whitespace appears before or after=. - Double-quoted
api_keyassignments are redacted when whitespace appears before or after=. - The original secret value is absent from the returned text.
- Existing no-whitespace forms such as
api_key='value'remain supported. - Non-sensitive surrounding text remains intact.
- Regression tests cover zero, one, and multiple whitespace characters around
=.
Installation Method
Source checkout using make build (uv sync --dev)
If you selected "Other", please specify
Not applicable
SDK Version
1.44.1, main@9d143aac35c2dcec9cbb046ff9f35ac5eb072f6a
Version Confirmation
- I have confirmed this bug exists on the LATEST version of OpenHands SDK
Python Version
3.13.2
Model Name (if applicable)
Not applicable; reproduced by directly invoking deterministic redaction code.
Operating System
MacOS
Logs and Error Messages
"prefix api_key='secret_value_12345' suffix" => "prefix api_key='<redacted>' suffix"
"prefix api_key = 'secret_value_12345' suffix" => "prefix api_key = 'secret_value_12345' suffix"
'prefix api_key = "secret_value_12345" suffix' => 'prefix api_key = "secret_value_12345" suffix'
Representative assertion:
AssertionError: assert 'secret_value_12345' not in "prefix api_key = 'secret_value_12345' suffix"
Minimal Code Sample
from openhands.sdk.utils.redact import redact_text_secrets
secret = "secret_value_12345"
text = f"prefix api_key = '{secret}' suffix"
result = redact_text_secrets(text)
print(result)
assert secret not in result
Screenshots and Additional Context
No screenshot is required; this is a deterministic unit-level reproduction using a synthetic secret.
The current implementation matches only forms where api_key and the quoted value are directly adjacent to =:
text = re.sub(
r"api_key='[^']*'",
"api_key='<redacted>'",
text,
)
text = re.sub(
r'api_key="[^"]*"',
'api_key="<redacted>"',
text,
)
Neither expression permits whitespace around =.
Relevant call sites use the returned value in command and ACP logging or diagnostics, including:
openhands-sdk/openhands/sdk/utils/command.py
openhands-sdk/openhands/sdk/agent/acp_agent.py
A possible fix is to accept optional whitespace around = while preserving the original quote style. Regression coverage should include:
- no whitespace;
- whitespace before
=; - whitespace after
=; - whitespace on both sides;
- single and double quotes;
- multiple spaces or tabs;
- preservation of surrounding non-sensitive text.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start at openhands/sdk/utils/redact.py and inspect redact_text_secrets, then run tests/sdk/utils/test_redact_assignment_whitespace.py with pytest. Update coverage so quoted api_key assignments with zero, one, or multiple whitespace characters around = are redacted for both quote styles while surrounding text and existing no-whitespace cases remain intact.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- security, testing
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 85/100