Your model's output is untrusted input. Find where your code forgets it.
Static analysis of the code around your LLM — the code that builds the prompt, calls the model and does something with the answer. Not the model. Not the prompt's resistance to a jailbreak. The code.
The free plan covers 3 projects and 30 analyses a month, with no limit on the number of users and no credit card. This analysis runs on it.
The problem
A model is not a library. It is an untrusted interpreter: you hand it a string and it hands one back. Whatever influenced that string — the user's own prompt, a page it was shown, a document it was given, another tool's result — influenced the answer.
The answer is harmless until the code gives it a capability. The danger is not that a model said something; it is that a program believed it. Every place a program treats generated text as code, a command, a query, a path or a URL is an injection with one extra hop — and unlike the question of whether a prompt can be jailbroken, that hop is visible in the source.
What Metivect looks for
Where model output reaches something that acts. These are the engine's own categories, severities and CWE identifiers.
A concrete example
Nine lines, and the whole shape of the risk.
from openai import OpenAI
client = OpenAI()
def run_plan(task: str):
answer = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": task}],
)
exec(answer.choices[0].message.content)client.chat.completions.create(…) — a call that returns model output. The analyzer binds the variable it was assigned to, which is what lets it follow the value across the function without inter-procedural analysis.answer holds attacker-influenced text. Nothing between the call and the sink inspects it, constrains it or maps it onto a fixed set of operations.exec(…) — the text becomes a program in the application's own process, with its credentials, its network and its filesystem.task — directly, or through a document the model was shown — chooses what runs. This is remote code execution with an extra hop.Run against the current engine, that snippet produces one finding: llm-output-to-code-execution, CWE-94, severity critical, confidence high, on the exec line. Confidence is high because the variable is bound to an actual completion call. A name that merely looks like a model response — llm_response, reply — is reported at medium confidence instead, because that is a guess and saying otherwise would be a lie about evidence.
What this is not
Other LLM-specific risks in the code
Each one was triggered on a sample against the current engine before it was written here.
Why you should believe any of this
Provider-agnostic by construction. The analyzer recognises the shape — something produces text, something consumes it — rather than one SDK's spelling. It names the framework it saw when it can (OpenAI, Anthropic, Google, Azure OpenAI, Mistral, Bedrock, Ollama, LiteLLM, LangChain, LangGraph, LlamaIndex, Semantic Kernel, AutoGen, CrewAI, Hugging Face) so a finding can say where it came from. That is a label on the finding, not a per-SDK integration, and a framework absent from that list is analysed exactly the same way.
See Metivect reason about a real-world system
Watch the interactive demo, then request a guided trial for your team.