brakeman-lib 4.3.1 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +24 -1
  3. data/README.md +35 -6
  4. data/bin/brakeman +2 -0
  5. data/lib/brakeman.rb +5 -3
  6. data/lib/brakeman/app_tree.rb +15 -1
  7. data/lib/brakeman/call_index.rb +7 -4
  8. data/lib/brakeman/checks.rb +16 -8
  9. data/lib/brakeman/checks/base_check.rb +2 -19
  10. data/lib/brakeman/checks/check_basic_auth_timing_attack.rb +1 -1
  11. data/lib/brakeman/checks/check_content_tag.rb +4 -4
  12. data/lib/brakeman/checks/check_create_with.rb +1 -1
  13. data/lib/brakeman/checks/check_cross_site_scripting.rb +3 -3
  14. data/lib/brakeman/checks/check_default_routes.rb +3 -3
  15. data/lib/brakeman/checks/check_deserialize.rb +1 -1
  16. data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
  17. data/lib/brakeman/checks/check_digest_dos.rb +4 -4
  18. data/lib/brakeman/checks/check_escape_function.rb +1 -1
  19. data/lib/brakeman/checks/check_execute.rb +5 -4
  20. data/lib/brakeman/checks/check_file_access.rb +13 -3
  21. data/lib/brakeman/checks/check_file_disclosure.rb +1 -1
  22. data/lib/brakeman/checks/check_filter_skipping.rb +1 -1
  23. data/lib/brakeman/checks/check_forgery_setting.rb +3 -3
  24. data/lib/brakeman/checks/check_header_dos.rb +3 -3
  25. data/lib/brakeman/checks/check_i18n_xss.rb +3 -3
  26. data/lib/brakeman/checks/check_jruby_xml.rb +1 -1
  27. data/lib/brakeman/checks/check_json_encoding.rb +3 -3
  28. data/lib/brakeman/checks/check_json_parsing.rb +8 -11
  29. data/lib/brakeman/checks/check_link_to.rb +3 -3
  30. data/lib/brakeman/checks/check_link_to_href.rb +2 -2
  31. data/lib/brakeman/checks/check_mail_to.rb +3 -3
  32. data/lib/brakeman/checks/check_mime_type_dos.rb +1 -1
  33. data/lib/brakeman/checks/check_model_attributes.rb +4 -4
  34. data/lib/brakeman/checks/check_model_serialize.rb +1 -1
  35. data/lib/brakeman/checks/check_nested_attributes.rb +3 -3
  36. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +1 -1
  37. data/lib/brakeman/checks/check_number_to_currency.rb +4 -4
  38. data/lib/brakeman/checks/check_quote_table_name.rb +2 -2
  39. data/lib/brakeman/checks/check_regex_dos.rb +1 -1
  40. data/lib/brakeman/checks/check_render.rb +2 -2
  41. data/lib/brakeman/checks/check_render_dos.rb +1 -1
  42. data/lib/brakeman/checks/check_render_inline.rb +1 -1
  43. data/lib/brakeman/checks/check_response_splitting.rb +1 -1
  44. data/lib/brakeman/checks/check_route_dos.rb +1 -1
  45. data/lib/brakeman/checks/check_safe_buffer_manipulation.rb +1 -1
  46. data/lib/brakeman/checks/check_sanitize_methods.rb +3 -3
  47. data/lib/brakeman/checks/check_secrets.rb +1 -1
  48. data/lib/brakeman/checks/check_select_tag.rb +1 -1
  49. data/lib/brakeman/checks/check_select_vulnerability.rb +1 -1
  50. data/lib/brakeman/checks/check_session_manipulation.rb +1 -1
  51. data/lib/brakeman/checks/check_session_settings.rb +1 -1
  52. data/lib/brakeman/checks/check_simple_format.rb +2 -2
  53. data/lib/brakeman/checks/check_single_quotes.rb +14 -10
  54. data/lib/brakeman/checks/check_skip_before_filter.rb +2 -2
  55. data/lib/brakeman/checks/check_sprockets_path_traversal.rb +39 -0
  56. data/lib/brakeman/checks/check_sql.rb +1 -1
  57. data/lib/brakeman/checks/check_sql_cves.rb +2 -2
  58. data/lib/brakeman/checks/check_strip_tags.rb +10 -8
  59. data/lib/brakeman/checks/check_symbol_dos.rb +1 -1
  60. data/lib/brakeman/checks/check_symbol_dos_cve.rb +1 -1
  61. data/lib/brakeman/checks/check_translate_bug.rb +7 -7
  62. data/lib/brakeman/checks/check_unsafe_reflection.rb +1 -1
  63. data/lib/brakeman/checks/check_unscoped_find.rb +1 -1
  64. data/lib/brakeman/checks/check_validation_regex.rb +1 -1
  65. data/lib/brakeman/checks/check_weak_hash.rb +18 -19
  66. data/lib/brakeman/checks/check_xml_dos.rb +1 -1
  67. data/lib/brakeman/checks/check_yaml_parsing.rb +1 -1
  68. data/lib/brakeman/format/style.css +8 -0
  69. data/lib/brakeman/messages.rb +220 -0
  70. data/lib/brakeman/options.rb +13 -0
  71. data/lib/brakeman/parsers/template_parser.rb +2 -2
  72. data/lib/brakeman/processors/alias_processor.rb +7 -0
  73. data/lib/brakeman/processors/config_processor.rb +4 -1
  74. data/lib/brakeman/processors/gem_processor.rb +30 -2
  75. data/lib/brakeman/processors/lib/call_conversion_helper.rb +2 -1
  76. data/lib/brakeman/processors/lib/rails3_route_processor.rb +0 -2
  77. data/lib/brakeman/processors/lib/rails4_config_processor.rb +18 -0
  78. data/lib/brakeman/processors/lib/render_helper.rb +5 -0
  79. data/lib/brakeman/processors/lib/render_path.rb +15 -0
  80. data/lib/brakeman/processors/library_processor.rb +1 -1
  81. data/lib/brakeman/report/report_base.rb +17 -161
  82. data/lib/brakeman/report/report_csv.rb +17 -0
  83. data/lib/brakeman/report/report_html.rb +34 -31
  84. data/lib/brakeman/report/report_json.rb +21 -0
  85. data/lib/brakeman/report/report_markdown.rb +13 -6
  86. data/lib/brakeman/report/report_table.rb +157 -0
  87. data/lib/brakeman/report/report_tabs.rb +3 -1
  88. data/lib/brakeman/report/report_text.rb +16 -0
  89. data/lib/brakeman/scanner.rb +5 -1
  90. data/lib/brakeman/tracker/config.rb +1 -1
  91. data/lib/brakeman/util.rb +0 -17
  92. data/lib/brakeman/version.rb +1 -1
  93. data/lib/brakeman/warning.rb +9 -4
  94. data/lib/brakeman/warning_codes.rb +1 -0
  95. metadata +13 -10
