brakeman-lib 4.4.0 → 4.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +63 -0
  3. data/README.md +6 -7
  4. data/lib/brakeman.rb +7 -0
  5. data/lib/brakeman/app_tree.rb +34 -22
  6. data/lib/brakeman/call_index.rb +54 -15
  7. data/lib/brakeman/checks.rb +7 -7
  8. data/lib/brakeman/checks/base_check.rb +75 -56
  9. data/lib/brakeman/checks/check_content_tag.rb +12 -0
  10. data/lib/brakeman/checks/check_cookie_serialization.rb +22 -0
  11. data/lib/brakeman/checks/check_cross_site_scripting.rb +15 -10
  12. data/lib/brakeman/checks/check_default_routes.rb +5 -0
  13. data/lib/brakeman/checks/check_deserialize.rb +49 -0
  14. data/lib/brakeman/checks/check_dynamic_finders.rb +1 -1
  15. data/lib/brakeman/checks/check_evaluation.rb +0 -1
  16. data/lib/brakeman/checks/check_execute.rb +44 -1
  17. data/lib/brakeman/checks/check_file_access.rb +7 -1
  18. data/lib/brakeman/checks/check_force_ssl.rb +27 -0
  19. data/lib/brakeman/checks/check_header_dos.rb +2 -2
  20. data/lib/brakeman/checks/check_i18n_xss.rb +2 -2
  21. data/lib/brakeman/checks/check_jruby_xml.rb +2 -2
  22. data/lib/brakeman/checks/check_json_parsing.rb +7 -2
  23. data/lib/brakeman/checks/check_link_to_href.rb +6 -1
  24. data/lib/brakeman/checks/check_mail_to.rb +1 -1
  25. data/lib/brakeman/checks/check_mime_type_dos.rb +2 -2
  26. data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
  27. data/lib/brakeman/checks/check_model_attributes.rb +12 -50
  28. data/lib/brakeman/checks/check_model_serialize.rb +1 -1
  29. data/lib/brakeman/checks/check_nested_attributes_bypass.rb +4 -4
  30. data/lib/brakeman/checks/check_reverse_tabnabbing.rb +54 -0
  31. data/lib/brakeman/checks/check_sanitize_methods.rb +2 -2
  32. data/lib/brakeman/checks/check_secrets.rb +1 -1
  33. data/lib/brakeman/checks/check_send.rb +0 -1
  34. data/lib/brakeman/checks/check_session_manipulation.rb +0 -1
  35. data/lib/brakeman/checks/check_session_settings.rb +15 -12
  36. data/lib/brakeman/checks/check_simple_format.rb +5 -0
  37. data/lib/brakeman/checks/check_skip_before_filter.rb +1 -1
  38. data/lib/brakeman/checks/check_sql.rb +27 -20
  39. data/lib/brakeman/checks/check_validation_regex.rb +1 -1
  40. data/lib/brakeman/checks/check_xml_dos.rb +2 -2
  41. data/lib/brakeman/checks/check_yaml_parsing.rb +10 -18
  42. data/lib/brakeman/differ.rb +16 -28
  43. data/lib/brakeman/file_parser.rb +6 -8
  44. data/lib/brakeman/file_path.rb +85 -0
  45. data/lib/brakeman/options.rb +7 -0
  46. data/lib/brakeman/parsers/haml_embedded.rb +44 -0
  47. data/lib/brakeman/parsers/slim_embedded.rb +44 -0
  48. data/lib/brakeman/parsers/template_parser.rb +8 -8
  49. data/lib/brakeman/processor.rb +4 -5
  50. data/lib/brakeman/processors/alias_processor.rb +49 -7
  51. data/lib/brakeman/processors/base_processor.rb +10 -7
  52. data/lib/brakeman/processors/controller_alias_processor.rb +10 -7
  53. data/lib/brakeman/processors/controller_processor.rb +9 -13
  54. data/lib/brakeman/processors/gem_processor.rb +10 -2
  55. data/lib/brakeman/processors/haml_template_processor.rb +92 -123
  56. data/lib/brakeman/processors/lib/call_conversion_helper.rb +4 -0
  57. data/lib/brakeman/processors/lib/find_all_calls.rb +27 -4
  58. data/lib/brakeman/processors/lib/find_call.rb +3 -64
  59. data/lib/brakeman/processors/lib/module_helper.rb +8 -8
  60. data/lib/brakeman/processors/lib/processor_helper.rb +3 -3
  61. data/lib/brakeman/processors/lib/rails2_config_processor.rb +4 -4
  62. data/lib/brakeman/processors/lib/rails2_route_processor.rb +2 -2
  63. data/lib/brakeman/processors/lib/rails3_config_processor.rb +3 -3
  64. data/lib/brakeman/processors/lib/rails3_route_processor.rb +2 -2
  65. data/lib/brakeman/processors/lib/render_helper.rb +2 -2
  66. data/lib/brakeman/processors/lib/render_path.rb +18 -1
  67. data/lib/brakeman/processors/library_processor.rb +5 -5
  68. data/lib/brakeman/processors/model_processor.rb +4 -5
  69. data/lib/brakeman/processors/output_processor.rb +5 -0
  70. data/lib/brakeman/processors/slim_template_processor.rb +16 -0
  71. data/lib/brakeman/processors/template_alias_processor.rb +32 -5
  72. data/lib/brakeman/processors/template_processor.rb +14 -10
  73. data/lib/brakeman/report.rb +3 -3
  74. data/lib/brakeman/report/ignore/config.rb +2 -3
  75. data/lib/brakeman/report/ignore/interactive.rb +2 -2
  76. data/lib/brakeman/report/pager.rb +1 -0
  77. data/lib/brakeman/report/report_base.rb +51 -6
  78. data/lib/brakeman/report/report_codeclimate.rb +3 -3
  79. data/lib/brakeman/report/report_hash.rb +1 -1
  80. data/lib/brakeman/report/report_html.rb +2 -2
  81. data/lib/brakeman/report/report_json.rb +1 -24
  82. data/lib/brakeman/report/report_table.rb +20 -4
  83. data/lib/brakeman/report/report_tabs.rb +1 -1
  84. data/lib/brakeman/report/report_text.rb +2 -2
  85. data/lib/brakeman/rescanner.rb +13 -12
  86. data/lib/brakeman/scanner.rb +24 -18
  87. data/lib/brakeman/tracker.rb +35 -7
  88. data/lib/brakeman/tracker/collection.rb +4 -3
  89. data/lib/brakeman/tracker/config.rb +44 -48
  90. data/lib/brakeman/tracker/constants.rb +2 -1
  91. data/lib/brakeman/util.rb +18 -147
  92. data/lib/brakeman/version.rb +1 -1
  93. data/lib/brakeman/warning.rb +27 -13
  94. data/lib/brakeman/warning_codes.rb +4 -0
  95. data/lib/ruby_parser/bm_sexp.rb +1 -1
  96. data/lib/ruby_parser/bm_sexp_processor.rb +1 -0
  97. metadata +58 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bcbe25d591f6a58b5aa50a84f07d66139df4de6ff9ec635f37bfddf6dcefce5b
