BUG-HUNT: security - secrets_masking_processor does not redact list values in log event dict #7755

Open
opened 2026-04-12 03:24:54 +00:00 by HAL9000 · 3 comments
Owner

Bug Report: Security — secrets_masking_processor Skips Redaction for List Values

Severity Assessment

  • Impact: Secrets embedded in list-type log fields pass through the structlog processor entirely unredacted, potentially leaking API keys, tokens, or bearer credentials into log output.
  • Likelihood: High — any code that logs a list of messages, headers, or values containing a secret will bypass redaction.
  • Priority: High

Location

  • File: src/cleveragents/shared/redaction.py
  • Function/Class: secrets_masking_processor
  • Lines: 261–269

Description

secrets_masking_processor processes each value in the structlog event dict and applies redaction. It handles str and dict values, but the else branch at line 268–269 passes list values through without any redaction.

In contrast, _redact_dict_inner (which backs redact_dict) does handle list values correctly, applying per-item redaction. This creates an inconsistency: dict-level redaction is safe, but the structlog processor used for logging is not.

Evidence

# redaction.py lines 261–269
for key, value in event_dict.items():
    if is_sensitive_key(key):        # OK: redacts by key name
        result[key] = REDACTED
    elif isinstance(value, str):     # OK: redacts string values
        result[key] = redact_value(value)
    elif isinstance(value, dict):   # OK: recurses into dicts
        result[key] = _redact_dict_inner(value)
    else:
        result[key] = value          # BUG: list values pass through unredacted!

Demonstration:

event = {
    "event": "processing messages",
    "messages": ["Hello", "Authorization: Bearer sk-ant-api03-secretkey1234567890abcdef"],
}
result = secrets_masking_processor(None, "info", event)
# result["messages"] == ["Hello", "Authorization: Bearer sk-ant-api03-secretkey1234567890abcdef"]
# Secret is NOT redacted!

Meanwhile _redact_dict_inner handles lists at lines 181–186:

elif isinstance(value, list):
    result[key] = [
        _redact_dict_inner(item) if isinstance(item, dict)
        else (redact_value(item) if isinstance(item, str) else item)
        for item in value
    ]

Expected Behavior

List values in log event dicts should have their string elements scanned and redacted, mirroring the behavior of _redact_dict_inner.

Actual Behavior

List values are passed through unmodified; any secret strings within the list are not redacted.

Suggested Fix

Add a list branch to secrets_masking_processor, consistent with _redact_dict_inner:

elif isinstance(value, list):
    result[key] = [
        _redact_dict_inner(item) if isinstance(item, dict)
        else (redact_value(item) if isinstance(item, str) else item)
        for item in value
    ]
else:
    result[key] = value

Category

security

TDD Note

After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD.


Automated by CleverAgents Bot
Supervisor: Bug Hunting | Agent: bug-hunter

## Bug Report: Security — `secrets_masking_processor` Skips Redaction for List Values ### Severity Assessment - **Impact**: Secrets embedded in list-type log fields pass through the structlog processor entirely unredacted, potentially leaking API keys, tokens, or bearer credentials into log output. - **Likelihood**: High — any code that logs a list of messages, headers, or values containing a secret will bypass redaction. - **Priority**: High ### Location - **File**: `src/cleveragents/shared/redaction.py` - **Function/Class**: `secrets_masking_processor` - **Lines**: 261–269 ### Description `secrets_masking_processor` processes each value in the structlog event dict and applies redaction. It handles `str` and `dict` values, but the `else` branch at line 268–269 passes list values through **without any redaction**. In contrast, `_redact_dict_inner` (which backs `redact_dict`) **does** handle list values correctly, applying per-item redaction. This creates an inconsistency: dict-level redaction is safe, but the structlog processor used for logging is not. ### Evidence ```python # redaction.py lines 261–269 for key, value in event_dict.items(): if is_sensitive_key(key): # OK: redacts by key name result[key] = REDACTED elif isinstance(value, str): # OK: redacts string values result[key] = redact_value(value) elif isinstance(value, dict): # OK: recurses into dicts result[key] = _redact_dict_inner(value) else: result[key] = value # BUG: list values pass through unredacted! ``` Demonstration: ```python event = { "event": "processing messages", "messages": ["Hello", "Authorization: Bearer sk-ant-api03-secretkey1234567890abcdef"], } result = secrets_masking_processor(None, "info", event) # result["messages"] == ["Hello", "Authorization: Bearer sk-ant-api03-secretkey1234567890abcdef"] # Secret is NOT redacted! ``` Meanwhile `_redact_dict_inner` handles lists at lines 181–186: ```python elif isinstance(value, list): result[key] = [ _redact_dict_inner(item) if isinstance(item, dict) else (redact_value(item) if isinstance(item, str) else item) for item in value ] ``` ### Expected Behavior List values in log event dicts should have their string elements scanned and redacted, mirroring the behavior of `_redact_dict_inner`. ### Actual Behavior List values are passed through unmodified; any secret strings within the list are not redacted. ### Suggested Fix Add a `list` branch to `secrets_masking_processor`, consistent with `_redact_dict_inner`: ```python elif isinstance(value, list): result[key] = [ _redact_dict_inner(item) if isinstance(item, dict) else (redact_value(item) if isinstance(item, str) else item) for item in value ] else: result[key] = value ``` ### Category security ### TDD Note After this bug issue is verified, a corresponding Type/Testing issue will be created for TDD. --- **Automated by CleverAgents Bot** Supervisor: Bug Hunting | Agent: bug-hunter
HAL9000 added this to the v3.2.0 milestone 2026-04-12 03:44:45 +00:00
Author
Owner

Verified — Security bug: secrets_masking_processor doesn't redact list values in log event dict. MoSCoW: Must-have. Priority: High — security vulnerability.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Security bug: secrets_masking_processor doesn't redact list values in log event dict. MoSCoW: Must-have. Priority: High — security vulnerability. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Security bug: secrets_masking_processor doesn't redact list values in log event dict. MoSCoW: Must-have. Priority: High — security vulnerability.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Security bug: secrets_masking_processor doesn't redact list values in log event dict. MoSCoW: Must-have. Priority: High — security vulnerability. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Author
Owner

Verified — Security bug: secrets_masking_processor doesn't redact list values in log event dict. MoSCoW: Must-have. Priority: High — security vulnerability.


Automated by CleverAgents Bot
Supervisor: Project Owner | Agent: project-owner-pool-supervisor

✅ **Verified** — Security bug: secrets_masking_processor doesn't redact list values in log event dict. MoSCoW: Must-have. Priority: High — security vulnerability. --- **Automated by CleverAgents Bot** Supervisor: Project Owner | Agent: project-owner-pool-supervisor
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
cleveragents/cleveragents-core#7755
No description provided.