Skip to content
Open
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
44 changes: 28 additions & 16 deletions strix/tools/proxy/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,23 +315,35 @@ def _format_search_hits(content: str, pattern: str) -> dict[str, Any]:
except re.error as exc:
return {"success": False, "error": f"Invalid regex: {exc}"}

hits = []
for match in regex.finditer(content):
start, end = match.span()
before = content[max(0, start - 40) : start]
after = content[end : end + 40]
hits.append(
{
"match": match.group(0),
"position": start,
"before": before,
"after": after,
},
)
if len(hits) >= 20:
break
# Guard against catastrophic backtracking (ReDoS): truncate the
# haystack so even a worst-case pattern cannot consume unbounded CPU.
_MAX_SEARCH_LEN = 1_048_576 # 1 MiB
search_content = content[:_MAX_SEARCH_LEN]
truncated = len(content) > _MAX_SEARCH_LEN

Comment on lines +320 to 323

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security Regex execution remains unbounded

When an agent searches an adversarial HTTP body with a catastrophic-backtracking expression such as (a+)+$, truncating the body to 1 MiB still permits exponential work on much shorter inputs, causing the synchronous search to block the event loop indefinitely; catching exceptions after finditer() returns does not enforce an execution bound.

How this was verified: view_request passes the agent-supplied pattern and decoded HTTP content directly to synchronous regex.finditer() without a regex timeout or interruptible execution boundary.

Knowledge Base Used: Tools Overview

Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/tools/proxy/tools.py
Line: 320-323

Comment:
**Regex execution remains unbounded**

When an agent searches an adversarial HTTP body with a catastrophic-backtracking expression such as `(a+)+$`, truncating the body to 1 MiB still permits exponential work on much shorter inputs, causing the synchronous search to block the event loop indefinitely; catching exceptions after `finditer()` returns does not enforce an execution bound.

**How this was verified:** `view_request` passes the agent-supplied pattern and decoded HTTP content directly to synchronous `regex.finditer()` without a regex timeout or interruptible execution boundary.

**Knowledge Base Used:** [Tools Overview](https://app.greptile.com/strix-org-3/-/custom-context/knowledge-base/usestrix/strix/-/docs/tools-overview.md)

How can I resolve this? If you propose a fix, please make it concise.

return {"success": True, "hits": hits, "total_hits": len(hits)}
hits = []
try:
for match in regex.finditer(search_content):
start, end = match.span()
before = search_content[max(0, start - 40) : start]
after = search_content[end : end + 40]
hits.append(
{
"match": match.group(0),
"position": start,
"before": before,
"after": after,
},
)
if len(hits) >= 20:
break
except (re.error, RecursionError) as exc:
return {"success": False, "error": f"Regex execution failed: {exc}"}

result: dict[str, Any] = {"success": True, "hits": hits, "total_hits": len(hits)}
if truncated:
result["warning"] = f"Content truncated to {_MAX_SEARCH_LEN} bytes for search"
return result


def _format_text_page(content: str, *, page: int, page_size: int) -> dict[str, Any]:
Expand Down