brakeman 5.0.0 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +46 -0
  3. data/README.md +10 -1
  4. data/bundle/load.rb +4 -3
  5. data/bundle/ruby/2.7.0/gems/parallel-1.20.1/MIT-LICENSE.txt +20 -0
  6. data/bundle/ruby/2.7.0/gems/parallel-1.20.1/lib/parallel.rb +523 -0
  7. data/bundle/ruby/2.7.0/gems/parallel-1.20.1/lib/parallel/processor_count.rb +42 -0
  8. data/bundle/ruby/2.7.0/gems/parallel-1.20.1/lib/parallel/version.rb +3 -0
  9. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/LICENSE.txt +0 -0
  10. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/NEWS.md +37 -0
  11. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/README.md +2 -14
  12. data/bundle/ruby/2.7.0/gems/rexml-3.2.5/lib/rexml.rb +3 -0
  13. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/attlistdecl.rb +0 -0
  14. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/attribute.rb +0 -0
  15. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/cdata.rb +0 -0
  16. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/child.rb +0 -0
  17. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/comment.rb +0 -0
  18. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/doctype.rb +55 -31
  19. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/document.rb +194 -34
  20. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/dtd/attlistdecl.rb +0 -0
  21. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/dtd/dtd.rb +0 -0
  22. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/dtd/elementdecl.rb +0 -0
  23. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/dtd/entitydecl.rb +0 -0
  24. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/dtd/notationdecl.rb +0 -0
  25. data/bundle/ruby/2.7.0/gems/rexml-3.2.5/lib/rexml/element.rb +2599 -0
  26. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/encoding.rb +0 -0
  27. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/entity.rb +0 -0
  28. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/formatters/default.rb +0 -0
  29. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/formatters/pretty.rb +0 -0
  30. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/formatters/transitive.rb +0 -0
  31. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/functions.rb +0 -0
  32. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/instruction.rb +0 -0
  33. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/light/node.rb +0 -8
  34. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/namespace.rb +0 -0
  35. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/node.rb +0 -0
  36. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/output.rb +0 -0
  37. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parent.rb +0 -0
  38. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parseexception.rb +0 -0
  39. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parsers/baseparser.rb +139 -39
  40. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parsers/lightparser.rb +0 -0
  41. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parsers/pullparser.rb +0 -0
  42. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parsers/sax2parser.rb +0 -0
  43. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parsers/streamparser.rb +0 -0
  44. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parsers/treeparser.rb +0 -0
  45. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parsers/ultralightparser.rb +0 -0
  46. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/parsers/xpathparser.rb +25 -11
  47. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/quickpath.rb +0 -0
  48. data/bundle/ruby/2.7.0/gems/rexml-3.2.5/lib/rexml/rexml.rb +37 -0
  49. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/sax2listener.rb +0 -0
  50. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/security.rb +0 -0
  51. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/source.rb +0 -0
  52. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/streamlistener.rb +0 -0
  53. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/text.rb +0 -0
  54. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/undefinednamespaceexception.rb +0 -0
  55. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/validation/relaxng.rb +0 -0
  56. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/validation/validation.rb +0 -0
  57. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/validation/validationexception.rb +0 -0
  58. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/xmldecl.rb +0 -0
  59. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/xmltokens.rb +0 -0
  60. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/xpath.rb +0 -0
  61. data/bundle/ruby/2.7.0/gems/{rexml-3.2.4 → rexml-3.2.5}/lib/rexml/xpath_parser.rb +36 -30
  62. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/History.rdoc +19 -0
  63. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/Manifest.txt +2 -0
  64. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/README.rdoc +0 -0
  65. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/compare/normalize.rb +2 -2
  66. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/debugging.md +190 -0
  67. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/rp_extensions.rb +0 -0
  68. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/rp_stringscanner.rb +0 -0
  69. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby20_parser.rb +2550 -2537
  70. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby20_parser.y +9 -1
  71. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby21_parser.rb +7148 -0
  72. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby21_parser.y +9 -1
  73. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby22_parser.rb +7185 -0
  74. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby22_parser.y +9 -1
  75. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby23_parser.rb +2585 -2561
  76. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby23_parser.y +9 -1
  77. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby24_parser.rb +2622 -2607
  78. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby24_parser.y +9 -1
  79. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby25_parser.rb +2612 -2598
  80. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby25_parser.y +9 -1
  81. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby26_parser.rb +2610 -2594
  82. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby26_parser.y +10 -1
  83. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby27_parser.rb +7358 -0
  84. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby27_parser.y +47 -1
  85. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby30_parser.rb +7358 -0
  86. data/bundle/ruby/2.7.0/gems/ruby_parser-3.16.0/lib/ruby30_parser.y +2703 -0
  87. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_lexer.rb +19 -0
  88. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_lexer.rex +1 -1
  89. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_lexer.rex.rb +1 -1
  90. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_parser.rb +2 -0
  91. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_parser.yy +57 -1
  92. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/lib/ruby_parser_extras.rb +2 -2
  93. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/tools/munge.rb +2 -2
  94. data/bundle/ruby/2.7.0/gems/{ruby_parser-3.15.1 → ruby_parser-3.16.0}/tools/ripper.rb +1 -1
  95. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/History.rdoc +6 -0
  96. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/Manifest.txt +0 -0
  97. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/README.rdoc +0 -0
  98. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/composite_sexp_processor.rb +0 -0
  99. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/pt_testcase.rb +2 -2
  100. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/sexp.rb +0 -0
  101. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/sexp_matcher.rb +0 -0
  102. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/sexp_processor.rb +1 -1
  103. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/strict_sexp.rb +0 -0
  104. data/bundle/ruby/2.7.0/gems/{sexp_processor-4.15.2 → sexp_processor-4.15.3}/lib/unique.rb +0 -0
  105. data/lib/brakeman.rb +23 -8
  106. data/lib/brakeman/checks/check_detailed_exceptions.rb +1 -1
  107. data/lib/brakeman/checks/check_evaluation.rb +1 -1
  108. data/lib/brakeman/checks/check_execute.rb +10 -0
  109. data/lib/brakeman/checks/check_mass_assignment.rb +4 -6
  110. data/lib/brakeman/checks/check_render.rb +15 -1
  111. data/lib/brakeman/checks/check_sanitize_methods.rb +2 -1
  112. data/lib/brakeman/checks/check_sql.rb +58 -8
  113. data/lib/brakeman/checks/check_verb_confusion.rb +1 -1
  114. data/lib/brakeman/commandline.rb +1 -1
  115. data/lib/brakeman/file_parser.rb +45 -15
  116. data/lib/brakeman/options.rb +7 -2
  117. data/lib/brakeman/parsers/template_parser.rb +24 -0
  118. data/lib/brakeman/processors/alias_processor.rb +105 -18
  119. data/lib/brakeman/processors/base_processor.rb +4 -4
  120. data/lib/brakeman/processors/controller_alias_processor.rb +6 -43
  121. data/lib/brakeman/processors/lib/call_conversion_helper.rb +10 -6
  122. data/lib/brakeman/processors/lib/rails4_config_processor.rb +2 -1
  123. data/lib/brakeman/processors/library_processor.rb +9 -0
  124. data/lib/brakeman/processors/model_processor.rb +31 -0
  125. data/lib/brakeman/report.rb +4 -1
  126. data/lib/brakeman/report/ignore/config.rb +4 -4
  127. data/lib/brakeman/report/ignore/interactive.rb +1 -1
  128. data/lib/brakeman/report/report_github.rb +31 -0
  129. data/lib/brakeman/report/report_sarif.rb +21 -2
  130. data/lib/brakeman/rescanner.rb +1 -1
  131. data/lib/brakeman/scanner.rb +4 -1
  132. data/lib/brakeman/tracker.rb +33 -4
  133. data/lib/brakeman/tracker/collection.rb +57 -7
  134. data/lib/brakeman/tracker/method_info.rb +70 -0
  135. data/lib/brakeman/util.rb +34 -18
  136. data/lib/brakeman/version.rb +1 -1
  137. data/lib/ruby_parser/bm_sexp.rb +14 -0
  138. metadata +104 -97
  139. data/bundle/ruby/2.7.0/gems/rexml-3.2.4/Gemfile +0 -6
  140. data/bundle/ruby/2.7.0/gems/rexml-3.2.4/lib/rexml/element.rb +0 -1269
  141. data/bundle/ruby/2.7.0/gems/rexml-3.2.4/lib/rexml/rexml.rb +0 -32
  142. data/bundle/ruby/2.7.0/gems/rexml-3.2.4/rexml.gemspec +0 -84
  143. data/bundle/ruby/2.7.0/gems/ruby_parser-3.15.1/debugging.md +0 -57
  144. data/bundle/ruby/2.7.0/gems/ruby_parser-3.15.1/lib/ruby21_parser.rb +0 -7140
  145. data/bundle/ruby/2.7.0/gems/ruby_parser-3.15.1/lib/ruby22_parser.rb +0 -7160
  146. data/bundle/ruby/2.7.0/gems/ruby_parser-3.15.1/lib/ruby27_parser.rb +0 -7224
