JS: Add prompt injection detection (CWE-1427) for OpenAI, Anthropic, and Google GenAI SDKs

Add experimental CodeQL query detecting prompt injection vulnerabilities
in JavaScript/TypeScript applications using AI SDK libraries.

Modeled frameworks:
- openai (OpenAI, AzureOpenAI): responses, chat.completions, completions,
  images, embeddings, beta.assistants, beta.threads, audio APIs
- @openai/agents: Agent instructions, handoffDescription, run/Runner.run,
  asTool, tool()
- @anthropic-ai/sdk: messages.create, beta.messages.create,
  beta.agents.create/update
- @google/genai (GoogleGenAI): generateContent, generateContentStream,
  generateImages, editImage, chats, live.connect

Includes role-based filtering (system/developer/assistant/model roles)
and constant-comparison sanitizer guard.
This commit is contained in:
BazookaMusic
2026-04-30 17:39:09 +02:00
parent 154d213fd2
commit 0b7133c4ce
15 changed files with 1382 additions and 0 deletions

View File

@@ -226,3 +226,28 @@ module Cryptography {
class CryptographicAlgorithm = SC::CryptographicAlgorithm;
}
/**
* A data-flow node that prompts an AI model.
*
* Extend this class to refine existing API models. If you want to model new APIs,
* extend `AIPrompt::Range` instead.
*/
class AIPrompt extends DataFlow::Node instanceof AIPrompt::Range {
/** Gets an input that is used as AI prompt. */
DataFlow::Node getAPrompt() { result = super.getAPrompt() }
}
/** Provides a class for modeling new AI prompting mechanisms. */
module AIPrompt {
/**
* A data-flow node that prompts an AI model.
*
* Extend this class to model new APIs. If you want to refine existing API models,
* extend `AIPrompt` instead.
*/
abstract class Range extends DataFlow::Node {
/** Gets an input that is used as AI prompt. */
abstract DataFlow::Node getAPrompt();
}
}

View File

@@ -0,0 +1,24 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Prompts can be constructed to bypass the original purposes of an agent and lead to sensitive data leak or
operations that were not intended.</p>
</overview>
<recommendation>
<p>Sanitize user input and also avoid using user input in developer or system level prompts.</p>
</recommendation>
<example>
<p>In the following examples, the cases marked GOOD show secure prompt construction; whereas in the case marked BAD they may be susceptible to prompt injection.</p>
<sample src="examples/example.py" />
</example>
<references>
<li>OpenAI: <a href="https://openai.github.io/openai-guardrails-python">Guardrails</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Prompt injection
* @kind path-problem
* @problem.severity error
* @security-severity 5.0
* @precision high
* @id js/prompt-injection
* @tags security
* experimental
* external/cwe/cwe-1427
*/
import javascript
import experimental.semmle.javascript.security.PromptInjection.PromptInjectionQuery
import PromptInjectionFlow::PathGraph
from PromptInjectionFlow::PathNode source, PromptInjectionFlow::PathNode sink
where PromptInjectionFlow::flowPath(source, sink)
select sink.getNode(), source, sink, "This prompt construction depends on a $@.", source.getNode(),
"user-provided value"

View File

@@ -0,0 +1,17 @@
from flask import Flask, request
from agents import Agent
from guardrails import GuardrailAgent
@app.route("/parameter-route")
def get_input():
input = request.args.get("input")
goodAgent = GuardrailAgent( # GOOD: Agent created with guardrails automatically configured.
config=Path("guardrails_config.json"),
name="Assistant",
instructions="This prompt is customized for " + input)
badAgent = Agent(
name="Assistant",
instructions="This prompt is customized for " + input # BAD: user input in agent instruction.
)

View File

@@ -0,0 +1,64 @@
/**
* Provides classes modeling security-relevant aspects of the `@anthropic-ai/sdk` package.
* See https://github.com/anthropics/anthropic-sdk-typescript
*/
private import javascript
module Anthropic {
/** Gets a reference to the `Anthropic` client instance. */
API::Node classRef() {
// Default export: import Anthropic from '@anthropic-ai/sdk'; new Anthropic()
result = API::moduleImport("@anthropic-ai/sdk").getInstance()
}
/** Gets a reference to a sink for the system prompt in the Anthropic messages API. */
API::Node getContentNode() {
exists(API::Node createParams |
// client.messages.create({ ... })
createParams = classRef()
.getMember("messages")
.getMember("create")
.getParameter(0)
or
// client.beta.messages.create({ ... })
createParams = classRef()
.getMember("beta")
.getMember("messages")
.getMember("create")
.getParameter(0)
|
// system: "string"
result = createParams.getMember("system")
or
// system: [{ type: "text", text: "..." }]
result = createParams.getMember("system").getArrayElement().getMember("text")
or
// messages: [{ role: "assistant", content: "..." }]
// Injecting content into what the model said from external sources is very likely an injection.
exists(API::Node msg |
msg = createParams.getMember("messages").getArrayElement() and
msg.getMember("role").asSink().mayHaveStringValue("assistant")
|
result = msg.getMember("content")
)
)
or
// client.beta.agents.create({ system: "..." })
result = classRef()
.getMember("beta")
.getMember("agents")
.getMember("create")
.getParameter(0)
.getMember("system")
or
// client.beta.agents.update(agentId, { system: "..." })
result = classRef()
.getMember("beta")
.getMember("agents")
.getMember("update")
.getParameter(1)
.getMember("system")
}
}

View File

@@ -0,0 +1,85 @@
/**
* Provides classes modeling security-relevant aspects of the `@google/genai` package.
* See https://github.com/googleapis/js-genai
*/
private import javascript
module GoogleGenAI {
/** Gets a reference to the `GoogleGenAI` client instance. */
API::Node clientRef() {
// import { GoogleGenAI } from '@google/genai'; const ai = new GoogleGenAI(...)
result =
API::moduleImport("@google/genai").getMember("GoogleGenAI").getInstance()
}
/** Gets a reference to a sink for prompt content in the Google GenAI SDK. */
API::Node getContentNode() {
exists(API::Node params |
// ai.models.generateContent({ contents, config })
// ai.models.generateContentStream({ contents, config })
params =
clientRef()
.getMember("models")
.getMember(["generateContent", "generateContentStream"])
.getParameter(0)
|
// config.systemInstruction
result = params.getMember("config").getMember("systemInstruction")
or
// contents: [{ role: "model", parts: [{ text: "..." }] }]
// Gemini uses "model" role instead of "assistant"
exists(API::Node msg |
msg = params.getMember("contents").getArrayElement() and
msg.getMember("role").asSink().mayHaveStringValue("model")
|
result = msg.getMember("parts").getArrayElement().getMember("text")
)
)
or
// ai.models.generateImages({ prompt, config })
result =
clientRef()
.getMember("models")
.getMember("generateImages")
.getParameter(0)
.getMember("prompt")
or
// ai.models.editImage({ prompt, referenceImages, config })
result =
clientRef()
.getMember("models")
.getMember("editImage")
.getParameter(0)
.getMember("prompt")
or
// ai.chats.create({ config: { systemInstruction: ... } })
result =
clientRef()
.getMember("chats")
.getMember("create")
.getParameter(0)
.getMember("config")
.getMember("systemInstruction")
or
// chat.sendMessage({ config: { systemInstruction: ... } })
result =
clientRef()
.getMember("chats")
.getMember("create")
.getReturn()
.getMember("sendMessage")
.getParameter(0)
.getMember("config")
.getMember("systemInstruction")
or
// ai.live.connect({ config: { systemInstruction: ... } })
result =
clientRef()
.getMember("live")
.getMember("connect")
.getParameter(0)
.getMember("config")
.getMember("systemInstruction")
}
}

