brakeman-min 0.5.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. data/CHANGES +529 -0
  2. data/README.md +74 -28
  3. data/bin/brakeman +60 -266
  4. data/lib/brakeman.rb +422 -0
  5. data/lib/brakeman/app_tree.rb +101 -0
  6. data/lib/brakeman/brakeman.rake +10 -0
  7. data/lib/brakeman/call_index.rb +215 -0
  8. data/lib/brakeman/checks.rb +180 -0
  9. data/lib/brakeman/checks/base_check.rb +538 -0
  10. data/lib/brakeman/checks/check_basic_auth.rb +89 -0
  11. data/lib/brakeman/checks/check_content_tag.rb +162 -0
  12. data/lib/brakeman/checks/check_cross_site_scripting.rb +334 -0
  13. data/lib/{checks → brakeman/checks}/check_default_routes.rb +13 -6
  14. data/lib/brakeman/checks/check_deserialize.rb +57 -0
  15. data/lib/brakeman/checks/check_digest_dos.rb +38 -0
  16. data/lib/brakeman/checks/check_escape_function.rb +21 -0
  17. data/lib/brakeman/checks/check_evaluation.rb +33 -0
  18. data/lib/brakeman/checks/check_execute.rb +98 -0
  19. data/lib/brakeman/checks/check_file_access.rb +62 -0
  20. data/lib/brakeman/checks/check_filter_skipping.rb +31 -0
  21. data/lib/brakeman/checks/check_forgery_setting.rb +54 -0
  22. data/lib/brakeman/checks/check_jruby_xml.rb +38 -0
  23. data/lib/brakeman/checks/check_json_parsing.rb +102 -0
  24. data/lib/brakeman/checks/check_link_to.rb +132 -0
  25. data/lib/brakeman/checks/check_link_to_href.rb +92 -0
  26. data/lib/{checks → brakeman/checks}/check_mail_to.rb +14 -13
  27. data/lib/brakeman/checks/check_mass_assignment.rb +143 -0
  28. data/lib/brakeman/checks/check_model_attr_accessible.rb +48 -0
  29. data/lib/brakeman/checks/check_model_attributes.rb +118 -0
  30. data/lib/brakeman/checks/check_model_serialize.rb +66 -0
  31. data/lib/{checks → brakeman/checks}/check_nested_attributes.rb +10 -6
  32. data/lib/brakeman/checks/check_quote_table_name.rb +40 -0
  33. data/lib/brakeman/checks/check_redirect.rb +177 -0
  34. data/lib/brakeman/checks/check_render.rb +62 -0
  35. data/lib/brakeman/checks/check_response_splitting.rb +21 -0
  36. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +31 -0
  37. data/lib/brakeman/checks/check_sanitize_methods.rb +54 -0
  38. data/lib/brakeman/checks/check_select_tag.rb +60 -0
  39. data/lib/brakeman/checks/check_select_vulnerability.rb +58 -0
  40. data/lib/brakeman/checks/check_send.rb +35 -0
  41. data/lib/brakeman/checks/check_send_file.rb +19 -0
  42. data/lib/brakeman/checks/check_session_settings.rb +145 -0
  43. data/lib/brakeman/checks/check_single_quotes.rb +101 -0
  44. data/lib/brakeman/checks/check_skip_before_filter.rb +62 -0
  45. data/lib/brakeman/checks/check_sql.rb +577 -0
  46. data/lib/brakeman/checks/check_strip_tags.rb +64 -0
  47. data/lib/brakeman/checks/check_symbol_dos.rb +67 -0
  48. data/lib/brakeman/checks/check_translate_bug.rb +45 -0
  49. data/lib/brakeman/checks/check_unsafe_reflection.rb +51 -0
  50. data/lib/brakeman/checks/check_validation_regex.rb +88 -0
  51. data/lib/brakeman/checks/check_without_protection.rb +64 -0
  52. data/lib/brakeman/checks/check_yaml_parsing.rb +121 -0
  53. data/lib/brakeman/differ.rb +66 -0
  54. data/lib/{format → brakeman/format}/style.css +28 -0
  55. data/lib/brakeman/options.rb +256 -0
  56. data/lib/brakeman/parsers/rails2_erubis.rb +6 -0
  57. data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +48 -0
  58. data/lib/{scanner_erubis.rb → brakeman/parsers/rails3_erubis.rb} +8 -21
  59. data/lib/brakeman/processor.rb +102 -0
  60. data/lib/brakeman/processors/alias_processor.rb +780 -0
  61. data/lib/{processors → brakeman/processors}/base_processor.rb +90 -74
  62. data/lib/brakeman/processors/config_processor.rb +14 -0
  63. data/lib/brakeman/processors/controller_alias_processor.rb +334 -0
  64. data/lib/brakeman/processors/controller_processor.rb +265 -0
  65. data/lib/{processors → brakeman/processors}/erb_template_processor.rb +21 -19
  66. data/lib/brakeman/processors/erubis_template_processor.rb +96 -0
  67. data/lib/brakeman/processors/gem_processor.rb +59 -0
  68. data/lib/{processors → brakeman/processors}/haml_template_processor.rb +26 -21
  69. data/lib/brakeman/processors/lib/find_all_calls.rb +185 -0
  70. data/lib/{processors → brakeman/processors}/lib/find_call.rb +23 -28
  71. data/lib/brakeman/processors/lib/find_return_value.rb +134 -0
  72. data/lib/brakeman/processors/lib/processor_helper.rb +82 -0
  73. data/lib/{processors/config_processor.rb → brakeman/processors/lib/rails2_config_processor.rb} +32 -35
  74. data/lib/{processors → brakeman/processors}/lib/rails2_route_processor.rb +60 -52
  75. data/lib/brakeman/processors/lib/rails3_config_processor.rb +129 -0
  76. data/lib/brakeman/processors/lib/rails3_route_processor.rb +282 -0
  77. data/lib/{processors → brakeman/processors}/lib/render_helper.rb +54 -20
  78. data/lib/brakeman/processors/lib/route_helper.rb +62 -0
  79. data/lib/{processors → brakeman/processors}/library_processor.rb +24 -17
  80. data/lib/{processors → brakeman/processors}/model_processor.rb +46 -22
  81. data/lib/{processors → brakeman/processors}/output_processor.rb +34 -40
  82. data/lib/brakeman/processors/route_processor.rb +17 -0
  83. data/lib/brakeman/processors/slim_template_processor.rb +113 -0
  84. data/lib/brakeman/processors/template_alias_processor.rb +120 -0
  85. data/lib/{processors → brakeman/processors}/template_processor.rb +10 -7
  86. data/lib/brakeman/report.rb +68 -0
  87. data/lib/brakeman/report/ignore/config.rb +130 -0
  88. data/lib/brakeman/report/ignore/interactive.rb +311 -0
  89. data/lib/brakeman/report/initializers/faster_csv.rb +7 -0
  90. data/lib/brakeman/report/initializers/multi_json.rb +29 -0
  91. data/lib/brakeman/report/renderer.rb +24 -0
  92. data/lib/brakeman/report/report_base.rb +279 -0
  93. data/lib/brakeman/report/report_csv.rb +56 -0
  94. data/lib/brakeman/report/report_hash.rb +22 -0
  95. data/lib/brakeman/report/report_html.rb +203 -0
  96. data/lib/brakeman/report/report_json.rb +46 -0
  97. data/lib/brakeman/report/report_table.rb +109 -0
  98. data/lib/brakeman/report/report_tabs.rb +17 -0
  99. data/lib/brakeman/report/templates/controller_overview.html.erb +18 -0
  100. data/lib/brakeman/report/templates/controller_warnings.html.erb +17 -0
  101. data/lib/brakeman/report/templates/error_overview.html.erb +25 -0
  102. data/lib/brakeman/report/templates/header.html.erb +44 -0
  103. data/lib/brakeman/report/templates/ignored_warnings.html.erb +21 -0
  104. data/lib/brakeman/report/templates/model_warnings.html.erb +17 -0
  105. data/lib/brakeman/report/templates/overview.html.erb +34 -0
  106. data/lib/brakeman/report/templates/security_warnings.html.erb +19 -0
  107. data/lib/brakeman/report/templates/template_overview.html.erb +17 -0
  108. data/lib/brakeman/report/templates/view_warnings.html.erb +30 -0
  109. data/lib/brakeman/report/templates/warning_overview.html.erb +13 -0
  110. data/lib/brakeman/rescanner.rb +446 -0
  111. data/lib/brakeman/scanner.rb +362 -0
  112. data/lib/brakeman/tracker.rb +296 -0
  113. data/lib/brakeman/util.rb +413 -0
  114. data/lib/brakeman/version.rb +3 -0
  115. data/lib/brakeman/warning.rb +217 -0
  116. data/lib/brakeman/warning_codes.rb +68 -0
  117. data/lib/ruby_parser/bm_sexp.rb +562 -0
  118. data/lib/ruby_parser/bm_sexp_processor.rb +230 -0
  119. metadata +152 -66
  120. data/lib/checks.rb +0 -71
  121. data/lib/checks/base_check.rb +0 -357
  122. data/lib/checks/check_cross_site_scripting.rb +0 -336
  123. data/lib/checks/check_evaluation.rb +0 -27
  124. data/lib/checks/check_execute.rb +0 -110
  125. data/lib/checks/check_file_access.rb +0 -46
  126. data/lib/checks/check_forgery_setting.rb +0 -42
  127. data/lib/checks/check_mass_assignment.rb +0 -74
  128. data/lib/checks/check_model_attributes.rb +0 -36
  129. data/lib/checks/check_redirect.rb +0 -98
  130. data/lib/checks/check_render.rb +0 -65
  131. data/lib/checks/check_send_file.rb +0 -15
  132. data/lib/checks/check_session_settings.rb +0 -79
  133. data/lib/checks/check_sql.rb +0 -146
  134. data/lib/checks/check_validation_regex.rb +0 -60
  135. data/lib/processor.rb +0 -86
  136. data/lib/processors/alias_processor.rb +0 -384
  137. data/lib/processors/controller_alias_processor.rb +0 -237
  138. data/lib/processors/controller_processor.rb +0 -202
  139. data/lib/processors/erubis_template_processor.rb +0 -85
  140. data/lib/processors/lib/find_model_call.rb +0 -39
  141. data/lib/processors/lib/processor_helper.rb +0 -36
  142. data/lib/processors/lib/rails3_route_processor.rb +0 -184
  143. data/lib/processors/lib/route_helper.rb +0 -34
  144. data/lib/processors/params_processor.rb +0 -77
  145. data/lib/processors/route_processor.rb +0 -11
  146. data/lib/processors/template_alias_processor.rb +0 -86
  147. data/lib/report.rb +0 -680
  148. data/lib/scanner.rb +0 -227
  149. data/lib/tracker.rb +0 -144
  150. data/lib/util.rb +0 -141
  151. data/lib/version.rb +0 -1
  152. data/lib/warning.rb +0 -99
