brakeman-lib 4.7.2 → 4.8.0

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: ef101a185ff582733d1564d862fcb87afbedb1df01482f1c8815f130bd886a0b
4
- data.tar.gz: 938ff3304347e001f5f21880d8d6dfca2bb1d3b26f29dae7d269db67350df70f
3
+ metadata.gz: 34099e8abef9a4c7108905ea8d956d01afbb6037cab597d2ad0beab8790a9060
4
+ data.tar.gz: 982be6bfad0eef60f17627001fef1873bad5a23eef76f687ea434d23773ba9b4
5
5
  SHA512:
6
- metadata.gz: de7d5d8fc614fd226145878158e4e75745e31afce19e87e84e73d19d5182b42128ff36d2a5fa45567286ec989513fa5c1ae40aa53353f56b04c3a7a52a11bef6
7
- data.tar.gz: 78006970c55993fbcf96ac56783d3ed04beb1d5bf54d4b716a04158796398577ded0fb1bd7b7070e91074a6daff48e762b6b49515c10ca6d27c84cde0e7b0531
6
+ metadata.gz: ce68529ca660b85d86b9569b4f8ddfe41c4b7b3bc724d1afc9860f91fa0a945eb473bbd5bb83b4c9d8e61ac6255ab0b11a42878921671a03a0964d2440912178
7
+ data.tar.gz: 1fbd104e129d5fce136d4c4984296a7daa6c88ffd8f22edbb576613cb6519547676f38364a2f728dcc2c9a322119d0024ae845baf7383ecf38b43038e9e129c6
data/CHANGES.md CHANGED
@@ -1,3 +1,15 @@
1
+ # Unreleased
2
+
3
+ * Add JUnit-XML report format (Naoki Kimura)
4
+ * Sort ignore files by fingerprint and line (Ngan Pham)
5
+ * Freeze call index results
6
+ * Fix output test when using newer Minitest
7
+ * Properly render confidence in Markdown report
8
+ * Report old warnings as fixed if zero warnings reported
9
+ * Catch dangerous concatenation in `CheckExecute` (Jacob Evelyn)
10
+ * Show user-friendly message when ignore config file has invalid JSON (D. Hicks)
11
+ * Initialize Rails version with `nil` (Carsten Wirth)
12
+
1
13
  # 4.7.2 - 2019-11-25
2
14
 
3
15
  * Remove version guard for `named_scope` vs. `scope`
@@ -231,6 +231,8 @@ module Brakeman
231
231
  [:to_text]
232
232
  when :table, :to_table
233
233
  [:to_table]
234
+ when :junit, :to_junit
235
+ [:to_junit]
234
236
  else
235
237
  [:to_text]
236
238
  end
@@ -258,6 +260,8 @@ module Brakeman
258
260
  :to_text
259
261
  when /\.table$/i
260
262
  :to_table
263
+ when /\.junit$/i
264
+ :to_junit
261
265
  else
262
266
  :to_text
263
267
  end
@@ -280,15 +280,6 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
280
280
  return location, line
281
281
  end
282
282
 
283
- #Checks if an expression contains string interpolation.
284
- #
285
- #Returns Match with :interp type if found.
286
- def include_interp? exp
287
- @string_interp = false
288
- process exp
289
- @string_interp
290
- end
291
-
292
283
  #Checks if _exp_ includes user input in the form of cookies, parameters,
293
284
  #request environment, or model attributes.
294
285
  #
@@ -504,4 +495,16 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
504
495
 
505
496
  @active_record_models
506
497
  end
498
+
499
+ STRING_METHODS = Set[:<<, :+, :concat, :prepend]
500
+ private_constant :STRING_METHODS
501
+
502
+ def string_building? exp
503
+ return false unless call? exp and STRING_METHODS.include? exp.method
504
+
505
+ node_type? exp.target, :str, :dstr or
506
+ node_type? exp.first_arg, :str, :dstr or
507
+ string_building? exp.target or
508
+ string_building? exp.first_arg
509
+ end
507
510
  end
@@ -55,8 +55,7 @@ class Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting
55
55
 
56
56
  @current_file = result[:location][:file]
