Skip to content

Commit 79641ce

Browse files
committed
fix: sys prompt leak detector
1 parent d55954c commit 79641ce

1 file changed

Lines changed: 27 additions & 2 deletions

File tree

finbot/ctf/detectors/implementations/system_prompt_leak.py

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,20 @@ async def check_event(self, event: dict[str, Any], db: Session) -> DetectionResu
112112
def _extract_texts(self, event: dict[str, Any]) -> tuple[str, str, str]:
113113
"""Extract system prompt, LLM output, and tool call text from event.
114114
115+
The system prompt is taken from ``request_dump`` (the messages sent to
116+
the LLM). The model's *response* — both text output and tool-call
117+
arguments — is taken from ``response_dump`` so that we always inspect
118+
the actual LLM output rather than relying on it appearing in a later
119+
``request_dump`` (which never happens for terminal calls like
120+
``complete_task``).
121+
122+
Prior assistant turns and tool calls from the conversation history in
123+
``request_dump`` are also included so that leaks spread across multiple
124+
turns are still caught.
125+
115126
Operational tool calls (e.g. update_vendor_status) naturally contain
116127
reasoning that mirrors the system prompt because the agent is *following*
117-
those rules. Including them causes the judge to flag normal onboarding
128+
those rules. Including them causes the judge to flag normal behaviour
118129
as a leak. ``exclude_tool_names`` (set in detector_config) lists tool
119130
names whose arguments should be omitted from the ``<tool_calls>`` bundle.
120131
"""
@@ -123,7 +134,8 @@ def _extract_texts(self, event: dict[str, Any]) -> tuple[str, str, str]:
123134
tool_call_text = ""
124135
exclude_tools: set[str] = set(self.config.get("exclude_tool_names", []))
125136

126-
request_dump = event.get("request_dump", None)
137+
# --- system prompt + prior turns from request_dump ---
138+
request_dump = event.get("request_dump")
127139
if request_dump:
128140
messages = request_dump.get("messages", [])
129141
for message in messages:
@@ -135,4 +147,17 @@ def _extract_texts(self, event: dict[str, Any]) -> tuple[str, str, str]:
135147
if message.get("name") not in exclude_tools:
136148
tool_call_text += str(message.get("arguments", ""))
137149

150+
# --- current response from response_dump ---
151+
response_dump = event.get("response_dump")
152+
if response_dump:
153+
# Text output from the current LLM response
154+
response_content = response_dump.get("content")
155+
if response_content:
156+
llm_output += response_content
157+
158+
# Tool-call arguments from the current LLM response
159+
for tc in response_dump.get("tool_calls") or []:
160+
if tc.get("name") not in exclude_tools:
161+
tool_call_text += str(tc.get("arguments", ""))
162+
138163
return system_prompt, llm_output, tool_call_text

0 commit comments

Comments
 (0)