@@ -90,7 +90,8 @@ class Brakeman::CheckSanitizeMethods < Brakeman::BaseCheck
90
90
  def loofah_vulnerable_cve_2018_8048?
91
91
  loofah_version = tracker.config.gem_version(:loofah)
92
92
 
93
- loofah_version and loofah_version < "2.2.1"
93
+ # 2.2.1 is fix version
94
+ loofah_version and version_between?("0.0.0", "2.2.0", loofah_version)
94
95
  end
95
96
 
96
97
  def warn_sanitizer_cve cve, link, upgrade_version
@@ -22,7 +22,19 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
22
22
  :find_by_sql, :maximum, :minimum, :pluck, :sum, :update_all]
23
23
  @sql_targets.concat [:from, :group, :having, :joins, :lock, :order, :reorder, :where] if tracker.options[:rails3]
24
24
  @sql_targets.concat [:find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :not] if tracker.options[:rails4]
25
- @sql_targets << :delete_by << :destroy_by if tracker.options[:rails6]
25
+
26
+ if tracker.options[:rails6]
27
+ @sql_targets.concat [:delete_by, :destroy_by, :rewhere, :reselect]
28
+
29
+ @sql_targets.delete :delete_all
30
+ @sql_targets.delete :destroy_all
31
+ end
32
+
33
+ if version_between?("6.1.0", "9.9.9")
34
+ @sql_targets.delete :order
35
+ @sql_targets.delete :reorder
36
+ @sql_targets.delete :pluck
37
+ end
26
38
 
