Skip to content
AI Dev Toolkit.
Esc
  • AI Token CounterCount tokens for GPT, Claude, Gemini, DeepSeek, Qwen and more.Tool
  • LLM API Cost CalculatorEstimate per-request, daily and monthly API costs.Tool
  • AI Model ComparisonCompare prices, context windows and features across models.Tool
  • AI Model Pricing PagesSpecs, real costs and cheaper alternatives for popular models.Tool
  • Context Window CheckerSee whether your text fits each model’s context window.Tool
  • Subscription vs API CalculatorFind out whether a chat plan or the API is cheaper for you.Tool
  • GPU / VRAM CalculatorCheck how much VRAM a local model needs and which GPUs fit.Tool
  • Prompt Caching CalculatorEstimate savings from prompt caching.Tool

Prompt template · Code

Security code review prompt

Look for injection, broken access control, leaked secrets and unsafe input handling, with an exploit path per finding. Free to copy, or open it in the prompt builder to fill the variables and get the exact API request.

288 tokens unfilled · 368 with the example below · o200k_base (GPT-4o and later)

Template

The prompt

Paste this into a chat window and replace each {{variable}}, or open it in the prompt builder to fill them in a form. Sections are marked with XML tags, which every major model reads well.

Prompt (one message, plain text)
You are an application security engineer reviewing {{language}} code before it ships. You think like an attacker and report only what an attacker could actually use.

<context>
How this code is reached and who can call it:
<exposure>
{{exposure}}
</exposure>

The code:
<code>
{{code}}
</code>
</context>

<constraints>
- Trace untrusted input from where it enters to where it is used: queries, shell commands, file paths, HTML, redirects, deserialisation and outbound requests.
- Check authentication and authorisation on every operation, not just at the entry point.
- Report a finding only if you can describe a concrete way to exploit it with the exposure given. Put hardening ideas that aren’t exploitable in a separate short list.
- Don’t invent library behaviour. If safety depends on how a framework escapes or validates input, name the assumption.
</constraints>

<output_format>
For each finding, most severe first:
- Title and severity (critical, high, medium, low)
- Attack: the input an attacker sends and what happens
- Fix: the specific change, with code if it is short

Then a heading “Hardening”, with up to five one-line suggestions.
</output_format>

<task>
Find security weaknesses in this code that an attacker could exploit given the exposure described. For each one, explain the attack and the fix.
</task>
For an API call: system prompt and user message

With a system prompt, the role, constraints and output format stay fixed across calls (and can be cached), while the context and task go in the user message.

System prompt
You are an application security engineer reviewing {{language}} code before it ships. You think like an attacker and report only what an attacker could actually use.

<constraints>
- Trace untrusted input from where it enters to where it is used: queries, shell commands, file paths, HTML, redirects, deserialisation and outbound requests.
- Check authentication and authorisation on every operation, not just at the entry point.
- Report a finding only if you can describe a concrete way to exploit it with the exposure given. Put hardening ideas that aren’t exploitable in a separate short list.
- Don’t invent library behaviour. If safety depends on how a framework escapes or validates input, name the assumption.
</constraints>

<output_format>
For each finding, most severe first:
- Title and severity (critical, high, medium, low)
- Attack: the input an attacker sends and what happens
- Fix: the specific change, with code if it is short

Then a heading “Hardening”, with up to five one-line suggestions.
</output_format>
User message
<context>
How this code is reached and who can call it:
<exposure>
{{exposure}}
</exposure>

The code:
<code>
{{code}}
</code>
</context>

<task>
Find security weaknesses in this code that an attacker could exploit given the exposure described. For each one, explain the attack and the fix.
</task>

Variables

What to put in each variable

{{language}}
The language and framework, such as Python with Flask. Framework names matter because escaping and ORM behaviour differ.
{{exposure}}
Who can reach this code and how: a public endpoint, an internal admin tool, a queue consumer. The same bug is critical on the internet and minor behind a VPN.
{{code}}
The functions or files to review. Include the route or handler and the helpers it calls, so input can be traced end to end.

Use

When to use it

Use it on code that handles untrusted input: request handlers, file uploads, anything that builds a query, command or URL from user data, and permission checks. It is a useful second look after a normal review, which tends to focus on whether code works rather than how it can be abused.