4
- data.tar.gz: eab5e03f499218467a5c45cc33c629e04e8e8743526b9a8f3add8a314d7d8e10
3
+ metadata.gz: e29d6575b47438b336589ce753da72098ad1b7465a4bb78625d5c666d8c1d0a3
4
+ data.tar.gz: 073d249eb7c1915c3c3be01345c25d050127eec929ff2dd77e276433f94606c1
5
5
  SHA512:
6
- metadata.gz: ea3d52f2efefe107a2673b9a6c574c864cc3908fdc908bca688f3b465ed0c4a47e02513dfe2f4da7dfe94c1c576df1b9bb20e4b865d2308a8db0a05435cc911c
7
- data.tar.gz: 130d5925f04b78320ee0e052a66baadf7eb6094f9572758f6013aa22af03e2031c3109df49dd72a62d974f05add592bdf6309ccbae8d03cdec600397b4ae3044
6
+ metadata.gz: 87488f856d5b69554147a900ea4a5e0f8826b8a4a1ca95090c472f279d3014a80d28acfa36e131b07c2a9d78a80ec807fa20b45f7e2c133575da164b8ccf3506
7
+ data.tar.gz: b73fe208fc26c26428b4883aa36e2b0c310f7b271fe9db0111cd5fc23d3fc040b8f90c0875f4736724685515ee6c3a6d20faf4cb1246042fab0fe66a6de41c61
data/CHANGES.md CHANGED
@@ -1,3 +1,66 @@
1
+ # 4.7.0
2
+
3
+ * Refactor `Brakeman::Differ#second_pass` (Benoit Côté-Jodoin)
4
+ * Ignore interpolation in `%W[]`
5
+ * Fix `version_between?` (Andrey Glushkov)
6
+ * Add support for `ruby_parser` 3.14.0
7
+ * Ignore `form_for` for XSS check
8
+ * Update Haml support to Haml 5.x
9
+ * Catch shell injection from `-c` shell commands (Jacob Evelyn)
10
+ * Correctly handle non-symbols in `CheckCookieSerialization` (Phil Turnbull)
11
+
12
+ # 4.6.1
13
+
14
+ * Fix Reverse Tabnabbing warning message (Steffen Schildknecht / Jörg Schiller)
15
+
16
+ # 4.6.0
17
+
18
+ * Skip calls to `dup`
19
+ * Add reverse tabnabbing check (Linos Giannopoulos)
20
+ * Better handling of gems with no version declared
21
+ * Warn people that Haml 5 is not fully supported (Jared Beck)
22
+ * Avoid warning about file access with `ActiveStorage::Filename#sanitized` (Tejas Bubane)
23
+ * Update loofah version for fixing CVE-2018-8048 (Markus Nölle)
24
+ * Restore `Warning#relative_path`
25
+ * Add check for cookie serialization with Marshal
26
+ * Index calls in initializers
27
+ * Improve template output handling in conditional branches
28
+ * Avoid assigning `nil` line numbers to `Sexp`s
29
+ * Add special warning code for custom checks
30
+ * Add call matching by regular expression
31
+
32
+ # 4.5.1
33
+
34
+ * Add `Brakeman::FilePath` to represent file paths
35
+ * Handle trailing comma in block args
36
+ * Properly handle empty partial name
37
+ * Use relative paths for `__FILE__`
38
+ * Convert `!!` calls to boolean value
39
+ * Add optional check for `config.force_ssl`
40
+ * Remove code for Ruby versions prior to 1.9
41
+ * Check `link_to` with block for href XSS
42
+ * Add SQL injection checks for `find_or_create_by` and friends
43
+ * Add deserialization warning for `Oj.load/object_load`
44
+ * Add initial Rails 6 support
45
+ * Add SQL injection checks for `destroy_by`/`delete_by`
46
+
47
+ # 4.5.0
48
+
49
+ * Update `ruby_parser`, use `ruby_parser-legacy`
50
+ * More thoroughly handle `Shellwords` escaping
51
+ * Handle non-integer version number comparisons
52
+ * Use `FileParser` in `Scanner` to parse files
53
+ * Add original exception to `Tracker#errors` list
54
+ * Add support for CoffeeScript in Slim templates
55
+ * Improve support for embedded template "filters"
56
+ * Remove Sass dependency
57
+ * Set location information in `CheckContentTag`
58
+ * Stop swallowing exceptions in `AliasProcessor`
59
+ * Avoid joining strings with different encodings
60
+ * Handle `**` inside Hash literals
61
+ * Better handling of splat/kwsplat arguments
62
+ * Improve "user input" reported for SQL injection
63
+
1
64
  # 4.4.0
