brakeman-min 4.10.1 → 5.0.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0d7f92e4d46129b7c1861da8af2bd27a0127640865d04dacc07e92dfcc2457a
4
- data.tar.gz: a917080c19ddeed2b76e9509e9ffc9cfde9ff103f506085ee22681c5d5feef4c
3
+ metadata.gz: ef344d29a0b41b77d931c49ce79ad88e4f17c4e69c51de28a850016029691d35
4
+ data.tar.gz: b2c7ddb17560b73213f8923e99c8dc96a37be8cd7697bf417d06f66368bfdf98
5
5
  SHA512:
6
- metadata.gz: 16bfcae603909f9a89719fdb3f92e7d5d75cabe4285e5b40bdff759da2cbc82f361b6c190a7b9f0d5fa1199e1bd9f39e17e9894966ddf646fba9463db1089a5f
7
- data.tar.gz: 766484db48898d8495a3ade3930edf52d508267f134b581938f426d43684c0bdd1929c3c0bc96cd73ce7e9c16e975e5b3fa24e7911f83c84fb396bf30349b830
6
+ metadata.gz: 7eb5af1fffd3fe230ddf3ea70d826a3eaf82cfc43090cde0b452f24f3382e2c6b1bfcfc84b997b3f4c0bb8088f635965e5297c78fd17a21e87d9aa11dc115d3e
7
+ data.tar.gz: 6edc3a49f3426c0a215afa6a99ddaf5d531a514df23bd51101f13b800b28640a6b0384f8343c26748e1fe1476159e5a229f904e0728784eb4648de85d22d9be8
data/CHANGES.md CHANGED
@@ -1,10 +1,12 @@
1
- # 4.10.1 - 2020-12-24
2
-
3
- * Declare REXML as a dependency (Ruby 3.0 compatibility)
4
- * Use `Sexp#sexp_body` instead of `Sexp#[..]` (Ruby 3.0 compatibility)
5
- * Prevent render loops when template names are absolute paths
6
- * Ensure RubyParser is passed file path as a String
7
- * Support new Haml 5.2.0 escaping method
1
+ # 5.0.0.pre1 - 2020-11-17
2
+
3
+ * Add check for (more) unsafe method reflection
4
+ * Suggest using `--force` if no Rails application is detected
5
+ * Add Sonarqube report format (Adam England)
6
+ * Add check for potential HTTP verb confusion
7
+ * Add `--[no-]skip-vendor` option
8
+ * Scan (almost) all Ruby files in project
9
+ * Add support for Haml 5.2.0
8
10
 
9
11
  # 4.10.0 - 2020-09-28
10
12
 
data/README.md CHANGED
@@ -76,7 +76,7 @@ To specify an output file for the results:
76
76
 
77
77
  brakeman -o output_file
78
78
 
79
- The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `junit`, `markdown`, `csv`, and `codeclimate`.
79
+ The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `junit`, `markdown`, `csv`, `codeclimate`, and `sonar`.
80
80
 
81
81
  Multiple output files can be specified:
82
82
 
@@ -21,6 +21,7 @@ module Brakeman
21
21
  end
22
22
  init_options[:additional_libs_path] = options[:additional_libs_path]
23
23
  init_options[:engine_paths] = options[:engine_paths]
24
+ init_options[:skip_vendor] = options[:skip_vendor]
24
25
  new(root, init_options)
25
26
  end
26
27
 
@@ -62,6 +63,7 @@ module Brakeman
62
63
  @engine_paths = init_options[:engine_paths] || []
63
64
  @absolute_engine_paths = @engine_paths.select { |path| path.start_with?(File::SEPARATOR) }
64
65
  @relative_engine_paths = @engine_paths - @absolute_engine_paths
66
+ @skip_vendor = init_options[:skip_vendor]
65
67
  @gemspec = nil
66
68
  @root_search_pattern = nil
67
69
  end
@@ -96,6 +98,10 @@ module Brakeman
96
98
  end
97
99
  end
98
100
 
101
+ def ruby_file_paths
102
+ find_paths(".").uniq
103
+ end
104
+
99
105
  def initializer_paths
100
106
  @initializer_paths ||= prioritize_concerns(find_paths("config/initializers"))
101
107
  end
@@ -109,8 +115,8 @@ module Brakeman
109
115
  end
110
116
 
111
117
  def template_paths