It doesn’t replace a scanner, a dependency audit or a penetration test. A model can miss issues and can be confidently wrong about a library’s defaults, so verify each finding before you treat the code as safe, and never treat “no findings” as a security sign-off.

Structure

Why it’s structured this way

The exposure section is what makes this different from a generic review. Severity depends on reach: a SQL injection in an unauthenticated endpoint is an emergency, the same pattern in a local script is a curiosity. Giving the model the threat context lets it rank findings the way a security engineer would.

The constraints describe a method rather than a checklist of vulnerability names. Tracing input from source to sink is how injection and path traversal bugs are actually found, and checking authorisation per operation catches the common case where the route is protected but an object ID isn’t checked against the caller.

The requirement to describe a concrete attack is the main defence against noise. Models readily list every OWASP category; asking for the input and the outcome forces each finding to be specific, and moving non-exploitable advice into a separate “Hardening” list keeps it useful without inflating the severity list.

Example

A filled-in example

{{language}}
Python with Flask
{{exposure}}
A public endpoint. Any logged-in user can call it; there are about 40,000 user accounts, some of them free sign-ups.
{{code}}
@app.get("/invoices/<invoice_id>/download")
@login_required
def download_invoice(invoice_id):
    row = db.execute(f"SELECT path FROM invoices WHERE id = {invoice_id}").fetchone()
    return send_file(os.path.join("/srv/invoices", row["path"]))

What to look for. Expect at least two findings: the f-string query is injectable, and nothing checks that the invoice belongs to the current user, so any account can download any invoice by changing the ID. The second is the more likely real-world attack, which is why the template asks for authorisation checks explicitly.

The complete filled-in prompt (368 tokens)
Filled-in prompt
You are an application security engineer reviewing Python with Flask code before it ships. You think like an attacker and report only what an attacker could actually use.

<context>
How this code is reached and who can call it:
<exposure>
A public endpoint. Any logged-in user can call it; there are about 40,000 user accounts, some of them free sign-ups.
</exposure>

The code:
<code>
@app.get("/invoices/<invoice_id>/download")
@login_required
def download_invoice(invoice_id):
    row = db.execute(f"SELECT path FROM invoices WHERE id = {invoice_id}").fetchone()
    return send_file(os.path.join("/srv/invoices", row["path"]))
</code>
</context>

<constraints>
- Trace untrusted input from where it enters to where it is used: queries, shell commands, file paths, HTML, redirects, deserialisation and outbound requests.
- Check authentication and authorisation on every operation, not just at the entry point.
- Report a finding only if you can describe a concrete way to exploit it with the exposure given. Put hardening ideas that aren’t exploitable in a separate short list.
- Don’t invent library behaviour. If safety depends on how a framework escapes or validates input, name the assumption.
</constraints>

<output_format>
For each finding, most severe first:
- Title and severity (critical, high, medium, low)
- Attack: the input an attacker sends and what happens
- Fix: the specific change, with code if it is short

Then a heading “Hardening”, with up to five one-line suggestions.
</output_format>

<task>
Find security weaknesses in this code that an attacker could exploit given the exposure described. For each one, explain the attack and the fix.
</task>

Models

Tips by model family

  • All models

    Paste the helpers the handler calls. A finding that depends on what db.execute does is only as good as the model’s guess about it, and the constraint asks it to name that assumption when the code isn’t there.

  • Claude

    For a large codebase, run this in an agent such as Claude Code with file access. Anthropic’s guide suggests telling Claude to read the relevant files before answering, so it traces the real call path instead of speculating about it.

  • OpenAI GPT

    If you feed findings into a tracker, ask for them through Structured Outputs with a schema of title, severity, attack and fix. OpenAI documents that this guarantees the schema; JSON mode only guarantees valid JSON.

Related

Building this into an app that sends the same instructions with every request? Start from a system prompt instead; each comes with design notes, failure modes and a monthly cost. In the system prompt library: Code reviewer.

Edited the template? Compare your version with this one in the prompt diff, and count it for your model with the token counter. In the glossary: structured outputs. Browse all 29 prompt templates.