View File

@@ -0,0 +1,199 @@
/**
* Provides classes modeling security-relevant aspects of the `openAI-Node` package.
* See https://github.com/openai/openai-node
*/
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"])
}
module OpenAI {
/** Gets a reference to the `openai.OpenAI` class. */
API::Node classRef() {
// Default export: import OpenAI from 'openai'; new OpenAI()
result = API::moduleImport("openai").getInstance()
or
// Named import: import { OpenAI, AzureOpenAI } from 'openai'; new AzureOpenAI()
result = API::moduleImport("openai").getMember(["OpenAI", "AzureOpenAI"]).getInstance()
}
/** Gets a reference to a potential property of `openai.OpenAI` called instructions which refers to the system prompt. */
API::Node getContentNode() {
// responses.create({ input: ..., instructions: ... })
// input can be a string or an array of message objects
exists(API::Node responsesCreate |
responsesCreate =
classRef()
.getMember("responses")
.getMember("create")
.getParameter(0)
|
// instructions: "string"
result = responsesCreate.getMember("instructions")
// intended that user data can flow into input
// or
// // input: "string"
// result = responsesCreate.getMember("input")
or
// input: [{ role: "system"/"developer", content: "..." }]
exists(API::Node msg |
msg = responsesCreate.getMember("input").getArrayElement() and
isSystemOrDevMessage(msg)
|
result = msg.getMember("content")
)
)
or
// chat.completions.create({ messages: [{ role: "system"/"developer", content: ... }] })
// content can be a string or an array of content parts
exists(API::Node msg, API::Node content |
msg =
classRef()
.getMember("chat")
.getMember("completions")
.getMember("create")
.getParameter(0)
.getMember("messages")
.getArrayElement() and
isSystemOrDevMessage(msg) and
content = msg.getMember("content")
|
// content: "string"
result = content
or
// content: [{ type: "text", text: "..." }]
result = content.getArrayElement().getMember("text")
)
or
// Legacy completions API: completions.create({ prompt: ... })
result =
classRef()
.getMember("completions")
.getMember("create")
.getParameter(0)
.getMember("prompt")
or
// images.generate({ prompt: ... }) and images.edit({ prompt: ... })
result =
classRef()
.getMember("images")
.getMember(["generate", "edit"])
.getParameter(0)
.getMember("prompt")
or
// embeddings.create({ input: ... })
result =
classRef()
.getMember("embeddings")
.getMember("create")
.getParameter(0)
.getMember("input")
or
// beta.assistants.create({ instructions: ... }) and beta.assistants.update(id, { instructions: ... })
result =
classRef()
.getMember("beta")
.getMember("assistants")
.getMember(["create", "update"])
.getParameter(0)
.getMember("instructions")
or
// beta.threads.runs.create(threadId, { instructions: ..., additional_instructions: ... })
result =
classRef()
.getMember("beta")
.getMember("threads")
.getMember("runs")
.getMember("create")
.getParameter(1)
.getMember(["instructions", "additional_instructions"])
or
// beta.threads.messages.create(threadId, { role: "system"/"developer", content: ... })
exists(API::Node msg |
msg =
classRef()
.getMember("beta")
.getMember("threads")
.getMember("messages")
.getMember("create")
.getParameter(1) and
isSystemOrDevMessage(msg)
|
result = msg.getMember("content")
)
or
// audio.transcriptions.create({ prompt: ... }) and audio.translations.create({ prompt: ... })
result =
classRef()
.getMember("audio")
.getMember(["transcriptions", "translations"])
.getMember("create")
.getParameter(0)
.getMember("prompt")
}
}
/**
* Provides models for agents SDK (instances of the `agents` class etc).
*
* See https://github.com/openai/openai-agents-js.
*/
module AgentSDK {
API::Node moduleRef() { result = API::moduleImport("@openai/agents") }
/** Gets a reference to the `agents.Runner` class. */
API::Node agentConstructor() { result = moduleRef().getMember("Agent") }
API::Node classInstance() { result = agentConstructor().getInstance() }
/** Gets a reference to the top-level run() or Runner.run() functions. */
API::Node run() {
// import { run } from '@openai/agents'; run(agent, input)
result = moduleRef().getMember("run")
or
// const runner = new Runner(); runner.run(agent, input)
result = moduleRef().getMember("Runner").getInstance().getMember("run")
}
API::Node asTool() { result = classInstance().getMember("asTool")}
API::Node toolFunction() { result = moduleRef().getMember("tool") }
/** Gets a reference to a potential property of `agents.Runner` called input which can refer to a system prompt depending on the role specified. */
API::Node getContentNode() {
// Agent({ instructions: ... })
result = agentConstructor()
.getParameter(0)
.getMember(["instructions", "handoffDescription"])
or
// Agent({ instructions: (runContext) => returnValue })
result = agentConstructor()
.getParameter(0)
.getMember("instructions")
.getReturn()
or
// run(agent, input) or runner.run(agent, input) — string input
result = run()
.getParameter(1)
or
// run(agent, [{ role: "system"/"developer", content: ... }])
exists(API::Node msg |
msg = run()
.getParameter(1)
.getArrayElement() and
isSystemOrDevMessage(msg)
|
result = msg.getMember("content")
)
or
// agent.asTool({..., toolDescription: ...})
result = asTool().getParameter(0).getMember("toolDescription")
or
// tool({..., description: ...})
result = toolFunction().getParameter(0).getMember("description")
}
}

View File

