Skip to content

Interview question · Application & API Security

Code review: this tool handler passes model output into a shell command and a SQL query. Find and fix the bugs.

The model is not the attacker, but whoever wrote the document it just read might be. Every argument in a tool call is untrusted input.

mediumNewPreparation tags: Anthropic, OpenAI, AmazonUpdated Nov 2026

TL;DR: The handler has command injection (model text in a shell string), SQL injection (model text concatenated into a query), path traversal (a model-chosen path outside the user's directory) and missing authorization (no check that the file belongs to the user). Model-generated tool arguments are untrusted input, because injected content can steer them. Fix with an argument array and no shell, parameterised SQL, filename validation plus path confinement to the user's directory, and identity from the session, not the model.

How to approach it

Read the handler as an entry point whose inputs come from the model, and remind the interviewer why that makes them untrusted. Then trace each argument to its sink, name the bug, and fix them one at a time. Run or reason through the fixed version against the attack inputs.

A strong answer

FIG · TOOL HANDLER REVIEW · FOUR BUGS FROM ONE ASSUMPTION
Model-generated arguments are untrusted input
  1. Command injection

    Model text in a shell string

    • Fix: no shell; argument list to a named program
  2. SQL injection

    Model text concatenated

    • Fix: parameterised query
  3. Path traversal

    Model-chosen path

    • Fix: resolve, then check inside the user's root
  4. Missing authorisation

    No owner check

    • Fix: verify the file belongs to the session user

The code under review. An agent can archive a user's report:

import os


def archive_report(db, args):
    # args is the JSON the model produced for the tool call
    os.system(f"gzip -k /srv/reports/{args['user']}/{args['filename']}")
    db.execute(
        f"INSERT INTO jobs (owner, title) "
        f"VALUES ('{args['user']}', '{args['title']}')"
    )

Findings.

#BugHow it is reachedImpact
1Command injectionfilename contains shell syntax such as ; or $( )Arbitrary commands as the service user
2SQL injectiontitle contains a quote and SQLRead or modify the jobs table and more
3Path traversalfilename like ../other-user/fileCompress or touch other users' files
4Broken authorizationuser comes from the modelThe agent acts on any user's directory

Each is reachable without the user being malicious: a document or email the agent read earlier can steer the model into producing these arguments.

Fixed handler.

import re
import sqlite3
import subprocess
from pathlib import Path

NAME = re.compile(r"[A-Za-z0-9._-]{1,100}")


def archive_report(
    db: sqlite3.Connection, base: Path, user_id: str, args: dict
) -> None:
    """Tool handler: args come from the model, so treat them as untrusted."""
    name = str(args.get("filename", ""))
    if not NAME.fullmatch(name) or name.startswith("."):
        raise ValueError("invalid filename")
    user_dir = (base / user_id).resolve()
    path = (user_dir / name).resolve()
    if not path.is_relative_to(user_dir) or not path.is_file():
        raise ValueError("file not found")
    # No shell: the path is one argument; "--" ends option parsing.
    subprocess.run(["gzip", "-k", "--", str(path)], check=True, timeout=30)
    title = str(args.get("title", ""))[:200]
    db.execute(
        "INSERT INTO jobs (owner, title, file) VALUES (?, ?, ?)",
        (user_id, title, name),
    )

What changed: user_id comes from the authenticated session and is passed in by the application, never read from the model; the filename is allowlisted and the resolved path must stay inside the user's directory; the command runs without a shell with -- before the path; SQL uses placeholders; the title is length-capped. Tested against ../bob/secret.txt, q3.txt; rm -rf /, -rf, .bashrc and /etc/passwd: all are rejected, and a title containing SQL is stored as plain text.

Defence in depth. Run the tool in a container or sandbox with access only to the reports volume, a database role that can only insert into jobs, and a per-session rate limit on tool calls. Prefer a library (Python's gzip module) over a subprocess where possible.

What interviewers probe next

  • "Why not ask the model to only produce safe filenames?" Instructions lower the rate; they cannot be the control, because injected text can override them.
  • "How would you catch this class of bug across many tools?" A shared tool-argument schema layer with strict types and validators, plus a review checklist for every handler.
  • "What if the user really is malicious?" Same fixes; the handler must be safe against its own user, too.

What if?

What if · interview follow-ups

Hold your answer as the constraints change

A feature lets an LLM generate commands and file paths that run on a build server, with results looked up in a database by the model-chosen name.

  1. Start here

    What vulnerabilities would you expect?

How it's scored

Weak: Finds the shell bug only, or "fixes" it by escaping strings or prompting the model.

Hire: Finds command and SQL injection and fixes both structurally.

Strong Hire: Also finds path traversal and model-supplied identity, fixes all four, explains why model output is untrusted, and adds sandboxing and least privilege.

Common mistakes

  • Trusting user or any identity field from the model.
  • Using shlex.quote with shell=True instead of removing the shell.
  • Checking for .. in the string instead of confining the resolved path.
You just read a free answer. Every topic opens its first 5 answers to anyone. Sign in and the count per topic goes to 10, and we keep track of how far you get in every topic.Google sign-in · free · no payment details
HOW DID IT GO?
0
READING SIGNED OUT

Signing in doubles your free answers, from 5 to 10 per topic, and the site starts remembering you: mastery per topic, bookmarks, and a next-focus recommendation. Free, no card.

Sign in free
UP NEXT ON YOUR JOURNEY
DISCUSSION · 0

No comments yet. Share a correction or a different way to answer it.