2
65
 
3
66
  * Set default encoding to UTF-8
data/README.md CHANGED
@@ -1,7 +1,6 @@
1
1
  [![Brakeman Logo](http://brakemanscanner.org/images/logo_medium.png)](http://brakemanscanner.org/)
2
2
 
3
- [![Build Status](https://travis-ci.org/presidentbeef/brakeman.svg?branch=master)](https://travis-ci.org/presidentbeef/brakeman)
4
- [![Maintainability](https://api.codeclimate.com/v1/badges/1b08a5c74695cb0d11ec/maintainability)](https://codeclimate.com/github/presidentbeef/brakeman/maintainability)
3
+ [![Build Status](https://circleci.com/gh/presidentbeef/brakeman.svg?style=svg)](https://circleci.com/gh/presidentbeef/brakeman)
5
4
  [![Test Coverage](https://api.codeclimate.com/v1/badges/1b08a5c74695cb0d11ec/test_coverage)](https://codeclimate.com/github/presidentbeef/brakeman/test_coverage)
6
5
  [![Gitter](https://badges.gitter.im/presidentbeef/brakeman.svg)](https://gitter.im/presidentbeef/brakeman)
7
6
 
@@ -47,25 +46,25 @@ Outside of Rails root:
47
46
 
48
47
  From a Rails application's root directory:
49
48
 
50
- docker run -v "$(pwd)":/code brakeman
49
+ docker run -v "$(pwd)":/code presidentbeef/brakeman
51
50
 
52
51
  With a little nicer color:
53
52
 
54
- docker run -v "$(pwd)":/code brakeman --color
53
+ docker run -v "$(pwd)":/code presidentbeef/brakeman --color
55
54
 
56
55
  For an HTML report:
57
56
 
58
- docker run -v "$(pwd)":/code brakeman -o brakeman_results.html
57
+ docker run -v "$(pwd)":/code presidentbeef/brakeman -o brakeman_results.html
59
58
 
60
59
  Outside of Rails root (note that the output file is relative to path/to/rails/application):
61
60
 
62
- docker run -v 'path/to/rails/application':/code brakeman -o brakeman_results.html
61
+ docker run -v 'path/to/rails/application':/code presidentbeef/brakeman -o brakeman_results.html
63
62
 
64
63
  # Compatibility
65
64
 
66
65
  Brakeman should work with any version of Rails from 2.3.x to 5.x.
67
66
 
68
- Brakeman can analyze code written with Ruby 1.8 syntax and newer, but requires at least Ruby 1.9.3 to run.
67
+ Brakeman can analyze code written with Ruby 1.8 syntax and newer, but requires at least Ruby 2.3.0 to run.
69
68
 
70
69
  # Basic Options
71
70
 
data/lib/brakeman.rb CHANGED
@@ -55,6 +55,9 @@ module Brakeman
55
55
  # * :print_report - if no output file specified, print to stdout (default: false)
56
56
  # * :quiet - suppress most messages (default: true)
57
57
  # * :rails3 - force Rails 3 mode (automatic)
58
+ # * :rails4 - force Rails 4 mode (automatic)
59
+ # * :rails5 - force Rails 5 mode (automatic)
60
+ # * :rails6 - force Rails 6 mode (automatic)
58
61
  # * :report_routes - show found routes on controllers (default: false)
59
62
  # * :run_checks - array of checks to run (run all if not specified)
60
63
  # * :safe_methods - array of methods to consider safe
@@ -99,6 +102,10 @@ module Brakeman
99
102
  elsif options[:rails5]
100
103
  options[:rails3] = true
101
104
  options[:rails4] = true
105
+ elsif options[:rails6]
106
+ options[:rails3] = true
107
+ options[:rails4] = true
108
+ options[:rails5] = true
102
109
  end
103
110
 
104
111
  options[:output_formats] = get_output_formats options
@@ -1,4 +1,5 @@
1
1
  require 'pathname'
2
+ require 'brakeman/file_path'
2
3
 
3
4
  module Brakeman
4
5
  class AppTree
@@ -62,31 +63,37 @@ module Brakeman
62
63
  @absolute_engine_paths = @engine_paths.select { |path| path.start_with?(File::SEPARATOR) }
63
64
  @relative_engine_paths = @engine_paths - @absolute_engine_paths
64
65
  @gemspec = nil
66
+ @root_search_pattern = nil
65
67
  end
66
68
 
67
- def expand_path(path)
68
- File.expand_path(path, @root)
69
+ # Create a new Brakeman::FilePath
70
+ def file_path(path)
71
+ Brakeman::FilePath.from_app_tree(self, path)
69
72
  end
70
73
 
71
- def read(path)
72
- File.read(File.join(@root, path))
74
+ # Should only be used by Brakeman::FilePath.
75
+ # Use AppTree#file_path(path).absolute instead.
76
+ def expand_path(path)
77
+ File.expand_path(path, @root)
73
78
  end
74
79
 
75
- # This variation requires full paths instead of paths based
76
- # off the project root. I'd prefer to get all the code outside
77
- # of AppTree using project-root based paths (e.g. app/models/user.rb)
78
- # instead of full paths, but I suspect it's an incompatible change.
79
- def read_path(path)
80
- File.read(path)
80
+ # Should only be used by Brakeman::FilePath
81
+ # Use AppTree#file_path(path).relative instead.
82
+ def relative_path(path)
83
+ pname = Pathname.new path
84
+ if path and not path.empty? and pname.absolute?
85
+ pname.relative_path_from(Pathname.new(self.root)).to_s
86
+ else
87
+ path
88
+ end
81
89
  end
82
90
 
83
91
  def exists?(path)
84
- File.exist?(File.join(@root, path))
85
- end
86
-
87
- # This is a pair for #read_path. Again, would like to kill these
88
- def path_exists?(path)
89
- File.exist?(path)
92
+ if path.is_a? Brakeman::FilePath
93
+ path.exists?
94
+ else
95
+ File.exist?(File.join(@root, path))
96
+ end
90
97
  end
91
98
 
92
99
  def initializer_paths
@@ -111,7 +118,7 @@ module Brakeman
111
118
  end
112
119
 
113
120
  def lib_paths
114
- @lib_files ||= find_paths("lib").reject { |path| path.include? "/generators/" or path.include? "lib/tasks/" or path.include? "lib/templates/" } +
121
+ @lib_files ||= find_paths("lib").reject { |path| path.relative.include? "/generators/" or path.relative.include? "lib/tasks/" or path.relative.include? "lib/templates/" } +
115
122
  find_additional_lib_paths +
116
123
  find_helper_paths +
117
124
  find_job_paths
@@ -125,7 +132,7 @@ module Brakeman
125
132
  if gemspecs.length > 1 or gemspecs.empty?
126
133
  @gemspec = false
127
134
  else
128
- @gemspec = File.basename(gemspecs.first)
135
+ @gemspec = file_path(File.basename(gemspecs.first))
129
136
  end
130
137
  end
131
138
 
@@ -155,7 +162,8 @@ module Brakeman
155
162
 
156
163
  def select_files(paths)
157
164
  paths = select_only_files(paths)
158
- reject_skipped_files(paths)
165
+ paths = reject_skipped_files(paths)
166
+ convert_to_file_paths(paths)
159
167
  end
160
168
 
161
169
  def select_only_files(paths)
@@ -190,8 +198,8 @@ module Brakeman
190
198
  def root_search_pattern
191
199
  return @root_search_pattern if @root_search_pattern
192
200
 
193
- abs = @absolute_engine_paths.to_a.map { |path| path.gsub /#{File::SEPARATOR}+$/, '' }
194
- rel = @relative_engine_paths.to_a.map { |path| path.gsub /#{File::SEPARATOR}+$/, '' }
201
+ abs = @absolute_engine_paths.to_a.map { |path| path.gsub(/#{File::SEPARATOR}+$/, '') }
202
+ rel = @relative_engine_paths.to_a.map { |path| path.gsub(/#{File::SEPARATOR}+$/, '') }
195
203
 
196
204
  roots = ([@root] + abs).join(",")
197
205
  rel_engines = (rel + [""]).join("/,")
@@ -199,7 +207,11 @@ module Brakeman
199
207
  end
200
208
 
201
209
  def prioritize_concerns paths
202
- paths.partition { |path| path.include? "concerns" }.flatten
210
+ paths.partition { |path| path.relative.include? "concerns" }.flatten
211
+ end
212
+
213
+ def convert_to_file_paths paths
214
+ paths.map { |path| file_path(path) }
203
215
  end
204
216
  end
205
217
  end
@@ -27,7 +27,7 @@ class Brakeman::CallIndex
27
27
  if options[:chained]
28
28
  return find_chain options
29
29
  #Find by narrowest category
30
- elsif target and method and target.is_a? Array and method.is_a? Array
30
+ elsif target.is_a? Array and method.is_a? Array
31
31
  if target.length > method.length
32
32
  calls = filter_by_target calls_by_methods(method), target
33
33
  else
@@ -35,6 +35,12 @@ class Brakeman::CallIndex
35
35
  calls = filter_by_method calls, method
36
36
  end
37
37
 
38
+ elsif target.is_a? Regexp and method
39
+ calls = filter_by_target(calls_by_method(method), target)
40
+
41
+ elsif method.is_a? Regexp and target
42
+ calls = filter_by_method(calls_by_target(target), method)
43
+
38
44
  #Find by target, then by methods, if provided
39
45
  elsif target
40
46
  calls = calls_by_target target
@@ -85,6 +91,16 @@ class Brakeman::CallIndex
85
91
  end
86
92
  end
87
93
 
94
+ def remove_indexes_by_file file
95
+ [@calls_by_method, @calls_by_target].each do |calls_by|
96
+ calls_by.each do |_name, calls|
97
+ calls.delete_if do |call|
98
+ call[:location][:file] == file
99
+ end
100
+ end
101
+ end
102
+ end
103
+
88
104
  def index_calls calls
89
105
  calls.each do |call|
90
106
  @calls_by_method[call[:method]] ||= []
@@ -116,8 +132,11 @@ class Brakeman::CallIndex
116
132
  end
117
133
 
118
134
  def calls_by_target target
119
- if target.is_a? Array
135
+ case target
136
+ when Array
120
137
  calls_by_targets target
138
+ when Regexp
139
+ calls_by_targets_regex target
121
140
  else
122
141
  @calls_by_target[target] || []
123
142
  end
@@ -133,10 +152,24 @@ class Brakeman::CallIndex
133
152
  calls
134
153
  end
135
154
 
155
+ def calls_by_targets_regex targets_regex
156
+ calls = []
157
+
158
+ @calls_by_target.each do |key, value|
159
+ case key
160
+ when String, Symbol
161
+ calls.concat value if key.match targets_regex
162
+ end
163
+ end
164
+
165
+ calls
166
+ end
167
+
136
168
  def calls_by_method method
137
- if method.is_a? Array
169
+ case method
170
+ when Array
138
171
  calls_by_methods method
139
- elsif method.is_a? Regexp
172
+ when Regexp
140
173
  calls_by_methods_regex method
141
174
  else
142
175
  @calls_by_method[method.to_sym] || []
@@ -156,26 +189,28 @@ class Brakeman::CallIndex
156
189
 
157
190
  def calls_by_methods_regex methods_regex
158
191
  calls = []
192
+
159
193
  @calls_by_method.each do |key, value|
160
- calls.concat value if key.to_s.match methods_regex
194
+ calls.concat value if key.match methods_regex
161
195
  end
162
- calls
163
- end
164
196
 
165
- def calls_with_no_target
166
- @calls_by_target[nil]
197
+ calls
167
198
  end
168
199
 
169
200
  def filter calls, key, value
170
- if value.is_a? Array
201
+ case value
202
+ when Array
171
203
  values = Set.new value
172
204
 
173
205
  calls.select do |call|
174
206
  values.include? call[key]
175
207
  end
176
- elsif value.is_a? Regexp
208
+ when Regexp
177
209
  calls.select do |call|
178
- call[key].to_s.match value
210
+ case call[key]
211
+ when String, Symbol
212
+ call[key].match value
213
+ end
179
214
  end
180
215
  else
181
216
  calls.select do |call|
@@ -197,15 +232,19 @@ class Brakeman::CallIndex
197
232
  end
198
233
 
199
234
  def filter_by_chain calls, target
200
- if target.is_a? Array
235
+ case target
236
+ when Array
201
237
  targets = Set.new target
202
238
 
203
239
  calls.select do |call|
204
240
  targets.include? call[:chain].first
205
241
  end
206
- elsif target.is_a? Regexp
242
+ when Regexp
207
243
  calls.select do |call|
208
- call[:chain].first.to_s.match target
244
+ case call[:chain].first
245
+ when String, Symbol
246
+ call[:chain].first.match target
247
+ end
209
248
  end
210
249
  else
211
250
  calls.select do |call|
@@ -109,13 +109,13 @@ class Brakeman::Checks
109
109
 
110
110
  #Run all the checks on the given Tracker.
111
111
  #Returns a new instance of Checks with the results.
112
- def self.run_checks(app_tree, tracker)
112
+ def self.run_checks(tracker)
113
113
  checks = self.checks_to_run(tracker)
114
114
  check_runner = self.new :min_confidence => tracker.options[:min_confidence]
115
- self.actually_run_checks(checks, check_runner, app_tree, tracker)
115
+ self.actually_run_checks(checks, check_runner, tracker)
116
116
  end
117
117
 
118
- def self.actually_run_checks(checks, check_runner, app_tree, tracker)
118
+ def self.actually_run_checks(checks, check_runner, tracker)
119
119
  threads = [] # Results for parallel
120
120
  results = [] # Results for sequential
121
121
  parallel = tracker.options[:parallel_checks]
@@ -127,10 +127,10 @@ class Brakeman::Checks
127
127
 
128
128
  if parallel
129
129
  threads << Thread.new do
130
- self.run_a_check(c, error_mutex, app_tree, tracker)
130
+ self.run_a_check(c, error_mutex, tracker)
131
131
  end
132
132
  else
133
- results << self.run_a_check(c, error_mutex, app_tree, tracker)
133
+ results << self.run_a_check(c, error_mutex, tracker)
134
134
  end
135
135
 
136
136
  #Maintain list of which checks were run
@@ -196,8 +196,8 @@ class Brakeman::Checks
196
196
  end
197
197
  end
198
198
 
199
- def self.run_a_check klass, mutex, app_tree, tracker
200
- check = klass.new(app_tree, tracker)
199
+ def self.run_a_check klass, mutex, tracker
200
+ check = klass.new(tracker)
201
201
 
202
202
  begin
203
203
  check.run_check
@@ -27,9 +27,9 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
27
27
  end
28
28
 
29
29
  #Initialize Check with Checks.
30
- def initialize(app_tree, tracker)
30
+ def initialize(tracker)
31
31
  super()
32
- @app_tree = app_tree
32
+ @app_tree = tracker.app_tree
33
33
  @results = [] #only to check for duplicates
34
34
  @warnings = []
35
35
  @tracker = tracker
@@ -39,24 +39,15 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
39
39
  @active_record_models = nil
40
40
  @mass_assign_disabled = nil
41
41
  @has_user_input = nil
42
+ @in_array = false
42
43
  @safe_input_attributes = Set[:to_i, :to_f, :arel_table, :id]
43
44
  @comparison_ops = Set[:==, :!=, :>, :<, :>=, :<=]
44
45
  end
45
46
 
46
47
  #Add result to result list, which is used to check for duplicates
47
- def add_result result, location = nil
48
- location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template]
49
- location = location[:name] if location.is_a? Hash
50
- location = location.name if location.is_a? Brakeman::Collection
51
- location = location.to_sym
52
-
53
- if result.is_a? Hash
54
- line = result[:call].original_line || result[:call].line
55
- elsif sexp? result
56
- line = result.original_line || result.line
57
- else
58
- raise ArgumentError
59
- end
48
+ def add_result result
49
+ location = get_location result
50
+ location, line = get_location result
60
51
 
61
52
  @results << [line, location, result]
62
53
  end
@@ -119,9 +110,16 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
119
110
  exp
120
111
  end
121
112
 
113
+ def process_array exp
114
+ @in_array = true
115
+ process_default exp
116
+ ensure
117
+ @in_array = false
118
+ end
119
+
122
120
  #Does not actually process string interpolation, but notes that it occurred.
123
121
  def process_dstr exp
124
- unless @string_interp # don't overwrite existing value
122
+ unless array_interp? exp or @string_interp # don't overwrite existing value
125
123
  @string_interp = Match.new(:interp, exp)
126
124
  end
127
125
 
@@ -130,6 +128,20 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
130
128
 
131
129
  private
132
130
 
131
+ # Checking for
132
+ #
133
+ # %W[#{a}]
134
+ #
135
+ # which will be parsed as
136
+ #
137
+ # s(:array, s(:dstr, "", s(:evstr, s(:call, nil, :a))))
138
+ def array_interp? exp
139
+ @in_array and
140
+ string_interp? exp and
141
+ exp[1] == "".freeze and
142
+ exp.length == 3 # only one interpolated value
143
+ end
144
+
133
145
  def always_safe_method? meth
134
146
  @safe_input_attributes.include? meth or
135
147
  @comparison_ops.include? meth
@@ -143,11 +155,11 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
143
155
  def warn options
144
156
  extra_opts = { :check => self.class.to_s }
145
157
 
146
- warning = Brakeman::Warning.new(options.merge(extra_opts))
147
- warning.file = file_for warning
148
- warning.relative_path = relative_path(warning.file)
158
+ if options[:file]
159
+ options[:file] = @app_tree.file_path(options[:file])
160
+ end
149
161
 
150
- @warnings << warning
162
+ @warnings << Brakeman::Warning.new(options.merge(extra_opts))
151
163
  end
152
164
 
153
165
  #Run _exp_ through OutputProcessor to get a nice String.
@@ -170,8 +182,9 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
170
182
  @mass_assign_disabled = true
171
183
  else
172
184
  #Check for ActiveRecord::Base.send(:attr_accessible, nil)
173
- tracker.check_initializers(:"ActiveRecord::Base", :attr_accessible).each do |result|
174
- call = result.call
185
+ tracker.find_call(target: :"ActiveRecord::Base", method: :attr_accessible).each do |result|
186
+ call = result[:call]
187
+
175
188
  if call? call
176
189
  if call.first_arg == Sexp.new(:nil)
177
190
  @mass_assign_disabled = true
@@ -180,26 +193,12 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
180
193
  end
181
194
  end
182
195
 
183
- unless @mass_assign_disabled
184
- tracker.check_initializers(:"ActiveRecord::Base", :send).each do |result|
185
- call = result.call
186
- if call? call
187
- if call.first_arg == Sexp.new(:lit, :attr_accessible) and call.second_arg == Sexp.new(:nil)
188
- @mass_assign_disabled = true
189
- break
190
- end
191
- end
192
- end
193
- end
194
-
195
196
  unless @mass_assign_disabled
196
197
  #Check for
197
198
  # class ActiveRecord::Base
198
199
  # attr_accessible nil
199
200
  # end
200
- matches = tracker.check_initializers([], :attr_accessible)
201
-
202
- matches.each do |result|
201
+ tracker.check_initializers([], :attr_accessible).each do |result|
203
202
  if result.module == "ActiveRecord" and result.result_class == :Base
204
203
  arg = result.call.first_arg
205
204
 
@@ -227,10 +226,8 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
227
226
  end
228
227
 
229
228
  unless @mass_assign_disabled
230
- matches = tracker.check_initializers(:"ActiveRecord::Base", [:send, :include])
231
-
232
- matches.each do |result|
233
- call = result.call
229
+ tracker.find_call(target: :"ActiveRecord::Base", method: [:send, :include]).each do |result|
230
+ call = result[:call]
234
231
  if call? call and (call.first_arg == forbidden_protection or call.second_arg == forbidden_protection)
235
232
  @mass_assign_disabled = true
236
233
  end
@@ -250,6 +247,22 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
250
247
  #This is to avoid reporting duplicates. Checks if the result has been
251
248
  #reported already from the same line number.
252
249
  def duplicate? result, location = nil
250
+ location, line = get_location result
251
+
252
+ @results.each do |r|
253
+ if r[0] == line and r[1] == location
254
+ if tracker.options[:combine_locations]
255
+ return true
256
+ elsif r[2] == result
257
+ return true
258
+ end
259
+ end
260
+ end
261
+
262
+ false
263
+ end
264
+
265
+ def get_location result
253
266
  if result.is_a? Hash
254
267
  line = result[:call].original_line || result[:call].line
255
268
  elsif sexp? result
@@ -258,23 +271,13 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
258
271
  raise ArgumentError
259
272
  end
260
273
 
261
- location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template]
274
+ location ||= (@current_template && @current_template.name) || @current_class || @current_module || @current_set || result[:location][:class] || result[:location][:template] || result[:location][:file].to_s
262
275
 
263
276
  location = location[:name] if location.is_a? Hash
264
277
  location = location.name if location.is_a? Brakeman::Collection
265
278
  location = location.to_sym
266
279
 
267
- @results.each do |r|
268
- if r[0] == line and r[1] == location
269
- if tracker.options[:combine_locations]
270
- return true
271
- elsif r[2] == result
272
- return true
273
- end
274
- end
275
- end
276
-
277
- false
280
+ return location, line
278
281
  end
279
282
 
280
283
  #Checks if an expression contains string interpolation.
@@ -348,6 +351,22 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
348
351
  when :or
349
352
  has_immediate_user_input? exp.lhs or
350
353
  has_immediate_user_input? exp.rhs
354
+ when :splat, :kwsplat
355
+ exp.each_sexp do |e|
356
+ match = has_immediate_user_input?(e)
357
+ return match if match
358
+ end
359
+
360
+ false
361
+ when :hash
362
+ if kwsplat? exp
363
+ exp[1].each_sexp do |e|
364
+ match = has_immediate_user_input?(e)
365
+ return match if match
366
+ end
367
+
368
+ false
369
+ end
351
370
  else
352
371
  false
353
372
  end
@@ -460,11 +479,11 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
460
479
  if gem_name and info = tracker.config.get_gem(gem_name)
461
480
  info
462
481
  elsif @app_tree.exists?("Gemfile")
463
- "Gemfile"
482
+ @app_tree.file_path "Gemfile"
464
483
  elsif @app_tree.exists?("gems.rb")
465
- "gems.rb"
484
+ @app_tree.file_path "gems.rb"
466
485
  else
467
- "config/environment.rb"
486
+ @app_tree.file_path "config/environment.rb"
468
487
  end
469
488
  end
470
489