27
39
  if version_between?("2.0.0", "3.9.9") or tracker.config.rails_version.nil?
28
40
  @sql_targets << :first << :last << :all
@@ -185,7 +197,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
185
197
  else
186
198
  check_find_arguments call.last_arg
187
199
  end
188
- when :where, :having, :find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,:not, :delete_by, :destroy_by
200
+ when :where, :rewhere, :having, :find_by, :find_by!, :find_or_create_by, :find_or_create_by!, :find_or_initialize_by,:not, :delete_by, :destroy_by
189
201
  check_query_arguments call.arglist
190
202
  when :order, :group, :reorder
191
203
  check_order_arguments call.arglist
@@ -199,7 +211,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
199
211
  unsafe_sql? call.first_arg
200
212
  when :sql
201
213
  unsafe_sql? call.first_arg
202
- when :update_all, :select
214
+ when :update_all, :select, :reselect
203
215
  check_update_all_arguments call.args
204
216
  when *@connection_calls
205
217
  check_by_sql_arguments call.first_arg
@@ -572,13 +584,17 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
572
584
  end
573
585
 
574
586
  IGNORE_METHODS_IN_SQL = Set[:id, :merge_conditions, :table_name, :quoted_table_name,
575
- :quoted_primary_key, :to_i, :to_f, :sanitize_sql, :sanitize_sql_array,
587
+ :quoted_primary_key, :to_i, :to_f, :sanitize_sql, :sanitize_sql_array, :sanitize_sql_like,
576
588
  :sanitize_sql_for_assignment, :sanitize_sql_for_conditions, :sanitize_sql_hash,
577
589
  :sanitize_sql_hash_for_assignment, :sanitize_sql_hash_for_conditions,
578
590
  :to_sql, :sanitize, :primary_key, :table_name_prefix, :table_name_suffix,
579
591
  :where_values_hash, :foreign_key, :uuid
580
592
  ]
