IntelliJ "Unsafe Query" Inspection: Fix and Suppress It
What the IntelliJ unsafe query inspection means, how to fix the SQL injection risk with parameterized queries, and how to suppress false positives.
Published:
Last Updated:
What the “Unsafe query” inspection means
When IntelliJ IDEA highlights a line of SQL-building code with a warning that reads “String concatenation as argument to SQL query” or simply marks the query as an unsafe query, it is telling you that a non-constant value is being glued into a SQL statement and sent to the database. This is one of IntelliJ’s data-flow inspections, registered internally under the identifier SqlSourceToSinkFlow.
The IDE does not just pattern-match for a + next to the word SELECT. It runs a source-to-sink data-flow analysis: it traces a value backward from the point where it enters the query (the sink) to where it originated (the source). If that value is not a compile-time constant - for example, a method parameter, an HTTP request field, or a row read from another table - and it reaches the SQL string through concatenation, IntelliJ flags the path. Concatenating untrusted or non-constant data into SQL is the textbook cause of SQL injection, the vulnerability that sits near the top of the OWASP risk lists year after year.
The warning is a yellow highlight by default, not a compile error, so your code still runs. That is exactly why it is easy to ignore and why it is worth understanding properly.
Why IntelliJ flags it
The inspection is built on taint analysis. Think of every value in your program as either “clean” (a literal, a constant, a value you have explicitly sanitized) or “tainted” (anything IntelliJ cannot prove is safe). When a tainted value flows into a sink without being parameterized, the path lights up.
Consider this method:
public User findUser(Connection conn, String username) throws SQLException {
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql); // IntelliJ: unsafe query
// ...
}
Here username is a method parameter. IntelliJ treats it as a source because nothing in the visible code proves it is safe. It is concatenated into sql, and sql is handed to executeQuery, a sink. If a caller passes ' OR '1'='1 - or worse, '; DROP TABLE users; -- - the database executes the attacker’s logic. The inspection is correct to fire.
The key signal is non-constant input reaching a query string by concatenation. Build the same string entirely from string literals and the warning disappears, because there is no tainted source to trace.
The right fix: parameterized queries
The correct fix is almost never “sanitize the string.” It is to stop building SQL by concatenation and let the database driver separate code from data. With a parameterized query, placeholders mark where values go, and the driver binds those values so they are always treated as data, never as executable SQL.
Bad - string concatenation:
String sql = "SELECT * FROM users WHERE username = '" + username + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
Good - JDBC PreparedStatement with placeholders:
String sql = "SELECT * FROM users WHERE username = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setString(1, username);
try (ResultSet rs = ps.executeQuery()) {
// read results
}
}
The ? is a bind parameter. setString ensures the username is passed out-of-band, so ' OR '1'='1 becomes a literal search value rather than a piece of logic. IntelliJ recognizes this pattern as safe and removes the warning.
The same principle applies across the Java data-access landscape:
- JPA / Hibernate JPQL: use named parameters instead of concatenation.
TypedQuery<User> q = em.createQuery( "SELECT u FROM User u WHERE u.username = :name", User.class); q.setParameter("name", username); - Spring JdbcTemplate: pass arguments as varargs after the SQL.
jdbcTemplate.query( "SELECT * FROM users WHERE username = ?", new UserRowMapper(), username); - MyBatis: use
#{}, which creates a bind parameter, not${}, which performs raw string substitution and is itself a frequent source of injection.<select id="findUser" resultType="User"> SELECT * FROM users WHERE username = #{username} </select>
One caveat that trips people up: bind parameters can only stand in for values, not for table names, column names, or keywords like ASC/DESC. When you genuinely need a dynamic identifier, validate it against a hard-coded allow-list before it touches the SQL. That allow-list check is also what turns a real warning into a legitimate suppression candidate, as covered next.
When it is a false positive and how to suppress it
The inspection is a heuristic, so it sometimes fires on code that is actually safe. Typical false positives:
- The value is assembled from compile-time constants that IntelliJ does not fold.
- A table or column name comes from an
enumor a hard-codedSetallow-list. - The input was already validated by a framework method IntelliJ cannot see through.
Once you have verified by hand that the input cannot be attacker-controlled, you can suppress the warning. IntelliJ offers several ways, in order of narrowest to broadest scope:
1. Suppress for a single statement. Put the caret on the warning, press Alt+Enter, expand the inspection submenu, and choose “Suppress for statement.” IntelliJ inserts a line comment:
//noinspection SqlSourceToSinkFlow
ResultSet rs = stmt.executeQuery(sql);
2. Suppress for the whole method with an annotation:
@SuppressWarnings("SqlSourceToSinkFlow")
public List<String> listColumns(String table) {
// 'table' is checked against an allow-list above
}
3. Change severity or disable globally in Settings > Editor > Inspections, under the Java SQL group, where you will find “String concatenation as argument to SQL query” and the related SqlSourceToSinkFlow entries. You can untick it, drop it to a weak warning, or scope it to specific modules. Profile changes stored in .idea/inspectionProfiles are shared with your team through version control.
4. Mark inputs as safe. IntelliJ also lets you teach the analysis: annotating a SQL string with @Language("SQL"), or marking trusted parameters and methods through the “Configure inspection” dialog, helps the data-flow engine recognize a value as clean so it stops flagging downstream uses.
A hard rule: suppression is not a fix. It silences the IDE permanently for that scope, including on code that future edits might make genuinely unsafe. Only reach for it when the source is a constant, an allow-listed identifier, or input you have provably validated. When in doubt, parameterize instead.
Catching SQL injection beyond the IDE
The unsafe query inspection only protects code while it is open in your editor, and only for developers who have not disabled it. To enforce the same rule across every branch and every contributor, run a SAST scanner in CI that fails the build on injection patterns. Tools like Semgrep, SonarQube, and Snyk Code all carry SQL injection rules that perform their own taint tracking at build time, so a suppressed or ignored IDE warning still gets caught before it merges. For deeper, enterprise-grade scanning, dedicated platforms such as Checkmarx extend the same analysis across large polyglot codebases.
The pragmatic setup is layered: IntelliJ’s inspection gives instant feedback as you type, and a CI-level SAST gate provides the enforcement that an IDE warning cannot. See our best SAST tools for 2026 for a side-by-side comparison of where each tool fits.
Further reading
- Best SAST tools in 2026 - compare scanners that catch SQL injection in CI.
- Static code analysis tools guide - how taint analysis and data-flow inspections work under the hood.
Frequently Asked Questions
What does the unsafe query inspection mean in IntelliJ IDEA?
The unsafe query inspection (also called 'String concatenation as argument to SQL query' and backed by the SqlSourceToSinkFlow analysis) warns that a non-constant value - often a variable that traces back to user input - is being concatenated into a SQL string and passed to a query method. IntelliJ's data-flow engine follows the value from its source to the database sink and flags the path because building SQL by string concatenation is the classic cause of SQL injection. The warning highlights the concatenation expression and suggests you switch to a parameterized query. It is a static heuristic, so it indicates a risk pattern rather than a confirmed exploit.
How do I suppress the unsafe query inspection in IntelliJ?
Place your caret on the warning, press Alt+Enter, and choose 'Suppress for statement' or 'Suppress for method' from the inspection submenu. For a single line, IntelliJ inserts a comment like '//noinspection SqlSourceToSinkFlow' directly above the statement. For a whole method it adds the annotation '@SuppressWarnings("SqlSourceToSinkFlow")'. You can also disable or lower the severity of the inspection globally in Settings > Editor > Inspections > Java > Probable bugs (or under the SQL group). Only suppress after you have confirmed the input is genuinely safe, because suppression hides the warning permanently for that scope.
Is the unsafe query inspection always a real SQL injection?
No. It is a static heuristic that flags any non-constant value flowing into a SQL string, so it produces false positives. Common false positives include values that are compile-time constants assembled across several variables, identifiers chosen from a hard-coded allow-list, table or column names selected from an enum, or input that has already been validated by a framework you wrote yourself that IntelliJ cannot see through. In those cases the path is safe even though the inspection fires. The inspection cannot reason about runtime validation, so you must verify the source yourself before deciding it is a false positive.
What is SqlSourceToSinkFlow in IntelliJ?
SqlSourceToSinkFlow is the internal short name (and suppression identifier) for the inspection that performs source-to-sink taint analysis on SQL. A 'source' is where untrusted or non-constant data enters - a method parameter, an HTTP request field, a result set value - and a 'sink' is a SQL execution method such as Statement.executeQuery, Connection.prepareStatement, or an EntityManager.createQuery call. The engine tracks whether tainted data reaches a sink through string concatenation without being parameterized. You use the literal string 'SqlSourceToSinkFlow' in a //noinspection comment or @SuppressWarnings annotation to silence it.
How do I fix the unsafe query warning correctly?
Replace string concatenation with a parameterized query so values travel as bound parameters rather than as part of the SQL text. With JDBC, use a PreparedStatement with '?' placeholders and setString/setInt to bind each value. With JPA or Hibernate, use named parameters like ':id' and query.setParameter. With Spring's JdbcTemplate, pass arguments as varargs after the SQL string. With MyBatis, use the '#{}' syntax, which binds parameters, instead of '${}', which performs raw substitution. Parameterization removes the warning because the database treats user data as data, not as executable SQL.
Where do I change the unsafe query inspection severity in IntelliJ?
Open Settings (Ctrl+Alt+S or Cmd+, on macOS), go to Editor > Inspections, then expand the Java category and find the SQL group containing 'String concatenation as argument to SQL query' and related SqlSourceToSinkFlow inspections. From there you can untick the inspection to disable it, change its severity from Warning to Weak Warning or Error, or scope it to specific modules using the scope selector. Changes saved to the project profile (stored in .idea/inspectionProfiles) are shared with your team through version control, while changes to the default profile stay local to your machine.
Explore More
Free Newsletter
Stay ahead with AI dev tools
Weekly insights on AI code review, static analysis, and developer productivity. No spam, unsubscribe anytime.
Join developers getting weekly AI tool insights.
Related Articles
How to Export Azure DevOps Data to Excel (6 Methods, 2026)
Step-by-step guide to export Azure DevOps work items, boards, pipelines, test results, and code review data to Excel using built-in tools, REST APIs, Power BI, CSV exports, and Analytics views.
March 20, 2026
how-toSourcery GitHub Integration: PR Review Setup
Set up the Sourcery GitHub integration for automated PR code review. Covers GitHub App install, repo config, custom rules, and team rollout.
March 17, 2026
how-toHow to Set Up Sourcery AI in PyCharm: Step-by-Step
Step-by-step guide to set up Sourcery AI in PyCharm. Covers plugin install, authentication, refactoring, code metrics, shortcuts, and troubleshooting.
March 17, 2026