@@ -0,0 +1,93 @@
/**
* Provides default sources, sinks and sanitizers for detecting
* "prompt injection"
* vulnerabilities, as well as extension points for adding your own.
*/
import javascript
private import semmle.javascript.dataflow.DataFlow
private import semmle.javascript.Concepts
private import semmle.javascript.security.dataflow.RemoteFlowSources
private import semmle.javascript.dataflow.internal.BarrierGuards
private import semmle.javascript.frameworks.data.ModelsAsData
private import experimental.semmle.javascript.frameworks.OpenAI
private import experimental.semmle.javascript.frameworks.Anthropic
private import experimental.semmle.javascript.frameworks.GoogleGenAI
/**
* Provides default sources, sinks and sanitizers for detecting
* "prompt injection"
* vulnerabilities, as well as extension points for adding your own.
*/
module PromptInjection {
/**
* A data flow source for "prompt injection" vulnerabilities.
*/
abstract class Source extends DataFlow::Node { }
/**
* A data flow sink for "prompt injection" vulnerabilities.
*/
abstract class Sink extends DataFlow::Node { }
/**
* A sanitizer for "prompt injection" vulnerabilities.
*/
abstract class Sanitizer extends DataFlow::Node { }
/**
* An active threat-model source, considered as a flow source.
*/
private class ActiveThreatModelSourceAsSource extends Source, ActiveThreatModelSource { }
/**
* A prompt to an AI model, considered as a flow sink.
*/
class AIPromptAsSink extends Sink {
AIPromptAsSink() { this = any(AIPrompt p).getAPrompt() }
}
private class SinkFromModel extends Sink {
SinkFromModel() { this = ModelOutput::getASinkNode("prompt-injection").asSink() }
}
private class PromptContentSink extends Sink {
PromptContentSink() {
this = OpenAI::getContentNode().asSink()
or
this = AgentSDK::getContentNode().asSink()
or
this = Anthropic::getContentNode().asSink()
or
this = GoogleGenAI::getContentNode().asSink()
}
}
private class ConstCompareAsSanitizerGuard extends Sanitizer {
ConstCompareAsSanitizerGuard()
{
this = DataFlow::MakeBarrierGuard<ConstCompareBarrierGuard>::getABarrierNode()
}
}
/**
* A comparison with a constant, considered as a sanitizer-guard.
*/
private class ConstCompareBarrierGuard extends DataFlow::ValueNode
{
override EqualityTest astNode;
ConstCompareBarrierGuard()
{
astNode.hasOperands(_, any(ConstantString cs))
}
predicate blocksExpr(boolean outcome, Expr e) {
outcome = astNode.getPolarity() and
e = astNode.getLeftOperand() and
e = astNode.getAnOperand() and
not e instanceof ConstantString
}
}
}

View File

@@ -0,0 +1,25 @@
/**
* Provides a taint-tracking configuration for detecting "prompt injection" vulnerabilities.
*
* Note, for performance reasons: only import this file if
* `PromptInjection::Configuration` is needed, otherwise
* `PromptInjectionCustomizations` should be imported instead.
*/
private import javascript
import semmle.javascript.dataflow.DataFlow
import semmle.javascript.dataflow.TaintTracking
import PromptInjectionCustomizations::PromptInjection
private module PromptInjectionConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node node) { node instanceof Source }
predicate isSink(DataFlow::Node node) { node instanceof Sink }
predicate isBarrier(DataFlow::Node node) { node instanceof Sanitizer }
predicate observeDiffInformedIncrementalMode() { any() }
}
/** Global taint-tracking for detecting "prompt injection" vulnerabilities. */
module PromptInjectionFlow = TaintTracking::Global<PromptInjectionConfig>;

View File