@@ -0,0 +1,89 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ #Checks if password is stored in controller
4
+ #when using http_basic_authenticate_with
5
+ #
6
+ #Only for Rails >= 3.1
7
+ class Brakeman::CheckBasicAuth < Brakeman::BaseCheck
8
+ Brakeman::Checks.add self
9
+
10
+ @description = "Checks for the use of http_basic_authenticate_with"
11
+
12
+ def run_check
13
+ return if version_between? "0.0.0", "3.0.99"
14
+
15
+ check_basic_auth_filter
16
+ check_basic_auth_request
17
+ end
18
+
19
+ def check_basic_auth_filter
20
+ controllers = tracker.controllers.select do |name, c|
21
+ c[:options][:http_basic_authenticate_with]
22
+ end
23
+
24
+ Hash[controllers].each do |name, controller|
25
+ controller[:options][:http_basic_authenticate_with].each do |call|
26
+
27
+ if pass = get_password(call) and string? pass
28
+ warn :controller => name,
29
+ :warning_type => "Basic Auth",
30
+ :warning_code => :basic_auth_password,
31
+ :message => "Basic authentication password stored in source code",
32
+ :code => call,
33
+ :confidence => 0,
34
+ :file => controller[:file]
35
+
36
+ break
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ # Look for
43
+ # authenticate_or_request_with_http_basic do |username, password|
44
+ # username == "foo" && password == "bar"
45
+ # end
46
+ def check_basic_auth_request
47
+ tracker.find_call(:target => nil, :method => :authenticate_or_request_with_http_basic).each do |result|
48
+ if include_password_literal? result
49
+ warn :result => result,
50
+ :code => @include_password,
51
+ :warning_type => "Basic Auth",
52
+ :warning_code => :basic_auth_password,
53
+ :message => "Basic authentication password stored in source code",
54
+ :confidence => 0
55
+ end
56
+ end
57
+ end
58
+
59
+ # Check if the block of a result contains a comparison of password to string
60
+ def include_password_literal? result
61
+ @password_var = result[:block_args].last
62
+ @include_password = false
63
+ process result[:block]
64
+ @include_password
65
+ end
66
+
67
+ # Looks for :== calls on password var
68
+ def process_call exp
69
+ target = exp.target
70
+
71
+ if node_type?(target, :lvar) and
72
+ target.value == @password_var and
73
+ exp.method == :== and
74
+ string? exp.first_arg
75
+
76
+ @include_password = exp
77
+ end
78
+
79
+ exp
80
+ end
81
+
82
+ def get_password call
83
+ arg = call.first_arg
84
+
85
+ return false if arg.nil? or not hash? arg
86
+
87
+ hash_access(arg, :password)
88
+ end
89
+ end
@@ -0,0 +1,162 @@
1
+ require 'brakeman/checks/check_cross_site_scripting'
2
+
3
+ #Checks for unescaped values in `content_tag`
4
+ #
5
+ # content_tag :tag, body
6
+ # ^-- Unescaped in Rails 2.x
7
+ #
8
+ # content_tag, :tag, body, attribute => value
9
+ # ^-- Unescaped in all versions
10
+ #
11
+ # content_tag, :tag, body, attribute => value
12
+ # ^
13
+ # |
14
+ # Escaped by default, can be explicitly escaped
15
+ # or not by passing in (true|false) as fourth argument
16
+ class Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting
17
+ Brakeman::Checks.add self
18
+
19
+ @description = "Checks for XSS in calls to content_tag"
20
+
21
+ def run_check
22
+ @ignore_methods = Set[:button_to, :check_box, :escapeHTML, :escape_once,
23
+ :field_field, :fields_for, :h, :hidden_field,
24
+ :hidden_field, :hidden_field_tag, :image_tag, :label,
25
+ :mail_to, :radio_button, :select,
26
+ :submit_tag, :text_area, :text_field,
27
+ :text_field_tag, :url_encode, :url_for,
28
+ :will_paginate].merge tracker.options[:safe_methods]
29
+
30
+ @known_dangerous = []
31
+ methods = tracker.find_call :target => false, :method => :content_tag
32
+
33
+ @models = tracker.models.keys
34
+ @inspect_arguments = tracker.options[:check_arguments]
35
+ @mark = nil
36
+
37
+ Brakeman.debug "Checking for XSS in content_tag"
38
+ methods.each do |call|
39
+ process_result call
40
+ end
41
+ end
42
+
43
+ def process_result result
44
+ return if duplicate? result
45
+
46
+ call = result[:call] = result[:call].dup
47
+
48
+ args = call.arglist
49
+
50
+ tag_name = args[1]
51
+ content = args[2]
52
+ attributes = args[3]
53
+ escape_attr = args[4]
54
+
55
+ @matched = false
56
+
57
+ #Silly, but still dangerous if someone uses user input in the tag type
58
+ check_argument result, tag_name
59
+
60
+ #Versions before 3.x do not escape body of tag, nor does the rails_xss gem
61
+ unless @matched or (tracker.options[:rails3] and not raw? content)
62
+ check_argument result, content
63
+ end
64
+
65
+ #Attribute keys are never escaped, so check them for user input
66
+ if not @matched and hash? attributes and not request_value? attributes
67
+ hash_iterate(attributes) do |k, v|
68
+ check_argument result, k
69
+ return if @matched
70
+ end
71
+ end
72
+
73
+ #By default, content_tag escapes attribute values passed in as a hash.
74
+ #But this behavior can be disabled. So only check attributes hash
75
+ #if they are explicitly not escaped.
76
+ if not @matched and attributes and false? escape_attr
77
+ if request_value? attributes or not hash? attributes
78
+ check_argument result, attributes
79
+ else #check hash values
80
+ hash_iterate(attributes) do |k, v|
81
+ check_argument result, v
82
+ return if @matched
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def check_argument result, exp
89
+ #Check contents of raw() calls directly
90
+ if call? exp and exp.method == :raw
91
+ arg = process exp.first_arg
92
+ else
93
+ arg = process exp
94
+ end
95
+
96
+ if input = has_immediate_user_input?(arg)
97
+ message = "Unescaped #{friendly_type_of input} in content_tag"
98
+
99
+ add_result result
100
+
101
+ warn :result => result,
102
+ :warning_type => "Cross Site Scripting",
103
+ :warning_code => :xss_content_tag,
104
+ :message => message,
105
+ :user_input => input.match,
106
+ :confidence => CONFIDENCE[:high],
107
+ :link_path => "content_tag"
108
+
109
+ elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(arg)
110
+ method = match[2]
111
+
112
+ unless IGNORE_MODEL_METHODS.include? method
113
+ add_result result
114
+
115
+ if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
116
+ confidence = CONFIDENCE[:high]
117
+ else
118
+ confidence = CONFIDENCE[:med]
119
+ end
120
+
121
+ warn :result => result,
122
+ :warning_type => "Cross Site Scripting",
123
+ :warning_code => :xss_content_tag,
124
+ :message => "Unescaped model attribute in content_tag",
125
+ :user_input => match,
126
+ :confidence => confidence,
127
+ :link_path => "content_tag"
128
+ end
129
+
130
+ elsif @matched
131
+ return if @matched.type == :model and tracker.options[:ignore_model_output]
132
+
133
+ message = "Unescaped #{friendly_type_of @matched} in content_tag"
134
+
135
+ add_result result
136
+
137
+ warn :result => result,
138
+ :warning_type => "Cross Site Scripting",
139
+ :warning_code => :xss_content_tag,
140
+ :message => message,
141
+ :user_input => @matched.match,
142
+ :confidence => CONFIDENCE[:med],
143
+ :link_path => "content_tag"
144
+ end
145
+ end
146
+
147
+ def process_call exp
148
+ if @mark
149
+ actually_process_call exp
150
+ else
151
+ @mark = true
152
+ actually_process_call exp
153
+ @mark = false
154
+ end
155
+
156
+ exp
157
+ end
158
+
159
+ def raw? exp
160
+ call? exp and exp.method == :raw
161
+ end
162
+ end
@@ -0,0 +1,334 @@
1
+ require 'brakeman/checks/base_check'
2
+ require 'brakeman/processors/lib/find_call'
3
+ require 'brakeman/processors/lib/processor_helper'
4
+ require 'brakeman/util'
5
+ require 'set'
6
+
7
+ #This check looks for unescaped output in templates which contains
8
+ #parameters or model attributes.
9
+ #
10
+ #For example:
11
+ #
12
+ # <%= User.find(:id).name %>
13
+ # <%= params[:id] %>
14
+ class Brakeman::CheckCrossSiteScripting < Brakeman::BaseCheck
15
+ Brakeman::Checks.add self
16
+
17
+ @description = "Checks for unescaped output in views"
18
+
19
+ #Model methods which are known to be harmless
20
+ IGNORE_MODEL_METHODS = Set[:average, :count, :maximum, :minimum, :sum, :id]
21
+
22
+ MODEL_METHODS = Set[:all, :find, :first, :last, :new]
23
+
24
+ IGNORE_LIKE = /^link_to_|(_path|_tag|_url)$/
25
+
26
+ HAML_HELPERS = Sexp.new(:colon2, Sexp.new(:const, :Haml), :Helpers)
27
+
28
+ XML_HELPER = Sexp.new(:colon2, Sexp.new(:const, :Erubis), :XmlHelper)
29
+
30
+ URI = Sexp.new(:const, :URI)
31
+
32
+ CGI = Sexp.new(:const, :CGI)
33
+
34
+ FORM_BUILDER = Sexp.new(:call, Sexp.new(:const, :FormBuilder), :new)
35
+
36
+ #Run check
37
+ def run_check
38
+ @ignore_methods = Set[:button_to, :check_box, :content_tag, :escapeHTML, :escape_once,
39
+ :field_field, :fields_for, :h, :hidden_field,
40
+ :hidden_field, :hidden_field_tag, :image_tag, :label,
41
+ :link_to, :mail_to, :radio_button, :select,
42
+ :submit_tag, :text_area, :text_field,
43
+ :text_field_tag, :url_encode, :url_for,
44
+ :will_paginate].merge tracker.options[:safe_methods]
45
+
46
+ @models = tracker.models.keys
47
+ @inspect_arguments = tracker.options[:check_arguments]
48
+
49
+ @known_dangerous = Set[:truncate, :concat]
50
+
51
+ if version_between? "2.0.0", "3.0.5"
52
+ @known_dangerous << :auto_link
53
+ elsif version_between? "3.0.6", "3.0.99"
54
+ @ignore_methods << :auto_link
55
+ end
56
+
57
+ if version_between? "2.0.0", "2.3.14"
58
+ @known_dangerous << :strip_tags
59
+ end
60
+
61
+ json_escape_on = false
62
+ initializers = tracker.check_initializers :ActiveSupport, :escape_html_entities_in_json=
63
+ initializers.each {|result| json_escape_on = true?(result.call.first_arg) }
64
+
65
+ if tracker.config[:rails][:active_support] and
66
+ true? tracker.config[:rails][:active_support][:escape_html_entities_in_json]
67
+
68
+ json_escape_on = true
69
+ end
70
+
71
+ if !json_escape_on or version_between? "0.0.0", "2.0.99"
72
+ @known_dangerous << :to_json
73
+ Brakeman.debug("Automatic to_json escaping not enabled, consider to_json dangerous")
74
+ else
75
+ @safe_input_attributes << :to_json
76
+ Brakeman.debug("Automatic to_json escaping is enabled.")
77
+ end
78
+
79
+ tracker.each_template do |name, template|
80
+ Brakeman.debug "Checking #{name} for XSS"
81
+
82
+ @current_template = template
83
+
84
+ template[:outputs].each do |out|
85
+ unless check_for_immediate_xss out
86
+ @matched = false
87
+ @mark = false
88
+ process out
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ def check_for_immediate_xss exp
95
+ return :duplicate if duplicate? exp
96
+
97
+ if exp.node_type == :output
98
+ out = exp.value
99
+ elsif exp.node_type == :escaped_output and raw_call? exp
100
+ out = exp.value.first_arg
101
+ end
102
+
103
+ if input = has_immediate_user_input?(out)
104
+ add_result exp
105
+
106
+ message = "Unescaped #{friendly_type_of input}"
107
+
108
+ warn :template => @current_template,
109
+ :warning_type => "Cross Site Scripting",
110
+ :warning_code => :cross_site_scripting,
111
+ :message => message,
112
+ :code => input.match,
113
+ :confidence => CONFIDENCE[:high]
114
+
115
+ elsif not tracker.options[:ignore_model_output] and match = has_immediate_model?(out)
116
+ method = if call? match
117
+ match.method
118
+ else
119
+ nil
120
+ end
121
+
122
+ unless IGNORE_MODEL_METHODS.include? method
123
+ add_result exp
124
+
125
+ if MODEL_METHODS.include? method or method.to_s =~ /^find_by/
126
+ confidence = CONFIDENCE[:high]
127
+ else
128
+ confidence = CONFIDENCE[:med]
129
+ end
130
+
131
+ message = "Unescaped model attribute"
132
+ link_path = "cross_site_scripting"
133
+ warning_code = :cross_site_scripting
134
+
135
+ if node_type?(out, :call, :attrasgn) && out.method == :to_json
136
+ message += " in JSON hash"
137
+ link_path += "_to_json"
138
+ warning_code = :xss_to_json
139
+ end
140
+
141
+ code = find_chain out, match
142
+ warn :template => @current_template,
143
+ :warning_type => "Cross Site Scripting",
144
+ :warning_code => warning_code,
145
+ :message => message,
146
+ :code => code,
147
+ :confidence => confidence,
148
+ :link_path => link_path
149
+ end
150
+
151
+ else
152
+ false
153
+ end
154
+ end
155
+
156
+ #Process an output Sexp
157
+ def process_output exp
158
+ process exp.value.dup
159
+ end
160
+
161
+ #Look for calls to raw()
162
+ #Otherwise, ignore
163
+ def process_escaped_output exp
164
+ unless check_for_immediate_xss exp
165
+ if raw_call? exp and not duplicate? exp
166
+ process exp.value.first_arg
167
+ end
168
+ end
169
+ exp
170
+ end
171
+
172
+ #Check a call for user input
173
+ #
174
+ #
175
+ #Since we want to report an entire call and not just part of one, use @mark
176
+ #to mark when a call is started. Any dangerous values inside will then
177
+ #report the entire call chain.
178
+ def process_call exp
179
+ if @mark
180
+ actually_process_call exp
181
+ else
182
+ @mark = true
183
+ actually_process_call exp
184
+ message = nil
185
+
186
+ if @matched
187
+ unless @matched.type and tracker.options[:ignore_model_output]
188
+ message = "Unescaped #{friendly_type_of @matched}"
189
+ end
190
+
191
+ if message and not duplicate? exp
192
+ add_result exp
193
+
194
+ link_path = "cross_site_scripting"
195
+ if @known_dangerous.include? exp.method
196
+ confidence = CONFIDENCE[:high]
197
+ if exp.method == :to_json
198
+ message += " in JSON hash"
199
+ link_path += "_to_json"
200
+ end
201
+ else
202
+ confidence = CONFIDENCE[:low]
203
+ end
204
+
205
+ warn :template => @current_template,
206
+ :warning_type => "Cross Site Scripting",
207
+ :warning_code => :xss_to_json,
208
+ :message => message,
209
+ :code => exp,
210
+ :user_input => @matched.match,
211
+ :confidence => confidence,
212
+ :link_path => link_path
213
+ end
214
+ end
215
+
216
+ @mark = @matched = false
217
+ end
218
+
219
+ exp
220
+ end
221
+
222
+ def actually_process_call exp
223
+ return if @matched
224
+ target = exp.target
225
+ if sexp? target
226
+ target = process target
227
+ end
228
+
229
+ method = exp.method
230
+
231
+ #Ignore safe items
232
+ if ignore_call? target, method
233
+ @matched = false
234
+ elsif sexp? target and model_name? target[1] #TODO: use method call?
235
+ @matched = Match.new(:model, exp)
236
+ elsif cookies? exp
237
+ @matched = Match.new(:cookies, exp)
238
+ elsif @inspect_arguments and params? exp
239
+ @matched = Match.new(:params, exp)
240
+ elsif @inspect_arguments
241
+ process_call_args exp
242
+ end
243
+ end
244
+
245
+ #Note that params have been found
246
+ def process_params exp
247
+ @matched = Match.new(:params, exp)
248
+ exp
249
+ end
250
+
251
+ #Note that cookies have been found
252
+ def process_cookies exp
253
+ @matched = Match.new(:cookies, exp)
254
+ exp
255
+ end
256
+
257
+ #Ignore calls to render
258
+ def process_render exp
259
+ exp
260
+ end
261
+
262
+ #Process as default
263
+ def process_string_interp exp
264
+ process_default exp
265
+ end
266
+
267
+ #Process as default
268
+ def process_format exp
269
+ process_default exp
270
+ end
271
+
272
+ #Ignore output HTML escaped via HAML
273
+ def process_format_escaped exp
274
+ exp
275
+ end
276
+
277
+ #Ignore condition in if Sexp
278
+ def process_if exp
279
+ process exp.then_clause if sexp? exp.then_clause
280
+ process exp.else_clause if sexp? exp.else_clause
281
+ exp
282
+ end
283
+
284
+ def raw_call? exp
285
+ exp.value.node_type == :call and exp.value.method == :raw
286
+ end
287
+
288
+ def ignore_call? target, method
289
+ ignored_method?(target, method) or
290
+ safe_input_attribute?(target, method) or
291
+ ignored_model_method?(method) or
292
+ form_builder_method?(target, method) or
293
+ haml_escaped?(target, method) or
294
+ boolean_method?(method) or
295
+ cgi_escaped?(target, method) or
296
+ xml_escaped?(target, method)
297
+ end
298
+
299
+ def ignored_model_method? method
300
+ @matched and
301
+ @matched.type == :model and
302
+ IGNORE_MODEL_METHODS.include? method
303
+ end
304
+
305
+ def ignored_method? target, method
306
+ target.nil? and
307
+ (@ignore_methods.include? method or method.to_s =~ IGNORE_LIKE)
308
+ end
309
+
310
+ def cgi_escaped? target, method
311
+ method == :escape and
312
+ (target == URI or target == CGI)
313
+ end
314
+
315
+ def haml_escaped? target, method
316
+ method == :html_escape and target == HAML_HELPERS
317
+ end
318
+
319
+ def xml_escaped? target, method
320
+ method == :escape_xml and target == XML_HELPER
321
+ end
322
+
323
+ def form_builder_method? target, method
324
+ target == FORM_BUILDER and @ignore_methods.include? method
325
+ end
326
+
327
+ def safe_input_attribute? target, method
328
+ target and @safe_input_attributes.include? method
329
+ end
330
+
331
+ def boolean_method? method
332
+ method.to_s.end_with? "?"
333
+ end
334
+ end