112
- @template_paths ||= find_paths("app/**/views", "*.{#{VIEW_EXTENSIONS}}") +
113
- find_paths("app/**/views", "*.{erb,haml,slim}").reject { |path| File.basename(path).count(".") > 1 }
118
+ @template_paths ||= find_paths(".", "*.{#{VIEW_EXTENSIONS}}") +
119
+ find_paths("**", "*.{erb,haml,slim}").reject { |path| File.basename(path).count(".") > 1 }
114
120
  end
115
121
 
116
122
  def layout_exists?(name)
@@ -163,7 +169,8 @@ module Brakeman
163
169
  def select_files(paths)
164
170
  paths = select_only_files(paths)
165
171
  paths = reject_skipped_files(paths)
166
- convert_to_file_paths(paths)
172
+ paths = convert_to_file_paths(paths)
173
+ reject_global_excludes(paths)
167
174
  end
168
175
 
169
176
  def select_only_files(paths)
@@ -182,6 +189,32 @@ module Brakeman
182
189
  end
183
190
  end
184
191
 
192
+ EXCLUDED_PATHS = %w[
193
+ /generators/
194
+ lib/tasks/
195
+ lib/templates/
196
+ db/
197
+ spec/
198
+ test/
199
+ tmp/
200
+ public/
201
+ log/
202
+ ]
203
+
204
+ def reject_global_excludes(paths)
205
+ paths.reject do |path|
206
+ relative_path = path.relative
207
+
208
+ if @skip_vendor and relative_path.include? 'vendor/'
209
+ true
210
+ else
211
+ EXCLUDED_PATHS.any? do |excluded|
212
+ relative_path.include? excluded
213
+ end
214
+ end
215
+ end
216
+ end
217
+
185
218
  def match_path files, path
186
219
  absolute_path = Pathname.new(path)
187
220
  # relative root never has a leading separator. But, we use a leading
@@ -208,7 +208,7 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
208
208
  if node_type? e, :if
209
209
  # If we're in a conditional, evaluate the `then` and `else` clauses to
210
210
  # see if they're dangerous.
211
- if res = dangerous?(e.sexp_body.sexp_body)
211
+ if res = dangerous?(e.values[1..-1])
212
212
  return res
213
213
  end
214
214
  elsif node_type? e, :or, :evstr, :dstr
@@ -29,7 +29,7 @@ class Brakeman::CheckRegexDoS < Brakeman::BaseCheck
29
29
  return unless original? result
30
30
 
31
31
  call = result[:call]
32
- components = call.sexp_body
32
+ components = call[1..-1]
33
33
 
34
34
  components.any? do |component|
35
35
  next unless sexp? component