@@ -0,0 +1,245 @@
edges
| agents_test.js:8:9:8:15 | persona | agents_test.js:16:36:16:42 | persona | provenance | |
| agents_test.js:8:9:8:15 | persona | agents_test.js:43:38:43:44 | persona | provenance | |
| agents_test.js:8:9:8:15 | persona | agents_test.js:51:37:51:43 | persona | provenance | |
| agents_test.js:8:9:8:15 | persona | agents_test.js:59:42:59:48 | persona | provenance | |
| agents_test.js:8:9:8:15 | persona | agents_test.js:73:49:73:55 | persona | provenance | |
| agents_test.js:8:9:8:15 | persona | agents_test.js:81:52:81:58 | persona | provenance | |
| agents_test.js:8:9:8:15 | persona | agents_test.js:96:49:96:55 | persona | provenance | |
| agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:8:9:8:15 | persona | provenance | |
| agents_test.js:9:9:9:13 | query | agents_test.js:67:32:67:36 | query | provenance | |
| agents_test.js:9:17:9:31 | req.query.query | agents_test.js:9:9:9:13 | query | provenance | |
| agents_test.js:16:36:16:42 | persona | agents_test.js:16:19:16:42 | "Talk l ... persona | provenance | |
| agents_test.js:16:36:16:42 | persona | agents_test.js:25:31:25:37 | persona | provenance | |
| agents_test.js:16:36:16:42 | persona | agents_test.js:33:31:33:37 | persona | provenance | |
| agents_test.js:16:36:16:42 | persona | agents_test.js:43:38:43:44 | persona | provenance | |
| agents_test.js:25:31:25:37 | persona | agents_test.js:25:14:25:37 | "Talk l ... persona | provenance | |
| agents_test.js:33:14:33:37 | "Talk l ... persona | agents_test.js:32:19:34:5 | return of method instructions | provenance | |
| agents_test.js:33:31:33:37 | persona | agents_test.js:33:14:33:37 | "Talk l ... persona | provenance | |
| agents_test.js:43:38:43:44 | persona | agents_test.js:43:25:43:44 | "Handles " + persona | provenance | |
| agents_test.js:43:38:43:44 | persona | agents_test.js:51:37:51:43 | persona | provenance | |
| agents_test.js:51:37:51:43 | persona | agents_test.js:51:22:51:43 | "Ask ab ... persona | provenance | |
| agents_test.js:51:37:51:43 | persona | agents_test.js:59:42:59:48 | persona | provenance | |
| agents_test.js:59:42:59:48 | persona | agents_test.js:59:18:59:48 | "Look u ... persona | provenance | |
| agents_test.js:59:42:59:48 | persona | agents_test.js:73:49:73:55 | persona | provenance | |
| agents_test.js:73:49:73:55 | persona | agents_test.js:73:32:73:55 | "Talk l ... persona | provenance | |
| agents_test.js:73:49:73:55 | persona | agents_test.js:81:52:81:58 | persona | provenance | |
| agents_test.js:81:52:81:58 | persona | agents_test.js:81:35:81:58 | "Talk l ... persona | provenance | |
| agents_test.js:81:52:81:58 | persona | agents_test.js:96:49:96:55 | persona | provenance | |
| agents_test.js:96:49:96:55 | persona | agents_test.js:96:32:96:55 | "Talk l ... persona | provenance | |
| anthropic_test.js:8:9:8:15 | persona | anthropic_test.js:17:30:17:36 | persona | provenance | |
| anthropic_test.js:8:9:8:15 | persona | anthropic_test.js:30:32:30:38 | persona | provenance | |
| anthropic_test.js:8:9:8:15 | persona | anthropic_test.js:45:35:45:41 | persona | provenance | |
| anthropic_test.js:8:9:8:15 | persona | anthropic_test.js:71:30:71:36 | persona | provenance | |
| anthropic_test.js:8:9:8:15 | persona | anthropic_test.js:84:32:84:38 | persona | provenance | |
| anthropic_test.js:8:9:8:15 | persona | anthropic_test.js:99:35:99:41 | persona | provenance | |
| anthropic_test.js:8:9:8:15 | persona | anthropic_test.js:110:30:110:36 | persona | provenance | |
| anthropic_test.js:8:9:8:15 | persona | anthropic_test.js:117:30:117:36 | persona | provenance | |
| anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:8:9:8:15 | persona | provenance | |
| anthropic_test.js:17:30:17:36 | persona | anthropic_test.js:17:13:17:36 | "Talk l ... persona | provenance | |
| anthropic_test.js:30:32:30:38 | persona | anthropic_test.js:30:15:30:38 | "Talk l ... persona | provenance | |
| anthropic_test.js:45:35:45:41 | persona | anthropic_test.js:45:18:45:41 | "Talk l ... persona | provenance | |
| anthropic_test.js:71:30:71:36 | persona | anthropic_test.js:71:13:71:36 | "Talk l ... persona | provenance | |
| anthropic_test.js:84:32:84:38 | persona | anthropic_test.js:84:15:84:38 | "Talk l ... persona | provenance | |
| anthropic_test.js:99:35:99:41 | persona | anthropic_test.js:99:18:99:41 | "Talk l ... persona | provenance | |
| anthropic_test.js:110:30:110:36 | persona | anthropic_test.js:110:13:110:36 | "Talk l ... persona | provenance | |
| anthropic_test.js:117:30:117:36 | persona | anthropic_test.js:117:13:117:36 | "Talk l ... persona | provenance | |
| gemini_test.js:8:9:8:15 | persona | gemini_test.js:18:43:18:49 | persona | provenance | |
| gemini_test.js:8:9:8:15 | persona | gemini_test.js:30:42:30:48 | persona | provenance | |
| gemini_test.js:8:9:8:15 | persona | gemini_test.js:59:43:59:49 | persona | provenance | |
| gemini_test.js:8:9:8:15 | persona | gemini_test.js:68:36:68:42 | persona | provenance | |
| gemini_test.js:8:9:8:15 | persona | gemini_test.js:76:36:76:42 | persona | provenance | |
| gemini_test.js:8:9:8:15 | persona | gemini_test.js:85:43:85:49 | persona | provenance | |
| gemini_test.js:8:9:8:15 | persona | gemini_test.js:95:43:95:49 | persona | provenance | |
| gemini_test.js:8:9:8:15 | persona | gemini_test.js:105:43:105:49 | persona | provenance | |
| gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:8:9:8:15 | persona | provenance | |
| gemini_test.js:18:43:18:49 | persona | gemini_test.js:18:26:18:49 | "Talk l ... persona | provenance | |
| gemini_test.js:30:42:30:48 | persona | gemini_test.js:30:25:30:48 | "Talk l ... persona | provenance | |
| gemini_test.js:59:43:59:49 | persona | gemini_test.js:59:26:59:49 | "Talk l ... persona | provenance | |
| gemini_test.js:68:36:68:42 | persona | gemini_test.js:68:13:68:42 | "Draw a ... persona | provenance | |
| gemini_test.js:76:36:76:42 | persona | gemini_test.js:76:13:76:42 | "Edit t ... persona | provenance | |
| gemini_test.js:85:43:85:49 | persona | gemini_test.js:85:26:85:49 | "Talk l ... persona | provenance | |
| gemini_test.js:95:43:95:49 | persona | gemini_test.js:95:26:95:49 | "Talk l ... persona | provenance | |
| gemini_test.js:105:43:105:49 | persona | gemini_test.js:105:26:105:49 | "Talk l ... persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:19:36:19:42 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:29:35:29:41 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:44:35:44:41 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:68:35:68:41 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:83:35:83:41 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:97:36:97:42 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:110:35:110:41 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:120:30:120:36 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:127:36:127:42 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:132:36:132:42 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:140:29:140:35 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:149:36:149:42 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:160:36:160:42 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:166:52:166:58 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:172:31:172:37 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:187:35:187:41 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:194:34:194:40 | persona | provenance | |
| openai_test.js:11:9:11:15 | persona | openai_test.js:200:49:200:55 | persona | provenance | |
| openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:11:9:11:15 | persona | provenance | |
| openai_test.js:19:36:19:42 | persona | openai_test.js:19:19:19:42 | "Talk l ... persona | provenance | |
| openai_test.js:29:35:29:41 | persona | openai_test.js:29:18:29:41 | "Talk l ... persona | provenance | |
| openai_test.js:44:35:44:41 | persona | openai_test.js:44:18:44:41 | "Talk l ... persona | provenance | |
| openai_test.js:68:35:68:41 | persona | openai_test.js:68:18:68:41 | "Talk l ... persona | provenance | |
| openai_test.js:83:35:83:41 | persona | openai_test.js:83:18:83:41 | "Talk l ... persona | provenance | |
| openai_test.js:97:36:97:42 | persona | openai_test.js:97:19:97:42 | "Talk l ... persona | provenance | |
| openai_test.js:110:35:110:41 | persona | openai_test.js:110:18:110:41 | "Talk l ... persona | provenance | |
| openai_test.js:120:30:120:36 | persona | openai_test.js:120:13:120:36 | "Talk l ... persona | provenance | |
| openai_test.js:127:36:127:42 | persona | openai_test.js:127:13:127:42 | "Draw a ... persona | provenance | |
| openai_test.js:132:36:132:42 | persona | openai_test.js:132:13:132:42 | "Edit t ... persona | provenance | |
| openai_test.js:140:29:140:35 | persona | openai_test.js:140:12:140:35 | "Embed ... persona | provenance | |
| openai_test.js:149:36:149:42 | persona | openai_test.js:149:19:149:42 | "Talk l ... persona | provenance | |
| openai_test.js:160:36:160:42 | persona | openai_test.js:160:19:160:42 | "Talk l ... persona | provenance | |
| openai_test.js:166:52:166:58 | persona | openai_test.js:166:30:166:58 | "Also t ... persona | provenance | |
| openai_test.js:172:31:172:37 | persona | openai_test.js:172:14:172:37 | "Talk l ... persona | provenance | |
| openai_test.js:187:35:187:41 | persona | openai_test.js:187:13:187:41 | "Transc ... persona | provenance | |
| openai_test.js:194:34:194:40 | persona | openai_test.js:194:13:194:40 | "Transl ... persona | provenance | |
| openai_test.js:200:49:200:55 | persona | openai_test.js:200:32:200:55 | "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 |
| agents_test.js:9:9:9:13 | query | semmle.label | query |
| agents_test.js:9:17:9:31 | req.query.query | semmle.label | req.query.query |
| agents_test.js:16:19:16:42 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| agents_test.js:16:36:16:42 | persona | semmle.label | persona |
| agents_test.js:25:14:25:37 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| agents_test.js:25:31:25:37 | persona | semmle.label | persona |
| agents_test.js:32:19:34:5 | return of method instructions | semmle.label | return of method instructions |
| agents_test.js:33:14:33:37 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| agents_test.js:33:31:33:37 | persona | semmle.label | persona |
| agents_test.js:43:25:43:44 | "Handles " + persona | semmle.label | "Handles " + persona |
| agents_test.js:43:38:43:44 | persona | semmle.label | persona |
| agents_test.js:51:22:51:43 | "Ask ab ... persona | semmle.label | "Ask ab ... persona |
| agents_test.js:51:37:51:43 | persona | semmle.label | persona |
| agents_test.js:59:18:59:48 | "Look u ... persona | semmle.label | "Look u ... persona |
| agents_test.js:59:42:59:48 | persona | semmle.label | persona |
| agents_test.js:67:32:67:36 | query | semmle.label | query |
| agents_test.js:73:32:73:55 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| agents_test.js:73:49:73:55 | persona | semmle.label | persona |
| agents_test.js:81:35:81:58 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| agents_test.js:81:52:81:58 | persona | semmle.label | persona |
| agents_test.js:96:32:96:55 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| agents_test.js:96:49:96:55 | persona | semmle.label | persona |
| anthropic_test.js:8:9:8:15 | persona | semmle.label | persona |
| anthropic_test.js:8:19:8:35 | req.query.persona | semmle.label | req.query.persona |
| anthropic_test.js:17:13:17:36 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| anthropic_test.js:17:30:17:36 | persona | semmle.label | persona |
| anthropic_test.js:30:15:30:38 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| anthropic_test.js:30:32:30:38 | persona | semmle.label | persona |
| anthropic_test.js:45:18:45:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| anthropic_test.js:45:35:45:41 | persona | semmle.label | persona |
| anthropic_test.js:71:13:71:36 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| anthropic_test.js:71:30:71:36 | persona | semmle.label | persona |
| anthropic_test.js:84:15:84:38 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| anthropic_test.js:84:32:84:38 | persona | semmle.label | persona |
| anthropic_test.js:99:18:99:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| anthropic_test.js:99:35:99:41 | persona | semmle.label | persona |
| anthropic_test.js:110:13:110:36 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| anthropic_test.js:110:30:110:36 | persona | semmle.label | persona |
| anthropic_test.js:117:13:117:36 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| anthropic_test.js:117:30:117:36 | persona | semmle.label | persona |
| gemini_test.js:8:9:8:15 | persona | semmle.label | persona |
| gemini_test.js:8:19:8:35 | req.query.persona | semmle.label | req.query.persona |
| gemini_test.js:18:26:18:49 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| gemini_test.js:18:43:18:49 | persona | semmle.label | persona |
| gemini_test.js:30:25:30:48 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| gemini_test.js:30:42:30:48 | persona | semmle.label | persona |
| gemini_test.js:59:26:59:49 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| gemini_test.js:59:43:59:49 | persona | semmle.label | persona |
| gemini_test.js:68:13:68:42 | "Draw a ... persona | semmle.label | "Draw a ... persona |
| gemini_test.js:68:36:68:42 | persona | semmle.label | persona |
| gemini_test.js:76:13:76:42 | "Edit t ... persona | semmle.label | "Edit t ... persona |
| gemini_test.js:76:36:76:42 | persona | semmle.label | persona |
| gemini_test.js:85:26:85:49 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| gemini_test.js:85:43:85:49 | persona | semmle.label | persona |
| gemini_test.js:95:26:95:49 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| gemini_test.js:95:43:95:49 | persona | semmle.label | persona |
| gemini_test.js:105:26:105:49 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| gemini_test.js:105:43:105:49 | persona | semmle.label | persona |
| openai_test.js:11:9:11:15 | persona | semmle.label | persona |
| openai_test.js:11:19:11:35 | req.query.persona | semmle.label | req.query.persona |
| openai_test.js:19:19:19:42 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:19:36:19:42 | persona | semmle.label | persona |
| openai_test.js:29:18:29:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:29:35:29:41 | persona | semmle.label | persona |
| openai_test.js:44:18:44:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:44:35:44:41 | persona | semmle.label | persona |
| openai_test.js:68:18:68:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:68:35:68:41 | persona | semmle.label | persona |
| openai_test.js:83:18:83:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:83:35:83:41 | persona | semmle.label | persona |
| openai_test.js:97:19:97:42 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:97:36:97:42 | persona | semmle.label | persona |
| openai_test.js:110:18:110:41 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:110:35:110:41 | persona | semmle.label | persona |
| openai_test.js:120:13:120:36 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:120:30:120:36 | persona | semmle.label | persona |
| openai_test.js:127:13:127:42 | "Draw a ... persona | semmle.label | "Draw a ... persona |
| openai_test.js:127:36:127:42 | persona | semmle.label | persona |
| openai_test.js:132:13:132:42 | "Edit t ... persona | semmle.label | "Edit t ... persona |
| openai_test.js:132:36:132:42 | persona | semmle.label | persona |
| openai_test.js:140:12:140:35 | "Embed ... persona | semmle.label | "Embed ... persona |
| openai_test.js:140:29:140:35 | persona | semmle.label | persona |
| openai_test.js:149:19:149:42 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:149:36:149:42 | persona | semmle.label | persona |
| openai_test.js:160:19:160:42 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:160:36:160:42 | persona | semmle.label | persona |
| openai_test.js:166:30:166:58 | "Also t ... persona | semmle.label | "Also t ... persona |
| openai_test.js:166:52:166:58 | persona | semmle.label | persona |
| openai_test.js:172:14:172:37 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:172:31:172:37 | persona | semmle.label | persona |
| openai_test.js:187:13:187:41 | "Transc ... persona | semmle.label | "Transc ... persona |
| openai_test.js:187:35:187:41 | persona | semmle.label | persona |
| openai_test.js:194:13:194:40 | "Transl ... persona | semmle.label | "Transl ... persona |
| openai_test.js:194:34:194:40 | persona | semmle.label | persona |
| openai_test.js:200:32:200:55 | "Talk l ... persona | semmle.label | "Talk l ... persona |
| openai_test.js:200:49:200:55 | 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 |
| agents_test.js:25:14:25:37 | "Talk l ... persona | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:25:14:25:37 | "Talk l ... persona | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
| agents_test.js:32:19:34:5 | return of method instructions | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:32:19:34:5 | return of method instructions | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
| agents_test.js:43:25:43:44 | "Handles " + persona | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:43:25:43:44 | "Handles " + persona | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
| agents_test.js:51:22:51:43 | "Ask ab ... persona | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:51:22:51:43 | "Ask ab ... persona | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
| agents_test.js:59:18:59:48 | "Look u ... persona | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:59:18:59:48 | "Look u ... persona | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
| agents_test.js:67:32:67:36 | query | agents_test.js:9:17:9:31 | req.query.query | agents_test.js:67:32:67:36 | query | This prompt construction depends on a $@. | agents_test.js:9:17:9:31 | req.query.query | user-provided value |
| agents_test.js:73:32:73:55 | "Talk l ... persona | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:73:32:73:55 | "Talk l ... persona | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
| agents_test.js:81:35:81:58 | "Talk l ... persona | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:81:35:81:58 | "Talk l ... persona | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
| agents_test.js:96:32:96:55 | "Talk l ... persona | agents_test.js:8:19:8:35 | req.query.persona | agents_test.js:96:32:96:55 | "Talk l ... persona | This prompt construction depends on a $@. | agents_test.js:8:19:8:35 | req.query.persona | user-provided value |
| anthropic_test.js:17:13:17:36 | "Talk l ... persona | anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:17:13:17:36 | "Talk l ... persona | This prompt construction depends on a $@. | anthropic_test.js:8:19:8:35 | req.query.persona | user-provided value |
| anthropic_test.js:30:15:30:38 | "Talk l ... persona | anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:30:15:30:38 | "Talk l ... persona | This prompt construction depends on a $@. | anthropic_test.js:8:19:8:35 | req.query.persona | user-provided value |
| anthropic_test.js:45:18:45:41 | "Talk l ... persona | anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:45:18:45:41 | "Talk l ... persona | This prompt construction depends on a $@. | anthropic_test.js:8:19:8:35 | req.query.persona | user-provided value |
| anthropic_test.js:71:13:71:36 | "Talk l ... persona | anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:71:13:71:36 | "Talk l ... persona | This prompt construction depends on a $@. | anthropic_test.js:8:19:8:35 | req.query.persona | user-provided value |
| anthropic_test.js:84:15:84:38 | "Talk l ... persona | anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:84:15:84:38 | "Talk l ... persona | This prompt construction depends on a $@. | anthropic_test.js:8:19:8:35 | req.query.persona | user-provided value |
| anthropic_test.js:99:18:99:41 | "Talk l ... persona | anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:99:18:99:41 | "Talk l ... persona | This prompt construction depends on a $@. | anthropic_test.js:8:19:8:35 | req.query.persona | user-provided value |
| anthropic_test.js:110:13:110:36 | "Talk l ... persona | anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:110:13:110:36 | "Talk l ... persona | This prompt construction depends on a $@. | anthropic_test.js:8:19:8:35 | req.query.persona | user-provided value |
| anthropic_test.js:117:13:117:36 | "Talk l ... persona | anthropic_test.js:8:19:8:35 | req.query.persona | anthropic_test.js:117:13:117:36 | "Talk l ... persona | This prompt construction depends on a $@. | anthropic_test.js:8:19:8:35 | req.query.persona | user-provided value |
| gemini_test.js:18:26:18:49 | "Talk l ... persona | gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:18:26:18:49 | "Talk l ... persona | This prompt construction depends on a $@. | gemini_test.js:8:19:8:35 | req.query.persona | user-provided value |
| gemini_test.js:30:25:30:48 | "Talk l ... persona | gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:30:25:30:48 | "Talk l ... persona | This prompt construction depends on a $@. | gemini_test.js:8:19:8:35 | req.query.persona | user-provided value |
| gemini_test.js:59:26:59:49 | "Talk l ... persona | gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:59:26:59:49 | "Talk l ... persona | This prompt construction depends on a $@. | gemini_test.js:8:19:8:35 | req.query.persona | user-provided value |
| gemini_test.js:68:13:68:42 | "Draw a ... persona | gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:68:13:68:42 | "Draw a ... persona | This prompt construction depends on a $@. | gemini_test.js:8:19:8:35 | req.query.persona | user-provided value |
| gemini_test.js:76:13:76:42 | "Edit t ... persona | gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:76:13:76:42 | "Edit t ... persona | This prompt construction depends on a $@. | gemini_test.js:8:19:8:35 | req.query.persona | user-provided value |
| gemini_test.js:85:26:85:49 | "Talk l ... persona | gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:85:26:85:49 | "Talk l ... persona | This prompt construction depends on a $@. | gemini_test.js:8:19:8:35 | req.query.persona | user-provided value |
| gemini_test.js:95:26:95:49 | "Talk l ... persona | gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:95:26:95:49 | "Talk l ... persona | This prompt construction depends on a $@. | gemini_test.js:8:19:8:35 | req.query.persona | user-provided value |
| gemini_test.js:105:26:105:49 | "Talk l ... persona | gemini_test.js:8:19:8:35 | req.query.persona | gemini_test.js:105:26:105:49 | "Talk l ... persona | This prompt construction depends on a $@. | gemini_test.js:8:19:8:35 | req.query.persona | user-provided value |
| openai_test.js:19:19:19:42 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:19:19:19:42 | "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:29:18:29:41 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:29:18:29:41 | "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:44:18:44:41 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:44:18:44:41 | "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:68:18:68:41 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:68:18:68:41 | "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:83:18:83:41 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:83:18:83:41 | "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:97:19:97:42 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:97:19:97:42 | "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:110:18:110:41 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:110:18:110:41 | "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:120:13:120:36 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:120:13:120:36 | "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:127:13:127:42 | "Draw a ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:127:13:127:42 | "Draw a ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |
| openai_test.js:132:13:132:42 | "Edit t ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:132:13:132:42 | "Edit t ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |
| openai_test.js:140:12:140:35 | "Embed ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:140:12:140:35 | "Embed ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |
| openai_test.js:149:19:149:42 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:149:19:149:42 | "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:160:19:160:42 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:160:19:160:42 | "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:166:30:166:58 | "Also t ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:166:30:166: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:172:14:172:37 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:172:14:172: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:187:13:187:41 | "Transc ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:187:13:187:41 | "Transc ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |
| openai_test.js:194:13:194:40 | "Transl ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:194:13:194:40 | "Transl ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |
| openai_test.js:200:32:200:55 | "Talk l ... persona | openai_test.js:11:19:11:35 | req.query.persona | openai_test.js:200:32:200:55 | "Talk l ... persona | This prompt construction depends on a $@. | openai_test.js:11:19:11:35 | req.query.persona | user-provided value |