581
593
 
594
+ def ignore_methods_in_sql
595
+ @ignore_methods_in_sql ||= IGNORE_METHODS_IN_SQL + (tracker.options[:sql_safe_methods] || [])
596
+ end
597
+
582
598
  def safe_value? exp
583
599
  return true unless sexp? exp
584
600
 
@@ -589,10 +605,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
589
605
  if exp.method == :to_s or exp.method == :to_sym
590
606
  safe_value? exp.target
591
607
  else
592
- IGNORE_METHODS_IN_SQL.include? exp.method or
593
- quote_call? exp or
594
- arel? exp or
595
- exp.method.to_s.end_with? "_id"
608
+ ignore_call? exp
596
609
  end
597
610
  when :if
598
611
  safe_value? exp.then_clause and safe_value? exp.else_clause
@@ -607,6 +620,17 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
607
620
  end
608
621
  end
609
622
 
623
+ def ignore_call? exp
624
+ return unless call? exp
625
+
626
+ ignore_methods_in_sql.include? exp.method or
627
+ quote_call? exp or
628
+ arel? exp or
629
+ exp.method.to_s.end_with? "_id" or
630
+ number_target? exp or
631
+ date_target? exp
632
+ end
633
+
610
634
  QUOTE_METHODS = [:quote, :quote_column_name, :quoted_date, :quote_string, :quote_table_name]
611
635
 
612
636
  def quote_call? exp
@@ -695,4 +719,30 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
695
719
  active_record_models.include? klass
696
720
  end
697
721
  end
722
+
723
+ def number_target? exp
724
+ return unless call? exp
725
+
726
+ if number? exp.target
727
+ true
728
+ elsif call? exp.target
729
+ number_target? exp.target
730
+ else
731
+ false
732
+ end
733
+ end
734
+
735
+ DATE_CLASS = s(:const, :Date)
736
+
737
+ def date_target? exp
738
+ return unless call? exp
739
+
740
+ if exp.target == DATE_CLASS
741
+ true
742
+ elsif call? exp.target
743
+ date_target? exp.target
744
+ else
745
+ false
746
+ end
747
+ end
698
748
  end
@@ -32,7 +32,7 @@ class Brakeman::CheckVerbConfusion < Brakeman::BaseCheck
32
32
  return
33
33
  end
34
34
 
35
- process method[:src]
35
+ process method.src
36
36
  end
37
37
 
38
38
  def process_if exp
@@ -126,7 +126,7 @@ module Brakeman
126
126
 
127
127
  ensure_ignore_notes_failed = false
128
128
  if tracker.options[:ensure_ignore_notes]
129
- fingerprints = Brakeman::ignore_file_entries_with_empty_notes tracker.ignored_filter&.file
129
+ fingerprints = Brakeman::ignore_file_entries_with_empty_notes tracker.ignored_filter&.file, options
130
130
 
131
131
  unless fingerprints.empty?
132
132
  ensure_ignore_notes_failed = true
@@ -1,3 +1,5 @@
1
+ require 'parallel'
2
+
1
3
  module Brakeman
2
4
  ASTFile = Struct.new(:path, :ast)
3
5
 
@@ -5,29 +7,62 @@ module Brakeman
5
7
  class FileParser
6
8
  attr_reader :file_list, :errors
7
9
 
8
- def initialize app_tree, timeout
10
+ def initialize app_tree, timeout, parallel = true
9
11
  @app_tree = app_tree
10
12
  @timeout = timeout
11
13
  @file_list = []
12
14
  @errors = []