@@ -0,0 +1,68 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckUnsafeReflectionMethods < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Checks for unsafe reflection to access methods"
7
+
8
+ def run_check
9
+ check_method
10
+ check_tap
11
+ check_to_proc
12
+ end
13
+
14
+ def check_method
15
+ tracker.find_call(method: :method, nested: true).each do |result|
16
+ argument = result[:call].first_arg
17
+
18
+ if user_input = include_user_input?(argument)
19
+ warn_unsafe_reflection(result, user_input)
20
+ end
21
+ end
22
+ end
23
+
24
+ def check_tap
25
+ tracker.find_call(method: :tap, nested: true).each do |result|
26
+ argument = result[:call].first_arg
27
+
28
+ # Argument is passed like a.tap(&argument)
29
+ if node_type? argument, :block_pass
30
+ argument = argument.value
31
+ end
32
+
33
+ if user_input = include_user_input?(argument)
34
+ warn_unsafe_reflection(result, user_input)
35
+ end
36
+ end
37
+ end
38
+
39
+ def check_to_proc
40
+ tracker.find_call(method: :to_proc, nested: true).each do |result|
41
+ target = result[:call].target
42
+
43
+ if user_input = include_user_input?(target)
44
+ warn_unsafe_reflection(result, user_input)
45
+ end
46
+ end
47
+ end
48
+
49
+ def warn_unsafe_reflection result, input
50
+ return unless original? result
51
+ method = result[:call].method
52
+
53
+ confidence = if input.type == :params
54
+ :high
55
+ else
56
+ :medium
57
+ end
58
+
59
+ message = msg("Unsafe reflection method ", msg_code(method), " called with ", msg_input(input))
60
+
61
+ warn :result => result,
62
+ :warning_type => "Remote Code Execution",
63
+ :warning_code => :unsafe_method_reflection,
64
+ :message => message,
65
+ :user_input => input,
66
+ :confidence => confidence
67
+ end
68
+ end
@@ -0,0 +1,75 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckVerbConfusion < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Check for uses of `request.get?` that might have unintentional behavior"
7
+
8
+ #Process calls
9
+ def run_check
10
+ calls = tracker.find_call(target: :request, methods: [:get?])
11
+
12
+ calls.each do |call|
13
+ process_result call
14
+ end
15
+ end
16
+
17
+ def process_result result
18
+ @current_result = result
19
+ @matched_call = result[:call]
20
+ klass = tracker.find_class(result[:location][:class])
21
+
22
+ # TODO: abstract into tracker.find_location ?
23
+ if klass.nil?
24
+ Brakeman.debug "No class found: #{result[:location][:class]}"
25
+ return
26
+ end
27
+
28
+ method = klass.get_method(result[:location][:method])
29
+
30
+ if method.nil?
31
+ Brakeman.debug "No method found: #{result[:location][:method]}"
32
+ return
33
+ end
34
+
35
+ process method[:src]
36
+ end
37
+
38
+ def process_if exp
39
+ if exp.condition == @matched_call
40
+ # Found `if request.get?`
41
+
42
+ # Do not warn if there is an `elsif` clause
43
+ if node_type? exp.else_clause, :if
44
+ return exp
45
+ end
46
+
47
+ warn_about_result @current_result, exp
48
+ end
49
+
50
+ exp
51
+ end
52
+
53
+ def warn_about_result result, code
54
+ return unless original? result
55
+
56
+ confidence = :weak
57
+ message = msg('Potential HTTP verb confusion. ',
58
+ msg_code('HEAD'),
59
+ ' is routed like ',
60
+ msg_code('GET'),
61
+ ' but ',
62
+ msg_code('request.get?'),
63
+ ' will return ',
64
+ msg_code('false')
65
+ )
66
+
67
+ warn :result => result,
68
+ :warning_type => "HTTP Verb Confusion",
69
+ :warning_code => :http_verb_confusion,
70
+ :message => message,
71
+ :code => code,
72
+ :user_input => result[:call],
73
+ :confidence => confidence
74
+ end
75
+ end
@@ -3,55 +3,51 @@ module Brakeman
3
3
 
4
4
  # This class handles reading and parsing files.
5
5
  class FileParser
6
- attr_reader :file_list
6
+ attr_reader :file_list, :errors
7
7
 
8
- def initialize tracker
9
- @tracker = tracker
10
- @timeout = @tracker.options[:parser_timeout]
11
- @app_tree = @tracker.app_tree
12
- @file_list = {}
8
+ def initialize app_tree, timeout
9
+ @app_tree = app_tree
10
+ @timeout = timeout
11
+ @file_list = []
12
+ @errors = []
13
13
  end
14
14
 
15
- def parse_files list, type
16
- read_files list, type do |path, contents|
15
+ def parse_files list
16
+ read_files list do |path, contents|
17
17
  if ast = parse_ruby(contents, path.relative)
18
18
  ASTFile.new(path, ast)
19
19
  end
20
20
  end
21
21
  end
22
22
 
23
- def read_files list, type
24
- @file_list[type] ||= []
25
-
23
+ def read_files list
26
24
  list.each do |path|
27
25
  file = @app_tree.file_path(path)
28
26
 
29
27
  result = yield file, file.read
28
+
30
29
  if result
31
- @file_list[type] << result
30
+ @file_list << result
32
31
  end
33
32
  end
34
33
  end
35
34
 
36
- # _path_ can be a string or a Brakeman::FilePath
37
35
  def parse_ruby input, path
38
- if path.is_a? Brakeman::FilePath
39
- path = path.relative
40
- end
41
-
42
36
  begin
43
37
  Brakeman.debug "Parsing #{path}"
44
38
  RubyParser.new.parse input, path, @timeout
45
39
  rescue Racc::ParseError => e
46
- @tracker.error e, "Could not parse #{path}"
47
- nil
40
+ error e.exception(e.message + "\nCould not parse #{path}")
48
41
  rescue Timeout::Error => e
49
- @tracker.error Exception.new("Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout"), caller
50
- nil
42
+ error Exception.new("Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout")
51
43
  rescue => e