View File

@@ -0,0 +1 @@
./experimental/Security/CWE-1427/PromptInjection.ql

View File

@@ -0,0 +1,110 @@
const express = require("express");
const { Agent, run, Runner, tool } = require("@openai/agents");
const { z } = require("zod");
const app = express();
app.get("/agents", async (req, res) => {
const persona = req.query.persona;
const query = req.query.query;
// === Agent constructor: instructions as string ===
// SHOULD ALERT
const agent1 = new Agent({
name: "Assistant",
instructions: "Talk like a " + persona, // $ Alert[js/prompt-injection]
});
// === Agent constructor: instructions as lambda ===
// SHOULD ALERT
const agent2 = new Agent({
name: "Dynamic",
instructions: (runContext) => {
return "Talk like a " + persona; // $ Alert[js/prompt-injection]
},
});
// SHOULD ALERT (async lambda)
const agent3 = new Agent({
name: "AsyncDynamic",
instructions: async (runContext) => {
return "Talk like a " + persona; // $ Alert[js/prompt-injection]
},
});
// === Agent constructor: handoffDescription ===
// SHOULD ALERT
const agent4 = new Agent({
name: "Specialist",
instructions: "Help with refunds",
handoffDescription: "Handles " + persona, // $ Alert[js/prompt-injection]
});
// === agent.asTool(): toolDescription ===
// SHOULD ALERT
agent1.asTool({
toolName: "helper",
toolDescription: "Ask about " + persona, // $ Alert[js/prompt-injection]
});
// === tool(): description ===
// SHOULD ALERT
const myTool = tool({
name: "lookup",
description: "Look up info about " + persona, // $ Alert[js/prompt-injection]
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => "result",
});
// === run() with string input ===
// SHOULD ALERT - string input to run() is used as a prompt
const r1 = await run(agent1, query); // $ Alert[js/prompt-injection]
// === run() with array input: system role ===
// SHOULD ALERT
const r2 = await run(agent1, [
{ role: "system", content: "Talk like a " + persona }, // $ Alert[js/prompt-injection]
{ role: "user", content: query },
]);
// === run() with array input: developer role ===
// SHOULD ALERT
const r3 = await run(agent1, [
{ role: "developer", content: "Talk like a " + persona }, // $ Alert[js/prompt-injection]
]);
// === run() with array input: user role ===
// SHOULD NOT ALERT
const r4 = await run(agent1, [
{ role: "user", content: query }, // OK - user role
]);
// === Runner instance: run() with system role ===
// SHOULD ALERT
const runner = new Runner();
const r5 = await runner.run(agent1, [
{ role: "system", content: "Talk like a " + persona }, // $ Alert[js/prompt-injection]
]);
// === Sanitizer: constant comparison ===
// SHOULD NOT ALERT
if (persona === "pirate") {
const agent5 = new Agent({
name: "Pirate",
instructions: "Talk like a " + persona, // OK - sanitized by constant check
});
}
res.send("done");
});