15
+ @parallel = parallel
13
16
  end
14
17
 
15
18
  def parse_files list
16
- read_files list do |path, contents|
17
- if ast = parse_ruby(contents, path.relative)
18
- ASTFile.new(path, ast)
19
+ if @parallel
20
+ parallel_options = {}
21
+ else
22
+ # Disable parallelism
23
+ parallel_options = { in_threads: 0 }
24
+ end
25
+
26
+ # Parse the files in parallel.
27
+ # By default, the parsing will be in separate processes.
28
+ # So we map the result to ASTFiles and/or Exceptions
29
+ # then partition them into ASTFiles and Exceptions
30
+ # and add the Exceptions to @errors
31
+ #
32
+ # Basically just a funky way to deal with two possible
33
+ # return types that are returned from isolated processes.
34
+ #
35
+ # Note this method no longer uses read_files
36
+ @file_list, new_errors = Parallel.map(list, parallel_options) do |file_name|
37
+ file_path = @app_tree.file_path(file_name)
38
+ contents = file_path.read
39
+
40
+ begin
41
+ if ast = parse_ruby(contents, file_path.relative)
42
+ ASTFile.new(file_name, ast)
43
+ end
44
+ rescue Exception => e
45
+ e
19
46
  end
47
+ end.compact.partition do |result|
48
+ result.is_a? ASTFile
20
49
  end
50
+
51
+ errors.concat new_errors
21
52
  end
22
53
 
23
54
  def read_files list
24
55
  list.each do |path|
25
56
  file = @app_tree.file_path(path)
26
57
 
27
- result = yield file, file.read
58
+ begin
59
+ result = yield file, file.read
28
60
 
29
- if result
30
- @file_list << result
61
+ if result
62
+ @file_list << result
63
+ end
64
+ rescue Exception => e
65
+ @errors << e
31
66
  end
32
67
  end
33
68
  end
@@ -42,17 +77,12 @@ module Brakeman
42
77
  Brakeman.debug "Parsing #{path}"
43
78
  RubyParser.new.parse input, path, @timeout
44
79
  rescue Racc::ParseError => e
45
- error e.exception(e.message + "\nCould not parse #{path}")
80
+ raise e.exception(e.message + "\nCould not parse #{path}")
46
81
  rescue Timeout::Error => e
47
- error Exception.new("Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout")
82
+ raise Exception.new("Parsing #{path} took too long (> #{@timeout} seconds). Try increasing the limit with --parser-timeout")
48
83
  rescue => e
49
- error e.exception(e.message + "\nWhile processing #{path}")
84
+ raise e.exception(e.message + "\nWhile processing #{path}")
50
85
  end
51
86
  end
52
-
53
- def error exception
54
- @errors << exception
55
- nil
56
- end
57
87
  end
58
88
  end
@@ -39,7 +39,7 @@ module Brakeman::Options
39
39
  OptionParser.new do |opts|
40
40
  opts.banner = "Usage: brakeman [options] rails/root/path"
41
41
 
42
- opts.on "-n", "--no-threads", "Run checks sequentially" do
42
+ opts.on "-n", "--no-threads", "Run checks and file parsing sequentially" do
43
43
  options[:parallel_checks] = false
44
44
  end
45
45
 
@@ -151,6 +151,11 @@ module Brakeman::Options
151
151
  options[:safe_methods].merge methods.map {|e| e.to_sym }
152
152
  end
153
153
 
154
+ opts.on "--sql-safe-methods meth1,meth2,etc", Array, "Do not warn of SQL if the input is wrapped in a safe method" do |methods|
155
+ options[:sql_safe_methods] ||= Set.new
156
+ options[:sql_safe_methods].merge methods.map {|e| e.to_sym }
157
+ end
158
+
154
159
  opts.on "--url-safe-methods method1,method2,etc", Array, "Do not warn of XSS if the link_to href parameter is wrapped in a safe method" do |methods|
155
160
  options[:url_safe_methods] ||= Set.new
156
161
  options[:url_safe_methods].merge methods.map {|e| e.to_sym }
