Files
ObsiGate/backend/tools/redaction.py
T

40 lines
1.5 KiB
Python

"""Secret redaction for tool results (Phase F).
Tool results are fed back to the LLM (in-app agent loop) or returned to an
external MCP client, so they must never leak credentials. ``read_file`` and
``read_file_raw`` already redact through the shared file service, but other
tools (``diff_backup``, ``search_*``) return content that has not been through
the redactor. This module applies :func:`backend.secret_redactor.redact` to
every string in a tool payload, recursively.
"""
from __future__ import annotations
from typing import Any
from backend.secret_redactor import redact
_MAX_DEPTH = 12
def redact_payload(payload: Any, _depth: int = 0) -> Any:
"""Return a copy of *payload* with every string secret-redacted.
Walks dicts, lists and tuples; scalars are returned unchanged. Strings are
run through :func:`backend.secret_redactor.redact`. A recursion cap avoids
pathological/cyclic structures (tool results are JSON-serialisable, so
cycles should not occur, but the guard keeps this safe).
"""
if _depth > _MAX_DEPTH:
return payload
if isinstance(payload, str):
redacted, count = redact(payload)
return redacted if count else payload
if isinstance(payload, dict):
return {key: redact_payload(value, _depth + 1) for key, value in payload.items()}
if isinstance(payload, list):
return [redact_payload(item, _depth + 1) for item in payload]
if isinstance(payload, tuple):
return tuple(redact_payload(item, _depth + 1) for item in payload)
return payload