View File

@@ -0,0 +1,133 @@
const express = require("express");
const Anthropic = require("@anthropic-ai/sdk");
const app = express();
const client = new Anthropic();
app.get("/test", async (req, res) => {
const persona = req.query.persona;
const query = req.query.query;
// === messages.create: system as string ===
// SHOULD ALERT
const m1 = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: "Talk like a " + persona, // $ Alert[js/prompt-injection]
messages: [{ role: "user", content: query }],
});
// === messages.create: system as TextBlockParam array ===
// SHOULD ALERT
const m2 = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: [
{
type: "text",
text: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
],
messages: [{ role: "user", content: query }],
});
// === messages.create: assistant role content ===
// SHOULD ALERT
const m3 = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [
{
role: "assistant",
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
{ role: "user", content: query },
],
});
// === messages.create: user role content ===
// SHOULD NOT ALERT
const m4 = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [
{
role: "user",
content: query, // OK - user role
},
],
});
// === beta.messages.create: system as string ===
// SHOULD ALERT
const bm1 = await client.beta.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: "Talk like a " + persona, // $ Alert[js/prompt-injection]
messages: [{ role: "user", content: query }],
});
// === beta.messages.create: system as TextBlockParam array ===
// SHOULD ALERT
const bm2 = await client.beta.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: [
{
type: "text",
text: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
],
messages: [{ role: "user", content: query }],
});
// === beta.messages.create: assistant role content ===
// SHOULD ALERT
const bm3 = await client.beta.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
messages: [
{
role: "assistant",
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
{ role: "user", content: query },
],
});
// === beta.agents.create: system ===
// SHOULD ALERT
const ba1 = await client.beta.agents.create({
model: "claude-sonnet-4-20250514",
system: "Talk like a " + persona, // $ Alert[js/prompt-injection]
});
// === beta.agents.update: system ===
// SHOULD ALERT
await client.beta.agents.update("agent_123", {
system: "Talk like a " + persona, // $ Alert[js/prompt-injection]
});
// === Sanitizer: constant comparison ===
// SHOULD NOT ALERT
if (persona === "pirate") {
const m5 = await client.messages.create({
model: "claude-sonnet-4-20250514",
max_tokens: 1024,
system: "Talk like a " + persona, // OK - sanitized by constant check
messages: [{ role: "user", content: query }],
});
}
res.send("done");
});