@@ -233,7 +238,7 @@ module Brakeman::Options
233
238
 
234
239
  opts.on "-f",
235
240
  "--format TYPE",
236
- [:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar],
241
+ [:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif, :sonar, :github],
237
242
  "Specify output formats. Default is text" do |type|
238
243
 
239
244
  type = "s" if type == :text
@@ -9,6 +9,7 @@ module Brakeman
9
9
  def initialize tracker, file_parser
10
10
  @tracker = tracker
11
11
  @file_parser = file_parser
12
+ @slim_smart = nil # Load slim/smart ?
12
13
  end
13
14
 
14
15
  def parse_template path, text
@@ -88,6 +89,14 @@ module Brakeman
88
89
 
89
90
  def parse_slim path, text
90
91
  Brakeman.load_brakeman_dependency 'slim'
92
+
93
+ if @slim_smart.nil? and load_slim_smart?
94
+ @slim_smart = true
95
+ Brakeman.load_brakeman_dependency 'slim/smart'
96
+ else
97
+ @slim_smart = false
98
+ end
99
+
91
100
  require_relative 'slim_embedded'
92
101
 
93
102
  Slim::Template.new(path,
@@ -95,6 +104,21 @@ module Brakeman
95
104
  :generator => Temple::Generators::RailsOutputBuffer) { text }.precompiled_template
96
105
  end
97
106
 
107
+ def load_slim_smart?
108
+ return !@slim_smart unless @slim_smart.nil?
109
+
110
+ # Terrible hack to find
111
+ # gem "slim", "~> 3.0.1", require: ["slim", "slim/smart"]
112
+ if tracker.app_tree.exists? 'Gemfile'
113
+ gemfile_contents = tracker.app_tree.file_path('Gemfile').read
114
+ if gemfile_contents.include? 'slim/smart'
115
+ return true
116
+ end
117
+ end
118
+
119
+ false
120
+ end
121
+
98
122
  def self.parse_inline_erb tracker, text
99
123
  fp = Brakeman::FileParser.new(tracker.app_tree, tracker.options[:parser_timeout])
100
124
  tp = self.new(tracker, fp)
@@ -183,6 +183,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
183
183
  return exp
184
184
  end
185
185
 
186
+ # If x(*[1,2,3]) change to x(1,2,3)
187
+ # if that's the only argument
188
+ if splat_array? exp.first_arg and exp.second_arg.nil?
189
+ exp.arglist = exp.first_arg[1].sexp_body
190
+ end
191
+
186
192
  target = exp.target
187
193
  method = exp.method
188
194
  first_arg = exp.first_arg
@@ -195,11 +201,20 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
195
201
  res = process_or_simple_operation(exp)
196
202
  return res if res
197
203
  elsif target == ARRAY_CONST and method == :new
198
- return Sexp.new(:array, *exp.args)
204
+ return Sexp.new(:array, *exp.args).line(exp.line)
199
205
  elsif target == HASH_CONST and method == :new and first_arg.nil? and !node_type?(@exp_context.last, :iter)
200
- return Sexp.new(:hash)
206
+ return Sexp.new(:hash).line(exp.line)
201
207
  elsif exp == RAILS_TEST or exp == RAILS_DEV
202
- return Sexp.new(:false)
208
+ return Sexp.new(:false).line(exp.line)
209
+ end
210
+
211
+ # For the simplest case of `Foo.thing`
212
+ if node_type? target, :const and first_arg.nil?
213
+ if tracker and (klass = tracker.find_class(class_name(target.value)))
214
+ if return_value = klass.get_simple_method_return_value(:class, method)
215
+ return return_value.deep_clone(exp.line)
216
+ end
217
+ end
203
218
  end
204
219
 
205
220
  #See if it is possible to simplify some basic cases
@@ -214,13 +229,28 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
214
229
  exp = math_op(:+, target, first_arg, exp)
215
230
  end
216
231
  when :-, :*, :/
