diff --git a/python/ql/lib/semmle/python/frameworks/OpenRouter.qll b/python/ql/lib/semmle/python/frameworks/OpenRouter.qll index 690d6a35311..3b43770ec1c 100644 --- a/python/ql/lib/semmle/python/frameworks/OpenRouter.qll +++ b/python/ql/lib/semmle/python/frameworks/OpenRouter.qll @@ -22,15 +22,10 @@ module OpenRouter { result = API::moduleImport("openrouter").getMember("OpenRouter").getReturn() } - /** Gets the message dictionaries passed to `chat.completions.create`. */ + /** Gets the message dictionaries passed to `chat.send`. */ private API::Node chatMessage() { result = - clientRef() - .getMember("chat") - .getMember("completions") - .getMember("create") - .getKeywordParameter("messages") - .getASubscript() + clientRef().getMember("chat").getMember("send").getKeywordParameter("messages").getASubscript() } /** Gets the content sink of a message dictionary, including the `text` of structured content. */ diff --git a/python/ql/lib/semmle/python/frameworks/anthropic.model.yml b/python/ql/lib/semmle/python/frameworks/anthropic.model.yml index 9977e961cdb..4d9dbc794a7 100644 --- a/python/ql/lib/semmle/python/frameworks/anthropic.model.yml +++ b/python/ql/lib/semmle/python/frameworks/anthropic.model.yml @@ -8,6 +8,8 @@ extensions: - ['Anthropic', 'Member[messages].Member[create,stream].Argument[system:].ListElement.DictionaryElement[text]', 'system-prompt-injection'] - ['Anthropic', 'Member[beta].Member[messages].Member[create,stream].Argument[system:]', 'system-prompt-injection'] - ['Anthropic', 'Member[beta].Member[messages].Member[create,stream].Argument[system:].ListElement.DictionaryElement[text]', 'system-prompt-injection'] + # The managed agents `system` field is a system-level prompt + - ['Anthropic', 'Member[beta].Member[agents].Member[create,update].Argument[system:]', 'system-prompt-injection'] - addsTo: pack: codeql/python-all diff --git a/python/ql/lib/semmle/python/frameworks/google-genai.model.yml b/python/ql/lib/semmle/python/frameworks/google-genai.model.yml index 9fcfa7a9f88..bd5ec9862e0 100644 --- a/python/ql/lib/semmle/python/frameworks/google-genai.model.yml +++ b/python/ql/lib/semmle/python/frameworks/google-genai.model.yml @@ -7,7 +7,7 @@ extensions: - ['google.genai', 'Member[types].Member[GenerateContentConfig].Argument[system_instruction:]', 'system-prompt-injection'] # User-level content - ['GoogleGenAI', 'Member[models].Member[generate_content,generate_content_stream].Argument[contents:]', 'user-prompt-injection'] - - ['GoogleGenAI', 'Member[models].Member[generate_images,generate_videos].Argument[prompt:]', 'user-prompt-injection'] + - ['GoogleGenAI', 'Member[models].Member[generate_images,generate_videos,edit_image].Argument[prompt:]', 'user-prompt-injection'] - ['GoogleGenAI', 'Member[chats].Member[create].ReturnValue.Member[send_message,send_message_stream].Argument[0]', 'user-prompt-injection'] - ['GoogleGenAI', 'Member[chats].Member[create].ReturnValue.Member[send_message,send_message_stream].Argument[message:]', 'user-prompt-injection'] diff --git a/python/ql/lib/semmle/python/frameworks/openrouter.model.yml b/python/ql/lib/semmle/python/frameworks/openrouter.model.yml index 97a2eff96fa..894212131c9 100644 --- a/python/ql/lib/semmle/python/frameworks/openrouter.model.yml +++ b/python/ql/lib/semmle/python/frameworks/openrouter.model.yml @@ -3,8 +3,11 @@ extensions: pack: codeql/python-all extensible: sinkModel data: + # `responses.send` instructions is a system-level prompt; input is user content + - ['OpenRouter', 'Member[responses].Member[send].Argument[instructions:]', 'system-prompt-injection'] + - ['OpenRouter', 'Member[responses].Member[send].Argument[input:]', 'user-prompt-injection'] # Embeddings input is user-level content - - ['OpenRouter', 'Member[embeddings].Member[create].Argument[input:]', 'user-prompt-injection'] + - ['OpenRouter', 'Member[embeddings].Member[generate].Argument[input:]', 'user-prompt-injection'] - addsTo: pack: codeql/python-all diff --git a/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/SystemPromptInjection.expected b/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/SystemPromptInjection.expected index 54b68fda62e..618aa1192d6 100644 --- a/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/SystemPromptInjection.expected +++ b/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/SystemPromptInjection.expected @@ -7,6 +7,8 @@ | anthropic_test.py:21:28:21:44 | ControlFlowNode for BinaryExpr | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | anthropic_test.py:21:28:21:44 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | anthropic_test.py:33:16:33:37 | ControlFlowNode for BinaryExpr | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | anthropic_test.py:33:16:33:37 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | anthropic_test.py:45:16:45:37 | ControlFlowNode for BinaryExpr | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | anthropic_test.py:45:16:45:37 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | +| anthropic_test.py:57:16:57:37 | ControlFlowNode for BinaryExpr | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | anthropic_test.py:57:16:57:37 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | +| anthropic_test.py:63:16:63:37 | ControlFlowNode for BinaryExpr | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | anthropic_test.py:63:16:63:37 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:17:22:17:46 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:17:22:17:46 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:22:22:22:46 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:22:22:22:46 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:26:28:26:51 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:26:28:26:51 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | @@ -14,55 +16,61 @@ | openai_test.py:61:28:61:51 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:61:28:61:51 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:73:22:73:46 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:73:22:73:46 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openrouter_test.py:18:28:18:51 | ControlFlowNode for BinaryExpr | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openrouter_test.py:18:28:18:51 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | +| openrouter_test.py:29:22:29:45 | ControlFlowNode for BinaryExpr | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openrouter_test.py:29:22:29:45 | ControlFlowNode for BinaryExpr | This system prompt depends on a $@. | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | edges | agent_test.py:2:26:2:32 | ControlFlowNode for ImportMember | agent_test.py:2:26:2:32 | ControlFlowNode for request | provenance | | | agent_test.py:2:26:2:32 | ControlFlowNode for request | agent_test.py:9:15:9:21 | ControlFlowNode for request | provenance | | | agent_test.py:2:26:2:32 | ControlFlowNode for request | agent_test.py:10:13:10:19 | ControlFlowNode for request | provenance | | -| agent_test.py:9:5:9:11 | ControlFlowNode for persona | agent_test.py:21:22:21:63 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:6 | -| agent_test.py:9:5:9:11 | ControlFlowNode for persona | agent_test.py:22:29:22:53 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:5 | +| agent_test.py:9:5:9:11 | ControlFlowNode for persona | agent_test.py:21:22:21:63 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:8 | +| agent_test.py:9:5:9:11 | ControlFlowNode for persona | agent_test.py:22:29:22:53 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:7 | | agent_test.py:9:5:9:11 | ControlFlowNode for persona | agent_test.py:31:28:31:51 | ControlFlowNode for BinaryExpr | provenance | | | agent_test.py:9:15:9:21 | ControlFlowNode for request | agent_test.py:9:15:9:26 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep | | agent_test.py:9:15:9:21 | ControlFlowNode for request | agent_test.py:10:13:10:24 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep | | agent_test.py:9:15:9:26 | ControlFlowNode for Attribute | agent_test.py:9:15:9:41 | ControlFlowNode for Attribute() | provenance | dict.get | | agent_test.py:9:15:9:41 | ControlFlowNode for Attribute() | agent_test.py:9:5:9:11 | ControlFlowNode for persona | provenance | | -| agent_test.py:10:5:10:9 | ControlFlowNode for topic | agent_test.py:14:21:14:63 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:7 | +| agent_test.py:10:5:10:9 | ControlFlowNode for topic | agent_test.py:14:21:14:63 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:9 | | agent_test.py:10:13:10:19 | ControlFlowNode for request | agent_test.py:10:13:10:24 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep | | agent_test.py:10:13:10:24 | ControlFlowNode for Attribute | agent_test.py:10:13:10:37 | ControlFlowNode for Attribute() | provenance | dict.get | | agent_test.py:10:13:10:37 | ControlFlowNode for Attribute() | agent_test.py:10:5:10:9 | ControlFlowNode for topic | provenance | | | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | anthropic_test.py:2:26:2:32 | ControlFlowNode for request | provenance | | | anthropic_test.py:2:26:2:32 | ControlFlowNode for request | anthropic_test.py:11:15:11:21 | ControlFlowNode for request | provenance | | -| anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:17:16:17:37 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:2 | +| anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:17:16:17:37 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:3 | | anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:21:28:21:44 | ControlFlowNode for BinaryExpr | provenance | | -| anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:33:16:33:37 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:2 | -| anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:45:16:45:37 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:1 | +| anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:33:16:33:37 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:3 | +| anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:45:16:45:37 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:2 | +| anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:57:16:57:37 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:1 | +| anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | anthropic_test.py:63:16:63:37 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:1 | | anthropic_test.py:11:15:11:21 | ControlFlowNode for request | anthropic_test.py:11:15:11:26 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep | | anthropic_test.py:11:15:11:26 | ControlFlowNode for Attribute | anthropic_test.py:11:15:11:41 | ControlFlowNode for Attribute() | provenance | dict.get | | anthropic_test.py:11:15:11:41 | ControlFlowNode for Attribute() | anthropic_test.py:11:5:11:11 | ControlFlowNode for persona | provenance | | | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:2:26:2:32 | ControlFlowNode for request | provenance | | | openai_test.py:2:26:2:32 | ControlFlowNode for request | openai_test.py:12:15:12:21 | ControlFlowNode for request | provenance | | -| openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:17:22:17:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:4 | -| openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:22:22:22:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:4 | +| openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:17:22:17:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:5 | +| openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:22:22:22:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:5 | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:26:28:26:51 | ControlFlowNode for BinaryExpr | provenance | | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:44:28:44:51 | ControlFlowNode for BinaryExpr | provenance | | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:61:28:61:51 | ControlFlowNode for BinaryExpr | provenance | | -| openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:73:22:73:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:3 | +| openai_test.py:12:5:12:11 | ControlFlowNode for persona | openai_test.py:73:22:73:46 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:4 | | openai_test.py:12:15:12:21 | ControlFlowNode for request | openai_test.py:12:15:12:26 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep | | openai_test.py:12:15:12:26 | ControlFlowNode for Attribute | openai_test.py:12:15:12:41 | ControlFlowNode for Attribute() | provenance | dict.get | | openai_test.py:12:15:12:41 | ControlFlowNode for Attribute() | openai_test.py:12:5:12:11 | ControlFlowNode for persona | provenance | | | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openrouter_test.py:2:26:2:32 | ControlFlowNode for request | provenance | | | openrouter_test.py:2:26:2:32 | ControlFlowNode for request | openrouter_test.py:10:15:10:21 | ControlFlowNode for request | provenance | | | openrouter_test.py:10:5:10:11 | ControlFlowNode for persona | openrouter_test.py:18:28:18:51 | ControlFlowNode for BinaryExpr | provenance | | +| openrouter_test.py:10:5:10:11 | ControlFlowNode for persona | openrouter_test.py:29:22:29:45 | ControlFlowNode for BinaryExpr | provenance | Sink:MaD:6 | | openrouter_test.py:10:15:10:21 | ControlFlowNode for request | openrouter_test.py:10:15:10:26 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep | | openrouter_test.py:10:15:10:26 | ControlFlowNode for Attribute | openrouter_test.py:10:15:10:41 | ControlFlowNode for Attribute() | provenance | dict.get | | openrouter_test.py:10:15:10:41 | ControlFlowNode for Attribute() | openrouter_test.py:10:5:10:11 | ControlFlowNode for persona | provenance | | models -| 1 | Sink: Anthropic; Member[beta].Member[messages].Member[create,stream].Argument[system:]; system-prompt-injection | -| 2 | Sink: Anthropic; Member[messages].Member[create,stream].Argument[system:]; system-prompt-injection | -| 3 | Sink: OpenAI; Member[beta].Member[assistants].Member[create].Argument[instructions:]; system-prompt-injection | -| 4 | Sink: OpenAI; Member[responses].Member[create].Argument[instructions:]; system-prompt-injection | -| 5 | Sink: agents; Member[Agent].Argument[handoff_description:]; system-prompt-injection | -| 6 | Sink: agents; Member[Agent].Argument[instructions:]; system-prompt-injection | -| 7 | Sink: agents; Member[FunctionTool].Argument[description:]; system-prompt-injection | +| 1 | Sink: Anthropic; Member[beta].Member[agents].Member[create,update].Argument[system:]; system-prompt-injection | +| 2 | Sink: Anthropic; Member[beta].Member[messages].Member[create,stream].Argument[system:]; system-prompt-injection | +| 3 | Sink: Anthropic; Member[messages].Member[create,stream].Argument[system:]; system-prompt-injection | +| 4 | Sink: OpenAI; Member[beta].Member[assistants].Member[create].Argument[instructions:]; system-prompt-injection | +| 5 | Sink: OpenAI; Member[responses].Member[create].Argument[instructions:]; system-prompt-injection | +| 6 | Sink: OpenRouter; Member[responses].Member[send].Argument[instructions:]; system-prompt-injection | +| 7 | Sink: agents; Member[Agent].Argument[handoff_description:]; system-prompt-injection | +| 8 | Sink: agents; Member[Agent].Argument[instructions:]; system-prompt-injection | +| 9 | Sink: agents; Member[FunctionTool].Argument[description:]; system-prompt-injection | nodes | agent_test.py:2:26:2:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | agent_test.py:2:26:2:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | @@ -88,6 +96,8 @@ nodes | anthropic_test.py:21:28:21:44 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | anthropic_test.py:33:16:33:37 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | anthropic_test.py:45:16:45:37 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| anthropic_test.py:57:16:57:37 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| anthropic_test.py:63:16:63:37 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | openai_test.py:2:26:2:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | | openai_test.py:12:5:12:11 | ControlFlowNode for persona | semmle.label | ControlFlowNode for persona | @@ -107,6 +117,7 @@ nodes | openrouter_test.py:10:15:10:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | openrouter_test.py:10:15:10:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | openrouter_test.py:18:28:18:51 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | +| openrouter_test.py:29:22:29:45 | ControlFlowNode for BinaryExpr | semmle.label | ControlFlowNode for BinaryExpr | subpaths testFailures | gemini_test.py:3:35:3:44 | Comment # $ Source | Missing result: Source | diff --git a/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/anthropic_test.py b/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/anthropic_test.py index 9482adfd26c..9763e81bb0d 100644 --- a/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/anthropic_test.py +++ b/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/anthropic_test.py @@ -51,4 +51,16 @@ async def get_input_anthropic(): ], ) + agent = client.beta.agents.create( + model="claude-sonnet-4-20250514", + name="assistant", + system="Talk like " + persona, # $ Alert[py/system-prompt-injection] + ) + + client.beta.agents.update( + agent_id=agent.id, + version=1, + system="Talk like " + persona, # $ Alert[py/system-prompt-injection] + ) + print(response1, response2, response3) diff --git a/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/openrouter_test.py b/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/openrouter_test.py index ed48b1a57c9..335a5c9672a 100644 --- a/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/openrouter_test.py +++ b/python/ql/test/query-tests/Security/CWE-1427-SystemPromptInjection/openrouter_test.py @@ -10,7 +10,7 @@ def get_input_openrouter(): persona = request.args.get("persona") query = request.args.get("query") - completion = client.chat.completions.create( + completion = client.chat.send( model="openai/gpt-4.1", messages=[ { @@ -23,4 +23,10 @@ def get_input_openrouter(): } ] ) - print(completion) + + response = client.responses.send( + model="openai/gpt-4.1", + instructions="Talk like a " + persona, # $ Alert[py/system-prompt-injection] + input=query, + ) + print(completion, response) diff --git a/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/UserPromptInjection.expected b/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/UserPromptInjection.expected index 55910182054..298a108281e 100644 --- a/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/UserPromptInjection.expected +++ b/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/UserPromptInjection.expected @@ -12,16 +12,18 @@ | openai_test.py:51:16:51:36 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:51:16:51:36 | ControlFlowNode for BinaryExpr | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openai_test.py:55:16:55:38 | ControlFlowNode for BinaryExpr | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openai_test.py:55:16:55:38 | ControlFlowNode for BinaryExpr | This prompt construction depends on a $@. | openai_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | | openrouter_test.py:21:28:21:32 | ControlFlowNode for query | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openrouter_test.py:21:28:21:32 | ControlFlowNode for query | This prompt construction depends on a $@. | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | +| openrouter_test.py:29:15:29:19 | ControlFlowNode for query | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openrouter_test.py:29:15:29:19 | ControlFlowNode for query | This prompt construction depends on a $@. | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | +| openrouter_test.py:34:15:34:19 | ControlFlowNode for query | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openrouter_test.py:34:15:34:19 | ControlFlowNode for query | This prompt construction depends on a $@. | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | user-provided value | edges | agent_test.py:2:26:2:32 | ControlFlowNode for ImportMember | agent_test.py:2:26:2:32 | ControlFlowNode for request | provenance | | | agent_test.py:2:26:2:32 | ControlFlowNode for request | agent_test.py:9:13:9:19 | ControlFlowNode for request | provenance | | -| agent_test.py:9:5:9:9 | ControlFlowNode for query | agent_test.py:13:38:13:42 | ControlFlowNode for query | provenance | Sink:MaD:5 | +| agent_test.py:9:5:9:9 | ControlFlowNode for query | agent_test.py:13:38:13:42 | ControlFlowNode for query | provenance | Sink:MaD:7 | | agent_test.py:9:5:9:9 | ControlFlowNode for query | agent_test.py:20:28:20:32 | ControlFlowNode for query | provenance | | | agent_test.py:9:5:9:9 | ControlFlowNode for query | agent_test.py:20:28:20:32 | ControlFlowNode for query | provenance | | | agent_test.py:9:13:9:19 | ControlFlowNode for request | agent_test.py:9:13:9:24 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep | | agent_test.py:9:13:9:24 | ControlFlowNode for Attribute | agent_test.py:9:13:9:37 | ControlFlowNode for Attribute() | provenance | dict.get | | agent_test.py:9:13:9:37 | ControlFlowNode for Attribute() | agent_test.py:9:5:9:9 | ControlFlowNode for query | provenance | | -| agent_test.py:18:13:21:13 | ControlFlowNode for Dict [Dictionary element at key content] | agent_test.py:17:15:22:9 | ControlFlowNode for List | provenance | Sink:MaD:6 Sink:MaD:6 | +| agent_test.py:18:13:21:13 | ControlFlowNode for Dict [Dictionary element at key content] | agent_test.py:17:15:22:9 | ControlFlowNode for List | provenance | Sink:MaD:8 Sink:MaD:8 | | agent_test.py:20:28:20:32 | ControlFlowNode for query | agent_test.py:18:13:21:13 | ControlFlowNode for Dict [Dictionary element at key content] | provenance | | | anthropic_test.py:2:26:2:32 | ControlFlowNode for ImportMember | anthropic_test.py:2:26:2:32 | ControlFlowNode for request | provenance | | | anthropic_test.py:2:26:2:32 | ControlFlowNode for request | anthropic_test.py:10:15:10:21 | ControlFlowNode for request | provenance | | @@ -62,6 +64,8 @@ edges | openrouter_test.py:2:26:2:32 | ControlFlowNode for ImportMember | openrouter_test.py:2:26:2:32 | ControlFlowNode for request | provenance | | | openrouter_test.py:2:26:2:32 | ControlFlowNode for request | openrouter_test.py:10:13:10:19 | ControlFlowNode for request | provenance | | | openrouter_test.py:10:5:10:9 | ControlFlowNode for query | openrouter_test.py:21:28:21:32 | ControlFlowNode for query | provenance | | +| openrouter_test.py:10:5:10:9 | ControlFlowNode for query | openrouter_test.py:29:15:29:19 | ControlFlowNode for query | provenance | Sink:MaD:6 | +| openrouter_test.py:10:5:10:9 | ControlFlowNode for query | openrouter_test.py:34:15:34:19 | ControlFlowNode for query | provenance | Sink:MaD:5 | | openrouter_test.py:10:13:10:19 | ControlFlowNode for request | openrouter_test.py:10:13:10:24 | ControlFlowNode for Attribute | provenance | AdditionalTaintStep | | openrouter_test.py:10:13:10:24 | ControlFlowNode for Attribute | openrouter_test.py:10:13:10:37 | ControlFlowNode for Attribute() | provenance | dict.get | | openrouter_test.py:10:13:10:37 | ControlFlowNode for Attribute() | openrouter_test.py:10:5:10:9 | ControlFlowNode for query | provenance | | @@ -70,8 +74,10 @@ models | 2 | Sink: OpenAI; Member[completions].Member[create].Argument[prompt:]; user-prompt-injection | | 3 | Sink: OpenAI; Member[images].Member[generate,edit].Argument[prompt:]; user-prompt-injection | | 4 | Sink: OpenAI; Member[responses].Member[create].Argument[input:]; user-prompt-injection | -| 5 | Sink: agents; Member[Runner].Member[run,run_sync,run_streamed].Argument[1]; user-prompt-injection | -| 6 | Sink: agents; Member[Runner].Member[run,run_sync,run_streamed].Argument[input:]; user-prompt-injection | +| 5 | Sink: OpenRouter; Member[embeddings].Member[generate].Argument[input:]; user-prompt-injection | +| 6 | Sink: OpenRouter; Member[responses].Member[send].Argument[input:]; user-prompt-injection | +| 7 | Sink: agents; Member[Runner].Member[run,run_sync,run_streamed].Argument[1]; user-prompt-injection | +| 8 | Sink: agents; Member[Runner].Member[run,run_sync,run_streamed].Argument[input:]; user-prompt-injection | nodes | agent_test.py:2:26:2:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember | | agent_test.py:2:26:2:32 | ControlFlowNode for request | semmle.label | ControlFlowNode for request | @@ -127,6 +133,8 @@ nodes | openrouter_test.py:10:13:10:24 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute | | openrouter_test.py:10:13:10:37 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() | | openrouter_test.py:21:28:21:32 | ControlFlowNode for query | semmle.label | ControlFlowNode for query | +| openrouter_test.py:29:15:29:19 | ControlFlowNode for query | semmle.label | ControlFlowNode for query | +| openrouter_test.py:34:15:34:19 | ControlFlowNode for query | semmle.label | ControlFlowNode for query | subpaths testFailures | agent_test.py:17:15:22:9 | ControlFlowNode for List | Unexpected result: Alert | @@ -134,5 +142,6 @@ testFailures | gemini_test.py:14:26:14:60 | Comment # $ Alert[py/user-prompt-injection] | Missing result: Alert[py/user-prompt-injection] | | gemini_test.py:24:40:24:74 | Comment # $ Alert[py/user-prompt-injection] | Missing result: Alert[py/user-prompt-injection] | | gemini_test.py:32:62:32:96 | Comment # $ Alert[py/user-prompt-injection] | Missing result: Alert[py/user-prompt-injection] | +| gemini_test.py:36:24:36:58 | Comment # $ Alert[py/user-prompt-injection] | Missing result: Alert[py/user-prompt-injection] | | langchain_test.py:17:43:17:77 | Comment # $ Alert[py/user-prompt-injection] | Missing result: Alert[py/user-prompt-injection] | | openai_test.py:20:15:29:9 | ControlFlowNode for List | Unexpected result: Alert | diff --git a/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/gemini_test.py b/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/gemini_test.py index 16f0828d7ac..a59cca8337c 100644 --- a/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/gemini_test.py +++ b/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/gemini_test.py @@ -30,4 +30,9 @@ def get_input_gemini(): chat = client.chats.create(model="gemini-2.0-flash") response3 = chat.send_message("Tell me about " + query) # $ Alert[py/user-prompt-injection] - print(response1, response2, response3) + + response4 = client.models.edit_image( + model="imagen-3.0-capability-001", + prompt=query, # $ Alert[py/user-prompt-injection] + ) + print(response1, response2, response3, response4) diff --git a/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/openrouter_test.py b/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/openrouter_test.py index 7b840ba80f7..7f8e881a231 100644 --- a/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/openrouter_test.py +++ b/python/ql/test/query-tests/Security/CWE-1427-UserPromptInjection/openrouter_test.py @@ -9,7 +9,7 @@ client = OpenRouter() def get_input_openrouter(): query = request.args.get("query") - completion = client.chat.completions.create( + completion = client.chat.send( model="openai/gpt-4.1", messages=[ { @@ -22,4 +22,15 @@ def get_input_openrouter(): } ] ) - print(completion) + + response = client.responses.send( + model="openai/gpt-4.1", + instructions="You are a helpful assistant.", + input=query, # $ Alert[py/user-prompt-injection] + ) + + embedding = client.embeddings.generate( + model="openai/text-embedding-3-small", + input=query, # $ Alert[py/user-prompt-injection] + ) + print(completion, response, embedding)