Overprivileged Tool is an AI code smell, a sign of trouble in LLM systems, in the Security group. A read tool has write access, or a run_sql tool accepts any statement.
A data assistant answers business questions by querying the database. The tool (anonymized):
TOOLS = [{ "name": "run_sql", "description": "Runs a SQL query on the company database and returns the result.", "input_schema": {"type": "object", "properties": {"query": {"type": "string"}}},}]def run_sql(query: str): with psycopg.connect(os.environ["DATABASE_URL"]) as conn: # application user, with write access return conn.execute(query).fetchall()
Someone in sales asked: “clean the test customers out of the list, they are polluting the report”. The model generated DELETE FROM customers WHERE email LIKE '%test%'. The tool ran it. Among the deleted rows were real customers with “test” in their email, such as “contested@” and “testa.ana@”.
The model did what it was asked, the way it understood it. The problem is that the tool let it do anything. In July 2025, Jason Lemkin, founder of SaaStr, publicly reported a similar case: Replit’s coding agent deleted his project’s production database during a code freeze. Replit’s CEO apologized and announced automatic separation between development and production databases.
Signs of an Overprivileged Tool:
The tool accepts generic commands (run_sql, run_shell, call_api, http_request).
Read tools use credentials with write permission.
The tool does not know who the end user is, and acts with the system’s permissions.
There is no limit on amount, quantity, or scope per call.
Safety depends on the model “choosing well” what to ask for.
flowchart LR
U(["1. Ambiguous request"]):::io --> M(["2. Model generates a command"]):::llm
M --> T["3. run_sql with full access"]:::tool
T --> DB[("4. Production database")]:::store
A request arrives, sometimes ambiguous, sometimes malicious.
The model translates it into a command.
The tool runs any command with the maximum access it has.
The effect happens with the whole system’s permissions.
Reasons
A generic tool is more flexible. One run_sql answers any question without writing ten tools.
Reusing the application’s connection. The connection string already exists; creating a read-only user takes work.
The prototype only read. Nobody imagined a request to delete.
Trust in the prompt. “Only run SELECT” is written in the prompt.
Three different excesses
The OWASP Top 10 for LLM Applications calls this risk excessive agency (LLM06 on the 2025 list) and splits it into three causes. Ask each one about your tool:
Excess
Question
In the example
Functionality
Does the tool offer more operations than the use case needs?
run_sql accepts DELETE, DROP, UPDATE
Permission
Can the credential do more than the tool should?
Application user, with write access to everything
Autonomy
Do high-impact actions run without confirmation?
The DELETE ran with no one approving it
Beginners tend to attack only the third row, with a human approval step. It helps, but approval wears people down: after the hundredth confirmation, they click without reading. Cut functionality and permission first.
Cost of ignoring
Data loss from a misread request.
Privilege escalation. A user without permission gets, through the assistant, what the system would not let them do directly.
Injection attacks. An instruction hidden in a document the agent reads becomes a command with full access. Greshake et al. (2023) showed this indirect injection working against real applications.
No auditing. With the system’s user, there is no way to tell who asked for what.
Worked example: the worst case of each call
A practical way to measure the smell is to ask: “if the model generates the worst possible call, what happens?”. Compare three versions of the same tool:
Version
Worst possible call
Damage
run_sql with the application user
DROP TABLE customers
Data loss in production
run_sql read-only, on a replica, with a timeout
A heavy SELECT on everything
Slow query, cut off at 10 s
revenue_by_month(year, month)
A month that does not exist
Validation error
Flexibility drops from top to bottom. So does the worst case. You pick the row where the risk fits your business.
The tool accepts generic commands. It becomes named, safe operations (search_orders, revenue_by_month), each with validated parameters. Target: Tool Use.
In PostgreSQL, narrowing the permission takes a few lines. The limit now holds in the database, for any SQL the model generates:
CREATE ROLE assistant_readonly LOGIN; -- password comes from the vaultGRANT SELECT ON sales_by_month, orders_summary TO assistant_readonly;ALTER ROLE assistant_readonly SET default_transaction_read_only = on;ALTER ROLE assistant_readonly SET statement_timeout = '10s';
The end user’s scope follows the same logic. Parameters that define whose data it is (customer id, store id, recipient email) come from the session, in code. Never from an argument the model fills in.
Payoff
The worst case of a wrong call becomes small and known.
The assistant cannot do what the user could not do.
Injections lose their effect, because the tool lacks the power being asked for.
Auditing with the requester’s identity.
When to ignore
An isolated, disposable environment. An analysis agent on a copy of the database, recreated every session, can have a broad run_sql. Isolation is the protection. See Unvalidated Executed Output.
Personal use with supervision. A developer who reviews each command before running it, on a database that is theirs alone.
Is it this smell?
Quick answers
What is Overprivileged Tool?
Overprivileged Tool is an AI code smell, a sign of trouble in LLM systems, in the Security group. A read tool has write access, or a run_sql tool accepts any statement.