57
57
 
58
- call = result[:call] = result[:call].dup
59
-
58
+ call = result[:call]
60
59
  args = call.arglist
61
60
 
62
61
  tag_name = args[1]
@@ -56,8 +56,20 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
56
56
 
57
57
  case call.method
58
58
  when :popen
59
- unless array? first_arg
60
- failure = include_user_input?(args) || dangerous_interp?(args)
59
+ # Normally, if we're in a `popen` call, we only are worried about shell
60
+ # injection when the argument is not an array, because array elements
61
+ # are always escaped by Ruby. However, an exception is when the array
62
+ # contains two values are something like "bash -c" because then the third
63
+ # element is effectively the command being run and might be a malicious
64
+ # executable if it comes (partially or fully) from user input.
65
+ if !array?(first_arg)
66
+ failure = include_user_input?(first_arg) ||
67
+ dangerous_interp?(first_arg) ||
68
+ dangerous_string_building?(first_arg)
69
+ elsif dash_c_shell_command?(first_arg[1], first_arg[2])
70
+ failure = include_user_input?(first_arg[3]) ||
71
+ dangerous_interp?(first_arg[3]) ||
72
+ dangerous_string_building?(first_arg[3])
61
73
  end
62
74
  when :system, :exec
63
75
  # Normally, if we're in a `system` or `exec` call, we only are worried
@@ -67,12 +79,18 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
67
79
  # the third argument is effectively the command being run and might be
68
80
  # a malicious executable if it comes (partially or fully) from user input.
69
81
  if dash_c_shell_command?(first_arg, call.second_arg)
70
- failure = include_user_input?(args[3]) || dangerous_interp?(args[3])
82
+ failure = include_user_input?(args[3]) ||
83
+ dangerous_interp?(args[3]) ||
84
+ dangerous_string_building?(args[3])
71
85
  else
72
- failure = include_user_input?(first_arg) || dangerous_interp?(first_arg)
86
+ failure = include_user_input?(first_arg) ||
87
+ dangerous_interp?(first_arg) ||
88
+ dangerous_string_building?(first_arg)
73
89
  end
74
90
  else
75
- failure = include_user_input?(args) || dangerous_interp?(args)
91
+ failure = include_user_input?(args) ||
92
+ dangerous_interp?(args) ||
93
+ dangerous_string_building?(args)
76
94
  end
77
95
 
78
96
  if failure and original? result
@@ -219,6 +237,23 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
219
237
  false
220
238
  end
221
239
 
240
+ #Checks if an expression contains string interpolation.
241
+ #
242
+ #Returns Match with :interp type if found.
243
+ def include_interp? exp
244
+ @string_interp = false
245
+ process exp
246
+ @string_interp
247
+ end
248
+
249
+ def dangerous_string_building? exp
250
+ if string_building?(exp) && res = dangerous?(exp)
251
+ return Match.new(:interp, res)
252
+ end
253
+
254
+ false
255
+ end
256
+
222
257
  def shell_escape? exp
223
258
  return false unless call? exp
224
259
 
@@ -34,7 +34,7 @@ class Brakeman::CheckLinkTo < Brakeman::CheckCrossSiteScripting
34
34
 
35
35
  #Have to make a copy of this, otherwise it will be changed to
36
36
  #an ignored method call by the code above.
37
- call = result[:call] = result[:call].dup
37
+ call = result[:call]
38
38
 
39
39
  first_arg = call.first_arg
40
40
  second_arg = call.second_arg
@@ -30,9 +30,7 @@ class Brakeman::CheckLinkToHref < Brakeman::CheckLinkTo
30
30
  end
31
31
 
32
32
  def process_result result
33
- #Have to make a copy of this, otherwise it will be changed to
34
- #an ignored method call by the code above.
35
- call = result[:call] = result[:call].dup
33
+ call = result[:call]
36
34
  @matched = false
37
35
 
38
36
  url_arg = if result[:block]
@@ -525,8 +525,6 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
525
525
  false
526
526
  end
527
527
 
528
- STRING_METHODS = Set[:<<, :+, :concat, :prepend]
529
-
530
528
  def check_for_string_building exp