217
- exp = math_op(method, target, first_arg, exp)
232
+ if method == :* and array? target
233
+ if string? first_arg
234
+ exp = process_array_join(target, first_arg)
235
+ end
236
+ else
237
+ exp = math_op(method, target, first_arg, exp)
238
+ end
218
239
  when :[]
219
240
  if array? target
220
241
  exp = process_array_access(target, exp.args, exp)
221
242
  elsif hash? target
222
243
  exp = process_hash_access(target, first_arg, exp)
223
244
  end
245
+ when :fetch
246
+ if array? target
247
+ # Not dealing with default value
248
+ # so just pass in first argument, but process_array_access expects
249
+ # an array of arguments.
250
+ exp = process_array_access(target, [first_arg], exp)
251
+ elsif hash? target
252
+ exp = process_hash_access(target, first_arg, exp)
253
+ end
224
254
  when :merge!, :update
225
255
  if hash? target and hash? first_arg
226
256
  target = process_hash_merge! target, first_arg
@@ -237,7 +267,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
237
267
  env[target_var] = target
238
268
  return target
239
269
  elsif string? target and string_interp? first_arg
240
- exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg.sexp_body(2))
270
+ exp = Sexp.new(:dstr, target.value + first_arg[1]).concat(first_arg.sexp_body(2)).line(exp.line)
241
271
  env[target_var] = exp
242
272
  elsif string? first_arg and string_interp? target
243
273
  if string? target.last
@@ -260,6 +290,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
260
290
  target = find_push_target(target_var)
261
291
  env[target] = exp unless target.nil? # Happens in TemplateAliasProcessor
262
292
  end
293
+ when :push
294
+ if array? target
295
+ target << first_arg
296
+ env[target_var] = target
297
+ return target
298
+ end
263
299
  when :first
264
300
  if array? target and first_arg.nil? and sexp? target[1]
265
301
  exp = target[1]
@@ -273,7 +309,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
273
309
  exp = target
274
310
  end
275
311
  when :join
276
- if array? target and target.length > 2 and (string? first_arg or first_arg.nil?)
312
+ if array? target and (string? first_arg or first_arg.nil?)
277
313
  exp = process_array_join(target, first_arg)
278
314
  end
279
315
  when :!
@@ -281,6 +317,15 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
281
317
  if call? target and target.method == :!
282
318
  exp = s(:or, s(:true).line(exp.line), s(:false).line(exp.line)).line(exp.line)
283
319
  end
320
+ when :values
321
+ # Hash literal
322
+ if node_type? target, :hash
323
+ exp = hash_values(target)
324
+ end
325
+ when :values_at
326
+ if node_type? target, :hash
327
+ exp = hash_values_at target, exp.args
328
+ end
284
329
  end
285
330
 
286
331
  exp
@@ -288,7 +333,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
288
333
 
289
334
  # Painful conversion of Array#join into string interpolation
290
335
  def process_array_join array, join_str
291
- result = s()
336
+ # Empty array
337
+ if array.length == 1
338
+ return s(:str, '').line(array.line)
339
+ end
340
+
341
+ result = s().line(array.line)
292
342
 
293
343
  join_value = if string? join_str
294
344
  join_str.value
@@ -296,8 +346,10 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
296
346
  nil
297
347
  end
298
348
 
299
- array[1..-2].each do |e|
300
- result << join_item(e, join_value)
349
+ if array.length > 2
350
+ array[1..-2].each do |e|
351
+ result << join_item(e, join_value)
352
+ end
301
353
  end
302
354
 
303
355
  result << join_item(array.last, nil)
@@ -326,24 +378,32 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
326
378
  result.unshift combined_first
327
379
 
328
380
  # Have to fix up strings that follow interpolation
329
- result.reduce(s(:dstr)) do |memo, e|
381
+ string = result.reduce(s(:dstr).line(array.line)) do |memo, e|
330
382
  if string? e and node_type? memo.last, :evstr
331
383
  e.value = "#{join_value}#{e.value}"
332
384
  elsif join_value and node_type? memo.last, :evstr and node_type? e, :evstr
333
- memo << s(:str, join_value)
385
+ memo << s(:str, join_value).line(e.line)
334
386
  end
