no-unsafe-output-handling
This rule identifies code patterns where AI-generated output is passed directly to dangerous functions that can execu...
Prevents using AI-generated content in dangerous operations like eval, SQL, or innerHTML.
📊 Rule Details
| Property | Value |
|---|---|
| Type | problem |
| Severity | 🔴 CRITICAL |
| OWASP LLM | LLM05: Improper Output Handling |
| OWASP Agentic | ASI05: Unexpected Code Execution |
| CWE | CWE-94: Improper Control of Code Generation |
| CVSS | 9.8 |
| Config Default | error (recommended, strict) |
🔍 What This Rule Detects
This rule identifies code patterns where AI-generated output is passed directly to dangerous functions that can execute code, manipulate the DOM, or run database queries.
❌ Incorrect Code
// Code execution
const result = await generateText({ prompt: 'Generate code' });
eval(result.text);
// Function constructor
new Function(result.text)();
// XSS via innerHTML
element.innerHTML = result.text;
// SQL injection
db.query(result.text);
// Shell execution
exec(result.text);✅ Correct Code
Hello⚙️ Options
| Option | Type | Default | Description |
|---|---|---|---|
aiOutputPatterns | string[] | ["result.text","response.text","completion","generated","aiOutput","aiResponse","llmOutput","llmResponse","modelOutput",".text"] | Variable patterns that suggest AI output |
🛡️ Why This Matters
Passing AI output to dangerous functions enables:
- Remote Code Execution (RCE) - Attackers can inject code via prompt manipulation
- Cross-Site Scripting (XSS) - Malicious scripts in generated HTML
- SQL Injection - Database manipulation via generated queries
- Command Injection - System command execution
🔗 Related Rules
require-validated-prompt- Validate input promptsrequire-output-filtering- Filter tool output
🔄 Changes in the 2026-10-10 FP/FN audit
- Tool inputs are model output. The parameters of a tool's
execute(intool({...}),dynamicTool({...}), or an object literal withinputSchema/parameters) are filled from the model's tool call, soexecute: async ({ command }) => execSync(command)is reported. - Sinks are matched by resolved callee, never by substring. Code execution:
global
eval/Function(call ornew),child_processexec/execSync/execFile/execFileSync/spawn/spawnSync/fork, andvmrunInNewContext/runInThisContext/runInContext/compileFunction/new vm.Script— resolved through imports andrequire. SQL: the first argument of.query/.execute/.raw/.run/.unsafe/$queryRawUnsafe/$executeRawUnsafe./re/.exec(result.text),evaluateAnswer(...)andtruncate(...)are no longer reported. - New sinks:
dangerouslySetInnerHTML={{ __html: … }}, andfetch(url)where model output chooses the URL or its leading part (new messageunsafeOutputInRequest, CWE-918). A fixed host with model input in the query string is not reported. - Calls are not output.
DOMPurify.sanitize(result.text)assigned toinnerHTMLis not reported; a string method on output (text.trim()) still is. textContentwas removed from the defaultaiOutputPatterns.- Options: the documented
defaultOptionsnow apply. Earlier versions readcontext.optionsand silently ran a shorter hard-coded list instead.
🔄 Changes in the 2026-10-11 zero-deferral pass
- Model output is followed through same-file declarations, reassignments,
string / array derivations and helpers:
const foo = wrap(text); eval(foo). - UI sources: a component parameter typed with an SDK message type
(
{ m }: { m: UIMessage }, a same-file props alias or interface naming one), and the result ofuseChat/useCompletion/useObject/useAssistantimported from the SDK — including throughmessages.map((m) => <Bubble m={m} />)into a same-file component. A same-named hook from another package is not one.
Known False Negatives
The following patterns are not detected due to static analysis limitations:
Custom Dangerous Functions
Why: Non-standard execution functions may not be detected.
// ❌ NOT DETECTED - Custom exec wrapper
executeCode(result.text); // Custom wrapper, not one of the recognised sinksMitigation: none available — the recognised sinks (global eval /
Function, child_process and vm functions resolved through imports, and
the SQL / fetch / HTML sinks listed above) are fixed and not configurable.
Call the underlying function directly at the point the AI output is used, or
wrap the call site in your own review.
Dynamic Function Invocation
Why: Dynamic property access is not analyzed.
// ❌ NOT DETECTED - Dynamic invocation
const method = 'eval';
window[method](result.text); // Dynamic accessMitigation: Avoid dynamic function invocation.
📚 References
- OWASP LLM05: Improper Output Handling
- OWASP ASI05: Unexpected Code Execution
- CWE-94: Improper Control of Code Generation
Error Message Format
The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:
🔒 CWE-94 OWASP:A05 CVSS:9.8 | Code Injection detected | CRITICAL [SOC2,PCI-DSS,ISO27001]
Fix: Review and apply the recommended fix | https://owasp.org/Top10/A05_2021/Message Components
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-94 OWASP:A05 CVSS:9.8 |
| Issue Description | Specific vulnerability | Code Injection detected |
| Severity & Compliance | Impact assessment | CRITICAL [SOC2,PCI-DSS,ISO27001] |
| Fix Instruction | Actionable remediation | Follow the remediation steps below |
| Technical Truth | Official reference | OWASP Top 10 |
Use this rule
npm install -D oxlint-plugin-vercel-ai-security{ "jsPlugins": ["oxlint-plugin-vercel-ai-security"], "rules": { "vercel-ai-security/no-unsafe-output-handling": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-vercel-ai-security'], rules: { 'vercel-ai-security/no-unsafe-output-handling': 'error', }, },});oxlint-plugin-vercel-ai-security re-exports eslint-plugin-vercel-ai-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.