531
529
  return unless call? exp
532
530
 
@@ -573,15 +571,6 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
573
571
  end
574
572
  end
575
573
 
576
- def string_building? exp
577
- return false unless call? exp and STRING_METHODS.include? exp.method
578
-
579
- node_type? exp.target, :str, :dstr or
580
- node_type? exp.first_arg, :str, :dstr or
581
- string_building? exp.target or
582
- string_building? exp.first_arg
583
- end
584
-
585
574
  IGNORE_METHODS_IN_SQL = Set[:id, :merge_conditions, :table_name, :quoted_table_name,
586
575
  :quoted_primary_key, :to_i, :to_f, :sanitize_sql, :sanitize_sql_array,
587
576
  :sanitize_sql_for_assignment, :sanitize_sql_for_conditions, :sanitize_sql_hash,
@@ -1,8 +1,6 @@
1
1
  # extracting the diff logic to it's own class for consistency. Currently handles
2
2
  # an array of Brakeman::Warnings or plain hash representations.
3
3
  class Brakeman::Differ
4
- DEFAULT_HASH = {:new => [], :fixed => []}
5
- OLD_WARNING_KEYS = [:warning_type, :location, :code, :message, :file, :link, :confidence, :user_input]
6
4
  attr_reader :old_warnings, :new_warnings
7
5
 
8
6
  def initialize new_warnings, old_warnings
@@ -11,9 +9,6 @@ class Brakeman::Differ
11
9
  end
12
10
 
13
11
  def diff
14
- # get the type of elements
15
- return DEFAULT_HASH if @new_warnings.empty?
16
-
17
12
  warnings = {}
18
13
  warnings[:new] = @new_warnings - @old_warnings
19
14
  warnings[:fixed] = @old_warnings - @new_warnings
@@ -225,7 +225,7 @@ module Brakeman::Options
225
225
 
226
226
  opts.on "-f",
227
227
  "--format TYPE",
228
- [:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table],
228
+ [:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit],
229
229
  "Specify output formats. Default is text" do |type|
230
230
 
231
231
  type = "s" if type == :text
@@ -60,7 +60,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
60
60
  end
61
61
 
62
62
  def process_call exp
63
- @calls << create_call_hash(exp)
63
+ @calls << create_call_hash(exp).freeze
64
64
  exp
65
65
  end
66
66
 
@@ -72,6 +72,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
72
72
 
73
73
  call_hash[:block] = exp.block
74
74
  call_hash[:block_args] = exp.block_args
75
+ call_hash.freeze
75
76
 
76
77
  @calls << call_hash
77
78
 
@@ -136,7 +137,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
136
137
  :call => exp,
137
138
  :nested => false,
138
139
  :location => make_location,
139
- :parent => @current_call }
140
+ :parent => @current_call }.freeze
140
141
  end
141
142
 
142
143
  #Gets the target of a call as a Symbol
@@ -6,7 +6,7 @@ require 'brakeman/report/report_base'
6
6
  class Brakeman::Report
7
7
  attr_reader :tracker
8
8
 
9
- VALID_FORMATS = [:to_html, :to_pdf, :to_csv, :to_json, :to_tabs, :to_hash, :to_s, :to_markdown, :to_codeclimate, :to_plain, :to_text]
9
+ VALID_FORMATS = [:to_html, :to_pdf, :to_csv, :to_json, :to_tabs, :to_hash, :to_s, :to_markdown, :to_codeclimate, :to_plain, :to_text, :to_junit]
10
10
 
11
11
  def initialize tracker
12
12
  @app_tree = tracker.app_tree
@@ -40,6 +40,9 @@ class Brakeman::Report
40
40
  return self.to_table
41
41
  when :to_pdf
42
42
  raise "PDF output is not yet supported."
43
+ when :to_junit
44
+ require_report 'junit'
45
+ Brakeman::Report::JUnit
43
46
  else
44
47
  raise "Invalid format: #{format}. Should be one of #{VALID_FORMATS.inspect}"
45
48
  end
@@ -97,7 +97,11 @@ module Brakeman
97
97
  # Read configuration to file