52
- @tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
53
- nil
44
+ error e.exception(e.message + "\nWhile processing #{path}")
54
45
  end
55
46
  end
47
+
48
+ def error exception
49
+ @errors << exception
50
+ nil
51
+ end
56
52
  end
57
53
  end
@@ -166,6 +166,10 @@ module Brakeman::Options
166
166
  options[:only_files].merge files
167
167
  end
168
168
 
169
+ opts.on "--[no-]skip-vendor", "Skip processing vendor directory (Default)" do |skip|
170
+ options[:skip_vendor] = skip
171
+ end
172
+
169
173
  opts.on "--skip-libs", "Skip processing lib directory" do
170
174
  options[:skip_libs] = true
171
175
  end
@@ -229,7 +233,7 @@ module Brakeman::Options
229
233
 
230
234
  opts.on "-f",
231
235
  "--format TYPE",
232
- [:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif],
236
+ [:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar],
233
237
  "Specify output formats. Default is text" do |type|
234
238
 
235
239
  type = "s" if type == :text
@@ -9,7 +9,6 @@ module Brakeman
9
9
  def initialize tracker, file_parser
10
10
  @tracker = tracker
11
11
  @file_parser = file_parser
12
- @file_parser.file_list[:templates] ||= []
13
12
  end
14
13
 
15
14
  def parse_template path, text
@@ -33,7 +32,7 @@ module Brakeman
33
32
  end
34
33
 
35
34
  if src and ast = @file_parser.parse_ruby(src, path)
36
- @file_parser.file_list[:templates] << TemplateFile.new(path, ast, name, type)
35
+ @file_parser.file_list << TemplateFile.new(path, ast, name, type)
37
36
  end
38
37
  rescue Racc::ParseError => e
39
38
  tracker.error e, "Could not parse #{path}"
@@ -97,7 +96,7 @@ module Brakeman
97
96
  end
98
97
 
99
98
  def self.parse_inline_erb tracker, text
100
- fp = Brakeman::FileParser.new(tracker)
99
+ fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
101
100
  tp = self.new(tracker, fp)
102
101
  src = tp.parse_erb '_inline_', text
103
102
  type = tp.erubis? ? :erubis : :erb
@@ -236,7 +236,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
236
236
  env[target_var] = target
237
237
  return target
238
238
  elsif string? target and string_interp? first_arg
239
- exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg.sexp_body(2))
239
+ exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg[2..-1])
240
240
  env[target_var] = exp
241
241
  elsif string? first_arg and string_interp? target
242
242
  if string? target.last
@@ -941,7 +941,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
941
941
  args = exp.args
942
942
  exp.pop # remove last arg
943
943
  if args.length > 1
944
- exp.arglist = args.sexp_body
944
+ exp.arglist = args[1..-1]
945
945
  end
946
946
  end
947
947
 
@@ -202,7 +202,7 @@ class Brakeman::ControllerProcessor < Brakeman::BaseProcessor
202
202
  end
203
203
 
204
204
  if node_type? exp.block, :block
205
- block_inner = exp.block.sexp_body
205
+ block_inner = exp.block[1..-1]
206
206
  else
207
207
  block_inner = [exp.block]
208
208
  end
@@ -0,0 +1,64 @@
1
+ module Brakeman
2
+ class FileTypeDetector < BaseProcessor
3
+ def initialize
4
+ super(nil)
5
+ reset
6
+ end
7
+
8
+ def detect_type(file)
9
+ reset
10
+ process(file.ast)
11
+
12
+ if @file_type.nil?
13
+ @file_type = guess_from_path(file.path.relative)
14
+ end
15
+
16
+ @file_type || :libs
17
+ end
18
+
19
+ MODEL_CLASSES = [
20
+ :'ActiveRecord::Base',
21
+ :ApplicationRecord
22
+ ]
23
+
24
+ def process_class exp
25
+ name = class_name(exp.class_name)
26
+ parent = class_name(exp.parent_name)
27
+
28
+ if name.match(/Controller$/)
29
+ @file_type = :controllers
30
+ return exp
31
+ elsif MODEL_CLASSES.include? parent
32
+ @file_type = :models
33
+ return exp
34
+ end
35
+
36
+ super
37
+ end
38
+
39
+ def guess_from_path path
40
+ case
41
+ when path.include?('app/models')
42
+ :models
43
+ when path.include?('app/controllers')
44
+ :controllers
45
+ when path.include?('config/initializers')
46
+ :initializers
47
+ when path.include?('lib/')
48
+ :libs
49
+ when path.match?(%r{config/environments/(?!production\.rb)$})
50
+ :skip
51
+ when path.match?(%r{environments/production\.rb$})
52
+ :skip
53
+ when path.match?(%r{application\.rb$})
54
+ :skip
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def reset
61
+ @file_type = nil
62
+ end
63
+ end
64
+ end
@@ -88,7 +88,7 @@ class Brakeman::OutputProcessor < Ruby2Ruby
88
88
 
