brakeman-lib 4.0.1 → 4.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a938d977d4606bb89f4b44c38c9aae53e78d0bd7
4
- data.tar.gz: 433a276f000a6c324ac4ddef16bebdb118f09227
3
+ metadata.gz: 25a56e16b745a1e4aff57461dc3b346db0c20a16
4
+ data.tar.gz: 998d68b4ab7e3ed1b288357ef101639eecc5c5bd
5
5
  SHA512:
6
- metadata.gz: 5bc9977cdb10fd71de60da8d8449abac3944ff14057eccbb60e7ae415333110e7d9fab9f293f44de693c872eae007bac24b58f278c6e795909793453fc85ed50
7
- data.tar.gz: ccc8093b78af39665e15c2d772bf2f3a37afc192c2c92b82f81f6b17727cae585c123d174b1b7f229fe624b35e53418bdede43456955297a7cbf3e4533cb1f83
6
+ metadata.gz: 623b9942d544ffa2671eef833f9cefebff610f1a1574f2e55b1c60336eb327b26f20413f900db982940c21f53d0ab91576afd0b1b0bb009dec9165392019d9dd
7
+ data.tar.gz: f3efcb07b4535768e48353bd72c36718137222ad8aeae2262b6fa69c88aef1899be5be6fbe28cd467c5bc43543b6924773ed5d3d73d6d439495e6fe17e64c457
@@ -1,3 +1,26 @@
1
+ # 4.1.0
2
+
3
+ * Process models as root sexp instead of each sexp
4
+ * Avoid CSRF warning in Rails 5.2 default config
5
+ * Show better location for Sass errors (Andrew Bromwich)
6
+ * Warn about dynamic values in `Arel.sql`
7
+ * Fix `include_paths` for Code Climate engine (Will Fleming)
8
+ * Add check for dangerous keys in `permit`
9
+ * Try to guess options for `less` pager
10
+ * Better processing of op_asgn1 (e.g. x[:y] += 1)
11
+ * Add optional check for divide by zero
12
+ * Remove errors about divide by zero
13
+ * Avoid warning about file access for temp files
14
+ * Do not warn on params.permit with safe values
15
+ * Add Sexp#call_chain
16
+ * Use HTTPS for warning links
17
+ * Handle nested destructuring/multiple assignment
18
+ * Leave results on screen after paging
19
+ * Do not page if results fit on screen
20
+ * Support `app_path` configuration for Code Climate engine (Noah Davis)
21
+ * Refactor Code Climate engine options parsing (Noah Davis)
22
+ * Fix upgrade version for CVE-2016-6316
23
+
1
24
  # 4.0.1
2
25
 
3
26
  * Disable pager when `CI` environment variable is set