98
98
  def read_from_file file = @file
99
99
  if File.exist? file
100
- @already_ignored = JSON.parse(File.read(file), :symbolize_names => true)[:ignored_warnings]
100
+ begin
101
+ @already_ignored = JSON.parse(File.read(file), :symbolize_names => true)[:ignored_warnings]
102
+ rescue => e
103
+ raise e, "\nError[#{e.class}] while reading brakeman ignore file: #{file}\n"
104
+ end
101
105
  else
102
106
  Brakeman.notify "[Notice] Could not find ignore configuration in #{file}"
103
107
  @already_ignored = []
@@ -118,7 +122,7 @@ module Brakeman
118
122
 
119
123
  w[:note] = @notes[w[:fingerprint]] || ""
120
124
  w
121
- end.sort_by { |w| w[:fingerprint] }
125
+ end.sort_by { |w| [w[:fingerprint], w[:line]] }
122
126
 
123
127
  output = {
124
128
  :ignored_warnings => warnings,
@@ -0,0 +1,104 @@
1
+ require 'time'
2
+ require "stringio"
3
+ require 'rexml/document'
4
+
5
+ class Brakeman::Report::JUnit < Brakeman::Report::Base
6
+ def generate_report
7
+ io = StringIO.new
8
+ doc = REXML::Document.new
9
+ doc.add REXML::XMLDecl.new '1.0', 'UTF-8'
10
+
11
+ test_suites = REXML::Element.new 'testsuites'
12
+ test_suites.add_attribute 'xmlns:brakeman', 'https://brakemanscanner.org/'
13
+ properties = test_suites.add_element 'brakeman:properties', { 'xml:id' => 'scan_info' }
14
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'app_path', 'brakeman:value' => tracker.app_path }
15
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'rails_version', 'brakeman:value' => rails_version }
16
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'security_warnings', 'brakeman:value' => all_warnings.length }
17
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'start_time', 'brakeman:value' => tracker.start_time.iso8601 }
18
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'end_time', 'brakeman:value' => tracker.end_time.iso8601 }
19
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'duration', 'brakeman:value' => tracker.duration }
20
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'checks_performed', 'brakeman:value' => checks.checks_run.join(',') }
21
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'number_of_controllers', 'brakeman:value' => tracker.controllers.length }
22
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'number_of_models', 'brakeman:value' => tracker.models.length - 1 }
23
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'ruby_version', 'brakeman:value' => number_of_templates(@tracker) }
24
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'number_of_templates', 'brakeman:value' => RUBY_VERSION }
25
+ properties.add_element 'brakeman:property', { 'brakeman:name' => 'brakeman_version', 'brakeman:value' => Brakeman::Version }
26
+
27
+ errors = test_suites.add_element 'brakeman:errors'
28
+ tracker.errors.each { |e|
29
+ error = errors.add_element 'brakeman:error'
30
+ error.add_attribute 'brakeman:message', e[:error]
31
+ e[:backtrace].each { |b|
32
+ backtrace = error.add_element 'brakeman:backtrace'
33
+ backtrace.add_text b
34
+ }
35
+ }
36
+
37
+ obsolete = test_suites.add_element 'brakeman:obsolete'
38
+ tracker.unused_fingerprints.each { |fingerprint|
39
+ obsolete.add_element 'brakeman:warning', { 'brakeman:fingerprint' => fingerprint }
40
+ }
41
+
42
+ ignored = test_suites.add_element 'brakeman:ignored'
43
+ ignored_warnings.each { |w|
44
+ warning = ignored.add_element 'brakeman:warning'
45
+ warning.add_attribute 'brakeman:message', w.message
46
+ warning.add_attribute 'brakeman:category', w.warning_type
47
+ warning.add_attribute 'brakeman:file', warning_file(w)
48
+ warning.add_attribute 'brakeman:line', w.line
49
+ warning.add_attribute 'brakeman:fingerprint', w.fingerprint
50
+ warning.add_attribute 'brakeman:confidence', TEXT_CONFIDENCE[w.confidence]
51
+ warning.add_attribute 'brakeman:code', w.format_code
52
+ warning.add_text w.to_s
53
+ }
54
+
55
+ hostname = `hostname`.strip
56
+ i = 0
57
+ all_warnings
58
+ .map { |warning| [warning.file, [warning]] }
59
+ .reduce({}) { |entries, entry|
60
+ key, value = entry
61
+ entries[key] = entries[key] ? entries[key].concat(value) : value
62
+ entries
63
+ }
64
+ .each { |file, warnings|
65
+ i += 1
66
+ test_suite = test_suites.add_element 'testsuite'
67
+ test_suite.add_attribute 'id', i
68
+ test_suite.add_attribute 'package', 'brakeman'
69
+ test_suite.add_attribute 'name', file.relative
70
+ test_suite.add_attribute 'timestamp', tracker.start_time.strftime('%FT%T')
71
+ test_suite.add_attribute 'hostname', hostname == '' ? 'localhost' : hostname
72
+ test_suite.add_attribute 'tests', checks.checks_run.length
73
+ test_suite.add_attribute 'failures', warnings.length
74
+ test_suite.add_attribute 'errors', '0'
75
+ test_suite.add_attribute 'time', '0'
76
+
77
+ test_suite.add_element 'properties'
78
+
79
+ warnings.each { |warning|
80
+ test_case = test_suite.add_element 'testcase'
81
+ test_case.add_attribute 'name', 'run_check'
82
+ test_case.add_attribute 'classname', warning.check
83
+ test_case.add_attribute 'time', '0'
84
+
85
+ failure = test_case.add_element 'failure'
86
+ failure.add_attribute 'message', warning.message
87
+ failure.add_attribute 'type', warning.warning_type
88
+ failure.add_attribute 'brakeman:fingerprint', warning.fingerprint
89
+ failure.add_attribute 'brakeman:file', warning_file(warning)
90
+ failure.add_attribute 'brakeman:line', warning.line
91
+ failure.add_attribute 'brakeman:confidence', TEXT_CONFIDENCE[warning.confidence]
92
+ failure.add_attribute 'brakeman:code', warning.format_code
93
+ failure.add_text warning.to_s
94
+ }
95
+
96
+ test_suite.add_element 'system-out'
97
+ test_suite.add_element 'system-err'
98
+ }
99
+
100
+ doc.add test_suites
101
+ doc.write io
102
+ io.string
103
+ end
104
+ end
@@ -84,7 +84,6 @@ class Brakeman::Report::Markdown < Brakeman::Report::Table
84
84
  end
