no-untrusted-content-in-prompt
Disallow untrusted content built into the Gemini system prompt
Disallow untrusted content built into the Gemini system prompt.
- CWE: CWE-1427 โ Improper Neutralization of Input Used for LLM Prompting
- OWASP: A03:2021 โ Injection ยท LLM Top 10: LLM01 Prompt Injection
- CVSS: 8.1 (High) ยท Preset:
strictonly
Why
A system prompt is instruction text. Whatever is spliced into it is read by the model as instructions, not as data โ so anyone who controls that value controls the agent's rules. The model has no way to tell "this part of my instructions came from a stranger" once the string is assembled.
This is the same argument as mcp-sdk-security/no-tool-description-injection, one layer down: tool descriptions and system prompts are both model-facing instruction surfaces, and both must be static.
What it reads
systemInstruction as a string, a Content ({ parts: [{ text }] }) or a list of parts โ directly on the request (@google/generative-ai) or under config (@google/genai). Request paths: generateContent, generateContentStream, getGenerativeModel, startChat, chats.create, caches.create, live.connect, sendMessage and sendMessageStream.
What counts as static
Text this file fixes is static, and is followed within the file (bounded, never across files):
- a string literal, an interpolation-free template or tagged template (
dedent`โฆ`), and concatenations,[โฆ].join()and string methods over those; - a
constholding any of the above, a member of a same-file object literal of prompts (PROMPTS[kind]), a ternary between static branches, and a same-file helper whose everyreturnis static; - an imported constant (
RECOMMENDED_PROMPT_PREFIX) and an environment read โ module code and operator configuration, not request data; - a value that can only print digits or a fixed format: a date (
new Date().toISOString()), a count (.length), arithmetic, a comparison, or a parameter typednumber/boolean.
A whole-value identifier the file cannot see into still counts as static โ a parameter (systemInstruction: systemPrompt inside a wrapper) or a global. Inside a template or a concatenation the same value is the dynamic half, and that is what gets reported, at the place it is spliced in. A request object or a message list held in a const is read the same way as one written inline.
Not this rule
- The Vercel AI SDK.
generateText({ system })andstreamText({ system })belong tovercel-ai-security/no-dynamic-system-prompt. This rule gates on Gemini's own request paths, so no line is ever reported by both. - User turns.
{ role: 'user', content: `${input}` }is the remediation, not the bug โ a user message is where runtime values belong, because the model reads them as data.
Known limitations
- The gate is file-level: once the Gemini SDK is imported, any call whose member path matches a request path is inspected, unless this file shows the receiver is something else (an object literal, or a
new X()of a class that does not extend the SDK). A receiver that is a parameter, an import or a call result is inspected โ clients are passed around, and resolving every one back to its construction would take cross-file analysis.
Incorrect
await ai.models.generateContent({
systemInstruction: `Answer as ${persona}.`,
contents,
});Correct
await ai.models.generateContent({
systemInstruction: 'You are a helpful assistant.',
contents: [{ role: 'user', parts: [{ text: `Persona: ${persona}` }] }],
});Use this rule
npm install -D oxlint-plugin-gemini-security{ "jsPlugins": ["oxlint-plugin-gemini-security"], "rules": { "gemini-security/no-untrusted-content-in-prompt": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-gemini-security'], rules: { 'gemini-security/no-untrusted-content-in-prompt': 'error', }, },});oxlint-plugin-gemini-security re-exports eslint-plugin-gemini-security/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it โ or follow the AI-code-security benchmarks behind these rules.