@@ -23,7 +23,7 @@ class Brakeman::CheckXMLDoS < Brakeman::BaseCheck
23
23
 
24
24
  return if has_workaround?
25
25
 
26
- message = "Rails #{version} is vulnerable to denial of service via XML parsing (CVE-2015-3227). Upgrade to Rails version #{fix_version}"
26
+ message = msg(msg_version(version), " is vulnerable to denial of service via XML parsing ", msg_cve("CVE-2015-3227"), ". Upgrade to ", msg_version(fix_version))
27
27
 
28
28
  warn :warning_type => "Denial of Service",
29
29
  :warning_code => :CVE_2015_3227,
@@ -22,7 +22,7 @@ class Brakeman::CheckYAMLParsing < Brakeman::BaseCheck
22
22
  "3.2.11"
23
23
  end
24
24
 
25
- message = "Rails #{rails_version} has a remote code execution vulnerability: upgrade to #{new_version} or disable XML parsing"
25
+ message = msg(msg_version(rails_version), " has a remote code execution vulnerability. Upgrade to ", msg_version(new_version), " or disable XML parsing")
26
26
 
27
27
  warn :warning_type => "Remote Code Execution",
28
28
  :warning_code => :CVE_2013_0156,
@@ -131,3 +131,11 @@ p {
131
131
  div.template_name:hover {
132
132
  background-color: white;
133
133
  }
134
+
135
+ span.code {
136
+ font-family: monospace;
137
+ }
138
+
139
+ span.filename {
140
+ font-family: monospace;
141
+ }
@@ -0,0 +1,220 @@
1
+ module Brakeman
2
+ module Messages
3
+ # Create a new message from a list of messages.
4
+ # Strings are converted to Brakeman::Messages::Plain objects.
5
+ def msg *args
6
+ parts = args.map do |a|
7
+ if a.is_a? String
8
+ Plain.new(a)
9
+ else
10
+ a
11
+ end
12
+ end
13
+
14
+ Message.new(*parts)
15
+ end
16
+
17
+ # Create a new code message fragment
18
+ def msg_code code
19
+ Code.new code
20
+ end
21
+
22
+ # Create a new message fragment with a CVE identifier
23
+ def msg_cve cve
24
+ CVE.new cve
25
+ end
26
+
27
+ # Create a new message fragment representing a file name
28
+ def msg_file str
29
+ Messages::FileName.new str
30
+ end
31
+
32
+ # Create a new message fragment from a user input type (e.g. `:params`).
33
+ # The input type will be converted to a friendly version (e.g. "parameter value").
34
+ def msg_input input
35
+ Input.new input
36
+ end
37
+
38
+ # Create a new message fragment which will not be modified during output
39
+ def msg_lit str
40
+ Literal.new str
41
+ end
42
+
43
+ # Create a new plain string message fragment
44
+ def msg_plain str
45
+ Plain.new str
46
+ end
47
+
48
+ # Create a message fragment representing the version of a library
49
+ def msg_version version, lib = "Rails"
50
+ Version.new version, lib
51
+ end
52
+ end
53
+ end
54
+
55
+ # Class to represent a list of message types
56
+ class Brakeman::Messages::Message
57
+ def initialize *args
58
+ @parts = args.map do |a|
59
+ case a
60
+ when String, Symbol
61
+ Brakeman::Messages::Plain.new(a.to_s)
62
+ else
63
+ a
64
+ end
65
+ end
66
+ end
67
+
68
+ def << msg
69
+ if msg.is_a? String
70
+ @parts << Brakeman::Messages::Plain.new(msg)
71
+ else
72
+ @parts << msg
73
+ end
74
+ end
75
+
76
+ def to_s
77
+ output = @parts.map(&:to_s).join
78
+
79
+ case @parts.first
80
+ when Brakeman::Messages::Code, Brakeman::Messages::Literal, Brakeman::Messages::Version
81
+ else
82
+ output[0] = output[0].capitalize
83
+ end
84
+
85
+ output
86
+ end
87
+
88
+ def to_html
89
+ require 'cgi'
90
+
91
+ output = @parts.map(&:to_html).join
92
+
93
+ case @parts.first
94
+ when Brakeman::Messages::Code, Brakeman::Messages::Literal, Brakeman::Messages::Version
95
+ else
96
+ output[0] = output[0].capitalize
97
+ end
98
+
99
+ output
100
+ end
101
+ end
102
+
103
+ class Brakeman::Messages::Code
104
+ def initialize code
105
+ @code = code.to_s
106
+ end
107
+
108
+ def to_s
109
+ "`#{@code}`"
110
+ end
111
+
112
+ def to_html
113
+ "<span class=\"code\">#{CGI.escapeHTML(@code)}</span>"
114
+ end
115
+ end
116
+
117
+ class Brakeman::Messages::CVE
118
+ def initialize cve
119
+ @cve = cve
120
+ end
121
+
122
+ def to_s
123
+ "(#{@cve})"
124
+ end
125
+
126
+ def to_html
127
+ "(<a href=\"https://cve.mitre.org/cgi-bin/cvename.cgi?name=#{@cve}\" target=\"_blank\" rel=\"noreferrer\">#{@cve}</a>)"
128
+ end
129
+ end
130
+
131
+ class Brakeman::Messages::FileName
132
+ def initialize file
133
+ @file = file
134
+ end
135
+
136
+ def to_s
137
+ "`#{@file}`"
138
+ end
139
+
140
+ def to_html
141
+ "<span class=\"filename\">#{CGI.escapeHTML(@file)}</span>"
142
+ end
143
+ end
144
+
145
+ class Brakeman::Messages::Input
146
+ def initialize input
147
+ @input = input
148
+ @value = friendly_type_of(@input)
149
+ end
150
+
151
+ def friendly_type_of input_type
152
+ if input_type.is_a? Brakeman::BaseCheck::Match
153
+ input_type = input_type.type
154
+ end
155
+
156
+ case input_type
157
+ when :params
158
+ "parameter value"
159
+ when :cookies
160
+ "cookie value"
161
+ when :request
162
+ "request value"
163
+ when :model
164
+ "model attribute"
165
+ else
166
+ "user input"
167
+ end
168
+ end
169
+
170
+ def to_s
171
+ @value
172
+ end
173
+
174
+ def to_html
175
+ self.to_s
176
+ end
177
+ end
178
+
179
+ class Brakeman::Messages::Literal
180
+ def initialize value
181
+ @value = value.to_s
182
+ end
183
+
184
+ def to_s
185
+ @value
186
+ end
187
+
188
+ def to_html
189
+ @value
190
+ end
191
+ end
192
+
193
+ class Brakeman::Messages::Plain
194
+ def initialize string
195
+ @value = string
196
+ end
197
+
198
+ def to_s
199
+ @value
200
+ end
201
+
202
+ def to_html
203
+ CGI.escapeHTML(@value)
204
+ end
205
+ end
206
+
207
+ class Brakeman::Messages::Version
208
+ def initialize version, lib
209
+ @version = version
210
+ @library = lib
211
+ end
212
+
213
+ def to_s
214
+ "#{@library} #{@version}"
215
+ end
216
+
217
+ def to_html
218
+ CGI.escapeHTML(self.to_s)
219
+ end
220
+ end
@@ -169,6 +169,19 @@ module Brakeman::Options
169
169
  options[:engine_paths].merge paths
170
170
  end
171
171
 
172
+ opts.on "-E", "--enable Check1,Check2,etc", Array, "Enable the specified checks" do |checks|
173
+ checks.map! do |check|
174
+ if check.start_with? "Check"
175
+ check
176
+ else
177
+ "Check" << check
178
+ end
179
+ end
180
+
181
+ options[:enable_checks] ||= Set.new
182
+ options[:enable_checks].merge checks
183
+ end
184
+
172
185
  opts.on "-t", "--test Check1,Check2,etc", Array, "Only run the specified checks" do |checks|
173
186
  checks.each_with_index do |s, index|
174
187
  if s[0,5] != "Check"
@@ -59,9 +59,9 @@ module Brakeman
59
59
  else
60
60
  require 'erb'
61
61
  src = if ERB.instance_method(:initialize).parameters.assoc(:key) # Ruby 2.6+
62
- ERB.new(text, trim_mode: path).src
62
+ ERB.new(text, trim_mode: '-').src
63
63
  else
64
- ERB.new(text, nil, path).src
64
+ ERB.new(text, nil, '-').src
65
65
  end
66
66
  src.sub!(/^#.*\n/, '') if Brakeman::Scanner::RUBY_1_9
67
67
  src
@@ -731,6 +731,13 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
731
731
  exp[2 + i] = process_if_branch branch
732
732
  end
733
733
  else
734
+ # Translate `if !...` into `unless ...`
735
+ # Technically they are different but that's only if someone overrides `!`
736
+ if call? condition and condition.method == :!
737
+ condition = condition.target
738
+ exps.reverse!
739
+ end
740
+
734
741
  was_inside = @inside_if
735
742
  @inside_if = true
736
743
 
@@ -1,11 +1,14 @@
1
1
  require 'brakeman/processors/base_processor'
2
2
  require 'brakeman/processors/alias_processor'
3
+ require 'brakeman/processors/lib/rails4_config_processor.rb'
3
4
  require 'brakeman/processors/lib/rails3_config_processor.rb'
4
5
  require 'brakeman/processors/lib/rails2_config_processor.rb'
5
6
 
6
7
  class Brakeman::ConfigProcessor
7
8
  def self.new tracker
8
- if tracker.options[:rails3]
9
+ if tracker.options[:rails4]
10
+ Brakeman::Rails4ConfigProcessor.new tracker
11
+ elsif tracker.options[:rails3]
9
12
  Brakeman::Rails3ConfigProcessor.new tracker
10
13
  else
11
14
  Brakeman::Rails2ConfigProcessor.new tracker
@@ -10,8 +10,17 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
10
10
 
11
11
  def process_gems gem_files
12
12
  @gem_files = gem_files
13
- @gemfile = gem_files[:gemfile][:file]
14
- process gem_files[:gemfile][:src]
13
+ @gemfile = gem_files[:gemfile] && gem_files[:gemfile][:file]
14
+ @gemspec = gem_files[:gemspec] && gem_files[:gemspec][:file]
15
+
16
+
17
+ if @gemspec
18
+ process gem_files[:gemspec][:src]
19
+ end
20
+
21
+ if @gemfile
22
+ process gem_files[:gemfile][:src]
23
+ end
15
24
 
16
25
  if gem_files[:gemlock]
17
26
  process_gem_lock
@@ -41,11 +50,30 @@ class Brakeman::GemProcessor < Brakeman::BasicProcessor
41
50
  @tracker.config.set_ruby_version version.value
42
51
  end
43
52
  end
53
+ elsif @inside_gemspec and exp.method == :add_dependency
54
+ if string? exp.first_arg and string? exp.last_arg
55
+ @tracker.config.add_gem exp.first_arg.value, exp.last_arg.value, @gemspec, exp.line
56
+ end
44
57
  end
45
58
 
46
59
  exp
47
60
  end
48
61
 
62
+ GEM_SPEC = s(:colon2, s(:const, :Gem), :Specification)
63
+
64
+ def process_iter exp
65
+ if exp.block_call.target == GEM_SPEC and exp.block_call.method == :new
66
+ @inside_gemspec = true
67
+ process exp.block if sexp? exp.block
68
+
69
+ exp
70
+ else
71
+ process_default exp
72
+ end
73
+ ensure
74
+ @inside_gemspec = false
75
+ end
76
+
49
77
  def process_gem_lock
50
78
  line_num = 1
51
79
  file = @gem_files[:gemlock][:file]
@@ -9,7 +9,8 @@ module Brakeman
9
9
  # Join two array literals into one.
10
10
  def join_arrays lhs, rhs, original_exp = nil
11
11
  if array? lhs and array? rhs
12
- result = Sexp.new(:array).line(lhs.line)
12
+ result = Sexp.new(:array)
13
+ result.line(lhs.line || rhs.line)
13
14
  result.concat lhs[1..-1]
14
15
  result.concat rhs[1..-1]
15
16
  result
@@ -183,8 +183,6 @@ class Brakeman::Rails3RoutesProcessor < Brakeman::BasicProcessor
183
183
  else
184
184
  add_route route[1], route[0]
185
185
  end
186
- elsif in_controller_block? and symbol? first_arg
187
- add_route first_arg
188
186
  else hash? first_arg
189
187
  hash_iterate first_arg do |k, v|
190
188
  if string? k
@@ -0,0 +1,18 @@
1
+ require 'brakeman/processors/lib/rails3_config_processor'
2
+
3
+ class Brakeman::Rails4ConfigProcessor < Brakeman::Rails3ConfigProcessor
4
+ APPLICATION_CONFIG = s(:call, s(:call, s(:const, :Rails), :application), :configure)
5
+
6
+ # Look for Rails.application.configure do ... end
7
+ def process_iter exp
8
+ if exp.block_call == APPLICATION_CONFIG
9
+ @inside_config = true
10
+ process exp.block if sexp? exp.block
11
+ @inside_config = false
12
+ else
13
+ super
14
+ end
15
+
16
+ exp
17
+ end
18
+ end
@@ -65,6 +65,11 @@ module Brakeman::RenderHelper
65
65
  return
66
66
  end
67
67
 
68
+ if called_from
69
+ # Track actual template that was rendered
70
+ called_from.last_template = template
71
+ end
72
+
68
73
  template_env = only_ivars(:include_request_vars)
69
74
 
70
75
  #Hash the environment and the source of the template to avoid
@@ -29,6 +29,17 @@ module Brakeman
29
29
  self
30
30
  end
31
31
 
32
+ def last_template= template
33
+ if @path.last
34
+ @path.last[:rendered] = {
35
+ name: template.name,
36
+ file: template.file,
37
+ }
38
+ else
39
+ Brakeman.debug "[Notice] No render path to add template information"
40
+ end
41
+ end
42
+
32
43
  def include_template? name
33
44
  name = name.to_sym
34
45
 
@@ -71,6 +82,10 @@ module Brakeman
71
82
  @path.length
72
83
  end
73
84
 
85
+ def map &block
86
+ @path.map &block
87
+ end
88
+
74
89
  def to_a
75
90
  @path.map do |loc|
76
91
  case loc[:type]