Skip to content
Closed
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
32 changes: 20 additions & 12 deletions agent/redact.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,18 @@ def _redact_form_body(text: str) -> str:
return _redact_query_string(text.strip())


def redact_sensitive_text(text: str) -> str:
def redact_sensitive_text(text: str, code_file: bool = False) -> str:
"""Apply all redaction patterns to a block of text.

Safe to call on any string -- non-matching text passes through unchanged.
Disabled when security.redact_secrets is false in config.yaml.

Args:
text: The text to redact.
code_file: When True, skips ENV-assignment and JSON-field regex patterns
to avoid false positives on source code content (e.g. MAX_TOKENS=***
or "apiKey": "test" in test fixtures). Prefix patterns, auth headers,
private keys, DB connstrings, JWTs, and URL secrets are always redacted.
"""
if text is None:
return None
Expand All @@ -271,17 +278,18 @@ def redact_sensitive_text(text: str) -> str:
# Known prefixes (sk-, ghp_, etc.)
text = _PREFIX_RE.sub(lambda m: _mask_token(m.group(1)), text)

# ENV assignments: OPENAI_API_KEY=sk-abc...
def _redact_env(m):
name, quote, value = m.group(1), m.group(2), m.group(3)
return f"{name}={quote}{_mask_token(value)}{quote}"
text = _ENV_ASSIGN_RE.sub(_redact_env, text)

# JSON fields: "apiKey": "value"
def _redact_json(m):
key, value = m.group(1), m.group(2)
return f'{key}: "{_mask_token(value)}"'
text = _JSON_FIELD_RE.sub(_redact_json, text)
# ENV assignments: OPENAI_API_KEY=*** (skip for code files — false positives)
if not code_file:
def _redact_env(m):
name, quote, value = m.group(1), m.group(2), m.group(3)
return f"{name}={quote}{_mask_token(value)}{quote}"
text = _ENV_ASSIGN_RE.sub(_redact_env, text)

# JSON fields: "apiKey": "***" (skip for code files — false positives)
def _redact_json(m):
key, value = m.group(1), m.group(2)
return f'{key}: "{_mask_token(value)}"'
text = _JSON_FIELD_RE.sub(_redact_json, text)

# Authorization headers
text = _AUTH_HEADER_RE.sub(
Expand Down
4 changes: 2 additions & 2 deletions tools/file_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ def read_file_tool(path: str, offset: int = 1, limit: int = 500, task_id: str =

# ── Redact secrets (after guard check to skip oversized content) ──
if result.content:
result.content = redact_sensitive_text(result.content)
result.content = redact_sensitive_text(result.content, code_file=True)
result_dict["content"] = result.content

# Large-file hint: if the file is big and the caller didn't ask
Expand Down Expand Up @@ -846,7 +846,7 @@ def search_tool(pattern: str, target: str = "content", path: str = ".",
if hasattr(result, 'matches'):
for m in result.matches:
if hasattr(m, 'content') and m.content:
m.content = redact_sensitive_text(m.content)
m.content = redact_sensitive_text(m.content, code_file=True)
result_dict = result.to_dict()

if count >= 3:
Expand Down
Loading