Skip to main content

SEC.INJ.RAW_INTERPOLATION — A runtime value was built into the statement instead of bound to it

  • Category: security
  • Severity: high
  • Level: 0
  • Downtime class: none — the finding is about a call site in PHP, not about a statement
  • Stability: stable
  • Suites: analyse
  • Applies to: every engine — it reads PHP source, not a server

What it reports

A raw-SQL call site whose text was assembled from something that is not known until the program runs: an interpolated string, a concatenation, a sprintf, an implode.

DB::table('orders')->whereRaw("status = '{$status}'")->get(); // reported
DB::select("SELECT * FROM orders WHERE status = '{$status}'"); // reported

How to answer it

Pass the value as a binding. Every method this rule looks at takes one.

DB::table('orders')->whereRaw('status = ?', [$status])->get(); // silent
DB::select('SELECT * FROM orders WHERE status = ?', [$status]); // silent

That is the whole fix, and it is not a workaround for the rule: a bound value never becomes part of the statement, so it cannot change what the statement does — whatever it contains.

What it looks at

Both halves of Laravel's raw surface:

  • the statement sinks on the DB facade and on a connection — select, selectOne, scalar, cursor, statement, unprepared, insert, update, delete, affectingStatement, selectResultSets, selectFromWriteConnection;
  • the fragment methods on the query builder — selectRaw, fromRaw, whereRaw, orWhereRaw, havingRaw, orHavingRaw, orderByRaw, groupByRaw, rawValue.

Eloquent counts. Order::query()->whereRaw(…) and $order->lines()->whereRaw(…) are the same call site as the query builder's, and the rule sees them.

What stays silent, on purpose

Parameterized calls. whereRaw('status = ?', [$status]) is idiomatic Laravel and is never reported. A rule that flagged it would be switched off within a week — taking the real findings with it.

A constant statement. Text assembled from class or global constants is still entirely the author's, so 'ANALYZE '.self::TABLE is not a finding. Neither is a lookup into a constant map, which is the shape a team uses as an allowlist.

whereIntegerInRaw() and its family. They cast every value to an integer before it reaches the statement, and that cast is the mitigation. Reporting them would flag the safe form of exactly the pattern this rule looks for.

An argument the analyzer could not read. A value assembled in another method, a property, a method call — the rule sees one call site and cannot follow it. That is undetermined, not a finding. Reporting doubt at high severity is how a security rule teaches a team to ignore it.

A whereRaw() on somebody else's class. The receiver's type decides, not the method name — a codebase is full of repositories and collections with methods that share a name.

What it does not claim

  • It is a syntactic pattern, not taint analysis. It reports that a runtime value reached the statement's text. It makes no claim that the value is attacker-controlled or reachable from a request — SQLens does not do taint analysis at any point.
  • It never quotes your query. A finding travels into CI logs, SARIF files and agent artifacts, and a reproduced fragment would be a second copy of whatever the statement touched. The message names the shape — interpolation, concatenation — and the method, never the SQL.
  • It sees exactly one call site, with no value flow across function boundaries.

Why High, when its neighbor is Low

SEC.INJ.RAW_SQL_WITHOUT_REASON reports a missing sentence: nobody wrote down why raw SQL was chosen. That is a policy gap.

This one reports that a value reached the statement rather than the parameters — the property that decides whether an injection is possible at all. Whether it is exploitable today depends on what flows into that value, which this rule cannot see and does not pretend to; what it can say is that the safe form was available and not used.

Turning it on

includes:
- vendor/pushery/sqlens-for-laravel/extension.neon

See the analyse suite for how it sits beside Larastan and phpstan-dba.

Sources