Safeguard
Security Guides•

What should you look for in SQL code during a pull request review?

Look for string built queries, user input in identifiers and over-privileged database accounts. Ask for parameterized queries and check the account.

Safeguard Security Team
4 min read

In a pull request, look for any query built by joining strings with request data. The fix to ask for is a parameterized query. Then check the odd cases where parameters cannot be used, such as column names and sort order, and confirm the database account has no more rights than it needs.

The primary defenses, in order

The OWASP SQL Injection Prevention Cheat Sheet lists them in this order: prepared statements with parameterized queries, stored procedures written safely, allow list input validation where bind variables are not legal, and escaping, which it strongly discourages because it is fragile and database specific.

For a reviewer, the first item becomes a rule of thumb. If the diff contains a query and a variable inside the same string, stop and ask why.

Quick red flags in a diff

What you seeWhy it matters
String concatenation or interpolation into SQLInput can change the query structure
A helper called raw, execute or query with a built stringBypasses the safe path of the framework
User input chosen as table, column or sort orderParameters cannot bind identifiers
Dynamic SQL inside a stored procedureProcedures are not automatically safe
New database user with broad rightsA bug becomes a bigger breach

The cases where parameters do not work

Identifiers such as table names, column names and ORDER BY cannot be bound as parameters. OWASP suggests taking identifiers from code rather than from user input. If a user must choose one, map the input to a fixed set of known names, for example in a switch statement, and reject everything else. For sort direction, convert the input to a boolean and pick ASC or DESC from that.

A small illustration of the review comment you might leave:

# risky: the column name comes straight from the request
query = "SELECT * FROM orders ORDER BY " + request.args["sort"]

# safer: the request only selects from known choices
allowed = {"date": "created_at", "total": "total_amount"}
column = allowed.get(request.args.get("sort"), "created_at")
query = "SELECT * FROM orders ORDER BY " + column

The second version still builds a string, but every part of it comes from your own code.

Do not trust validation alone

OWASP notes that allow list validation is a secondary defense in all cases, even with bind variables, and a primary defense only when nothing else is possible. Validated input is not automatically safe to concatenate into SQL. Treat validation as a layer, not the fix.

Check the account behind the query

The cheat sheet is blunt: do not give application accounts DBA or admin type access. Use a separate account per application and limit table access with views or procedures where it makes sense. When a pull request adds a new connection string or migration role, read the grants. An injection bug under a read only account exposes less than the same bug under an owner account.

Try it yourself

Run the code scan in the demo and look at how a finding points to the exact line where input reaches a query.

Click through it at your own pace, or open it full screen.

Where Safeguard fits

Safeguard scans dependencies and builds an SBOM, and you can try the free scan at gold.safeguard.sh/scan. Query construction in your own code is a review and testing job.

Quick check

Is escaping user input as good as parameterization?

No. OWASP strongly discourages escaping as a primary defense because it is fragile and differs between databases.

Are stored procedures always safe?

No. They are equally effective only if written without unsafe dynamic SQL, and execute rights can push applications toward over-privileged accounts.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.