ruby.rails.security.brakeman.check-sql.check-sql
semgrep
Author
unknown
Download Count*
License
Found potential SQL injection due to unsafe SQL query construction via $X. Where possible, prefer parameterized queries.
Run Locally
Run in CI
Defintion
rules:
- id: check-sql
mode: taint
pattern-sources:
- pattern-either:
- pattern: |
cookies[...]
- patterns:
- pattern: |
cookies. ... .$PROPERTY[...]
- metavariable-regex:
metavariable: $PROPERTY
regex: (?!signed|encrypted)
- pattern: |
params[...]
- pattern: |
request.env[...]
pattern-sanitizers:
- patterns:
- pattern-either:
- patterns:
- pattern: $X
- pattern-either:
- pattern-inside: |
:$KEY => $X
- pattern-inside: |
["...",$X,...]
- pattern: |
params[...].to_i
- pattern: |
params[...].to_f
- patterns:
- pattern: |
params[...] ? $A : $B
- metavariable-pattern:
metavariable: $A
patterns:
- pattern-not: |
params[...]
- metavariable-pattern:
metavariable: $B
patterns:
- pattern-not: |
params[...]
pattern-sinks:
- patterns:
- pattern: $X
- pattern-not-inside: |
$P.where("...",...)
- pattern-not-inside: |
$P.where(:$KEY => $VAL,...)
- pattern-either:
- pattern-inside: |
$P.$M(...)
- pattern-inside: |
$P.$M("...",...)
- pattern-inside: |
class $P < ActiveRecord::Base
...
end
- metavariable-regex:
metavariable: $M
regex: (where|find|first|last|select|minimum|maximum|calculate|sum|average)
message: Found potential SQL injection due to unsafe SQL query construction via
$X. Where possible, prefer parameterized queries.
languages:
- ruby
severity: ERROR
metadata:
source-rule-url: https://github.com/presidentbeef/brakeman/blob/main/lib/brakeman/checks/check_sql.rb
category: security
cwe:
- "CWE-89: Improper Neutralization of Special Elements used in an SQL
Command ('SQL Injection')"
owasp:
- A01:2017 - Injection
- A03:2021 - Injection
technology:
- ruby
- rails
references:
- https://owasp.org/www-community/attacks/SQL_Injection
- https://github.com/presidentbeef/brakeman/blob/main/test/apps/rails3.1/app/models/product.rb
cwe2022-top25: true
cwe2021-top25: true
subcategory:
- vuln
likelihood: MEDIUM
impact: MEDIUM
confidence: MEDIUM
license: Commons Clause License Condition v1.0[LGPL-2.1-only]
vulnerability_class:
- SQL Injection
Examples
check-sql.rb
class Product < ActiveRecord::Base
def test_find_order
#ruleid: check-sql
Product.find(:all, :order => params[:order])
#ruleid: check-sql
Product.find(:all, :conditions => 'admin = 1', :order => "name #{params[:order]}")
end
def test_find_group
#ruleid: check-sql
Product.find(:all, :conditions => 'admin = 1', :group => params[:group])
#ruleid: check-sql
Product.find(:all, :conditions => 'admin = 1', :group => "something, #{params[:group]}")
end
def test_find_having
#ruleid: check-sql
Product.find(:first, :conditions => 'admin = 1', :having => "x = #{params[:having]}")
#ok: check-sql
Product.find(:first, :conditions => 'admin = 1', :having => { :x => params[:having]})
#ok: check-sql
Product.find(:first, :conditions => ['name = ?', params[:name]], :having => [ 'x = ?', params[:having]])
#ruleid: check-sql
Product.find(:first, :conditions => ['name = ?', params[:name]], :having => [ "admin = ? and x = #{params[:having]}", cookies[:admin]])
#ruleid: check-sql
Product.find(:first, :conditions => ['name = ?', params[:name]], :having => [ "admin = ? and x = '" + params[:having] + "'", cookies[:admin]])
end
def test_find_joins
#ok: check-sql
Product.find(:first, :conditions => 'admin = 1', :joins => "LEFT JOIN comments ON comments.post_id = id")
#ruleid: check-sql
Product.find(:first, :conditions => 'admin = 1', :joins => "LEFT JOIN comments ON comments.#{params[:join]} = id")
#ok: check-sql
Product.find(:first, :conditions => 'admin = 1', :joins => [:x, :y])
#ruleid: check-sql
Product.find(:first, :conditions => 'admin = 1', :joins => ["LEFT JOIN comments ON comments.#{params[:join]} = id", :x, :y])
end
def test_find_select
#ok: check-sql
Product.find(:last, :conditions => 'admin = 1', :select => "name")
#ruleid: check-sql
Product.find(:last, :conditions => 'admin = 1', :select => params[:column])
#ruleid: check-sql
Product.find(:last, :conditions => 'admin = 1', :select => "name, #{params[:column]}")
#ruleid: check-sql
Product.find(:last, :conditions => 'admin = 1', :select => "name, " + params[:column])
end
def test_find_from
#ok: check-sql
Product.find(:last, :conditions => 'admin = 1', :from => "users")
#ruleid: check-sql
Product.find(:last, :conditions => 'admin = 1', :from => params[:table])
#ruleid: check-sql
Product.find(:last, :conditions => 'admin = 1', :from => "#{params[:table]}")
end
def test_find_lock
#ok: check-sql
Product.find(:last, :conditions => 'admin = 1', :lock => true)
#ruleid: check-sql
Product.find(:last, :conditions => 'admin = 1', :lock => params[:lock])
#ruleid: check-sql
Product.find(:last, :conditions => 'admin = 1', :lock => "LOCK #{params[:lock]}")
end
def test_where
#ok: check-sql
Product.where("admin = 1")
#ok: check-sql
Product.where("admin = ?", params[:admin])
#ok: check-sql
Product.where(["admin = ?", params[:admin]])
#ok: check-sql
Product.where(["admin = :admin", { :admin => params[:admin] }])
#ok: check-sql
Product.where(:admin => params[:admin])
#ok: check-sql
Product.where(:admin => params[:admin], :some_param => params[:some_param])
#ruleid: check-sql
Product.where("admin = '#{params[:admin]}'").first
#ruleid: check-sql
Product.where(["admin = ? AND user_name = #{@name}", params[:admin]])
end
TOTALLY_SAFE = "some safe string"
def test_constant_interpolation
#ok: check-sql
Product.first("blah = #{TOTALLY_SAFE}")
end
def test_local_interpolation
#this is a weak finding and should be covered by a different rule
#ok: check-sql
Product.first("blah = #{local_var}")
end
def test_conditional_args_in_sql
#ruleid: check-sql
Product.last("blah = '#{something ? params[:blah] : TOTALLY_SAFE}'")
#ok: check-sql
Product.last("blah = '#{params[:blah] ? 1 : 0}'")
#ruleid: check-sql
Product.last("blah = '#{params[:blah] ? params[:blah] : 0}'")
#ruleid: check-sql
Product.last("blah = '#{params[:blah] ? 1 : params[:blah]}'")
end
def test_params_in_args
#ruleid: check-sql
Product.last("blah = '#{something(params[:blah])}'")
end
def test_params_to_i
#ok: check-sql
Product.last("blah = '#{params[:id].to_i}'")
end
def test_more_if_statements
if some_condition
x = params[:x]
else
x = "BLAH"
end
y = if some_other_condition
params[:x]
"blah"
else
params[:y]
"blah"
end
#ruleid: check-sql
Product.last("blah = '#{x}'")
#ok: check-sql
Product.last("blah = '#{y}'")
#ok: check-sql
Product.where("blah = 1").group(y)
end
def test_calculations
#ruleid: check-sql
Product.calculate(:count, :all, :conditions => "blah = '#{params[:blah]}'")
#ruleid: check-sql
Product.minimum(:price, :conditions => "blah = #{params[:blach]}")
#ruleid: check-sql
Product.maximum(:price, :group => params[:columns])
#ruleid: check-sql
Product.average(:price, :conditions => ["blah = #{params[:columns]} and x = ?", x])
#ruleid: check-sql
Product.sum(params[:columns])
end
def test_select
#ok: check-sql
Product.select([:price, :sku])
#ruleid: check-sql
Product.select params[:columns]
end
def test_conditional_in_options
x = params[:x] == y ? "created_at ASC" : "created_at DESC"
z = params[:y] == y ? "safe" : "totally safe"
#ok: check-sql
Product.all(:order => x, :having => z, :select => z, :from => z,
:group => z)
end
def test_or_interpolation
#ok: check-sql
Product.where("blah = #{1 or 2}")
end
def test_params_to_f
#ok: check-sql
Product.last("blah = '#{params[:id].to_f}'")
end
def test_interpolation_in_first_arg
#ruleid: check-sql
Product.where("x = #{params[:x]} AND y = ?", y)
end
def test_to_sql_interpolation
#ok: check-sql
prices = Product.select(:price).where("created_at < :time").to_sql
#ok: check-sql
where("price IN (#{prices}) OR whatever", :price => some_price)
end
end
Short Link: https://sg.run/vpgb