View File

@@ -0,0 +1,126 @@
const express = require("express");
const { GoogleGenAI } = require("@google/genai");
const app = express();
const ai = new GoogleGenAI({ apiKey: "test-key" });
app.get("/test", async (req, res) => {
const persona = req.query.persona;
const query = req.query.query;
// === generateContent: systemInstruction ===
// SHOULD ALERT
const g1 = await ai.models.generateContent({
model: "gemini-2.0-flash",
contents: "Hello",
config: {
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
});
// === generateContent: contents with model role ===
// SHOULD ALERT
const g2 = await ai.models.generateContent({
model: "gemini-2.0-flash",
contents: [
{
role: "model",
parts: [{ text: "Talk like a " + persona }], // $ Alert[js/prompt-injection]
},
{
role: "user",
parts: [{ text: query }],
},
],
});
// === generateContent: contents with user role ===
// SHOULD NOT ALERT
const g3 = await ai.models.generateContent({
model: "gemini-2.0-flash",
contents: [
{
role: "user",
parts: [{ text: query }], // OK - user role
},
],
});
// === generateContentStream: systemInstruction ===
// SHOULD ALERT
const g4 = await ai.models.generateContentStream({
model: "gemini-2.0-flash",
contents: "Hello",
config: {
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
});
// === generateImages: prompt ===
// SHOULD ALERT
const g5 = await ai.models.generateImages({
model: "imagen-3.0-generate-002",
prompt: "Draw a picture of " + persona, // $ Alert[js/prompt-injection]
});
// === editImage: prompt ===
// SHOULD ALERT
const g6 = await ai.models.editImage({
model: "imagen-3.0-capability-001",
prompt: "Edit to look like " + persona, // $ Alert[js/prompt-injection]
});
// === chats.create: systemInstruction ===
// SHOULD ALERT
const chat = ai.chats.create({
model: "gemini-2.0-flash",
config: {
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
});
// === chat.sendMessage: per-request systemInstruction ===
// SHOULD ALERT
await chat.sendMessage({
message: query,
config: {
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
});
// === live.connect: systemInstruction ===
// SHOULD ALERT
const session = await ai.live.connect({
model: "gemini-2.0-flash-live-001",
config: {
systemInstruction: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
callbacks: {
onmessage: (msg) => {},
},
});
// === Sanitizer: constant comparison ===
// SHOULD NOT ALERT
if (persona === "pirate") {
const g7 = await ai.models.generateContent({
model: "gemini-2.0-flash",
contents: "Hello",
config: {
systemInstruction: "Talk like a " + persona, // OK - sanitized by constant check
},
});
}
res.send("done");
});

View File

@@ -0,0 +1,215 @@
const express = require("express");
const OpenAI = require("openai");
const { AzureOpenAI } = require("openai");
const { Agent, run, Runner, tool } = require("@openai/agents");
const app = express();
const client = new OpenAI();
const azureClient = new AzureOpenAI();
app.get("/test", async (req, res) => {
const persona = req.query.persona;
const query = req.query.query;
// === OpenAI Responses API ===
// instructions: tainted string (SHOULD ALERT)
const r1 = await client.responses.create({
model: "gpt-4.1",
instructions: "Talk like a " + persona, // $ Alert[js/prompt-injection]
input: "Hello",
});
// input as array with system role (SHOULD ALERT)
const r2 = await client.responses.create({
model: "gpt-4.1",
input: [
{
role: "system",
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
{
role: "user",
content: query, // OK - user role
},
],
});
// input as array with developer role (SHOULD ALERT)
const r3 = await client.responses.create({
model: "gpt-4.1",
input: [
{
role: "developer",
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
],
});
// input as array with user role (SHOULD NOT ALERT)
const r4 = await client.responses.create({
model: "gpt-4.1",
input: [
{
role: "user",
content: query, // OK - user role is expected to carry user input
},
],
});
// === Chat Completions API ===
// messages with system role (SHOULD ALERT)
const c1 = await client.chat.completions.create({
model: "gpt-4.1",
messages: [
{
role: "system",
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
{
role: "user",
content: query, // OK - user role
},
],
});
// messages with developer role (SHOULD ALERT)
const c2 = await client.chat.completions.create({
model: "gpt-4.1",
messages: [
{
role: "developer",
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
],
});
// messages with content as array of content parts (SHOULD ALERT)
const c3 = await client.chat.completions.create({
model: "gpt-4.1",
messages: [
{
role: "system",
content: [
{
type: "text",
text: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
],
},
],
});
// Azure client (SHOULD ALERT)
const c4 = await azureClient.chat.completions.create({
model: "gpt-4.1",
messages: [
{
role: "developer",
content: "Talk like a " + persona, // $ Alert[js/prompt-injection]
},
],
});
// === Legacy Completions API ===
// prompt (SHOULD ALERT)
const l1 = await client.completions.create({
model: "gpt-3.5-turbo-instruct",
prompt: "Talk like a " + persona, // $ Alert[js/prompt-injection]
});
// === Images API ===
// images.generate (SHOULD ALERT)
const i1 = await client.images.generate({
prompt: "Draw a picture of " + persona, // $ Alert[js/prompt-injection]
});
// images.edit (SHOULD ALERT)
const i2 = await client.images.edit({
prompt: "Edit to look like " + persona, // $ Alert[js/prompt-injection]
});
// === Embeddings API ===
// embeddings.create (SHOULD ALERT)
const e1 = await client.embeddings.create({
model: "text-embedding-3-small",
input: "Embed this: " + persona, // $ Alert[js/prompt-injection]
});
// === Assistants API (beta) ===
// assistants.create (SHOULD ALERT)
const a1 = await client.beta.assistants.create({
name: "Test Agent",
model: "gpt-4.1",
instructions: "Talk like a " + persona, // $ Alert[js/prompt-injection]
});
// assistants.update (SHOULD ALERT)
await client.beta.assistants.update("asst_123", {
instructions: "Talk like a " + persona, // $ Alert[js/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]
});
// 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]
});
// 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]
});
// threads.messages.create with user role (SHOULD NOT ALERT)
await client.beta.threads.messages.create("thread_123", {
role: "user",
content: query, // OK - user role
});
// === Audio API ===
// audio.transcriptions.create (SHOULD ALERT)
const at1 = await client.audio.transcriptions.create({
file: "audio.mp3",
model: "whisper-1",
prompt: "Transcribe about " + persona, // $ Alert[js/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]
});
// === Object assigned to variable first ===
// Should still be caught via data flow
const opts = { instructions: "Talk like a " + persona }; // $ Alert[js/prompt-injection]
const r5 = await client.responses.create(opts);
// === Sanitizer: constant comparison ===
// Should NOT alert - guarded by constant comparison
if (persona === "pirate") {
const r6 = await client.responses.create({
model: "gpt-4.1",
instructions: "Talk like a " + persona, // OK - sanitized by constant check
input: "Hello",
});
}
res.send("done");
});