89
89
  def process_iter exp
90
90
  call = process exp[1]
91
- block = process_rlist exp.sexp_body(3)
91
+ block = process_rlist exp[3..-1]
92
92
  out = "#{call} do\n #{block}\n end"
93
93
 
94
94
  out
@@ -20,11 +20,6 @@ class Brakeman::TemplateAliasProcessor < Brakeman::AliasProcessor
20
20
 
21
21
  #Process template
22
22
  def process_template name, args, _, line = nil
23
- # Strip forward slash from beginning of template path.
24
- # This also happens in RenderHelper#process_template but
25
- # we need it here too to accurately avoid circular renders below.
26
- name = name.to_s.gsub(/^\//, "")
27
-
28
23
  if @called_from
29
24
  if @called_from.include_template? name
30
25
  Brakeman.debug "Skipping circular render from #{@template.name} to #{name}"
@@ -0,0 +1,38 @@
1
+ class Brakeman::Report::Sonar < Brakeman::Report::Base
2
+ def generate_report
3
+ report_object = {
4
+ issues: all_warnings.map { |warning| issue_json(warning) }
5
+ }
6
+ return JSON.pretty_generate report_object
7
+ end
8
+
9
+ private
10
+
11
+ def issue_json(warning)
12
+ {
13
+ engineId: "Brakeman",
14
+ ruleId: warning.warning_code,
15
+ type: "VULNERABILITY",
16
+ severity: severity_level_for(warning.confidence),
17
+ primaryLocation: {
18
+ message: warning.message,
19
+ filePath: warning.file.relative,
20
+ textRange: {
21
+ "startLine": warning.line || 1,
22
+ "endLine": warning.line || 1,
23
+ }
24
+ },
25
+ effortMinutes: (4 - warning.confidence) * 15
26
+ }
27
+ end
28
+
29
+ def severity_level_for(confidence)
30
+ if confidence == 0
31
+ "CRITICAL"
32
+ elsif confidence == 1
33
+ "MAJOR"
34
+ else
35
+ "MINOR"
36
+ end
37
+ end
38
+ end
@@ -45,6 +45,9 @@ class Brakeman::Report
45
45
  Brakeman::Report::JUnit
46
46
  when :to_sarif
47
47
  return self.to_sarif
48
+ when :to_sonar
49
+ require_report 'sonar'
50
+ Brakeman::Report::Sonar
48
51
  else
49
52
  raise "Invalid format: #{format}. Should be one of #{VALID_FORMATS.inspect}"
50
53
  end
@@ -69,6 +72,11 @@ class Brakeman::Report
69
72
  generate Brakeman::Report::JSON
70
73
  end
71
74
 
75
+ def to_sonar
76
+ require_report 'sonar'
77
+ generate Brakeman::Report::Sonar
78
+ end
79
+
72
80
  def to_table
73
81
  require_report 'table'
74
82
  generate Brakeman::Report::Table
@@ -132,10 +132,11 @@ class Brakeman::Rescanner < Brakeman::Scanner
132
132
  template_name = template_path_to_name(path)
133
133
 
134
134
  tracker.reset_template template_name
135
- fp = Brakeman::FileParser.new(tracker)
135
+ fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
136
136
  template_parser = Brakeman::TemplateParser.new(tracker, fp)
137
137
  template_parser.parse_template path, path.read
138
- process_template fp.file_list[:templates].first
138
+ tracker.add_errors(fp.errors)
139
+ process_template fp.file_list.first
139
140
 
140
141
  @processor.process_template_alias tracker.templates[template_name]
141
142
 
@@ -390,9 +391,10 @@ class Brakeman::Rescanner < Brakeman::Scanner
390
391
 
391
392
  def parse_ruby_files list
392
393
  paths = list.select(&:exists?)
393
- file_parser = Brakeman::FileParser.new(tracker)
394
- file_parser.parse_files paths, :rescan
395
- file_parser.file_list[:rescan]
394
+ file_parser = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
395
+ file_parser.parse_files paths
396
+ tracker.add_errors(file_parser.errors)
397
+ file_parser.file_list
396
398
  end
397
399
  end
398
400
 
@@ -7,6 +7,7 @@ begin
7
7
  require 'brakeman/app_tree'
8
8
  require 'brakeman/file_parser'
9
9
  require 'brakeman/parsers/template_parser'
10
+ require 'brakeman/processors/lib/file_type_detector'
10
11
  rescue LoadError => e
11
12
  $stderr.puts e.message
12
13
  $stderr.puts "Please install the appropriate dependency."
@@ -23,7 +24,10 @@ class Brakeman::Scanner
23
24
  @app_tree = Brakeman::AppTree.from_options(options)
24
25
 
25
26
  if (!@app_tree.root || !@app_tree.exists?("app")) && !options[:force_scan]
26
- raise Brakeman::NoApplication, "Please supply the path to a Rails application (looking in #{@app_tree.root})."
27
+ message = "Please supply the path to a Rails application (looking in #{@app_tree.root}).\n" <<
28
+ " Use `--force` to run a scan anyway - for example if there are many applications in one directory."
29
+
30
+ raise Brakeman::NoApplication, message
27
31
  end
28
32
 
29
33
  @processor = processor || Brakeman::Processor.new(@app_tree, options)
@@ -43,6 +47,8 @@ class Brakeman::Scanner
43
47
  process_config
44
48
  Brakeman.notify "Parsing files..."
45
49
  parse_files
50
+ Brakeman.notify "Detecting file types..."
51
+ detect_file_types
46
52
  Brakeman.notify "Processing initializers..."
47
53
  process_initializers
48
54
  Brakeman.notify "Processing libs..."
@@ -65,29 +71,47 @@ class Brakeman::Scanner
65
71
  end
66
72
 
67
73
  def parse_files
68
- fp = Brakeman::FileParser.new tracker
69
-
70
- files = {
71
- :initializers => @app_tree.initializer_paths,
72
- :controllers => @app_tree.controller_paths,
73
- :models => @app_tree.model_paths
74
- }
75
-
76
- unless options[:skip_libs]
77
- files[:libs] = @app_tree.lib_paths
78
- end
74
+ fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
79
75
 
80
- files.each do |name, paths|
81
- fp.parse_files paths, name
82
- end
76
+ fp.parse_files tracker.app_tree.ruby_file_paths
83
77
 
84
78
  template_parser = Brakeman::TemplateParser.new(tracker, fp)
85
79
 
86
- fp.read_files(@app_tree.template_paths, :templates) do |path, contents|
80
+ fp.read_files(@app_tree.template_paths) do |path, contents|
87
81
  template_parser.parse_template path, contents
88
82
  end
89
83
 
90
- @file_list = fp.file_list
84
+ # Collect errors raised during parsing
85
+ tracker.add_errors(fp.errors)
86
+
87
+ @parsed_files = fp.file_list
88
+ end
89
+
90
+ def detect_file_types
91
+ @file_list = {
92
+ controllers: [],
93
+ initializers: [],
94
+ libs: [],
95
+ models: [],
96
+ templates: [],
97
+ }
98
+
99
+ detector = Brakeman::FileTypeDetector.new
100
+
101
+ @parsed_files.each do |file|
102
+ if file.is_a? Brakeman::TemplateParser::TemplateFile
103
+ @file_list[:templates] << file
104
+ else
105
+ type = detector.detect_type(file)
106
+ unless type == :skip
107
+ if @file_list[type].nil?
108
+ raise type.to_s
109
+ else
110
+ @file_list[type] << file
111
+ end
112
+ end
113
+ end
114
+ end
91
115
  end
92
116
 
93
117
  #Process config/environment.rb and config/gems.rb
@@ -325,7 +349,7 @@ class Brakeman::Scanner
325
349
  end
326
350
 
327
351
  def parse_ruby_file file
328
- fp = Brakeman::FileParser.new(self.tracker)
352
+ fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
329
353
  fp.parse_ruby(file.read, file)
330
354
  end
331
355
  end
@@ -125,7 +125,7 @@ module Brakeman
125
125
  value = args[-1][2]
126
126
  case value.node_type
127
127
  when :array
128
- filter[option] = value.sexp_body.map {|v| v[1] }
128
+ filter[option] = value[1..-1].map {|v| v[1] }
129
129
  when :lit, :str
130
130
  filter[option] = value[1]
131
131
  else
@@ -68,6 +68,12 @@ class Brakeman::Tracker
68
68
  }
