39
loading...
This website collects cookies to deliver better user experience
class Task < ApplicationRecord
enum status: {
pending: 0,
success: 1,
failed: 3,
}
NOT_FAILURES = ['pending', 'success'].freeze
end
class TaskRunner
def get_failures
start_time = Date.beginning_of_quarter
end_time = Date.end_of_quarter
no_failure_enums = Task.statuses.values_at(*Task::NOT_FAILURES)
query = <<~QUERY
SELECT COUNT(*)
FROM `tasks`
WHERE `tasks`.`status` NOT IN (#{no_failure_enums.join(',')})
AND `tasks`.`time_end` BETWEEN #{start_time} AND #{end_time}
QUERY
# Line below triggers a SQL injection warning
Task.connection.select_all(query)
end
end
Confidence: High
Category: SQL Injection
Check: SQL
Message: Possible SQL injection
Code: Task.connection.select_all("SELECT COUNT(*)\nFROM `filings`\nWHERE `filings`.`status` NOT IN (#{Task.statuses.values_at(*["pending", "success"]).join(",")})\nAND `filings`.`time_end` BETWEEN #{Date.beginning_of_quarter} AND #{Date.end_of_quarter}\n")
File: app/models/task.rb
Line: 25
Task.connection.select_all(
"SELECT COUNT(*) \
FROM `filings` \
WHERE `filings`.`status` NOT IN (#{Task.statuses.values_at(*["pending", "success"]).join(",")}) \
AND `filings`.`time_end` BETWEEN #{Date.beginning_of_quarter} \
AND #{Date.end_of_quarter}"
)
Task.statuses.values_at(*["pending", "success"]).join(",")
no_failure_enums.join(',')
).Task.statuses
is actually a constant - it's defined using enum
in the Task
class. This code is grabbing the integer values for the given enums and joining them back into a comma-separated string.Task.statuses.values_at(*["pending", "success"]).join(",")
*["pending", "success"]
. This code converts an array of strings to individual method arguments (i.e., values_at("pending", "success")
.Task.statuses.values_at("pending", "success").join(",")
values_at
is Hash#values_at
- it returns an array of values from the hash table for the given keys.Hash#values
at the same time. (Check out the pull request here)h = { a: 1, b: 2, c: 3 }
h.values_at(:a, :c) #=> [1, 3]
Task.statuses.values_at("pending", "success").join(",")
Task.statuses
is.enum
!Task.statuses
is a method that returns a hash value. Instead of implementing a special case for that, I thought this would be a good time to support methods with single, simple return values.class Dog
def self.sound
'bark'
end
end
Dog.sound
returns 'bark'
, I could implement enums as method definitions that return simple arrays or hashes. (More on this later!)enum
essentially defines a bunch of methods.class Task < ApplicationRecord
enum status: {
pending: 0,
success: 1,
failed: 3,
}
end
Task.statuses # the one we care about!
Task.status
Task#status
Task#pending?
Task#success?
# ..etc.
status
and statuses
methods:class Task < ApplicationRecord
def self.statuses
{
pending: 0,
success: 1,
failed: 3,
}
end
end
Task.statuses
returns a hard-coded hash table.status
but we need statuses
.pluralize
.enum
support and proper pluralization, this code:Task.statuses.values_at(*["pending", "success"]).join(",")
{
pending: 0,
success: 1,
failed: 3,
}.values_at(*["pending", "success"]).join(",")
{
pending: 0,
success: 1,
failed: 3,
}.values_at("pending", "success").join(",")
[:BRAKEMAN_SAFE_LITERAL, :BRAKEMAN_SAFE_LITERAL].join(",")
"BRAKEMAN_SAFE_LITERAL,BRAKEMAN_SAFE_LITERAL"
Task.connection.select_all(
"SELECT COUNT(*) \
FROM `filings` \
WHERE `filings`.`status` NOT IN (#{"BRAKEMAN_SAFE_LITERAL,BRAKEMAN_SAFE_LITERAL"}) \
AND `filings`.`time_end` BETWEEN #{Date.beginning_of_quarter} \
AND #{Date.end_of_quarter}"
)
Confidence: Medium
Category: SQL Injection
Check: SQL
Message: Possible SQL injection
Code: Task.connection.select_all("SELECT COUNT(*)\nFROM `filings`\nWHERE `filings`.`status` NOT IN (#{"BRAKEMAN_SAFE_LITERAL,BRAKEMAN_SAFE_LITERAL"})\nAND `filings`.`time_end` BETWEEN #{Date.beginning_of_quarter} AND #{Date.end_of_quarter}\n")
File: app/models/task.rb
Line: 25
Date.beginning_of_quarter
or Date.end_of_quarter
are, so it generates a lower confidence warning about it. For SQL injection, Brakeman is pretty paranoid about any string interpolation, even if it's not sure the values are "dangerous".Date
is likely to be safe, so now Brakeman ignores Date
calls in SQL.