data/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
  [![Brakeman Pro Logo](https://brakemanpro.com/images/bmp_square_white.png)](https://brakemanpro.com)
3
3
 
4
4
  [![Build Status](https://travis-ci.org/presidentbeef/brakeman.svg?branch=master)](https://travis-ci.org/presidentbeef/brakeman)
5
- [![Code Climate](https://codeclimate.com/github/presidentbeef/brakeman/badges/gpa.svg)](https://codeclimate.com/github/presidentbeef/brakeman)
6
- [![Test Coverage](https://codeclimate.com/github/presidentbeef/brakeman/badges/coverage.svg)](https://codeclimate.com/github/presidentbeef/brakeman/coverage)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/1b08a5c74695cb0d11ec/maintainability)](https://codeclimate.com/github/presidentbeef/brakeman/maintainability)
6
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/1b08a5c74695cb0d11ec/test_coverage)](https://codeclimate.com/github/presidentbeef/brakeman/test_coverage)
7
7
  [![Gitter](https://badges.gitter.im/presidentbeef/brakeman.svg)](https://gitter.im/presidentbeef/brakeman)
8
8
 
9
9
  # Brakeman
data/lib/brakeman.rb CHANGED
@@ -402,39 +402,12 @@ module Brakeman
402
402
  puts tracker.report.format(output_format)
403
403
  end
404
404
  else
405
- page_output tracker.report.format(output_formats.first)
406
- end
407
- end
408
- private_class_method :write_report_to_formats
409
-
410
- def self.page_output text
411
- ci = ENV["CI"]
405
+ require "brakeman/report/pager"
412
406
 
413
- if ci.is_a? String and ci.downcase == "true"
414
- puts text
415
- elsif system("which less")
416
- # Adapted from https://github.com/piotrmurach/tty-pager/
417
- write_io = open("|less -R", 'w')
418
- pid = write_io.pid
419
-
420
- write_io.write(text)
421
- write_io.close
422
-
423
- Process.waitpid2(pid, Process::WNOHANG)
424
- else
425
- load_brakeman_dependency 'highline'
426
- h = ::HighLine.new
427
- h.page_at = :auto
428
- h.say text
407
+ Brakeman::Pager.new(tracker).page_report(tracker.report, output_formats.first)
429
408
  end
430
- rescue Errno::ECHILD
431
- # on jruby 9x waiting on pid raises (per tty-pager)
432
- true
433
- rescue => e
434
- warn "[Error] #{e}"
435
- warn "[Error] Could not use pager. Set --no-pager to avoid this issue."
436
- puts text
437
409
  end
410
+ private_class_method :write_report_to_formats
438
411
 
439
412
  #Rescan a subset of files in a Rails application.
440
413
  #
@@ -445,41 +445,16 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
445
445
  false
446
446
  end
447
447
 
448
- #Returns true if low_version <= RAILS_VERSION <= high_version
449
- #
450
- #If the Rails version is unknown, returns false.
451
- def version_between? low_version, high_version, current_version = nil
452
- current_version ||= rails_version
453
- return false unless current_version
454
-
455
- version = current_version.split(".").map! { |n| n.to_i }
456
- low_version = low_version.split(".").map! { |n| n.to_i }
457
- high_version = high_version.split(".").map! { |n| n.to_i }
458
-
459
- version.each_with_index do |v, i|
460
- if v < low_version.fetch(i, 0)
461
- return false
462
- elsif v > low_version.fetch(i, 0)
463
- break
464
- end
465
- end
466
-
467
- version.each_with_index do |v, i|
468
- if v > high_version.fetch(i, 0)
469
- return false
470
- elsif v < high_version.fetch(i, 0)
471
- break
472
- end
473
- end
474
-
475
- true
476
- end
477
-
478
448
  def lts_version? version
479
449
  tracker.config.has_gem? :'railslts-version' and
480
450
  version_between? version, "2.3.18.99", tracker.config.gem_version(:'railslts-version')
481
451
  end
482
452
 
453
+
454
+ def version_between? low_version, high_version, current_version = nil
455
+ tracker.config.version_between? low_version, high_version, current_version
456
+ end
457
+
483
458
  def gemfile_or_environment gem_name = :rails
484
459
  if gem_name and info = tracker.config.get_gem(gem_name)
485
460
  info
@@ -170,7 +170,7 @@ class Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting
170
170
  when version_between?("4.0.0", "4.2.7.0")
171
171
  "4.2.7.1"
172
172
  when version_between?("5.0.0", "5.0.0")
173
- "5.0.0"
173
+ "5.0.0.1"
174
174
  when (version.nil? and tracker.options[:rails3])
175
175
  "3.2.22.4"
176
176
  when (version.nil? and tracker.options[:rails4])
@@ -0,0 +1,40 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckDivideByZero < Brakeman::BaseCheck
4
+ Brakeman::Checks.add_optional self
5
+
6
+ @description = "Warns on potential division by zero"
7
+
8
+ def run_check
9
+ tracker.find_call(:method => :"/").each do |result|
10
+ check_division result
11
+ end
12
+ end
13
+
14
+ def check_division result
15
+ call = result[:call]
16
+
17
+ denominator = call.first_arg
18
+
19
+ if number? denominator and denominator.value == 0
20
+ numerator = call.target
21
+
22
+ if number? numerator
23
+ if numerator.value.is_a? Float
24
+ return # 0.0 / 0 is NaN and 1.0 / 0 is Infinity
25
+ else
26
+ confidence = :medium
27
+ end
28
+ else
29
+ confidence = :weak
30
+ end
31
+
32
+ warn :result => result,
33
+ :warning_type => "Divide by Zero",
34
+ :warning_code => :divide_by_zero,
35
+ :message => "Potential division by zero",
36
+ :confidence => confidence,
37
+ :user_input => denominator
38
+ end
39
+ end
40
+ end
@@ -47,7 +47,8 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
47
47
  end
48
48
  end
49
49
 
50
- if match
50
+ if match and not temp_file? match.match
51
+
51
52
  message = "#{friendly_type_of(match).capitalize} used in file name"
52
53
 
53
54
  warn :result => result,
@@ -59,4 +60,12 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
59
60
  :user_input => match
60
61
  end
61
62
  end
63
+
64
+ def temp_file? exp
65
+ if call? exp
66
+ return true if exp.call_chain.include? :tempfile
67
+
68
+ params? exp.target and exp.method == :path
69
+ end
70
+ end
62
71
  end
@@ -10,6 +10,8 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
10
10
  @description = "Verifies that protect_from_forgery is enabled in direct subclasses of ActionController::Base"
11
11
 
12
12
  def run_check
13
+ return if tracker.config.default_protect_from_forgery?
14
+
13
15
  tracker.controllers
14
16
  .select { |_, controller| controller.parent == :"ActionController::Base" }
15
17
  .each do |name, controller|
@@ -0,0 +1,43 @@
1
+ require 'brakeman/checks/base_check'
2
+
3
+ class Brakeman::CheckPermitAttributes < Brakeman::BaseCheck
4
+ Brakeman::Checks.add self
5
+
6
+ @description = "Warn on potentially dangerous attributes whitelisted via permit"
7
+
8
+ SUSPICIOUS_KEYS = {
9
+ admin: :high,
10
+ account_id: :high,
11
+ role: :medium,
12
+ banned: :medium,
13
+ }
14
+
15
+ def run_check
16
+ tracker.find_call(:method => :permit).each do |result|
17
+ check_permit result
18
+ end
19
+ end
20
+
21
+ def check_permit result
22
+ call = result[:call]
23
+
24
+ call.each_arg do |arg|
25
+ if symbol? arg
26
+ if SUSPICIOUS_KEYS.key? arg.value
27
+ warn_on_permit_key result, arg
28
+ elsif arg.value.match /_id$/
29
+ warn_on_permit_key result, arg, :medium
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ def warn_on_permit_key result, key, confidence = nil
36
+ warn :result => result,
37
+ :warning_type => "Mass Assignment",
38
+ :warning_code => :dangerous_permit_key,
39
+ :message => "Potentially dangerous key allowed for mass assignment",
40
+ :confidence => (confidence || SUSPICIOUS_KEYS[key.value]),
41
+ :user_input => key
42
+ end
43
+ end
@@ -34,10 +34,13 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
34
34
  call = result[:call]
35
35
  method = call.method
36
36
 
37
+ opt = call.first_arg
38
+
37
39
  if method == :redirect_to and
38
40
  not only_path?(call) and
39
- not explicit_host?(call.first_arg) and
40
- not slice_call?(call.first_arg) and
41
+ not explicit_host?(opt) and
42
+ not slice_call?(opt) and
43
+ not safe_permit?(opt) and
41
44
  res = include_user_input?(call)
42
45
 
43
46
  if res.type == :immediate
@@ -232,4 +235,20 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
232
235
  return unless call? exp
233
236
  exp.method == :slice
234
237
  end
238
+
239
+ DANGEROUS_KEYS = [:host, :subdomain, :domain, :port]
240
+
241
+ def safe_permit? exp
242
+ if call? exp and params? exp.target and exp.method == :permit
243
+ exp.each_arg do |opt|
244
+ if symbol? opt and DANGEROUS_KEYS.include? opt.value
245
+ return false
246
+ end
247
+ end
248
+
249
+ return true
250
+ end
251
+
252
+ false
253
+ end
235
254
  end
@@ -38,7 +38,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
38
38
  @connection_calls.concat [:add_limit!, :add_offset_limit!, :add_lock!]
39
39
  end
40
40
 
41
- @expected_targets = active_record_models.keys + [:connection, :"ActiveRecord::Base"]
41
+ @expected_targets = active_record_models.keys + [:connection, :"ActiveRecord::Base", :Arel]
42
42
 
43
43
  Brakeman.debug "Finding possible SQL calls on models"
44
44
  calls = tracker.find_call(:methods => @sql_targets, :nested => true)
@@ -53,6 +53,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
53
53
 
54
54
  calls.concat tracker.find_call(:targets => @expected_targets, :methods => @connection_calls, :chained => true).select { |result| connect_call? result }
55
55
 
56
+ calls.concat tracker.find_call(:target => :Arel, :method => :sql)
57
+
56
58
  Brakeman.debug "Finding calls to named_scope or scope"
57
59
  calls.concat find_scope_calls
58
60
 
@@ -194,6 +196,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
194
196
  check_lock_arguments call.first_arg
195
197
  when :pluck
196
198
  unsafe_sql? call.first_arg
199
+ when :sql
200
+ unsafe_sql? call.first_arg
197
201
  when :update_all, :select
198
202
  check_update_all_arguments call.args
199
203
  when *@connection_calls
@@ -0,0 +1,97 @@
1
+ require 'pathname'
2
+
3
+ module Brakeman
4
+ module Codeclimate
5
+ class EngineConfiguration
6
+
7
+ def initialize(engine_config = {})
8
+ @engine_config = engine_config
9
+ end
10
+
11
+ def options
12
+ default_options.merge(configured_options)
13
+ end
14
+
15
+ private
16
+
17
+ attr_reader :engine_config
18
+
19
+ def default_options
20
+ @default_options = {
21
+ :output_format => :codeclimate,
22
+ :quiet => true,
23
+ :pager => false,
24
+ :app_path => Dir.pwd
25
+ }
26
+ if system("test -w /dev/stdout")
27
+ @default_options[:output_files] = ["/dev/stdout"]
28
+ end
29
+ @default_options
30
+ end
31
+
32
+ def configured_options
33
+ @configured_options = {}
34
+ # ATM this gets parsed as a string instead of bool: "config":{ "debug":"true" }
35
+ if brakeman_configuration["debug"] && brakeman_configuration["debug"].to_s.downcase == "true"
36
+ @configured_options[:debug] = true
37
+ @configured_options[:report_progress] = false
38
+ end
39
+
40
+ if active_include_paths
41
+ @configured_options[:only_files] = active_include_paths
42
+ end
43
+
44
+ if brakeman_configuration["app_path"]
45
+ @configured_options[:path_prefix] = brakeman_configuration["app_path"]
46
+ path = Pathname.new(Dir.pwd) + brakeman_configuration["app_path"]
47
+ @configured_options[:app_path] = path.to_s
48
+ end
49
+ @configured_options
50
+ end
51
+
52
+ def brakeman_configuration
53
+ if engine_config["config"]
54
+ engine_config["config"]
55
+ else
56
+ {}
57
+ end
58
+ end
59
+
60
+ def active_include_paths
61
+ @active_include_paths ||=
62
+ if brakeman_configuration["app_path"]
63
+ stripped_include_paths(brakeman_configuration["app_path"])
64
+ else
65
+ engine_config["include_paths"] && engine_config["include_paths"].compact
66
+ end
67
+ end
68
+
69
+ def stripped_include_paths(prefix)
70
+ subprefixes = path_subprefixes(prefix)
71
+ engine_config["include_paths"] && engine_config["include_paths"].map do |path|
72
+ next unless path
73
+ stripped_include_path(prefix, subprefixes, path)
74
+ end.compact
75
+ end
76
+
77
+ def path_subprefixes(path)
78
+ Pathname.new(path).each_filename.inject([]) do |memo, piece|
79
+ memo <<
80
+ if memo.any?
81
+ File.join(memo.last, piece)
82
+ else
83
+ File.join(piece)
84
+ end
85
+ end
86
+ end
87
+
88
+ def stripped_include_path(prefix, subprefixes, path)
89
+ if path.start_with?(prefix)
90
+ path.sub(%r{^#{prefix}/?}, "./")
91
+ elsif subprefixes.any? { |subprefix| path =~ %r{^#{subprefix}/?$} }
92
+ "./"
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -36,9 +36,7 @@ module Brakeman
36
36
  @file_parser.file_list[:templates] << TemplateFile.new(path, ast, name, type)
37
37
  end
38
38
  rescue Racc::ParseError => e
39
- tracker.error e, "could not parse #{path}"
40
- rescue Haml::Error => e
41
- tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
39
+ tracker.error e, "Could not parse #{path}"
42
40
  rescue StandardError, LoadError => e
43
41
  tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
44
42
  end
@@ -78,6 +76,12 @@ module Brakeman
78
76
  Haml::Engine.new(text,
79
77
  :filename => path,
80
78
  :escape_html => tracker.config.escape_html?).precompiled.gsub(/([^\\])\\n/, '\1')
79
+ rescue Haml::Error => e
80
+ tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
81
+ nil
82
+ rescue Sass::SyntaxError => e
83
+ tracker.error e, "While processing #{path}"
84
+ nil
81
85
  end
82
86
 
83
87
  def parse_slim path, text
@@ -54,7 +54,7 @@ module Brakeman
54
54
  #Process a model source
55
55
  def process_model src, file_name
56
56
  result = ModelProcessor.new(@tracker).process_model src, file_name
57
- AliasProcessor.new(@tracker).process_all result if result
57
+ AliasProcessor.new(@tracker).process result if result
58
58
  end
59
59
 
60
60
  #Process either an ERB or HAML template
@@ -235,14 +235,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
235
235
  end
236
236
  when :/
237
237
  if number? target and number? first_arg
238
- if first_arg.value == 0 and not target.value.is_a? Float
239
- if @tracker
240
- location = [@current_class, @current_method, "line #{first_arg.line}"].compact.join(' ')
241
- require 'brakeman/processors/output_processor'
242
- code = Brakeman::OutputProcessor.new.format(exp)
243
- @tracker.error Exception.new("Potential divide by zero: #{code} (#{location})")
244
- end
245
- else
238
+ unless first_arg.value == 0 and not target.value.is_a? Float
246
239
  exp = Sexp.new(:lit, target.value / first_arg.value)
247
240
  end
248
241
  end
@@ -500,6 +493,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
500
493
  #
501
494
  # x, y = z, w
502
495
  def process_masgn exp
496
+ exp[2] = process exp[2] if sexp? exp[2]
497
+
498
+ if node_type? exp[2], :to_ary and array? exp[2][1]
499
+ exp[2] = exp[2][1]
500
+ end
501
+
503
502
  unless array? exp[1] and array? exp[2] and exp[1].length == exp[2].length
504
503
  return process_default(exp)
505
504
  end
@@ -514,9 +513,20 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
514
513
  vars.each_with_index do |var, i|
515
514
  val = vals[i]
516
515
  if val
517
- assign = var.dup
518
- assign.rhs = val
519
- process assign
516
+
517
+ # This happens with nested destructuring like
518
+ # x, (a, b) = blah
519
+ if node_type? var, :masgn
520
+ # Need to add value to masgn exp
521
+ m = var.dup
522
+ m[2] = s(:to_ary, val)
523
+
524
+ process_masgn m
525
+ else
526
+ assign = var.dup
527
+ assign.rhs = val
528
+ process assign
529
+ end
520
530
  end
521
531
  end
522
532
 
@@ -550,19 +560,26 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
550
560
  #Assignments like this
551
561
  # x[:y] ||= 1
552
562
  def process_op_asgn1 exp
553
- return process_default(exp) if exp[3] != :"||"
563
+ target_var = exp[1]
564
+ target_var &&= target_var.deep_clone
554
565
 
555
566
  target = exp[1] = process(exp[1])
556
567
  index = exp[2][1] = process(exp[2][1])
557
568
  value = exp[4] = process(exp[4])
558
569
  match = Sexp.new(:call, target, :[], index)
559
570
 
560
- unless env[match]
561
- if request_value? target
562
- env[match] = match.combine(value)
563
- else
564
- env[match] = value
571
+ if exp[3] == :"||"
572
+ unless env[match]
573
+ if request_value? target
574
+ env[match] = match.combine(value)
575
+ else
576
+ env[match] = value
577
+ end
565
578
  end
579
+ else
580
+ new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value)
581
+
582
+ env[match] = new_value
566
583
  end
567
584
 
568
585
  exp
@@ -0,0 +1,110 @@
1
+ module Brakeman
2
+ class Pager
3
+ def initialize tracker, pager = :less, output = $stdout
4
+ @tracker = tracker
5
+ @pager = pager
6
+ @output = output
7
+ end
8
+
9
+ def page_report report, format
10
+ if @pager == :less
11
+ set_color
12
+ end
13
+
14
+ text = report.format(format)
15
+
16
+ if in_ci?
17
+ no_pager text
18
+ else
19
+ page_output text
20
+ end
21
+ end
22
+
23
+ def page_output text
24
+ case @pager
25
+ when :none
26
+ no_pager text
27
+ when :highline
28
+ page_via_highline text
29
+ when :less
30
+ if less_available?
31
+ page_via_less text
32
+ else
33
+ page_via_highline text
34
+ end
35
+ else
36
+ no_pager text
37
+ end
38
+ end
39
+
40
+ def no_pager text
41
+ @output.puts text
42
+ end
43
+
44
+ def page_via_highline text
45
+ Brakeman.load_brakeman_dependency 'highline'
46
+ h = ::HighLine.new($stdin, @output)
47
+ h.page_at = :auto
48
+ h.say text
49
+ end
50
+
51
+ def page_via_less text
52
+ # Adapted from https://github.com/piotrmurach/tty-pager/
53
+
54
+ write_io = open("|less #{less_options.join}", 'w')
55
+ pid = write_io.pid
56
+
57
+ write_io.write(text)
58
+ write_io.close
59
+
60
+ Process.waitpid2(pid, Process::WNOHANG)
61
+ rescue Errno::ECHILD
62
+ # on jruby 9x waiting on pid raises (per tty-pager)
63
+ true
64
+ rescue => e
65
+ warn "[Error] #{e}"
66
+ warn "[Error] Could not use pager. Set --no-pager to avoid this issue."
67
+ no_pager text
68
+ end
69
+
70
+ def in_ci?
71
+ ci = ENV["CI"]
72
+
73
+ ci.is_a? String and ci.downcase == "true"
74
+ end
75
+
76
+ def less_available?
77
+ return @less_available unless @less_available.nil?
78
+
79
+ @less_available = system("which less > /dev/null")
80
+ end
81
+
82
+ def less_options
83
+ # -R show colors
84
+ # -F exit if output fits on one screen
85
+ # -X do not clear screen after less exits
86
+
87
+ return @less_options if @less_options
88
+
89
+ @less_options = []
90
+
91
+ if system("which less > /dev/null")
92
+ less_help = `less -?`
93
+
94
+ ["-R ", "-F ", "-X "].each do |opt|
95
+ if less_help.include? opt
96
+ @less_options << opt
97
+ end
98
+ end
99
+ end
100
+
101
+ @less_options
102
+ end
103
+
104
+ def set_color
105
+ unless @tracker and less_options.include? "-R "
106
+ @tracker.options[:output_color] = false
107
+ end
108
+ end
109
+ end
110
+ end
@@ -1,5 +1,6 @@
1
1
  require "json"
2
2
  require "yaml"
3
+ require "pathname"
3
4
 
4
5
  class Brakeman::Report::CodeClimate < Brakeman::Report::Base
5
6
  DOCUMENTATION_PATH = File.expand_path("../../../../docs/warning_types", __FILE__)
@@ -24,7 +25,7 @@ class Brakeman::Report::CodeClimate < Brakeman::Report::Base
24
25
  severity: severity_level_for(warning.confidence),
25
26
  remediation_points: remediation_points_for(warning_code_name),
26
27
  location: {
27
- path: warning.relative_path,
28
+ path: file_path(warning),
28
29
  lines: {
29
30
  begin: warning.line || 1,
30
31
  end: warning.line || 1,
@@ -67,4 +68,12 @@ class Brakeman::Report::CodeClimate < Brakeman::Report::Base
67
68
 
68
69
  File.read(filename) if File.exist?(filename)
69
70
  end
71
+
72
+ def file_path(warning)
73
+ fp = Pathname.new(warning.relative_path)
74
+ if tracker.options[:path_prefix]
75
+ fp = Pathname.new(tracker.options[:path_prefix]) + fp
76
+ end
77
+ fp.to_s
78
+ end
70
79
  end
@@ -24,6 +24,20 @@ module Brakeman
24
24
  @rails[:action_controller][:allow_forgery_protection] == Sexp.new(:false)
25
25
  end
26
26
 
27
+ def default_protect_from_forgery?
28
+ if version_between? "5.2.0", "9.9.9"
29
+ if @rails[:action_controller] and
30
+ @rails[:action_controller][:default_protect_from_forgery] == Sexp.new(:false)
31
+
32
+ return false
33
+ else
34
+ return true
35
+ end
36
+ end
37
+
38
+ false
39
+ end
40
+
27
41
  def erubis?
28
42
  @erubis
29
43
  end
@@ -101,6 +115,36 @@ module Brakeman
101
115
  end
102
116
  end
103
117
 
118
+ #Returns true if low_version <= RAILS_VERSION <= high_version
119
+ #
120
+ #If the Rails version is unknown, returns false.
121
+ def version_between? low_version, high_version, current_version = nil
122
+ current_version ||= rails_version
123
+ return false unless current_version
124
+
125
+ version = current_version.split(".").map!(&:to_i)
126
+ low_version = low_version.split(".").map!(&:to_i)
127
+ high_version = high_version.split(".").map!(&:to_i)
128
+
129
+ version.each_with_index do |v, i|
130
+ if v < low_version.fetch(i, 0)
131
+ return false
132
+ elsif v > low_version.fetch(i, 0)
133
+ break
134
+ end
135
+ end
136
+
137
+ version.each_with_index do |v, i|
138
+ if v > high_version.fetch(i, 0)
139
+ return false
140
+ elsif v < high_version.fetch(i, 0)
141
+ break
142
+ end
143
+ end
144
+
145
+ true
146
+ end
147
+
104
148
  def session_settings
105
149
  @rails[:action_controller] &&
106
150
  @rails[:action_controller][:session]
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "4.0.1"
2
+ Version = "4.1.0"
3
3
  end
@@ -196,11 +196,11 @@ class Brakeman::Warning
196
196
  if @link_path.start_with? "http"
197
197
  @link = @link_path
198
198
  else
199
- @link = "http://brakemanscanner.org/docs/warning_types/#{@link_path}"
199
+ @link = "https://brakemanscanner.org/docs/warning_types/#{@link_path}"
200
200
  end
201
201
  else
202
202
  warning_path = self.warning_type.to_s.downcase.gsub(/\s+/, '_') + "/"
203
- @link = "http://brakemanscanner.org/docs/warning_types/#{warning_path}"
203
+ @link = "https://brakemanscanner.org/docs/warning_types/#{warning_path}"
204
204
  end
205
205
 
206
206
  @link
@@ -105,6 +105,8 @@ module Brakeman::WarningCodes
105
105
  :secret_in_source => 101,
106
106
  :CVE_2016_6316 => 102,
107
107
  :CVE_2016_6317 => 103,
108
+ :divide_by_zero => 104,
109
+ :dangerous_permit_key => 105,
108
110
  }
109
111
 
110
112
  def self.code name
@@ -4,6 +4,7 @@
4
4
  class Sexp
5
5
  attr_accessor :original_line, :or_depth
6
6
  ASSIGNMENT_BOOL = [:gasgn, :iasgn, :lasgn, :cvdecl, :cvasgn, :cdecl, :or, :and, :colon2, :op_asgn_or]
7
+ CALLS = [:call, :attrasgn, :safe_call, :safe_attrasgn]
7
8
 
8
9
  def method_missing name, *args
9
10
  #Brakeman does not use this functionality,
@@ -307,6 +308,21 @@ class Sexp
307
308
  end
308
309
  end
309
310
 
311
+ def call_chain
312
+ expect :call, :attrasgn, :safe_call, :safe_attrasgn
313
+
314
+ chain = []
315
+ call = self
316
+
317
+ while call.class == Sexp and CALLS.include? call.first
318
+ chain << call.method
319
+ call = call.target
320
+ end
321
+
322
+ chain.reverse!
323
+ chain
324
+ end
325
+
310
326
  #Returns condition of an if expression:
311
327
  #
312
328
  # s(:if,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.1
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain:
11
11
  - brakeman-public_cert.pem
12
- date: 2017-09-25 00:00:00.000000000 Z
12
+ date: 2017-12-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
@@ -198,7 +198,7 @@ executables:
198
198
  extensions: []
199
199
  extra_rdoc_files: []
200
200
  files:
201
- - CHANGES
201
+ - CHANGES.md
202
202
  - FEATURES
203
203
  - README.md
204
204
  - bin/brakeman
@@ -216,6 +216,7 @@ files:
216
216
  - lib/brakeman/checks/check_deserialize.rb
217
217
  - lib/brakeman/checks/check_detailed_exceptions.rb
218
218
  - lib/brakeman/checks/check_digest_dos.rb
219
+ - lib/brakeman/checks/check_divide_by_zero.rb
219
220
  - lib/brakeman/checks/check_dynamic_finders.rb
220
221
  - lib/brakeman/checks/check_escape_function.rb
221
222
  - lib/brakeman/checks/check_evaluation.rb
@@ -240,6 +241,7 @@ files:
240
241
  - lib/brakeman/checks/check_nested_attributes.rb
241
242
  - lib/brakeman/checks/check_nested_attributes_bypass.rb
242
243
  - lib/brakeman/checks/check_number_to_currency.rb
244
+ - lib/brakeman/checks/check_permit_attributes.rb
243
245
  - lib/brakeman/checks/check_quote_table_name.rb
244
246
  - lib/brakeman/checks/check_redirect.rb
245
247
  - lib/brakeman/checks/check_regex_dos.rb
@@ -274,6 +276,7 @@ files:
274
276
  - lib/brakeman/checks/check_without_protection.rb
275
277
  - lib/brakeman/checks/check_xml_dos.rb
276
278
  - lib/brakeman/checks/check_yaml_parsing.rb
279
+ - lib/brakeman/codeclimate/engine_configuration.rb
277
280
  - lib/brakeman/commandline.rb
278
281
  - lib/brakeman/differ.rb
279
282
  - lib/brakeman/file_parser.rb
@@ -318,6 +321,7 @@ files:
318
321
  - lib/brakeman/report/config/remediation.yml
319
322
  - lib/brakeman/report/ignore/config.rb
320
323
  - lib/brakeman/report/ignore/interactive.rb
324
+ - lib/brakeman/report/pager.rb
321
325
  - lib/brakeman/report/renderer.rb
322
326
  - lib/brakeman/report/report_base.rb
323
327
  - lib/brakeman/report/report_codeclimate.rb