335
387
 
336
388
  memo << e
337
389
  end
390
+
391
+ # Convert (:dstr, "hello world")
392
+ # to (:str, "hello world")
393
+ if string.length == 2 and string.last.is_a? String
394
+ string[0] = :str
395
+ end
396
+
397
+ string
338
398
  end
339
399
 
340
400
  def join_item item, join_value
341
401
  if item.is_a? String
342
402
  "#{item}#{join_value}"
343
403
  elsif string? item or symbol? item or number? item
344
- s(:str, "#{item.value}#{join_value}")
404
+ s(:str, "#{item.value}#{join_value}").line(item.line)
345
405
  else
346
- s(:evstr, item)
406
+ s(:evstr, item).line(item.line)
347
407
  end
348
408
  end
349
409
 
@@ -359,6 +419,11 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
359
419
  s(:call, TEMP_FILE_CLASS, :new).line(line)
360
420
  end
361
421
 
422
+ def splat_array? exp
423
+ node_type? exp, :splat and
424
+ node_type? exp[1], :array
425
+ end
426
+
362
427
  def process_iter exp
363
428
  @exp_context.push exp
364
429
  exp[1] = process exp.block_call
@@ -679,7 +744,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
679
744
  end
680
745
  end
681
746
  else
682
- new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value)
747
+ new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value).line(exp.line)
683
748
 
684
749
  env[match] = new_value
685
750
  end
@@ -738,6 +803,18 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
738
803
  exp
739
804
  end
740
805
 
806
+ def hash_or_array_include_all_literals? exp
807
+ return unless call? exp and sexp? exp.target
808
+ target = exp.target
809
+
810
+ case target.node_type
811
+ when :hash
812
+ hash_include_all_literals? exp
813
+ else
814
+ array_include_all_literals? exp
815
+ end
816
+ end
817
+
741
818
  # Check if exp is a call to Array#include? on an array literal
742
819
  # that contains all literal values. For example:
743
820
  #
@@ -756,6 +833,16 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
756
833
  (all_literals? exp.target or dir_glob? exp.target)
757
834
  end
758
835
 
836
+ # Check if exp is a call to Hash#include? on a hash literal
837
+ # that contains all literal values. For example:
838
+ #
839
+ # {x: 1}.include? x
840
+ def hash_include_all_literals? exp
841
+ call? exp and
842
+ exp.method == :include? and
843
+ all_literals? exp.target, :hash
844
+ end
845
+
759
846
  #Sets @inside_if = true
760
847
  def process_if exp
761
848
  if @ignore_ifs.nil?
@@ -796,7 +883,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
796
883
  scope do
797
884
  @branch_env = env.current
798
885
  branch_index = 2 + i # s(:if, condition, then_branch, else_branch)
799
- if i == 0 and array_include_all_literals? condition
886
+ if i == 0 and hash_or_array_include_all_literals? condition
800
887
  # If the condition is ["a", "b"].include? x
801
888
  # set x to "a" inside the true branch
802
889
  var = condition.first_arg
@@ -804,7 +891,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
804
891
  env.current[var] = safe_literal(var.line)
805
892
  exp[branch_index] = process_if_branch branch
806
893
  env.current[var] = previous_value
807
- elsif i == 1 and array_include_all_literals? condition and early_return? branch
894
+ elsif i == 1 and hash_or_array_include_all_literals? condition and early_return? branch
808
895
  var = condition.first_arg
809
896
  env.current[var] = safe_literal(var.line)
810
897
  exp[branch_index] = process_if_branch branch
@@ -1002,8 +1089,8 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
1002
1089
  method_name = call.method
1003
1090
 
1004
1091
  #Look for helper methods and see if we can get a return value
1005
- if found_method = find_method(method_name, @current_class)
1006
- helper = found_method[:method]
1092
+ if found_method = tracker.find_method(method_name, @current_class)
1093
+ helper = found_method.src
1007
1094
 
1008
1095
  if sexp? helper
1009
1096
  value = process_helper_method helper, call.args