69
69
  end
70
70
 
71
+ def add_errors exceptions
72
+ exceptions.each do |e|
73
+ error(e)
74
+ end
75
+ end
76
+
71
77
  #Run a set of checks on the current information. Results will be stored
72
78
  #in Tracker#checks.
73
79
  def run_checks
data/lib/brakeman/util.rb CHANGED
@@ -321,7 +321,7 @@ module Brakeman::Util
321
321
  if node_type? current, :class
322
322
  return true
323
323
  elsif sexp? current
324
- todo = current.sexp_body.concat todo
324
+ todo = current[1..-1].concat todo
325
325
  end
326
326
  end
327
327
 
@@ -334,7 +334,7 @@ module Brakeman::Util
334
334
  if args.empty? or args.first.empty?
335
335
  #nothing to do
336
336
  elsif node_type? args.first, :arglist
337
- call.concat args.first.sexp_body
337
+ call.concat args.first[1..-1]
338
338
  elsif args.first.node_type.is_a? Sexp #just a list of args
339
339
  call.concat args.first
340
340
  else
@@ -368,8 +368,13 @@ module Brakeman::Util
368
368
  #
369
369
  # views/test/something.html.erb -> test/something
370
370
  def template_path_to_name path
371
- names = path.relative.split("/")
371
+ names = path.relative.split('/')
372
372
  names.last.gsub!(/(\.(html|js)\..*|\.(rhtml|haml|erb|slim))$/, '')
