提交 837e215b 编写于 作者: J Justin

Merge pull request #85 from presidentbeef/include_detected_value_in_warning

Include detected user input value in warnings and highlight in reports
...@@ -23,6 +23,7 @@ module Brakeman ...@@ -23,6 +23,7 @@ module Brakeman
# * :config_file - configuration file # * :config_file - configuration file
# * :escape_html - escape HTML by default (automatic) # * :escape_html - escape HTML by default (automatic)
# * :exit_on_warn - return false if warnings found, true otherwise. Not recommended for library use (default: false) # * :exit_on_warn - return false if warnings found, true otherwise. Not recommended for library use (default: false)
# * :highlight_user_input - highlight user input in reported warnings (default: true)
# * :html_style - path to CSS file # * :html_style - path to CSS file
# * :ignore_model_output - consider models safe (default: false) # * :ignore_model_output - consider models safe (default: false)
# * :message_limit - limit length of messages # * :message_limit - limit length of messages
...@@ -113,6 +114,7 @@ module Brakeman ...@@ -113,6 +114,7 @@ module Brakeman
:min_confidence => 2, :min_confidence => 2,
:combine_locations => true, :combine_locations => true,
:collapse_mass_assignment => true, :collapse_mass_assignment => true,
:highlight_user_input => true,
:ignore_redirect_to_model => true, :ignore_redirect_to_model => true,
:ignore_model_output => false, :ignore_model_output => false,
:message_limit => 100, :message_limit => 100,
......
...@@ -12,6 +12,8 @@ class Brakeman::BaseCheck < SexpProcessor ...@@ -12,6 +12,8 @@ class Brakeman::BaseCheck < SexpProcessor
CONFIDENCE = { :high => 0, :med => 1, :low => 2 } CONFIDENCE = { :high => 0, :med => 1, :low => 2 }
Match = Struct.new(:type, :match)
#Initialize Check with Checks. #Initialize Check with Checks.
def initialize tracker def initialize tracker
super() super()
...@@ -66,13 +68,13 @@ class Brakeman::BaseCheck < SexpProcessor ...@@ -66,13 +68,13 @@ class Brakeman::BaseCheck < SexpProcessor
process exp[3] process exp[3]
if params? exp[1] if params? exp[1]
@has_user_input = :params @has_user_input = Match.new(:params, exp)
elsif cookies? exp[1] elsif cookies? exp[1]
@has_user_input = :cookies @has_user_input = Match.new(:cookies, exp)
elsif request_env? exp[1] elsif request_env? exp[1]
@has_user_input = :request @has_user_input = Match.new(:request, exp)
elsif sexp? exp[1] and model_name? exp[1][1] elsif sexp? exp[1] and model_name? exp[1][1]
@has_user_input = :model @has_user_input = Match.new(:model, exp)
end end
exp exp
...@@ -92,13 +94,13 @@ class Brakeman::BaseCheck < SexpProcessor ...@@ -92,13 +94,13 @@ class Brakeman::BaseCheck < SexpProcessor
#Note that params are included in current expression #Note that params are included in current expression
def process_params exp def process_params exp
@has_user_input = :params @has_user_input = Match.new(:params, exp)
exp exp
end end
#Note that cookies are included in current expression #Note that cookies are included in current expression
def process_cookies exp def process_cookies exp
@has_user_input = :cookies @has_user_input = Match.new(:cookies, exp)
exp exp
end end
...@@ -206,19 +208,26 @@ class Brakeman::BaseCheck < SexpProcessor ...@@ -206,19 +208,26 @@ class Brakeman::BaseCheck < SexpProcessor
#Does not actually process string interpolation, but notes that it occurred. #Does not actually process string interpolation, but notes that it occurred.
def process_string_interp exp def process_string_interp exp
@string_interp = true @string_interp = Match.new(:interp, exp)
exp exp
end end
#Checks if an expression contains string interpolation. #Checks if an expression contains string interpolation.
#
#Returns Match with :interp type if found.
def include_interp? exp def include_interp? exp
@string_interp = false @string_interp = false
process exp process exp
@string_interp @string_interp
end end
#Checks if _exp_ includes parameters or cookies, but this only works #Checks if _exp_ includes user input in the form of cookies, parameters,
#with the base process_default. #request environment, or model attributes.
#
#If found, returns a struct containing a type (:cookies, :params, :request, :model) and
#the matching expression (Match#type and Match#match).
#
#Returns false otherwise.
def include_user_input? exp def include_user_input? exp
@has_user_input = false @has_user_input = false
process exp process exp
...@@ -227,24 +236,24 @@ class Brakeman::BaseCheck < SexpProcessor ...@@ -227,24 +236,24 @@ class Brakeman::BaseCheck < SexpProcessor
#This is used to check for user input being used directly. #This is used to check for user input being used directly.
# #
#Returns false if none is found, otherwise it returns an array ##If found, returns a struct containing a type (:cookies, :params, :request) and
#where the first element is the type of user input #the matching expression (Match#type and Match#match).
#(either :params or :cookies) and the second element is the matching #
#expression #Returns false otherwise.
def has_immediate_user_input? exp def has_immediate_user_input? exp
if exp.nil? if exp.nil?
false false
elsif params? exp elsif params? exp
return :params, exp return Match.new(:params, exp)
elsif cookies? exp elsif cookies? exp
return :cookies, exp return Match.new(:cookies, exp)
elsif call? exp elsif call? exp
if params? exp[1] if params? exp[1]
return :params, exp return Match.new(:params, exp)
elsif cookies? exp[1] elsif cookies? exp[1]
return :cookies, exp return Match.new(:cookies, exp)
elsif request_env? exp[1] elsif request_env? exp[1]
return :request, exp return Match.new(:request, exp)
else else
false false
end end
...@@ -253,10 +262,8 @@ class Brakeman::BaseCheck < SexpProcessor ...@@ -253,10 +262,8 @@ class Brakeman::BaseCheck < SexpProcessor
when :string_interp when :string_interp
exp.each do |e| exp.each do |e|
if sexp? e if sexp? e
type, match = has_immediate_user_input?(e) match = has_immediate_user_input?(e)
if type return match if match
return type, match
end
end end
end end
false false
...@@ -265,10 +272,8 @@ class Brakeman::BaseCheck < SexpProcessor ...@@ -265,10 +272,8 @@ class Brakeman::BaseCheck < SexpProcessor
if exp[1].node_type == :rlist if exp[1].node_type == :rlist
exp[1].each do |e| exp[1].each do |e|
if sexp? e if sexp? e
type, match = has_immediate_user_input?(e) match = has_immediate_user_input?(e)
if type return match if match
return type, match
end
end end
end end
false false
......
...@@ -83,11 +83,10 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck ...@@ -83,11 +83,10 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
out = exp[1][3][1] out = exp[1][3][1]
end end
type, match = has_immediate_user_input? out if input = has_immediate_user_input?(out)
if type
add_result exp add_result exp
case type
case input.type
when :params when :params
message = "Unescaped parameter value" message = "Unescaped parameter value"
when :cookies when :cookies
...@@ -101,8 +100,8 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck ...@@ -101,8 +100,8 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
warn :template => @current_template, warn :template => @current_template,
:warning_type => "Cross Site Scripting", :warning_type => "Cross Site Scripting",
:message => message, :message => message,
:line => match.line, :line => input.match.line,
:code => match, :code => input.match,
:confidence => CONFIDENCE[:high] :confidence => CONFIDENCE[:high]
elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(out) elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(out)
...@@ -161,29 +160,35 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck ...@@ -161,29 +160,35 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
actually_process_call exp actually_process_call exp
message = nil message = nil
if @matched == :model and not tracker.options[:ignore_model_output] if @matched
message = "Unescaped model attribute" case @matched.type
elsif @matched == :params when :model
message = "Unescaped parameter value" unless tracker.options[:ignore_model_output]
elsif @matched == :cookies message = "Unescaped model attribute"
message = "Unescaped cookie value" end
end when :params
message = "Unescaped parameter value"
if message and not duplicate? exp when :cookies
add_result exp message = "Unescaped cookie value"
if exp[1].nil? and @known_dangerous.include? exp[2]
confidence = CONFIDENCE[:high]
else
confidence = CONFIDENCE[:low]
end end
warn :template => @current_template, if message and not duplicate? exp
:warning_type => "Cross Site Scripting", add_result exp
:message => message,
:line => exp.line, if exp[1].nil? and @known_dangerous.include? exp[2]
:code => exp, confidence = CONFIDENCE[:high]
:confidence => confidence else
confidence = CONFIDENCE[:low]
end
warn :template => @current_template,
:warning_type => "Cross Site Scripting",
:message => message,
:line => exp.line,
:code => exp,
:user_input => @matched.match,
:confidence => confidence
end
end end
@mark = @matched = false @mark = @matched = false
...@@ -204,7 +209,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck ...@@ -204,7 +209,7 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
#Ignore safe items #Ignore safe items
if (target.nil? and (@ignore_methods.include? method or method.to_s =~ IGNORE_LIKE)) or if (target.nil? and (@ignore_methods.include? method or method.to_s =~ IGNORE_LIKE)) or
(@matched == :model and IGNORE_MODEL_METHODS.include? method) or (@matched and @matched.type == :model and IGNORE_MODEL_METHODS.include? method) or
(target == HAML_HELPERS and method == :html_escape) or (target == HAML_HELPERS and method == :html_escape) or
((target == URI or target == CGI) and method == :escape) or ((target == URI or target == CGI) and method == :escape) or
(target == XML_HELPER and method == :escape_xml) or (target == XML_HELPER and method == :escape_xml) or
...@@ -214,11 +219,11 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck ...@@ -214,11 +219,11 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
#exp[0] = :ignore #should not be necessary #exp[0] = :ignore #should not be necessary
@matched = false @matched = false
elsif sexp? exp[1] and model_name? exp[1][1] elsif sexp? exp[1] and model_name? exp[1][1]
@matched = :model @matched = Match.new(:model, exp)
elsif cookies? exp elsif cookies? exp
@matched = :cookies @matched = Match.new(:cookies, exp)
elsif @inspect_arguments and params? exp elsif @inspect_arguments and params? exp
@matched = :params @matched = Match.new(:params, exp)
elsif @inspect_arguments elsif @inspect_arguments
process args process args
end end
...@@ -226,13 +231,13 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck ...@@ -226,13 +231,13 @@ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
#Note that params have been found #Note that params have been found
def process_params exp def process_params exp
@matched = :params @matched = Match.new(:params, exp)
exp exp
end end
#Note that cookies have been found #Note that cookies have been found
def process_cookies exp def process_cookies exp
@matched = :cookies @matched = Match.new(:cookies, exp)
exp exp
end end
......
...@@ -20,11 +20,12 @@ class Brakeman::CheckEvaluation < Brakeman::BaseCheck ...@@ -20,11 +20,12 @@ class Brakeman::CheckEvaluation < Brakeman::BaseCheck
#Warns if eval includes user input #Warns if eval includes user input
def process_result result def process_result result
if include_user_input? result[:call][-1] if input = include_user_input?(result[:call][-1])
warn :result => result, warn :result => result,
:warning_type => "Dangerous Eval", :warning_type => "Dangerous Eval",
:message => "User input in eval", :message => "User input in eval",
:code => result[:call], :code => result[:call],
:user_input => input.match,
:confidence => CONFIDENCE[:high] :confidence => CONFIDENCE[:high]
end end
end end
......
...@@ -54,6 +54,7 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck ...@@ -54,6 +54,7 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
:message => "Possible command injection", :message => "Possible command injection",
:line => call.line, :line => call.line,
:code => call, :code => call,
:user_input => failure.match,
:confidence => confidence :confidence => confidence
end end
end end
...@@ -75,16 +76,19 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck ...@@ -75,16 +76,19 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
exp = result[:call] exp = result[:call]
if include_user_input? exp if input = include_user_input?(exp)
confidence = CONFIDENCE[:high] confidence = CONFIDENCE[:high]
user_input = input.match
else else
confidence = CONFIDENCE[:med] confidence = CONFIDENCE[:med]
user_input = nil
end end
warning = { :warning_type => "Command Injection", warning = { :warning_type => "Command Injection",
:message => "Possible command injection", :message => "Possible command injection",
:line => exp.line, :line => exp.line,
:code => exp, :code => exp,
:user_input => user_input,
:confidence => confidence } :confidence => confidence }
if result[:location][0] == :template if result[:location][0] == :template
......
...@@ -28,13 +28,14 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck ...@@ -28,13 +28,14 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
file_name = call[3][1] file_name = call[3][1]
if check = include_user_input?(file_name) if input = include_user_input?(file_name)
unless duplicate? result unless duplicate? result
add_result result add_result result
if check == :params case input.type
when :params
message = "Parameter" message = "Parameter"
elsif check == :cookies when :cookies
message = "Cookie" message = "Cookie"
else else
message = "User input" message = "User input"
...@@ -47,7 +48,8 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck ...@@ -47,7 +48,8 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
:message => message, :message => message,
:confidence => CONFIDENCE[:high], :confidence => CONFIDENCE[:high],
:line => call.line, :line => call.line,
:code => call :code => call,
:user_input => input.match
end end
end end
end end
......
...@@ -60,10 +60,9 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting ...@@ -60,10 +60,9 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
def check_argument result, exp def check_argument result, exp
arg = process exp arg = process exp
type, match = has_immediate_user_input? arg
if type if input = has_immediate_user_input?(arg)
case type case input.type
when :params when :params
message = "Unescaped parameter value in link_to" message = "Unescaped parameter value in link_to"
when :cookies when :cookies
...@@ -76,7 +75,9 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting ...@@ -76,7 +75,9 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
warn :result => result, warn :result => result,
:warning_type => "Cross Site Scripting", :warning_type => "Cross Site Scripting",
:message => message, :message => message,
:user_input => input.match,
:confidence => CONFIDENCE[:high] :confidence => CONFIDENCE[:high]
elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(arg) elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(arg)
method = match[2] method = match[2]
...@@ -92,13 +93,14 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting ...@@ -92,13 +93,14 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
warn :result => result, warn :result => result,
:warning_type => "Cross Site Scripting", :warning_type => "Cross Site Scripting",
:message => "Unescaped model attribute in link_to", :message => "Unescaped model attribute in link_to",
:user_input => match,
:confidence => confidence :confidence => confidence
end end
elsif @matched elsif @matched
if @matched == :model and not tracker.options[:ignore_model_output] if @matched.type == :model and not tracker.options[:ignore_model_output]
message = "Unescaped model attribute in link_to" message = "Unescaped model attribute in link_to"
elsif @matched == :params elsif @matched.type == :params
message = "Unescaped parameter value in link_to" message = "Unescaped parameter value in link_to"
end end
...@@ -108,6 +110,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting ...@@ -108,6 +110,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
warn :result => result, warn :result => result,
:warning_type => "Cross Site Scripting", :warning_type => "Cross Site Scripting",
:message => message, :message => message,
:user_input => @matched.match,
:confidence => CONFIDENCE[:med] :confidence => CONFIDENCE[:med]
end end
end end
......
...@@ -40,10 +40,9 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo ...@@ -40,10 +40,9 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
#with something before the user input #with something before the user input
return if node_type?(url_arg, :string_interp) && !url_arg[1].chomp.empty? return if node_type?(url_arg, :string_interp) && !url_arg[1].chomp.empty?
type, match = has_immediate_user_input? url_arg
if type if input = has_immediate_user_input?(url_arg)
case type case input.type
when :params when :params
message = "Unsafe parameter value in link_to href" message = "Unsafe parameter value in link_to href"
when :cookies when :cookies
...@@ -57,6 +56,7 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo ...@@ -57,6 +56,7 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
warn :result => result, warn :result => result,
:warning_type => "Cross Site Scripting", :warning_type => "Cross Site Scripting",
:message => message, :message => message,
:user_input => input.match,
:confidence => CONFIDENCE[:high] :confidence => CONFIDENCE[:high]
end end
elsif has_immediate_model? url_arg elsif has_immediate_model? url_arg
...@@ -72,9 +72,9 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo ...@@ -72,9 +72,9 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
# attack. # attack.
elsif @matched elsif @matched
if @matched == :model and not tracker.options[:ignore_model_output] if @matched.type == :model and not tracker.options[:ignore_model_output]
message = "Unsafe model attribute in link_to href" message = "Unsafe model attribute in link_to href"
elsif @matched == :params elsif @matched.type == :params
message = "Unsafe parameter value in link_to href" message = "Unsafe parameter value in link_to href"
end end
...@@ -83,6 +83,7 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo ...@@ -83,6 +83,7 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
warn :result => result, warn :result => result,
:warning_type => "Cross Site Scripting", :warning_type => "Cross Site Scripting",
:message => message, :message => message,
:user_input => @matched.match,
:confidence => CONFIDENCE[:med] :confidence => CONFIDENCE[:med]
end end
end end
......
...@@ -52,10 +52,17 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck ...@@ -52,10 +52,17 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
if attr_protected and tracker.options[:ignore_attr_protected] if attr_protected and tracker.options[:ignore_attr_protected]
return return
elsif include_user_input? call[3] and not hash? call[3][1] and not attr_protected elsif input = include_user_input?(call[3])
confidence = CONFIDENCE[:high] if not hash? call[3][1] and not attr_protected
confidence = CONFIDENCE[:high]
user_input = input.match
else
confidence = CONFIDENCE[:low]
user_input = input.match
end
else else
confidence = CONFIDENCE[:low] confidence = CONFIDENCE[:low]
user_input = nil
end end
warn :result => res, warn :result => res,
...@@ -63,6 +70,7 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck ...@@ -63,6 +70,7 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
:message => "Unprotected mass assignment", :message => "Unprotected mass assignment",
:line => call.line, :line => call.line,
:code => call, :code => call,
:user_input => user_input,
:confidence => confidence :confidence => confidence
end end
......
...@@ -28,7 +28,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck ...@@ -28,7 +28,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
if method == :redirect_to and not only_path?(call) and res = include_user_input?(call) if method == :redirect_to and not only_path?(call) and res = include_user_input?(call)
add_result result add_result result
if res == :immediate if res.type == :immediate
confidence = CONFIDENCE[:high] confidence = CONFIDENCE[:high]
else else
confidence = CONFIDENCE[:low] confidence = CONFIDENCE[:low]
...@@ -39,6 +39,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck ...@@ -39,6 +39,7 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
:message => "Possible unprotected redirect", :message => "Possible unprotected redirect",
:line => call.line, :line => call.line,
:code => call, :code => call,
:user_input => res.match,
:confidence => confidence :confidence => confidence
end end
end end
...@@ -64,16 +65,18 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck ...@@ -64,16 +65,18 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
call[3].each do |arg| call[3].each do |arg|
if call? arg if call? arg
if request_value? arg or request_value? arg[1] if request_value? arg
return :immediate return Match.new(:immediate, arg)
elsif request_value? arg[1]
return Match.new(:immediate, arg[1])
elsif arg[2] == :url_for and include_user_input? arg elsif arg[2] == :url_for and include_user_input? arg
return :immediate return Match.new(:immediate, arg)
#Ignore helpers like some_model_url? #Ignore helpers like some_model_url?
elsif arg[2].to_s =~ /_(url|path)$/ elsif arg[2].to_s =~ /_(url|path)$/
return false return false
end end
elsif request_value? arg elsif request_value? arg
return :immediate return Match.new(:immediate, arg)
end end
end end
......
...@@ -32,11 +32,10 @@ class Brakeman::CheckRender < Brakeman::BaseCheck ...@@ -32,11 +32,10 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
if sexp? view and not duplicate? result if sexp? view and not duplicate? result
add_result result add_result result
type, match = has_immediate_user_input? view
if type if input = has_immediate_user_input?(view)
confidence = CONFIDENCE[:high] confidence = CONFIDENCE[:high]
elsif type = include_user_input?(view) elsif input = include_user_input?(view)
if node_type? view, :string_interp, :dstr if node_type? view, :string_interp, :dstr
confidence = CONFIDENCE[:med] confidence = CONFIDENCE[:med]
else else
...@@ -48,7 +47,7 @@ class Brakeman::CheckRender < Brakeman::BaseCheck ...@@ -48,7 +47,7 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
message = "Render path contains " message = "Render path contains "
case type case input.type
when :params when :params
message << "parameter value" message << "parameter value"
when :cookies when :cookies
...@@ -66,6 +65,7 @@ class Brakeman::CheckRender < Brakeman::BaseCheck ...@@ -66,6 +65,7 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
warn :result => result, warn :result => result,
:warning_type => "Dynamic Render Path", :warning_type => "Dynamic Render Path",
:message => message, :message => message,
:user_input => input.match,
:confidence => confidence :confidence => confidence
end end
end end
......
...@@ -19,19 +19,21 @@ class Brakeman::CheckSend < Brakeman::BaseCheck ...@@ -19,19 +19,21 @@ class Brakeman::CheckSend < Brakeman::BaseCheck
args = process result[:call][3] args = process result[:call][3]
target = process result[:call][1] target = process result[:call][1]
if has_immediate_user_input? args[1] if input = has_immediate_user_input?(args[1])
warn :result => result, warn :result => result,
:warning_type => "Dangerous Send", :warning_type => "Dangerous Send",
:message => "User controlled method execution", :message => "User controlled method execution",
:code => result[:call], :code => result[:call],
:user_input => input.match,
:confidence => CONFIDENCE[:high] :confidence => CONFIDENCE[:high]
end end
if has_immediate_user_input?(target) if input = has_immediate_user_input?(target)
warn :result => result, warn :result => result,
:warning_type => "Dangerous Send", :warning_type => "Dangerous Send",
:message => "User defined target of method invocation", :message => "User defined target of method invocation",
:code => result[:call], :code => result[:call],
:user_input => input.match,
:confidence => CONFIDENCE[:med] :confidence => CONFIDENCE[:med]
end end
end end
......
...@@ -122,15 +122,18 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck ...@@ -122,15 +122,18 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
if failed and not call.original_line and not duplicate? result if failed and not call.original_line and not duplicate? result
add_result result add_result result
if include_user_input? args[-1] if input = include_user_input?(args[-1])
confidence = CONFIDENCE[:high] confidence = CONFIDENCE[:high]
user_input = input.match
else else
confidence = CONFIDENCE[:med] confidence = CONFIDENCE[:med]
user_input = nil
end end
warn :result => result, warn :result => result,
:warning_type => "SQL Injection", :warning_type => "SQL Injection",
:message => "Possible SQL injection", :message => "Possible SQL injection",
:user_input => user_input,
:confidence => confidence :confidence => confidence
end end
......
...@@ -48,10 +48,12 @@ class Brakeman::CheckWithoutProtection < Brakeman::BaseCheck ...@@ -48,10 +48,12 @@ class Brakeman::CheckWithoutProtection < Brakeman::BaseCheck
if true? value if true? value
add_result res add_result res
if include_user_input? call[3] if input = include_user_input?(call[3])
confidence = CONFIDENCE[:high] confidence = CONFIDENCE[:high]
user_input = input.match
else else
confidence = CONFIDENCE[:med] confidence = CONFIDENCE[:med]
user_input = nil
end end
warn :result => res, warn :result => res,
...@@ -59,6 +61,7 @@ class Brakeman::CheckWithoutProtection < Brakeman::BaseCheck ...@@ -59,6 +61,7 @@ class Brakeman::CheckWithoutProtection < Brakeman::BaseCheck
:message => "Unprotected mass assignment", :message => "Unprotected mass assignment",
:line => call.line, :line => call.line,
:code => call, :code => call,
:user_input => user_input,
:confidence => confidence :confidence => confidence
end end
......
...@@ -108,3 +108,7 @@ p { ...@@ -108,3 +108,7 @@ p {
pre.context { pre.context {
margin-bottom: 1px; margin-bottom: 1px;
} }
.user_input {
background-color: #fcecab;
}
...@@ -144,6 +144,10 @@ module Brakeman::Options ...@@ -144,6 +144,10 @@ module Brakeman::Options
options[:combine_locations] = combine options[:combine_locations] = combine
end end
opts.on "--[no-]highlights", "Highlight user input in report" do |highlight|
options[:highlight_user_input] = highlight
end
opts.on "-m", "--routes", "Report controller information" do opts.on "-m", "--routes", "Report controller information" do
options[:report_routes] = true options[:report_routes] = true
end end
......
...@@ -34,6 +34,7 @@ class Brakeman::Report ...@@ -34,6 +34,7 @@ class Brakeman::Report
@checks = tracker.checks @checks = tracker.checks
@element_id = 0 #Used for HTML ids @element_id = 0 #Used for HTML ids
@warnings_summary = nil @warnings_summary = nil
@highlight_user_input = tracker.options[:highlight_user_input]
end end
#Generate summary table of what was parsed #Generate summary table of what was parsed
...@@ -97,6 +98,7 @@ class Brakeman::Report ...@@ -97,6 +98,7 @@ class Brakeman::Report
w["Message"] = with_context warning, w["Message"] w["Message"] = with_context warning, w["Message"]
else else
w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]] w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
w["Message"] = text_message warning, w["Message"]
end end
warning_messages << w warning_messages << w
...@@ -134,6 +136,7 @@ class Brakeman::Report ...@@ -134,6 +136,7 @@ class Brakeman::Report
w["Message"] = with_context warning, w["Message"] w["Message"] = with_context warning, w["Message"]
else else
w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]] w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
w["Message"] = text_message warning, w["Message"]
end end
warnings << w warnings << w
...@@ -169,6 +172,7 @@ class Brakeman::Report ...@@ -169,6 +172,7 @@ class Brakeman::Report
w["Message"] = with_context warning, w["Message"] w["Message"] = with_context warning, w["Message"]
else else
w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]] w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
w["Message"] = text_message warning, w["Message"]
end end
warnings << w warnings << w
...@@ -204,6 +208,7 @@ class Brakeman::Report ...@@ -204,6 +208,7 @@ class Brakeman::Report
w["Message"] = with_context warning, w["Message"] w["Message"] = with_context warning, w["Message"]
else else
w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]] w["Confidence"] = TEXT_CONFIDENCE[w["Confidence"]]
w["Message"] = text_message warning, w["Message"]
end end
warnings << w warnings << w
...@@ -485,6 +490,28 @@ class Brakeman::Report ...@@ -485,6 +490,28 @@ class Brakeman::Report
@warnings_summary = summary @warnings_summary = summary
end end
#Escape warning message and highlight user input in text output
def text_message warning, message
if @highlight_user_input and warning.user_input
user_input = warning.format_user_input
message.gsub(user_input, "+#{user_input}+")
else
message
end
end
#Escape warning message and highlight user input in HTML output
def html_message warning, message
message = CGI.escapeHTML(message)
if @highlight_user_input and warning.user_input
user_input = warning.format_user_input
message.gsub!(user_input, "<span class=\"user_input\">#{user_input}</span>")
end
message
end
#Generate HTML for warnings, including context show/hidden via Javascript #Generate HTML for warnings, including context show/hidden via Javascript
def with_context warning, message def with_context warning, message
...@@ -495,12 +522,14 @@ class Brakeman::Report ...@@ -495,12 +522,14 @@ class Brakeman::Report
tracker.options[:message_limit] > 0 and tracker.options[:message_limit] > 0 and
message.length > tracker.options[:message_limit] message.length > tracker.options[:message_limit]
full_message = message full_message = html_message(warning, message)
message = message[0..tracker.options[:message_limit]] << "..." message = message[0..tracker.options[:message_limit]] << "..."
end end
message = html_message(warning, message)
if context.empty? and not full_message if context.empty? and not full_message
return CGI.escapeHTML(message) return message
end end
@element_id += 1 @element_id += 1
...@@ -510,10 +539,10 @@ class Brakeman::Report ...@@ -510,10 +539,10 @@ class Brakeman::Report
alt = false alt = false
output = "<div class='warning_message' onClick=\"toggle('#{code_id}');toggle('#{message_id}');toggle('#{full_message_id}')\" >" << output = "<div class='warning_message' onClick=\"toggle('#{code_id}');toggle('#{message_id}');toggle('#{full_message_id}')\" >" <<
if full_message if full_message
"<span id='#{message_id}' style='display:block' >#{CGI.escapeHTML(message)}</span>" << "<span id='#{message_id}' style='display:block' >#{message}</span>" <<
"<span id='#{full_message_id}' style='display:none'>#{CGI.escapeHTML(full_message)}</span>" "<span id='#{full_message_id}' style='display:none'>#{full_message}</span>"
else else
CGI.escapeHTML(message) message
end << end <<
"<table id='#{code_id}' class='context' style='display:none'>" << "<table id='#{code_id}' class='context' style='display:none'>" <<
"<caption>#{(warning.file || '').gsub(tracker.options[:app_path], "")}</caption>" "<caption>#{(warning.file || '').gsub(tracker.options[:app_path], "")}</caption>"
......
#The Warning class stores information about warnings #The Warning class stores information about warnings
class Brakeman::Warning class Brakeman::Warning
attr_reader :called_from, :check, :class, :confidence, :controller, attr_reader :called_from, :check, :class, :confidence, :controller,
:line, :method, :model, :template, :warning_set, :warning_type :line, :method, :model, :template, :user_input, :warning_set, :warning_type
attr_accessor :code, :context, :file, :message attr_accessor :code, :context, :file, :message
...@@ -12,7 +12,7 @@ class Brakeman::Warning ...@@ -12,7 +12,7 @@ class Brakeman::Warning
@view_name = nil @view_name = nil
[:called_from, :check, :class, :code, :confidence, :controller, :file, :line, [:called_from, :check, :class, :code, :confidence, :controller, :file, :line,
:message, :method, :model, :template, :warning_set, :warning_type].each do |option| :message, :method, :model, :template, :user_input, :warning_set, :warning_type].each do |option|
self.instance_variable_set("@#{option}", options[option]) self.instance_variable_set("@#{option}", options[option])
end end
...@@ -74,6 +74,12 @@ class Brakeman::Warning ...@@ -74,6 +74,12 @@ class Brakeman::Warning
Brakeman::OutputProcessor.new.format(self.code).gsub(/(\t|\r|\n)+/, " ") Brakeman::OutputProcessor.new.format(self.code).gsub(/(\t|\r|\n)+/, " ")
end end
#Return String of the user input formatted and
#stripped of newlines and tabs.
def format_user_input
Brakeman::OutputProcessor.new.format(self.user_input).gsub(/(\t|\r|\n)+/, " ")
end
#Return formatted warning message #Return formatted warning message
def format_message def format_message
return @format_message if @format_message return @format_message if @format_message
...@@ -143,6 +149,7 @@ class Brakeman::Warning ...@@ -143,6 +149,7 @@ class Brakeman::Warning
:line => self.line, :line => self.line,
:code => (@code && self.format_code), :code => (@code && self.format_code),
:location => location, :location => location,
:user_input => (@user_input && self.format_user_input),
:confidence => TEXT_CONFIDENCE[self.confidence] :confidence => TEXT_CONFIDENCE[self.confidence]
} }
end end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册