brakeman-lib 4.4.0 → 4.7.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 (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