373
- names[(names.index("views") + 1)..-1].join("/").to_sym
373
+
374
+ if names.include? 'views'
375
+ names[(names.index('views') + 1)..-1]
376
+ else
377
+ names
378
+ end.join('/').to_sym
374
379
  end
375
380
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "4.10.1"
2
+ Version = "5.0.0.pre1"
3
3
  end
@@ -119,6 +119,8 @@ module Brakeman::WarningCodes
119
119
  :CVE_2020_8159 => 115,
120
120
  :CVE_2020_8166 => 116,
121
121
  :erb_template_injection => 117,
122
+ :http_verb_confusion => 118,
123
+ :unsafe_method_reflection => 119,
122
124
 
123
125
  :custom_check => 9090,
124
126
  }
data/lib/brakeman.rb CHANGED
@@ -66,6 +66,7 @@ module Brakeman
66
66
  # * :run_checks - array of checks to run (run all if not specified)
67
67
  # * :safe_methods - array of methods to consider safe
68
68
  # * :skip_libs - do not process lib/ directory (default: false)
69
+ # * :skip_vendor - do not process vendor/ directory (default: true)
69
70
  # * :skip_checks - checks not to run (run all if not specified)
70
71
  # * :absolute_paths - show absolute path of each file (default: false)
71
72
  # * :summary_only - only output summary section of report for plain/table (:summary_only, :no_summary, true)
@@ -191,6 +192,7 @@ module Brakeman
191
192
  :report_progress => true,
192
193
  :safe_methods => Set.new,
193
194
  :skip_checks => Set.new,
195
+ :skip_vendor => true,
194
196
  }
195
197
  end
196
198
 
@@ -239,6 +241,8 @@ module Brakeman
239
241
  [:to_junit]
240
242
  when :sarif, :to_sarif
241
243
  [:to_sarif]
244
+ when :sonar, :to_sonar
245
+ [:to_sonar]
242
246
  else
243
247
  [:to_text]
244
248
  end
@@ -270,6 +274,8 @@ module Brakeman
270
274
  :to_junit
271
275
  when /\.sarif$/i
272
276
  :to_sarif
277
+ when /\.sonar$/i
278
+ :to_sonar
273
279
  else
274
280
  :to_text
275
281
  end
@@ -175,7 +175,7 @@ class Sexp
175
175
  start_index = 3
176
176
 
177
177
  if exp.is_a? Sexp and exp.node_type == :arglist
178
- exp = exp.sexp_body
178
+ exp = exp[1..-1]
179
179
  end
180
180
 
181
181
  exp.each_with_index do |e, i|
