mirror of
https://github.com/github/codeql.git
synced 2026-06-11 07:51:13 +02:00
add openrouter support
This commit is contained in:
19
javascript/ql/lib/ext/openrouter.model.yml
Normal file
19
javascript/ql/lib/ext/openrouter.model.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/javascript-all
|
||||
extensible: typeModel
|
||||
data:
|
||||
- ["openrouter.Client", "@openrouter/sdk", "Instance"]
|
||||
- ["openrouter.Client", "@openrouter/sdk", "Member[OpenRouter].Instance"]
|
||||
- ["openrouter.Agent", "@openrouter/agent", "Member[OpenRouter].Instance"]
|
||||
|
||||
- addsTo:
|
||||
pack: codeql/javascript-all
|
||||
extensible: sinkModel
|
||||
data:
|
||||
- ["@openrouter/agent", "Member[callModel].Argument[0].Member[instructions]", "system-prompt-injection"]
|
||||
- ["openrouter.Agent", "Member[callModel].Argument[0].Member[instructions]", "system-prompt-injection"]
|
||||
- ["@openrouter/agent", "Member[tool].Argument[0].Member[description]", "system-prompt-injection"]
|
||||
- ["openrouter.Client", "Member[embeddings].Member[create].Argument[0].Member[input]", "user-prompt-injection"]
|
||||
- ["@openrouter/agent", "Member[callModel].Argument[0].Member[input]", "user-prompt-injection"]
|
||||
- ["openrouter.Agent", "Member[callModel].Argument[0].Member[input]", "user-prompt-injection"]
|
||||
124
javascript/ql/lib/semmle/javascript/frameworks/OpenRouter.qll
Normal file
124
javascript/ql/lib/semmle/javascript/frameworks/OpenRouter.qll
Normal file
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
* Provides classes modeling security-relevant aspects of the OpenRouter JS/TS SDKs.
|
||||
* See https://openrouter.ai/docs/client-sdks/typescript (`@openrouter/sdk`) and
|
||||
* https://openrouter.ai/docs/agent-sdk/overview (`@openrouter/agent`).
|
||||
*
|
||||
* Structurally typed sinks (instructions, input, description, etc.) have been moved to
|
||||
* Models as Data: javascript/ql/lib/ext/openrouter.model.yml
|
||||
*
|
||||
* This file retains only role-filtered sinks that require inspecting a sibling
|
||||
* `role` property, which MaD cannot express.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
|
||||
/** Holds if `msg` is a message array element with a privileged role. */
|
||||
private predicate isSystemOrDevMessage(API::Node msg) {
|
||||
msg.getMember("role").asSink().mayHaveStringValue(["system", "developer", "assistant"])
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the OpenRouter Client SDK (`@openrouter/sdk`).
|
||||
*/
|
||||
module OpenRouter {
|
||||
/** Gets a reference to an `@openrouter/sdk` client instance. */
|
||||
private API::Node clientRef() {
|
||||
// Default export: import OpenRouter from '@openrouter/sdk'; new OpenRouter()
|
||||
result = API::moduleImport("@openrouter/sdk").getInstance()
|
||||
or
|
||||
// Named import: import { OpenRouter } from '@openrouter/sdk'; new OpenRouter()
|
||||
result = API::moduleImport("@openrouter/sdk").getMember("OpenRouter").getInstance()
|
||||
}
|
||||
|
||||
/** Gets the parameter object of a chat completion call. */
|
||||
private API::Node chatCreateParams() {
|
||||
// client.chat.send({ messages: [...] })
|
||||
result = clientRef().getMember("chat").getMember("send").getParameter(0)
|
||||
or
|
||||
// OpenAI-compatible surface: client.chat.completions.create({ messages: [...] })
|
||||
result =
|
||||
clientRef().getMember("chat").getMember("completions").getMember("create").getParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets role-filtered system/developer/assistant message sinks.
|
||||
* These require checking a sibling `role` property and cannot be expressed in MaD.
|
||||
*/
|
||||
API::Node getSystemOrAssistantPromptNode() {
|
||||
// chat.send/completions.create({ messages: [{ role: "system"/"developer"/"assistant", content: ... }] })
|
||||
exists(API::Node msg, API::Node content |
|
||||
msg = chatCreateParams().getMember("messages").getArrayElement() and
|
||||
isSystemOrDevMessage(msg) and
|
||||
content = msg.getMember("content")
|
||||
|
|
||||
result = content
|
||||
or
|
||||
result = content.getArrayElement().getMember("text")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets role-filtered user message sinks.
|
||||
* These require checking a sibling `role` property and cannot be expressed in MaD.
|
||||
*/
|
||||
API::Node getUserPromptNode() {
|
||||
// chat.send/completions.create({ messages: [{ role: "user", content: ... }] })
|
||||
exists(API::Node msg, API::Node content |
|
||||
msg = chatCreateParams().getMember("messages").getArrayElement() and
|
||||
not isSystemOrDevMessage(msg) and
|
||||
content = msg.getMember("content")
|
||||
|
|
||||
result = content
|
||||
or
|
||||
result = content.getArrayElement().getMember("text")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides models for the OpenRouter Agent SDK (`@openrouter/agent`).
|
||||
*
|
||||
* Structurally typed sinks have been moved to openrouter.model.yml.
|
||||
* This module retains only role-filtered sinks that MaD cannot express.
|
||||
*/
|
||||
module OpenRouterAgent {
|
||||
/** Gets a reference to the `@openrouter/agent` module. */
|
||||
private API::Node moduleRef() { result = API::moduleImport("@openrouter/agent") }
|
||||
|
||||
/** Gets a `callModel` invocation's parameter object (top-level and instance forms). */
|
||||
private API::Node callModelParams() {
|
||||
// import { callModel } from '@openrouter/agent'; callModel({ ... })
|
||||
result = moduleRef().getMember("callModel").getParameter(0)
|
||||
or
|
||||
// import { OpenRouter } from '@openrouter/agent'; new OpenRouter(...).callModel({ ... })
|
||||
result = moduleRef().getMember("OpenRouter").getInstance().getMember("callModel").getParameter(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets role-filtered system/developer/assistant message sinks.
|
||||
* These require checking a sibling `role` property and cannot be expressed in MaD.
|
||||
*/
|
||||
API::Node getSystemOrAssistantPromptNode() {
|
||||
// callModel({ messages/input: [{ role: "system"/"developer"/"assistant", content: ... }] })
|
||||
exists(API::Node msg |
|
||||
msg = callModelParams().getMember(["messages", "input"]).getArrayElement() and
|
||||
isSystemOrDevMessage(msg)
|
||||
|
|
||||
result = msg.getMember("content")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets role-filtered user message sinks.
|
||||
* These require checking a sibling `role` property and cannot be expressed in MaD.
|
||||
*/
|
||||
API::Node getUserPromptNode() {
|
||||
// callModel({ messages/input: [{ role: "user", content: ... }] })
|
||||
exists(API::Node msg |
|
||||
msg = callModelParams().getMember(["messages", "input"]).getArrayElement() and
|
||||
not isSystemOrDevMessage(msg)
|
||||
|
|
||||
result = msg.getMember("content")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ private import semmle.javascript.frameworks.data.ModelsAsData
|
||||
private import semmle.javascript.frameworks.OpenAI
|
||||
private import semmle.javascript.frameworks.Anthropic
|
||||
private import semmle.javascript.frameworks.GoogleGenAI
|
||||
private import semmle.javascript.frameworks.OpenRouter
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
@@ -64,6 +65,10 @@ module SystemPromptInjection {
|
||||
this = Anthropic::getSystemOrAssistantPromptNode().asSink()
|
||||
or
|
||||
this = GoogleGenAI::getSystemOrAssistantPromptNode().asSink()
|
||||
or
|
||||
this = OpenRouter::getSystemOrAssistantPromptNode().asSink()
|
||||
or
|
||||
this = OpenRouterAgent::getSystemOrAssistantPromptNode().asSink()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ private import semmle.javascript.frameworks.data.ModelsAsData
|
||||
private import semmle.javascript.frameworks.OpenAI
|
||||
private import semmle.javascript.frameworks.Anthropic
|
||||
private import semmle.javascript.frameworks.GoogleGenAI
|
||||
private import semmle.javascript.frameworks.OpenRouter
|
||||
|
||||
/**
|
||||
* Provides default sources, sinks and sanitizers for detecting
|
||||
@@ -65,6 +66,10 @@ module UserPromptInjection {
|
||||
this = GoogleGenAI::getUserPromptNode().asSink()
|
||||
or
|
||||
this = AgentSDK::getUserPromptNode().asSink()
|
||||
or
|
||||
this = OpenRouter::getUserPromptNode().asSink()
|
||||
or
|
||||
this = OpenRouterAgent::getUserPromptNode().asSink()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,25 @@ edges
|
||||
| openai_test.js:158:52:158:58 | persona | openai_test.js:158:30:158:58 | "Also t ... persona | provenance | |
|
||||
| openai_test.js:164:31:164:37 | persona | openai_test.js:164:14:164:37 | "Talk l ... persona | provenance | |
|
||||
| openai_test.js:192:49:192:55 | persona | openai_test.js:192:32:192:55 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:23:35:23:41 | persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:38:35:38:41 | persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:52:36:52:42 | persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:78:35:78:41 | persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:88:36:88:42 | persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:98:35:98:41 | persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:109:35:109:41 | persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:118:36:118:42 | persona | provenance | |
|
||||
| openrouter_test.js:12:9:12:15 | persona | openrouter_test.js:125:35:125:41 | persona | provenance | |
|
||||
| openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:12:9:12:15 | persona | provenance | |
|
||||
| openrouter_test.js:23:35:23:41 | persona | openrouter_test.js:23:18:23:41 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:38:35:38:41 | persona | openrouter_test.js:38:18:38:41 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:52:36:52:42 | persona | openrouter_test.js:52:19:52:42 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:78:35:78:41 | persona | openrouter_test.js:78:18:78:41 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:88:36:88:42 | persona | openrouter_test.js:88:19:88:42 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:98:35:98:41 | persona | openrouter_test.js:98:18:98:41 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:109:35:109:41 | persona | openrouter_test.js:109:18:109:41 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:118:36:118:42 | persona | openrouter_test.js:118:19:118:42 | "Talk l ... persona | provenance | |
|
||||
| openrouter_test.js:125:35:125:41 | persona | openrouter_test.js:125:18:125:41 | "Talk l ... persona | provenance | |
|
||||
nodes
|
||||
| agents_test.js:8:9:8:15 | persona | semmle.label | persona |
|
||||
| agents_test.js:8:19:8:35 | req.query.persona | semmle.label | req.query.persona |
|
||||
@@ -195,6 +214,26 @@ nodes
|
||||
| openai_test.js:164:31:164:37 | persona | semmle.label | persona |
|
||||
| openai_test.js:192:32:192:55 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openai_test.js:192:49:192:55 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:12:9:12:15 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:12:19:12:35 | req.query.persona | semmle.label | req.query.persona |
|
||||
| openrouter_test.js:23:18:23:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:23:35:23:41 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:38:18:38:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:38:35:38:41 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:52:19:52:42 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:52:36:52:42 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:78:18:78:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:78:35:78:41 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:88:19:88:42 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:88:36:88:42 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:98:18:98:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:98:35:98:41 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:109:18:109:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:109:35:109:41 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:118:19:118:42 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:118:36:118:42 | persona | semmle.label | persona |
|
||||
| openrouter_test.js:125:18:125:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
|
||||
| openrouter_test.js:125:35:125:41 | persona | semmle.label | persona |
|
||||
subpaths
|
||||
#select
|
||||
| agents_test.js:16:19:16:42 | "Talk l ... persona | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:16:19:16:42 | "Talk l ... persona | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
|
||||
@@ -236,3 +275,12 @@ subpaths
|
||||
| openai_test.js:158:30:158:58 | "Also t ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:158:30:158:58 | "Also t ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |
|
||||
| openai_test.js:164:14:164:37 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:164:14:164:37 | "Talk l ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |
|
||||
| openai_test.js:192:32:192:55 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:192:32:192:55 | "Talk l ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:23:18:23:41 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:23:18:23:41 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:38:18:38:41 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:38:18:38:41 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:52:19:52:42 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:52:19:52:42 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:78:18:78:41 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:78:18:78:41 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:88:19:88:42 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:88:19:88:42 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:98:18:98:41 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:98:18:98:41 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:109:18:109:41 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:109:18:109:41 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:118:19:118:42 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:118:19:118:42 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
| openrouter_test.js:125:18:125:41 | "Talk l ... persona | openrouter_test.js:12:19:12:35 | req.query.persona | openrouter_test.js:125:18:125:41 | "Talk l ... persona | This prompt construction depends on a $@. | openrouter_test.js:12:19:12:35 | req.query.persona | user-provided value |
|
||||
|
||||
@@ -13,7 +13,7 @@ app.get("/agents", async (req, res) => {
|
||||
// SHOULD ALERT
|
||||
const agent1 = new Agent({
|
||||
name: "Assistant",
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === Agent constructor: instructions as lambda ===
|
||||
@@ -22,7 +22,7 @@ app.get("/agents", async (req, res) => {
|
||||
const agent2 = new Agent({
|
||||
name: "Dynamic",
|
||||
instructions: (runContext) => {
|
||||
return "Talk like a " + persona; // $ Alert[js/prompt-injection]
|
||||
return "Talk like a " + persona; // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
});
|
||||
|
||||
@@ -30,7 +30,7 @@ app.get("/agents", async (req, res) => {
|
||||
const agent3 = new Agent({
|
||||
name: "AsyncDynamic",
|
||||
instructions: async (runContext) => {
|
||||
return "Talk like a " + persona; // $ Alert[js/prompt-injection]
|
||||
return "Talk like a " + persona; // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
});
|
||||
|
||||
@@ -40,7 +40,7 @@ app.get("/agents", async (req, res) => {
|
||||
const agent4 = new Agent({
|
||||
name: "Specialist",
|
||||
instructions: "Help with refunds",
|
||||
handoffDescription: "Handles " + persona, // $ Alert[js/prompt-injection]
|
||||
handoffDescription: "Handles " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === agent.asTool(): toolDescription ===
|
||||
@@ -48,7 +48,7 @@ app.get("/agents", async (req, res) => {
|
||||
// SHOULD ALERT
|
||||
agent1.asTool({
|
||||
toolName: "helper",
|
||||
toolDescription: "Ask about " + persona, // $ Alert[js/prompt-injection]
|
||||
toolDescription: "Ask about " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === tool(): description ===
|
||||
@@ -56,7 +56,7 @@ app.get("/agents", async (req, res) => {
|
||||
// SHOULD ALERT
|
||||
const myTool = tool({
|
||||
name: "lookup",
|
||||
description: "Look up info about " + persona, // $ Alert[js/prompt-injection]
|
||||
description: "Look up info about " + persona, // $ Alert[js/system-prompt-injection]
|
||||
parameters: z.object({ query: z.string() }),
|
||||
execute: async ({ query }) => "result",
|
||||
});
|
||||
@@ -70,7 +70,7 @@ app.get("/agents", async (req, res) => {
|
||||
|
||||
// SHOULD ALERT
|
||||
const r2 = await run(agent1, [
|
||||
{ role: "system", content: "Talk like a " + persona }, // $ Alert[js/prompt-injection]
|
||||
{ role: "system", content: "Talk like a " + persona }, // $ Alert[js/system-prompt-injection]
|
||||
{ role: "user", content: query },
|
||||
]);
|
||||
|
||||
@@ -78,7 +78,7 @@ app.get("/agents", async (req, res) => {
|
||||
|
||||
// SHOULD ALERT
|
||||
const r3 = await run(agent1, [
|
||||
{ role: "developer", content: "Talk like a " + persona }, // $ Alert[js/prompt-injection]
|
||||
{ role: "developer", content: "Talk like a " + persona }, // $ Alert[js/system-prompt-injection]
|
||||
]);
|
||||
|
||||
// === run() with array input: user role ===
|
||||
@@ -93,7 +93,7 @@ app.get("/agents", async (req, res) => {
|
||||
// SHOULD ALERT
|
||||
const runner = new Runner();
|
||||
const r5 = await runner.run(agent1, [
|
||||
{ role: "system", content: "Talk like a " + persona }, // $ Alert[js/prompt-injection]
|
||||
{ role: "system", content: "Talk like a " + persona }, // $ Alert[js/system-prompt-injection]
|
||||
]);
|
||||
|
||||
// === Sanitizer: constant comparison ===
|
||||
|
||||
@@ -14,7 +14,7 @@ app.get("/test", async (req, res) => {
|
||||
const m1 = await client.messages.create({
|
||||
model: "claude-sonnet-4-20250514",
|
||||
max_tokens: 1024,
|
||||
system: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
system: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
messages: [{ role: "user", content: query }],
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ app.get("/test", async (req, res) => {
|
||||
system: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
text: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
messages: [{ role: "user", content: query }],
|
||||
@@ -42,7 +42,7 @@ app.get("/test", async (req, res) => {
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
{ role: "user", content: query },
|
||||
],
|
||||
@@ -68,7 +68,7 @@ app.get("/test", async (req, res) => {
|
||||
const bm1 = await client.beta.messages.create({
|
||||
model: "claude-sonnet-4-20250514",
|
||||
max_tokens: 1024,
|
||||
system: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
system: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
messages: [{ role: "user", content: query }],
|
||||
});
|
||||
|
||||
@@ -81,7 +81,7 @@ app.get("/test", async (req, res) => {
|
||||
system: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
text: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
messages: [{ role: "user", content: query }],
|
||||
@@ -96,7 +96,7 @@ app.get("/test", async (req, res) => {
|
||||
messages: [
|
||||
{
|
||||
role: "assistant",
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
{ role: "user", content: query },
|
||||
],
|
||||
@@ -107,14 +107,14 @@ app.get("/test", async (req, res) => {
|
||||
// SHOULD ALERT
|
||||
const ba1 = await client.beta.agents.create({
|
||||
model: "claude-sonnet-4-20250514",
|
||||
system: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
system: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === beta.agents.update: system ===
|
||||
|
||||
// SHOULD ALERT
|
||||
await client.beta.agents.update("agent_123", {
|
||||
system: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
system: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === Barrier: user-role content in shared message array ===
|
||||
@@ -138,7 +138,7 @@ app.get("/test", async (req, res) => {
|
||||
// SHOULD ALERT — tainted data goes into system role; barrier on user role
|
||||
// must not suppress the system-role taint path.
|
||||
const messages2 = [
|
||||
{ role: "system", content: "Talk like a " + persona }, // $ Alert[js/prompt-injection]
|
||||
{ role: "system", content: "Talk like a " + persona }, // $ Alert[js/system-prompt-injection]
|
||||
{ role: "user", content: query },
|
||||
];
|
||||
const systemMsg2 = messages2.find((m) => m.role === "system");
|
||||
|
||||
@@ -15,7 +15,7 @@ app.get("/test", async (req, res) => {
|
||||
model: "gemini-2.0-flash",
|
||||
contents: "Hello",
|
||||
config: {
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
});
|
||||
|
||||
@@ -27,7 +27,7 @@ app.get("/test", async (req, res) => {
|
||||
contents: [
|
||||
{
|
||||
role: "model",
|
||||
parts: [{ text: "Talk like a " + persona }], // $ Alert[js/prompt-injection]
|
||||
parts: [{ text: "Talk like a " + persona }], // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
@@ -56,7 +56,7 @@ app.get("/test", async (req, res) => {
|
||||
model: "gemini-2.0-flash",
|
||||
contents: "Hello",
|
||||
config: {
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
});
|
||||
|
||||
@@ -65,7 +65,7 @@ app.get("/test", async (req, res) => {
|
||||
// SHOULD ALERT
|
||||
const g5 = await ai.models.generateImages({
|
||||
model: "imagen-3.0-generate-002",
|
||||
prompt: "Draw a picture of " + persona, // $ Alert[js/prompt-injection]
|
||||
prompt: "Draw a picture of " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === editImage: prompt ===
|
||||
@@ -73,7 +73,7 @@ app.get("/test", async (req, res) => {
|
||||
// SHOULD ALERT
|
||||
const g6 = await ai.models.editImage({
|
||||
model: "imagen-3.0-capability-001",
|
||||
prompt: "Edit to look like " + persona, // $ Alert[js/prompt-injection]
|
||||
prompt: "Edit to look like " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === chats.create: systemInstruction ===
|
||||
@@ -82,7 +82,7 @@ app.get("/test", async (req, res) => {
|
||||
const chat = ai.chats.create({
|
||||
model: "gemini-2.0-flash",
|
||||
config: {
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
});
|
||||
|
||||
@@ -92,7 +92,7 @@ app.get("/test", async (req, res) => {
|
||||
await chat.sendMessage({
|
||||
message: query,
|
||||
config: {
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
});
|
||||
|
||||
@@ -102,7 +102,7 @@ app.get("/test", async (req, res) => {
|
||||
const session = await ai.live.connect({
|
||||
model: "gemini-2.0-flash-live-001",
|
||||
config: {
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
systemInstruction: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
callbacks: {
|
||||
onmessage: (msg) => {},
|
||||
|
||||
@@ -13,16 +13,16 @@ app.get("/test", async (req, res) => {
|
||||
|
||||
// === SystemMessage (SHOULD ALERT) ===
|
||||
|
||||
const sysMsg1 = new SystemMessage("Talk like a " + persona); // $ Alert[js/prompt-injection]
|
||||
const sysMsg1 = new SystemMessage("Talk like a " + persona); // $ Alert[js/system-prompt-injection]
|
||||
|
||||
const sysMsg2 = new SystemMessage({
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === createAgent with systemPrompt (SHOULD ALERT) ===
|
||||
|
||||
const agent = createAgent({
|
||||
systemPrompt: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
systemPrompt: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === Barrier test: user role content in shared array (SHOULD NOT ALERT) ===
|
||||
|
||||
@@ -16,7 +16,7 @@ app.get("/test", async (req, res) => {
|
||||
// instructions: tainted string (SHOULD ALERT)
|
||||
const r1 = await client.responses.create({
|
||||
model: "gpt-4.1",
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
input: "Hello",
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ app.get("/test", async (req, res) => {
|
||||
input: [
|
||||
{
|
||||
role: "system",
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
@@ -41,7 +41,7 @@ app.get("/test", async (req, res) => {
|
||||
input: [
|
||||
{
|
||||
role: "developer",
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -65,7 +65,7 @@ app.get("/test", async (req, res) => {
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
@@ -80,7 +80,7 @@ app.get("/test", async (req, res) => {
|
||||
messages: [
|
||||
{
|
||||
role: "developer",
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -94,7 +94,7 @@ app.get("/test", async (req, res) => {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
text: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -107,7 +107,7 @@ app.get("/test", async (req, res) => {
|
||||
messages: [
|
||||
{
|
||||
role: "developer",
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -117,19 +117,19 @@ app.get("/test", async (req, res) => {
|
||||
// prompt (SHOULD ALERT)
|
||||
const l1 = await client.completions.create({
|
||||
model: "gpt-3.5-turbo-instruct",
|
||||
prompt: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
prompt: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === Images API ===
|
||||
|
||||
// images.generate (SHOULD ALERT)
|
||||
const i1 = await client.images.generate({
|
||||
prompt: "Draw a picture of " + persona, // $ Alert[js/prompt-injection]
|
||||
prompt: "Draw a picture of " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// images.edit (SHOULD ALERT)
|
||||
const i2 = await client.images.edit({
|
||||
prompt: "Edit to look like " + persona, // $ Alert[js/prompt-injection]
|
||||
prompt: "Edit to look like " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === Assistants API (beta) ===
|
||||
@@ -138,30 +138,30 @@ app.get("/test", async (req, res) => {
|
||||
const a1 = await client.beta.assistants.create({
|
||||
name: "Test Agent",
|
||||
model: "gpt-4.1",
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// assistants.update (SHOULD ALERT)
|
||||
await client.beta.assistants.update("asst_123", {
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// threads.runs.create (SHOULD ALERT)
|
||||
const tr1 = await client.beta.threads.runs.create("thread_123", {
|
||||
assistant_id: "asst_123",
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// threads.runs.create with additional_instructions (SHOULD ALERT)
|
||||
const tr2 = await client.beta.threads.runs.create("thread_123", {
|
||||
assistant_id: "asst_123",
|
||||
additional_instructions: "Also talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
additional_instructions: "Also talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// threads.messages.create with system role (SHOULD ALERT)
|
||||
await client.beta.threads.messages.create("thread_123", {
|
||||
role: "system",
|
||||
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// threads.messages.create with user role (SHOULD NOT ALERT)
|
||||
@@ -176,20 +176,20 @@ app.get("/test", async (req, res) => {
|
||||
const at1 = await client.audio.transcriptions.create({
|
||||
file: "audio.mp3",
|
||||
model: "whisper-1",
|
||||
prompt: "Transcribe about " + persona, // $ Alert[js/prompt-injection]
|
||||
prompt: "Transcribe about " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// audio.translations.create (SHOULD ALERT)
|
||||
const atl1 = await client.audio.translations.create({
|
||||
file: "audio.mp3",
|
||||
model: "whisper-1",
|
||||
prompt: "Translate about " + persona, // $ Alert[js/prompt-injection]
|
||||
prompt: "Translate about " + persona, // $ Alert[js/system-prompt-injection]
|
||||
});
|
||||
|
||||
// === Object assigned to variable first ===
|
||||
|
||||
// Should still be caught via data flow
|
||||
const opts = { instructions: "Talk like a " + persona }; // $ Alert[js/prompt-injection]
|
||||
const opts = { instructions: "Talk like a " + persona }; // $ Alert[js/system-prompt-injection]
|
||||
const r5 = await client.responses.create(opts);
|
||||
|
||||
// === Sanitizer: constant comparison ===
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
const express = require("express");
|
||||
const OpenRouter = require("@openrouter/sdk");
|
||||
const { OpenRouter: OpenRouterNamed } = require("@openrouter/sdk");
|
||||
const { callModel, tool } = require("@openrouter/agent");
|
||||
const { OpenRouter: OpenRouterAgent } = require("@openrouter/agent");
|
||||
|
||||
const app = express();
|
||||
const client = new OpenRouter();
|
||||
const namedClient = new OpenRouterNamed();
|
||||
|
||||
app.get("/test", async (req, res) => {
|
||||
const persona = req.query.persona;
|
||||
const query = req.query.query;
|
||||
|
||||
// === OpenRouter Client SDK: chat.send ===
|
||||
|
||||
// messages with system role (SHOULD ALERT)
|
||||
const s1 = await client.chat.send({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: query, // OK - user role
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// messages with developer role (SHOULD ALERT)
|
||||
const s2 = await client.chat.send({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "developer",
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// messages with content as array of content parts (SHOULD ALERT)
|
||||
const s3 = await client.chat.send({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// messages with user role (SHOULD NOT ALERT)
|
||||
const s4 = await client.chat.send({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: query, // OK - user role is expected to carry user input
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// === OpenRouter Client SDK: chat.completions.create (OpenAI-compatible) ===
|
||||
|
||||
// messages with system role (SHOULD ALERT)
|
||||
const c1 = await namedClient.chat.completions.create({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// === OpenRouter Agent SDK: callModel ===
|
||||
|
||||
// instructions: tainted string (SHOULD ALERT)
|
||||
const a1 = await callModel({
|
||||
model: "openai/gpt-4o",
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
input: "Hello",
|
||||
});
|
||||
|
||||
// messages with system role (SHOULD ALERT)
|
||||
const a2 = await callModel({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// input array with developer role (SHOULD ALERT)
|
||||
const a3 = await callModel({
|
||||
model: "openai/gpt-4o",
|
||||
input: [
|
||||
{
|
||||
role: "developer",
|
||||
content: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// instance form: new OpenRouter().callModel (SHOULD ALERT)
|
||||
const agent = new OpenRouterAgent();
|
||||
const a4 = await agent.callModel({
|
||||
model: "openai/gpt-4o",
|
||||
instructions: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
input: "Hello",
|
||||
});
|
||||
|
||||
// tool description (SHOULD ALERT)
|
||||
const t1 = tool({
|
||||
name: "lookup",
|
||||
description: "Talk like a " + persona, // $ Alert[js/system-prompt-injection]
|
||||
inputSchema: {},
|
||||
execute: async () => {},
|
||||
});
|
||||
|
||||
// input array with user role (SHOULD NOT ALERT)
|
||||
const a5 = await callModel({
|
||||
model: "openai/gpt-4o",
|
||||
input: [
|
||||
{
|
||||
role: "user",
|
||||
content: query, // OK - user role
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
res.send("ok");
|
||||
});
|
||||
@@ -44,6 +44,15 @@ edges
|
||||
| openai_user_test.js:15:9:15:17 | userInput | openai_user_test.js:201:27:201:35 | userInput | provenance | |
|
||||
| openai_user_test.js:15:9:15:17 | userInput | openai_user_test.js:205:30:205:38 | userInput | provenance | |
|
||||
| openai_user_test.js:15:21:15:39 | req.query.userInput | openai_user_test.js:15:9:15:17 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | openrouter_user_test.js:22:18:22:26 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | openrouter_user_test.js:36:19:36:27 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | openrouter_user_test.js:50:18:50:26 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | openrouter_user_test.js:59:12:59:20 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | openrouter_user_test.js:68:12:68:20 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | openrouter_user_test.js:77:18:77:26 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | openrouter_user_test.js:88:18:88:26 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | openrouter_user_test.js:97:12:97:20 | userInput | provenance | |
|
||||
| openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:12:9:12:17 | userInput | provenance | |
|
||||
nodes
|
||||
| anthropic_user_test.js:8:9:8:17 | userInput | semmle.label | userInput |
|
||||
| anthropic_user_test.js:8:21:8:39 | req.query.userInput | semmle.label | req.query.userInput |
|
||||
@@ -94,6 +103,16 @@ nodes
|
||||
| openai_user_test.js:196:30:196:38 | userInput | semmle.label | userInput |
|
||||
| openai_user_test.js:201:27:201:35 | userInput | semmle.label | userInput |
|
||||
| openai_user_test.js:205:30:205:38 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:12:9:12:17 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:12:21:12:39 | req.query.userInput | semmle.label | req.query.userInput |
|
||||
| openrouter_user_test.js:22:18:22:26 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:36:19:36:27 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:50:18:50:26 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:59:12:59:20 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:68:12:68:20 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:77:18:77:26 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:88:18:88:26 | userInput | semmle.label | userInput |
|
||||
| openrouter_user_test.js:97:12:97:20 | userInput | semmle.label | userInput |
|
||||
subpaths
|
||||
#select
|
||||
| anthropic_user_test.js:18:18:18:26 | userInput | anthropic_user_test.js:8:21:8:39 | req.query.userInput | anthropic_user_test.js:18:18:18:26 | userInput | This prompt construction depends on a $@. | anthropic_user_test.js:8:21:8:39 | req.query.userInput | user-provided value |
|
||||
@@ -137,3 +156,11 @@ subpaths
|
||||
| openai_user_test.js:196:30:196:38 | userInput | openai_user_test.js:15:21:15:39 | req.query.userInput | openai_user_test.js:196:30:196:38 | userInput | This prompt construction depends on a $@. | openai_user_test.js:15:21:15:39 | req.query.userInput | user-provided value |
|
||||
| openai_user_test.js:201:27:201:35 | userInput | openai_user_test.js:15:21:15:39 | req.query.userInput | openai_user_test.js:201:27:201:35 | userInput | This prompt construction depends on a $@. | openai_user_test.js:15:21:15:39 | req.query.userInput | user-provided value |
|
||||
| openai_user_test.js:205:30:205:38 | userInput | openai_user_test.js:15:21:15:39 | req.query.userInput | openai_user_test.js:205:30:205:38 | userInput | This prompt construction depends on a $@. | openai_user_test.js:15:21:15:39 | req.query.userInput | user-provided value |
|
||||
| openrouter_user_test.js:22:18:22:26 | userInput | openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:22:18:22:26 | userInput | This prompt construction depends on a $@. | openrouter_user_test.js:12:21:12:39 | req.query.userInput | user-provided value |
|
||||
| openrouter_user_test.js:36:19:36:27 | userInput | openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:36:19:36:27 | userInput | This prompt construction depends on a $@. | openrouter_user_test.js:12:21:12:39 | req.query.userInput | user-provided value |
|
||||
| openrouter_user_test.js:50:18:50:26 | userInput | openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:50:18:50:26 | userInput | This prompt construction depends on a $@. | openrouter_user_test.js:12:21:12:39 | req.query.userInput | user-provided value |
|
||||
| openrouter_user_test.js:59:12:59:20 | userInput | openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:59:12:59:20 | userInput | This prompt construction depends on a $@. | openrouter_user_test.js:12:21:12:39 | req.query.userInput | user-provided value |
|
||||
| openrouter_user_test.js:68:12:68:20 | userInput | openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:68:12:68:20 | userInput | This prompt construction depends on a $@. | openrouter_user_test.js:12:21:12:39 | req.query.userInput | user-provided value |
|
||||
| openrouter_user_test.js:77:18:77:26 | userInput | openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:77:18:77:26 | userInput | This prompt construction depends on a $@. | openrouter_user_test.js:12:21:12:39 | req.query.userInput | user-provided value |
|
||||
| openrouter_user_test.js:88:18:88:26 | userInput | openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:88:18:88:26 | userInput | This prompt construction depends on a $@. | openrouter_user_test.js:12:21:12:39 | req.query.userInput | user-provided value |
|
||||
| openrouter_user_test.js:97:12:97:20 | userInput | openrouter_user_test.js:12:21:12:39 | req.query.userInput | openrouter_user_test.js:97:12:97:20 | userInput | This prompt construction depends on a $@. | openrouter_user_test.js:12:21:12:39 | req.query.userInput | user-provided value |
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
const express = require("express");
|
||||
const OpenRouter = require("@openrouter/sdk");
|
||||
const { OpenRouter: OpenRouterNamed } = require("@openrouter/sdk");
|
||||
const { callModel } = require("@openrouter/agent");
|
||||
const { OpenRouter: OpenRouterAgent } = require("@openrouter/agent");
|
||||
|
||||
const app = express();
|
||||
const client = new OpenRouter();
|
||||
const namedClient = new OpenRouterNamed();
|
||||
|
||||
app.get("/test", async (req, res) => {
|
||||
const userInput = req.query.userInput;
|
||||
|
||||
// === OpenRouter Client SDK: chat.send ===
|
||||
|
||||
// messages with user role (SHOULD ALERT)
|
||||
await client.chat.send({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: userInput, // $ Alert[js/user-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// messages with user role, content parts (SHOULD ALERT)
|
||||
await client.chat.send({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: userInput, // $ Alert[js/user-prompt-injection]
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// === OpenRouter Client SDK: chat.completions.create (OpenAI-compatible) ===
|
||||
|
||||
await namedClient.chat.completions.create({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: userInput, // $ Alert[js/user-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// === OpenRouter Client SDK: embeddings ===
|
||||
|
||||
await client.embeddings.create({
|
||||
model: "openai/text-embedding-3-small",
|
||||
input: userInput, // $ Alert[js/user-prompt-injection]
|
||||
});
|
||||
|
||||
// === OpenRouter Agent SDK: callModel ===
|
||||
|
||||
// input as string (SHOULD ALERT)
|
||||
await callModel({
|
||||
model: "openai/gpt-4o",
|
||||
instructions: "You are a helpful assistant",
|
||||
input: userInput, // $ Alert[js/user-prompt-injection]
|
||||
});
|
||||
|
||||
// input array with user role (SHOULD ALERT)
|
||||
await callModel({
|
||||
model: "openai/gpt-4o",
|
||||
input: [
|
||||
{
|
||||
role: "user",
|
||||
content: userInput, // $ Alert[js/user-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// messages with user role (SHOULD ALERT)
|
||||
await callModel({
|
||||
model: "openai/gpt-4o",
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
content: userInput, // $ Alert[js/user-prompt-injection]
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// instance form: new OpenRouter().callModel (SHOULD ALERT)
|
||||
const agent = new OpenRouterAgent();
|
||||
await agent.callModel({
|
||||
model: "openai/gpt-4o",
|
||||
input: userInput, // $ Alert[js/user-prompt-injection]
|
||||
});
|
||||
|
||||
res.send("ok");
|
||||
});
|
||||
Reference in New Issue
Block a user