rastray 0.15.0

Blazing-fast static analysis CLI for security, dependency, and performance audits.
# RSTR-INJ-008 — Rails `.where("... #{params[...]} ...")`

## Summary

A Rails ActiveRecord query method (`.where`, `.find_by_sql`,
`.update_all`, `.delete_all`, etc.) is called with a string-interpolated
SQL fragment that contains `params[...]`. Ruby evaluates the `#{...}`
substitution *before* the string reaches ActiveRecord, so the database
sees an attacker-controlled query — classic SQL injection.

ActiveRecord's hash form (`.where(id: params[:id])`) and the positional
form (`.where("id = ?", params[:id])`) both delegate to prepared
statements and are safe.

## Severity

`Critical`.

## Languages

Ruby (Rails).

## What rastray flags

```ruby
User.where("id = '#{params[:user][:id]}'")                # ← flagged
User.find_by_sql("SELECT * FROM users WHERE id = #{params[:id]}")  # ← flagged
Order.update_all("status = '#{params[:s]}'")              # ← flagged
```

## What rastray deliberately does *not* flag

Safe ActiveRecord forms:

```ruby
User.where(id: params[:id])                                  # hash form
User.where("id = ?", params[:id])                            # positional
User.where("id = :id", id: params[:id])                      # named
```

These compile to prepared statements; the value never reaches the SQL
parser as code.

## How to fix it

Use one of the two parameterised forms:

```ruby
# Hash form (preferred when matching one column)
User.where(id: params[:id])

# Positional form (when you need a custom predicate)
User.where("name LIKE ?", "%#{params[:q]}%")

# Named placeholders for multi-column queries
User.where("name LIKE :q OR email LIKE :q", q: "%#{params[:q]}%")
```

For more complex queries, build the query progressively:

```ruby
scope = User.all
scope = scope.where(role: params[:role])        if params[:role].present?
scope = scope.where("created_at >= ?", since)   if since.present?
scope.order(:name)
```

Even when the value looks "safe" (a number, a UUID), use the
parameterised form. Type coercion happens at the binding layer and
removes any ambiguity.

## References

- [Rails Guides: SQL Injection]https://guides.rubyonrails.org/security.html#sql-injection
- [OWASP SQL Injection Prevention Cheat Sheet]https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
- [CWE-89]https://cwe.mitre.org/data/definitions/89.html