@@ -198,10 +198,10 @@ class Sexp
198
198
 
199
199
  case self.node_type
200
200
  when :call, :attrasgn, :safe_call, :safe_attrasgn
201
- self.sexp_body(3).unshift :arglist
201
+ self[3..-1].unshift :arglist
202
202
  when :super, :zsuper
203
203
  if self[1]
204
- self.sexp_body.unshift :arglist
204
+ self[1..-1].unshift :arglist
205
205
  else
206
206
  Sexp.new(:arglist)
207
207
  end
@@ -218,13 +218,13 @@ class Sexp
218
218
  case self.node_type
219
219
  when :call, :attrasgn, :safe_call, :safe_attrasgn
220
220
  if self[3]
221
- self.sexp_body(3)
221
+ self[3..-1]
222
222
  else
223
223
  Sexp.new
224
224
  end
225
225
  when :super, :zsuper
226
226
  if self[1]
227
- self.sexp_body
227
+ self[1..-1]
228
228
  else
229
229
  Sexp.new
230
230
  end
@@ -512,7 +512,7 @@ class Sexp
512
512
  self.slice!(index..-1) #Remove old body
513
513
 
514
514
  if exp.first == :rlist
515
- exp = exp.sexp_body
515
+ exp = exp[1..-1]
516
516
  end
517
517
 
518
518
  #Insert new body
@@ -529,11 +529,11 @@ class Sexp
529
529
 
530
530
  case self.node_type
531
531
  when :defn, :class
532
- self.sexp_body(3)
532
+ self[3..-1]
533
533
  when :defs
534
- self.sexp_body(4)
534
+ self[4..-1]
535
535
  when :module
536
- self.sexp_body(2)
536
+ self[2..-1]
537
537
  end
538
538
  end
539
539
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman-min
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.10.1
4
+ version: 5.0.0.pre1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-25 00:00:00.000000000 Z
11
+ date: 2020-11-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -225,8 +225,10 @@ files:
225
225
  - lib/brakeman/checks/check_template_injection.rb
226
226
  - lib/brakeman/checks/check_translate_bug.rb
227
227
  - lib/brakeman/checks/check_unsafe_reflection.rb
228
+ - lib/brakeman/checks/check_unsafe_reflection_methods.rb
228
229
  - lib/brakeman/checks/check_unscoped_find.rb
229
230
  - lib/brakeman/checks/check_validation_regex.rb
231
+ - lib/brakeman/checks/check_verb_confusion.rb
230
232
  - lib/brakeman/checks/check_weak_hash.rb
231
233
  - lib/brakeman/checks/check_without_protection.rb
232
234
  - lib/brakeman/checks/check_xml_dos.rb
@@ -257,6 +259,7 @@ files:
257
259
  - lib/brakeman/processors/haml_template_processor.rb
258
260
  - lib/brakeman/processors/lib/basic_processor.rb
259
261
  - lib/brakeman/processors/lib/call_conversion_helper.rb
262
+ - lib/brakeman/processors/lib/file_type_detector.rb
260
263
  - lib/brakeman/processors/lib/find_all_calls.rb
261
264
  - lib/brakeman/processors/lib/find_call.rb
262
265
  - lib/brakeman/processors/lib/find_return_value.rb
@@ -293,6 +296,7 @@ files:
293
296
  - lib/brakeman/report/report_junit.rb
294
297
  - lib/brakeman/report/report_markdown.rb
295
298
  - lib/brakeman/report/report_sarif.rb
299
+ - lib/brakeman/report/report_sonar.rb
296
300
  - lib/brakeman/report/report_table.rb
297
301
  - lib/brakeman/report/report_tabs.rb
298
302
  - lib/brakeman/report/report_text.rb
@@ -342,14 +346,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
342
346
  requirements:
343
347
  - - ">="
344
348
  - !ruby/object:Gem::Version
345
- version: '0'
349
+ version: 2.4.0
346
350
  required_rubygems_version: !ruby/object:Gem::Requirement
347
351
  requirements:
348
- - - ">="
352
+ - - ">"
349
353
  - !ruby/object:Gem::Version
350
- version: '0'
354
+ version: 1.3.1
351
355
  requirements: []
352
- rubygems_version: 3.2.2
356
+ rubygems_version: 3.1.2
353
357
  signing_key:
354
358
  specification_version: 4
355
359
  summary: Security vulnerability scanner for Ruby on Rails.