OpenHands / OpenHands/software-agent-sdk

[Bug]: redact_text_secrets leaves api_key assignments with spaces around = unredacted

Open Beginner friendly
#4,765 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug priority:high ready-for-dev sdk security
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
  1. Check out release v1.44.1 at commit 9d143aac35c2dcec9cbb046ff9f35ac5eb072f6a.

  2. Set up the development environment:

make build
  1. 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
  1. Run:
uv run pytest \
    tests/sdk/utils/test_redact_assignment_whitespace.py \
    -q
  1. Observe that both parameterized cases fail because redact_text_secrets returns the original input without removing the secret.
Acceptance Criteria
  • Single-quoted api_key assignments are redacted when whitespace appears before or after =.
  • Double-quoted api_key assignments 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.