Commit 5969018
fix(responses): delimit untrusted web_search/file_search tool output before feeding it back to the model (#6337)
Fixes #6263
## What
`web_search`, `file_search`, and `knowledge_search` results were placed
into the model's next-turn context verbatim, with no boundary between
trusted instructions and untrusted, externally-sourced content (scraped
web pages, indexed documents). An attacker who controls a page that gets
searched or indexed could inject text the model treats as an instruction
rather than as data (indirect prompt injection).
## Fix
Wraps the text portions of results from these three tools in explicit
`<untrusted_tool_output>` delimiters with a short instruction that the
enclosed content is untrusted data to analyze, never instructions to
follow. MCP tool output and other tool types are unaffected, matching
the scope of the reported issue. Image content parts pass through
unwrapped.
Two additional issues surfaced during an edge-case pass over this same
code path and are fixed here too, since they live in the exact function
this PR already touches:
1. **Delimiter-collision escaping.** Content containing a literal
`</untrusted_tool_output>` could close the delimited block early and
make injected text that follows look like it sits outside the untrusted
region — defeating the wrapping with itself. Both tags are now escaped
inside untrusted content before wrapping, case-insensitively (case
variation like `</UNTRUSTED_TOOL_OUTPUT>` is a trivial, well-known
evasion of a naive case-sensitive match).
2. **Empty results reported as failure.** A successful search that
legitimately returns empty content (zero results) was fed to the model
as `"Tool execution failed"`, because the pre-existing check used
truthiness (`if result and result_content:`) rather than distinguishing
"no result at all" from "a result with empty content." Changed to an
explicit `is not None` check.
## Known limitation (documented, not a blocker)
Whitespace-padded tag variants (e.g. `< /untrusted_tool_output >`) and
Unicode-homoglyph tricks are not caught by the current escaping —
closing that fully would need a structurally different defense (e.g. a
per-request random delimiter token instead of a static string). Noted
explicitly in the `_escape_delimiter_collisions` docstring as a
reasonable follow-up rather than silently left unstated.
## Tests
New
`tests/unit/providers/inline/responses/builtin/responses/test_tool_executor.py`
(13 tests, this module previously had zero coverage):
- Delimiting applied correctly for
`web_search`/`file_search`/`knowledge_search`, both string and mixed
text+image list content shapes.
- MCP tool output confirmed *not* wrapped (out of scope).
- Delimiter-collision escaping, including the case-insensitivity fix,
confirmed to neutralize an embedded fake close-tag without breaking the
real one.
- Empty-content-not-reported-as-failure, with a sanity check that a
genuinely missing result (`result=None`) still correctly reports
failure.
One existing test
(`test_openai_responses_tools.py::test_create_openai_response_with_string_input_with_tools`)
asserted tool output survives verbatim (`content == "Dublin"`); updated
to `"Dublin" in content` since that's the new, correct contract given
the delimiting.
Every new/changed assertion was confirmed to fail against the pre-fix
code (via `git stash`) before the corresponding fix landed.
## Verification
- `uv run pytest tests/unit/providers/inline/responses/builtin/
tests/unit/providers/responses/builtin/`: 373 passed, no regressions
- `uv run ruff check` / `ruff format --check`: clean
- `uv run mypy`: no issues
- `uv run pre-commit run --files <changed files>`: all hooks passed
(license header, FIPS check, SQL-injection lint, logging conventions,
codegen-drift checks all N/A since no API/provider schema was touched)
---------
Signed-off-by: Praveen Mittal <pkmittal28@gmail.com>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matthew Farrellee <matt@cs.wisc.edu>1 parent 74647be commit 5969018
79 files changed
Lines changed: 175995 additions & 4 deletions
File tree
- docs/docs/api-openai
- src/ogx/providers/inline/responses/builtin/responses
- tests
- integration/responses/recordings
- unit/providers
- inline/responses/builtin/responses
- responses/builtin
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
37 | 37 | | |
38 | 38 | | |
39 | | - | |
| 39 | + | |
40 | 40 | | |
41 | 41 | | |
42 | 42 | | |
| |||
Lines changed: 70 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
| 9 | + | |
9 | 10 | | |
10 | 11 | | |
11 | 12 | | |
| |||
50 | 51 | | |
51 | 52 | | |
52 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
53 | 116 | | |
54 | 117 | | |
55 | 118 | | |
| |||
540 | 603 | | |
541 | 604 | | |
542 | 605 | | |
543 | | - | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
544 | 611 | | |
545 | 612 | | |
546 | 613 | | |
| |||
562 | 629 | | |
563 | 630 | | |
564 | 631 | | |
| 632 | + | |
| 633 | + | |
565 | 634 | | |
566 | 635 | | |
567 | 636 | | |
| |||
0 commit comments