85
85
 
86
86
  def convert_warning warning, original
87
- warning["Confidence"] = TEXT_CONFIDENCE[warning["Confidence"]]
88
87
  warning["Message"] = markdown_message original, warning["Message"]
89
88
  warning["Warning Type"] = "[#{warning['Warning Type']}](#{original.link})" if original.link
90
89
  warning
@@ -15,6 +15,7 @@ module Brakeman
15
15
  @escape_html = nil
16
16
  @erubis = nil
17
17
  @ruby_version = ""
18
+ @rails_version = nil
18
19
  end
19
20
 
20
21
  def default_protect_from_forgery?
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "4.7.2"
2
+ Version = "4.8.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.7.2
4
+ version: 4.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-11-25 00:00:00.000000000 Z
11
+ date: 2020-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -348,6 +348,7 @@ files:
348
348
  - lib/brakeman/report/report_hash.rb
349
349
  - lib/brakeman/report/report_html.rb
350
350
  - lib/brakeman/report/report_json.rb
351
+ - lib/brakeman/report/report_junit.rb
351
352
  - lib/brakeman/report/report_markdown.rb
352
353
  - lib/brakeman/report/report_table.rb
353
354
  - lib/brakeman/report/report_tabs.rb
@@ -405,7 +406,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
405
406
  - !ruby/object:Gem::Version
406
407
  version: '0'
407
408
  requirements: []
408
- rubygems_version: 3.0.3
409
+ rubygems_version: 3.1.2
409
410
  signing_key:
410
411
  specification_version: 4
411
412
  summary: Security vulnerability scanner for Ruby on Rails.