brakeman-min 7.0.2 → 7.1.1

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
  SHA256:
3
- metadata.gz: 5e8740dbaf52c1cba5a1c5afde01f41180ecf808e417d0cdcc274affbdcc684d
4
- data.tar.gz: 83180ce5a3bfbf62f03ddf4fdbdf396ab66fc3a1b41dcdc5d5ff59d0699d5cff
3
+ metadata.gz: dd61d8da658d0f4da21e1f854a04c0e9423a91797bd93ec2a754ee6cc113570f
4
+ data.tar.gz: 2a725267c5b8296686867da71b9c2286c4ed423c12bb43f204dd10697afbe08f
5
5
  SHA512:
6
- metadata.gz: 0c149490c10d73ac6d43f310b97c6bbed414062c544e4d8de4cd6e0df7bbf898973b09f7e17fafe1b689f66fad7e41a31e82eb710043497e00aa848b3aadeeb9
7
- data.tar.gz: c32763c096908fbd24f0f418168d6e59879bf5aa03626d45fced5eb013d4fed1fe606f448a257379132af3675cc6fcc2563400b3eda5005669d530fb5e74200c
6
+ metadata.gz: a85e7dfae5e7dad5a99043c1f785c063a9deee56ab797d70a718ef3f6f8031dbe6b9a3307124d83c016443ae34ef15485a3f594282edf8775797a90d64a5384c
7
+ data.tar.gz: 70fd83ef6e68861ff57a4809098548c729fcbb67e164f541a5da4a523bb2e7a0d9a3e43f730332317e13917c9432a132af5393a8e5f0368d06abb2ea32ff6b52
data/CHANGES.md CHANGED
@@ -1,3 +1,27 @@
1
+ # 7.1.1 - 2025-11-03
2
+
3
+ * Fix false positive when calling `with_content` on ViewComponents (Peer Allan)
4
+ * Word wrap text output in pager
5
+ * Consider Tempfile.create.path as safe input (Ali Ismayilov)
6
+ * Exclude directories before searching for files
7
+ * Check each side of `or` SQL arguments
8
+ * Ignore attribute builder in Haml 6
9
+ * Add `FilePath#to_path` for Ruby 3.5 compatibility (S-H-GAMELINKS)
10
+ * Fix SQL injection check for calculate method (Rohan Sharma)
11
+ * Fix missing `td` in HTML report (John Hawthorn)
12
+ * Check for unsafe SQL when two arguments are passed to AR methods (Patrick Brinich-Langlois)
13
+
14
+ # 7.1.0 - 2025-07-18
15
+
16
+ * Add EOL dates for Rails 8.0 and Ruby 3.4
17
+ * Support render model shortcut
18
+ * Use lazy file lists for AppTree
19
+ * Add Haml 6.x support
20
+ * Improve ignored warnings layout in HTML report (Sebastien Savater)
21
+ * Update JUnit report for CircleCI (Philippe Bernery)
22
+ * Only load escape functionality from cgi library (Earlopain)
23
+ * Add `--ensure-no-obsolete-ignore-entries` option (viralpraxis)
24
+
1
25
  # 7.0.2 - 2025-04-04
2
26
 
3
27
  * Fix error with empty `BUNDLE_GEMFILE` env variable
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  [![Brakeman Logo](http://brakemanscanner.org/images/logo_medium.png)](http://brakemanscanner.org/)
2
2
 
3
3
  [![Build Status](https://circleci.com/gh/presidentbeef/brakeman.svg?style=svg)](https://circleci.com/gh/presidentbeef/brakeman)
4
- [![Test Coverage](https://api.codeclimate.com/v1/badges/1b08a5c74695cb0d11ec/test_coverage)](https://codeclimate.com/github/presidentbeef/brakeman/test_coverage)
4
+ [![Code Coverage](https://qlty.sh/gh/presidentbeef/projects/brakeman/coverage.svg)](https://qlty.sh/gh/presidentbeef/projects/brakeman)
5
5
 
6
6
  # Brakeman
7
7
 
@@ -33,6 +33,7 @@ module Brakeman
33
33
  # * "path1/" - Matches any path that contains "path1" in the project directory.
34
34
  # * "/path1/ - Matches any path that is rooted at "path1" in the project directory.
35
35
  #
36
+ # TODO: This is wacky and I don't like it.
36
37
  def self.regex_for_paths(paths)
37
38
  path_regexes = paths.map do |f|
38
39
  # If path ends in a file separator then we assume it is a path rather
@@ -145,6 +146,17 @@ module Brakeman
145
146
  end
146
147
  end
147
148
 
149
+
150
+ # Call this to be able to marshall the AppTree
151
+ def marshallable
152
+ @initializer_paths = @initializer_paths.to_a
153
+ @controller_paths = @controller_paths.to_a
154
+ @template_paths = @template_paths.to_a
155
+ @lib_files = @file_paths.to_a
156
+
157
+ self
158
+ end
159
+
148
160
  private
149
161
 
150
162
  def find_helper_paths
@@ -160,7 +172,7 @@ module Brakeman
160
172
  end
161
173
 
162
174
  def find_paths(directory, extensions = ".rb")
163
- select_files(glob_files(directory, "*", extensions).sort)
175
+ select_files(glob_files(directory, "*", extensions))
164
176
  end
165
177
 
166
178
  def glob_files(directory, name, extensions = ".rb")
@@ -179,10 +191,15 @@ module Brakeman
179
191
  end
180
192
 
181
193
  files = patterns.flat_map { |pattern| Dir.glob(pattern) }
182
- files.uniq
194
+ files.uniq.lazy
183
195
  else
184
- pattern = "#{root_search_pattern}#{directory}/**/#{name}#{extensions}"
185
- Dir.glob(pattern)
196
+ if directory == '.'
197
+ pattern = File.join(top_directories_pattern, '**', "#{name}#{extensions}")
198
+ else
199
+ pattern = "#{root_search_pattern}#{directory}/**/#{name}#{extensions}"
200
+ end
201
+
202
+ Dir.glob(pattern).lazy
186
203
  end
187
204
  end
188
205
 
@@ -191,7 +208,8 @@ module Brakeman
191
208
  paths = reject_skipped_files(paths)
192
209
  paths = convert_to_file_paths(paths)
193
210
  paths = reject_global_excludes(paths)
194
- reject_directories(paths)
211
+ paths = reject_directories(paths)
212
+ paths
195
213
  end
196
214
 
197
215
  def reject_directories(paths)
@@ -245,18 +263,47 @@ module Brakeman
245
263
  end
246
264
 
247
265
  def match_path files, path
266
+ # TODO: Converting to Pathnames and Strings seems like a lot
267
+ # of converting that could perhaps all be handled in Brakeman::FilePath
268
+ # instead?
248
269
  absolute_path = Pathname.new(path)
270
+
249
271
  # relative root never has a leading separator. But, we use a leading
250
272
  # separator in a @skip_files entry to imply that a directory is
251
273
  # "absolute" with respect to the project directory.
252
- project_relative_path = File.join(
253
- File::SEPARATOR,
254
- absolute_path.relative_path_from(@project_root_path).to_s
255
- )
274
+ #
275
+ # Also directories need a trailing separator.
276
+ project_relative_path = if File.directory?(path)
277
+ File.join(
278
+ File::SEPARATOR,
279
+ absolute_path.relative_path_from(@project_root_path).to_s,
280
+ File::SEPARATOR
281
+ )
282
+ else
283
+ File.join(
284
+ File::SEPARATOR,
285
+ absolute_path.relative_path_from(@project_root_path).to_s
286
+ )
287
+ end
256
288
 
257
289
  files.match(project_relative_path)
258
290
  end
259
291
 
292
+ def top_directories_pattern
293
+ top_dirs = convert_to_file_paths(Dir.glob(File.join(root_search_pattern, '*/')))
294
+ top_dirs.reject! { |d| File.symlink?(d) or !File.directory?(d) }
295
+ top_dirs = reject_global_excludes(top_dirs)
296
+ top_dirs = reject_skipped_files(top_dirs)
297
+
298
+ if top_dirs.empty?
299
+ # Fall back to searching everything, otherwise the empty pattern
300
+ # will start searching from the global root
301
+ root_search_pattern
302
+ else
303
+ "{#{top_dirs.join(',')}}"
304
+ end
305
+ end
306
+
260
307
  def root_search_pattern
261
308
  return @root_search_pattern if @root_search_pattern
262
309
  @root_search_pattern = search_pattern(@root)
@@ -151,10 +151,13 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
151
151
  method[-1] == "?"
152
152
  end
153
153
 
154
- TEMP_FILE_PATH = s(:call, s(:call, s(:const, :Tempfile), :new), :path).freeze
154
+ TEMP_FILE_PATH = [
155
+ s(:call, s(:call, s(:const, :Tempfile), :new), :path).freeze,
156
+ s(:call, s(:call, s(:const, :Tempfile), :create), :path).freeze
157
+ ].freeze
155
158
 
156
159
  def temp_file_path? exp
157
- exp == TEMP_FILE_PATH
160
+ TEMP_FILE_PATH.include? exp
158
161
  end
159
162
 
160
163
  #Report a warning
@@ -25,5 +25,6 @@ class Brakeman::CheckEOLRails < Brakeman::EOLCheck
25
25
  ['7.0.0', '7.0.99'] => Date.new(2025, 4, 1),
26
26
  ['7.1.0', '7.1.99'] => Date.new(2025, 10, 1),
27
27
  ['7.2.0', '7.2.99'] => Date.new(2026, 8, 9),
28
+ ['8.0.0', '8.0.99'] => Date.new(2026, 10, 7),
28
29
  }
29
30
  end
@@ -25,5 +25,6 @@ class Brakeman::CheckEOLRuby < Brakeman::EOLCheck
25
25
  ['3.1.0', '3.1.99'] => Date.new(2025, 3, 31),
26
26
  ['3.2.0', '3.2.99'] => Date.new(2026, 3, 31),
27
27
  ['3.3.0', '3.3.99'] => Date.new(2027, 3, 31),
28
+ ['3.4.0', '3.4.99'] => Date.new(2028, 3, 31),
28
29
  }
29
30
  end
@@ -101,6 +101,11 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
101
101
  def renderable? exp
102
102
  return false unless call?(exp) and constant?(exp.target)
103
103
 
104
+ if exp.method == :with_content
105
+ exp = exp.target
106
+ end
107
+
108
+ return false unless constant?(exp.target)
104
109
  target_class_name = class_name(exp.target)
105
110
  known_renderable_class?(target_class_name) or tracker.find_method(:render_in, target_class_name)
106
111
  end
@@ -188,11 +188,15 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
188
188
  when :find_by_sql, :count_by_sql
189
189
  check_by_sql_arguments call.first_arg
190
190
  when :calculate
191
- check_find_arguments call.third_arg
191
+ if call.arglist.length > 2
192
+ unsafe_sql?(call.second_arg) or check_find_arguments(call.third_arg)
193
+ elsif call.arglist.length > 1
194
+ unsafe_sql?(call.second_arg)
195
+ end
192
196
  when :last, :first, :all
193
197
  check_find_arguments call.first_arg
194
198
  when :average, :count, :maximum, :minimum, :sum
195
- if call.length > 5
199
+ if call.arglist.length > 1
196
200
  unsafe_sql?(call.first_arg) or check_find_arguments(call.last_arg)
197
201
  else
198
202
  check_find_arguments call.last_arg
@@ -315,6 +319,9 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
315
319
  check_hash_keys arg
316
320
  elsif node_type? arg, :lit, :str
317
321
  nil
322
+ elsif node_type? arg, :or
323
+ check_query_arguments(arg.lhs) or
324
+ check_query_arguments(arg.rhs)
318
325
  else
319
326
  #Hashes are safe...but we check above for hash, so...?
320
327
  unsafe_sql? arg, :ignore_hash
@@ -145,6 +145,11 @@ module Brakeman
145
145
  quit Brakeman::Errors_Found_Exit_Code
146
146
  end
147
147
 
148
+ if tracker.options[:ensure_no_obsolete_ignore_entries] && tracker.unused_fingerprints.any?
149
+ warn '[Error] Obsolete ignore entries were found, exiting with an error code.'
150
+ quit Brakeman::Obsolete_Ignore_Entries_Exit_Code
151
+ end
152
+
148
153
  if ensure_ignore_notes_failed
149
154
  quit Brakeman::Empty_Ignore_Note_Exit_Code
150
155
  end
@@ -68,6 +68,10 @@ module Brakeman
68
68
  self.absolute
69
69
  end
70
70
 
71
+ # Required for Pathname compatibility.
72
+ # Ruby 3.5+ requires Pathname#initialize to receive a String or an object with to_path method.
73
+ alias to_path to_str
74
+
71
75
  # Returns a string with the absolute path.
72
76
  def to_s
73
77
  self.to_str
@@ -86,7 +86,7 @@ class Brakeman::Messages::Message
86
86
  end
87
87
 
88
88
  def to_html
89
- require 'cgi'
89
+ require 'cgi/escape'
90
90
 
91
91
  output = @parts.map(&:to_html).join
92
92
 
@@ -71,6 +71,10 @@ module Brakeman::Options
71
71
  options[:ensure_ignore_notes] = true
72
72
  end
73
73
 
74
+ opts.on "--ensure-no-obsolete-ignore-entries", "Fail when an obsolete ignore entry is found" do
75
+ options[:ensure_no_obsolete_ignore_entries] = true
76
+ end
77
+
74
78
  opts.on "-3", "--rails3", "Force Rails 3 mode" do
75
79
  options[:rails3] = true
76
80
  end
@@ -0,0 +1,23 @@
1
+ [:Coffee, :CoffeeScript, :Markdown, :Sass].each do |name|
2
+ klass = Module.const_get("Haml::Filters::#{name}")
3
+
4
+ klass.define_method(:compile) do |node|
5
+ temple = [:multi]
6
+ temple << [:static, "<script>\n"]
7
+ temple << compile_with_tilt(node)
8
+ temple << [:static, "</script>"]
9
+ temple
10
+ end
11
+
12
+ klass.define_method(:compile_with_tilt) do |node|
13
+ # From Haml
14
+ text = ::Haml::Util.unescape_interpolation(node.value[:text]).gsub(/(\\+)n/) do |s|
15
+ escapes = $1.size
16
+ next s if escapes % 2 == 0
17
+ "#{'\\' * (escapes - 1)}\n"
18
+ end
19
+ text.prepend("\n").sub!(/\n"\z/, '"')
20
+
21
+ [:dynamic, "BrakemanFilter.render(#{text})"]
22
+ end
23
+ end
@@ -24,6 +24,7 @@ module Brakeman
24
24
  type = :erubis if erubis?
25
25
  parse_erb path, text
26
26
  when :haml
27
+ type = :haml6 if haml6?
27
28
  parse_haml path, text
28
29
  when :slim
29
30
  parse_slim path, text
@@ -74,19 +75,43 @@ module Brakeman
74
75
  end
75
76
 
76
77
  def parse_haml path, text
77
- Brakeman.load_brakeman_dependency 'haml'
78
- require_relative 'haml_embedded'
78
+ if haml6?
79
+ require_relative 'haml6_embedded'
80
+
81
+ Haml::Template.new(filename: path.relative,
82
+ :escape_html => tracker.config.escape_html?,
83
+ generator: Temple::Generators::RailsOutputBuffer,
84
+ use_html_safe: true,
85
+ buffer_class: 'ActionView::OutputBuffer',
86
+ disable_capture: true,
87
+ ) { text }.precompiled_template
88
+ else
89
+ require_relative 'haml_embedded'
79
90
 
80
- Haml::Engine.new(text,
81
- :filename => path,
82
- :escape_html => tracker.config.escape_html?,
83
- :escape_filter_interpolations => tracker.config.escape_filter_interpolations?
84
- ).precompiled.gsub(/([^\\])\\n/, '\1')
91
+ Haml::Engine.new(text,
92
+ :filename => path,
93
+ :escape_html => tracker.config.escape_html?,
94
+ :escape_filter_interpolations => tracker.config.escape_filter_interpolations?
95
+ ).precompiled.gsub(/([^\\])\\n/, '\1')
96
+ end
85
97
  rescue Haml::Error => e
86
98
  tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
87
99
  nil
88
100
  end
89
101
 
102
+ def haml6?
103
+ return @haml6 unless @haml6.nil?
104
+
105
+ Brakeman.load_brakeman_dependency 'haml'
106
+ major_version = Haml::VERSION.split('.').first.to_i
107
+
108
+ if major_version >= 6
109
+ @haml6 = true
110
+ else
111
+ @haml6 = false
112
+ end
113
+ end
114
+
90
115
  def parse_slim path, text
91
116
  Brakeman.load_brakeman_dependency 'slim'
92
117
 
@@ -63,6 +63,8 @@ module Brakeman
63
63
  result = ErbTemplateProcessor.new(@tracker, name, called_from, file_name).process src
64
64
  when :haml
65
65
  result = HamlTemplateProcessor.new(@tracker, name, called_from, file_name).process src
66
+ when :haml6
67
+ result = Haml6TemplateProcessor.new(@tracker, name, called_from, file_name).process src
66
68
  when :erubis
67
69
  result = ErubisTemplateProcessor.new(@tracker, name, called_from, file_name).process src
68
70
  when :slim
@@ -436,6 +436,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
436
436
  exp.method == :open
437
437
  end
438
438
 
439
+ def temp_file_create? exp
440
+ call? exp and
441
+ exp.target == TEMP_FILE_CLASS and
442
+ exp.method == :create
443
+ end
444
+
439
445
  def temp_file_new line
440
446
  s(:call, TEMP_FILE_CLASS, :new).line(line)
441
447
  end
@@ -465,6 +471,9 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
465
471
  elsif temp_file_open? call
466
472
  local = Sexp.new(:lvar, block_args.last)
467
473
  env.current[local] = temp_file_new(exp.line)
474
+ elsif temp_file_create? call
475
+ local = Sexp.new(:lvar, block_args.last)
476
+ env.current[local] = temp_file_new(exp.line)
468
477
  else
469
478
  block_args.each do |e|
470
479
  #Force block arg(s) to be local
@@ -205,6 +205,7 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
205
205
  rest = process rest
206
206
  result = Sexp.new(:render, render_type, value, rest)
207
207
  result.line(exp.line)
208
+
208
209
  result
209
210
  end
210
211
 
@@ -240,6 +241,7 @@ class Brakeman::BaseProcessor < Brakeman::SexpProcessor
240
241
  elsif first_arg.nil?
241
242
  type = :default
242
243
  elsif not hash? first_arg
244
+ # Maybe do partial if in view?
243
245
  type = :action
244
246
  value = first_arg
245
247
  end
@@ -0,0 +1,92 @@
1
+ require 'brakeman/processors/haml_template_processor'
2
+
3
+ class Brakeman::Haml6TemplateProcessor < Brakeman::HamlTemplateProcessor
4
+
5
+ OUTPUT_BUFFER = s(:ivar, :@output_buffer)
6
+ HAML_UTILS = s(:colon2, s(:colon3, :Haml), :Util)
7
+ HAML_UTILS2 = s(:colon2, s(:const, :Haml), :Util)
8
+ # @output_buffer = output_buffer || ActionView::OutputBuffer.new
9
+ AV_SAFE_BUFFER = s(:or, s(:call, nil, :output_buffer), s(:call, s(:colon2, s(:const, :ActionView), :OutputBuffer), :new))
10
+ EMBEDDED_FILTER = s(:const, :BrakemanFilter)
11
+
12
+ def initialize(*)
13
+ super
14
+
15
+ # Because of how Haml 6 handles line breaks -
16
+ # we have to track where _haml_compiler variables are assigned.
17
+ # then change the line number of where they are output to where
18
+ # they are assigned.
19
+ #
20
+ # Like this:
21
+ #
22
+ # ; _haml_compiler1 = (params[:x];
23
+ # ; ); @output_buffer.safe_concat((((::Haml::Util.escape_html_safe((_haml_compiler1))).to_s).to_s));
24
+ #
25
+ # `_haml_compiler1` is output a line after it's assigned,
26
+ # but the assignment matches the "real" line where it is output in the template.
27
+ @compiler_assigns = {}
28
+ end
29
+
30
+ # @output_buffer.safe_concat
31
+ def buffer_append? exp
32
+ call? exp and
33
+ output_buffer? exp.target and
34
+ exp.method == :safe_concat
35
+ end
36
+
37
+ def process_lasgn exp
38
+ if exp.lhs.match?(/_haml_compiler\d+/)
39
+ @compiler_assigns[exp.lhs] = exp.rhs
40
+ ignore
41
+ else
42
+ exp
43
+ end
44
+ end
45
+
46
+ def process_lvar exp
47
+ if exp.value.match?(/_haml_compiler\d+/)
48
+ exp = @compiler_assigns[exp.value] || exp
49
+ end
50
+
51
+ exp
52
+ end
53
+
54
+ def is_escaped? exp
55
+ return unless call? exp
56
+
57
+ html_escaped? exp or
58
+ javascript_escaped? exp
59
+ end
60
+
61
+ def javascript_escaped? call
62
+ # TODO: Adding here to match existing behavior for HAML,
63
+ # but really this is not safe and needs to be revisited
64
+ call.method == :j or
65
+ call.method == :escape_javascript
66
+ end
67
+
68
+ def html_escaped? call
69
+ (call.target == HAML_UTILS or call.target == HAML_UTILS2) and
70
+ (call.method == :escape_html or call.method == :escape_html_safe)
71
+ end
72
+
73
+ def output_buffer? exp
74
+ exp == OUTPUT_BUFFER or
75
+ exp == AV_SAFE_BUFFER
76
+ end
77
+
78
+ def normalize_output arg
79
+ arg = super(arg)
80
+
81
+ if embedded_filter? arg
82
+ super(arg.first_arg)
83
+ else
84
+ arg
85
+ end
86
+ end
87
+
88
+ # Handle our "fake" embedded filters
89
+ def embedded_filter? arg
90
+ call? arg and arg.method == :render and arg.target == EMBEDDED_FILTER
91
+ end
92
+ end
@@ -84,6 +84,12 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
84
84
  :escape_once_without_haml_xss
85
85
  ]
86
86
 
87
+ def is_escaped? exp
88
+ return unless call? exp
89
+
90
+ haml_helpers? exp.target and ESCAPE_METHODS.include? exp.method
91
+ end
92
+
87
93
  def get_pushed_value exp, default = :output
88
94
  return exp unless sexp? exp
89
95
 
@@ -113,7 +119,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
113
119
  when :call
114
120
  if exp.method == :to_s or exp.method == :strip
115
121
  get_pushed_value(exp.target, default)
116
- elsif haml_helpers? exp.target and ESCAPE_METHODS.include? exp.method
122
+ elsif is_escaped? exp
117
123
  get_pushed_value(exp.first_arg, :escaped_output)
118
124
  elsif @javascript and (exp.method == :j or exp.method == :escape_javascript) # TODO: Remove - this is not safe
119
125
  get_pushed_value(exp.first_arg, :escaped_output)
@@ -160,7 +166,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
160
166
  def haml_attribute_builder? exp
161
167
  call? exp and
162
168
  exp.target == ATTRIBUTE_BUILDER and
163
- exp.method == :build
169
+ (exp.method == :build or exp.method == :build_id)
164
170
  end
165
171
 
166
172
  def fix_textareas? exp
@@ -9,7 +9,14 @@ module Brakeman::RenderHelper
9
9
  @rendered = true
10
10
  case exp.render_type
11
11
  when :action, :template, :inline
12
- process_action exp[2][1], exp[3], exp.line
12
+ action = exp[2]
13
+ args = exp[3]
14
+
15
+ if string? action or symbol? action
16
+ process_action action.value, args, exp.line
17
+ else
18
+ process_model_action action, args
19
+ end
13
20
  when :default
14
21
  begin
15
22
  process_template template_name, exp[3], nil, exp.line
@@ -49,6 +56,36 @@ module Brakeman::RenderHelper
49
56
  def process_action name, args, line
50
57
  if name.is_a? String or name.is_a? Symbol
51
58
  process_template template_name(name), args, nil, line
59
+ else
60
+ Brakeman.debug "Not processing render #{name.inspect}"
61
+ end
62
+ end
63
+
64
+ SINGLE_RECORD = [:first, :find, :last, :new]
65
+ COLLECTION = [:all, :where]
66
+
67
+ def process_model_action action, args
68
+ return unless call? action
69
+
70
+ method = action.method
71
+
72
+ klass = get_class_target(action) || Brakeman::Tracker::UNKNOWN_MODEL
73
+ name = Sexp.new(:lit, klass.downcase)
74
+
75
+ if SINGLE_RECORD.include? method
76
+ # Set a local variable with name based on class of model
77
+ # and value of the value passed to render
78
+ local_key = Sexp.new(:lit, :locals)
79
+ locals = hash_access(args, local_key) || Sexp.new(:hash)
80
+ hash_insert(locals, name, action)
81
+ hash_insert(args, local_key, locals)
82
+
83
+ process_partial name, args, action.line
84
+ elsif COLLECTION.include? method
85
+ collection_key = Sexp.new(:lit, :collection)
86
+ hash_insert(args, collection_key, action)
87
+
88
+ process_partial name, args, action.line
52
89
  end
53
90
  end
54
91
 
@@ -56,7 +56,7 @@ class Brakeman::TemplateProcessor < Brakeman::BaseProcessor
56
56
  # Pull out actual output value from template
57
57
  def normalize_output arg
58
58
  if call? arg and [:to_s, :html_safe!, :freeze].include? arg.method
59
- arg.target
59
+ normalize_output(arg.target) # sometimes it's foo.to_s.to_s
60
60
  elsif node_type? arg, :if
61
61
  branches = [arg.then_clause, arg.else_clause].compact
62
62
 
@@ -92,7 +92,7 @@ module Brakeman
92
92
  if system("which less > /dev/null")
93
93
  less_help = `less -?`
94
94
 
95
- ["-R ", "-F ", "-X "].each do |opt|
95
+ ["-R ", "-F ", "-X ", " --wordwrap"].each do |opt|
96
96
  if less_help.include? opt
97
97
  @less_options << opt
98
98
  end
@@ -1,4 +1,4 @@
1
- require 'cgi'
1
+ require 'cgi/escape'
2
2
  require 'brakeman/report/report_table.rb'
3
3
 
4
4
  class Brakeman::Report::HTML < Brakeman::Report::Table
@@ -9,50 +9,7 @@ class Brakeman::Report::JUnit < Brakeman::Report::Base
9
9
  doc.add REXML::XMLDecl.new '1.0', 'UTF-8'
10
10
 
11
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
12
 
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', w.confidence_name
51
- warning.add_attribute 'brakeman:code', w.format_code
52
- warning.add_text w.to_s
53
- }
54
-
55
- hostname = `hostname`.strip
56
13
  i = 0
57
14
  all_warnings
58
15
  .map { |warning| [warning.file, [warning]] }
@@ -66,35 +23,25 @@ class Brakeman::Report::JUnit < Brakeman::Report::Base
66
23
  test_suite = test_suites.add_element 'testsuite'
67
24
  test_suite.add_attribute 'id', i
68
25
  test_suite.add_attribute 'package', 'brakeman'
69
- test_suite.add_attribute 'name', file.relative
26
+ test_suite.add_attribute 'file', file.relative
70
27
  test_suite.add_attribute 'timestamp', tracker.start_time.strftime('%FT%T')
71
- test_suite.add_attribute 'hostname', hostname == '' ? 'localhost' : hostname
72
28
  test_suite.add_attribute 'tests', checks.checks_run.length
73
29
  test_suite.add_attribute 'failures', warnings.length
74
30
  test_suite.add_attribute 'errors', '0'
75
31
  test_suite.add_attribute 'time', '0'
76
32
 
77
- test_suite.add_element 'properties'
78
-
79
33
  warnings.each { |warning|
80
34
  test_case = test_suite.add_element 'testcase'
81
- test_case.add_attribute 'name', 'run_check'
82
- test_case.add_attribute 'classname', warning.check
35
+ test_case.add_attribute 'name', warning.check.sub(/^Brakeman::/, '')
36
+ test_case.add_attribute 'file', file.relative
37
+ test_case.add_attribute 'line', warning.line if warning.line
83
38
  test_case.add_attribute 'time', '0'
84
39
 
85
40
  failure = test_case.add_element 'failure'
86
41
  failure.add_attribute 'message', warning.message
87
42
  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', warning.confidence_name
92
- failure.add_attribute 'brakeman:code', warning.format_code
93
43
  failure.add_text warning.to_s
94
44
  }
95
-
96
- test_suite.add_element 'system-out'
97
- test_suite.add_element 'system-err'
98
45
  }
99
46
 
100
47
  doc.add test_suites
@@ -9,10 +9,15 @@
9
9
  function toggle(context) {
10
10
  var elem = document.getElementById(context);
11
11
 
12
- if (elem.style.display != "block")
12
+ if (elem.style.display != "block") {
13
13
  elem.style.display = "block";
14
- else
14
+
15
+ elem.querySelectorAll("table").forEach(function(table) {
16
+ $(table).DataTable().columns.adjust();
17
+ });
18
+ } else {
15
19
  elem.style.display = "none";
20
+ }
16
21
 
17
22
  elem.parentNode.scrollIntoView();
18
23
  }
@@ -46,7 +51,7 @@
46
51
  <tr>
47
52
  <td><%= tracker.app_path %></td>
48
53
  <td><%= rails_version %></td>
49
- <td><%= brakeman_version %>
54
+ <td><%= brakeman_version %></td>
50
55
  <td>
51
56
  <%= tracker.start_time %><br><br>
52
57
  <%= tracker.duration %> seconds
@@ -1,6 +1,6 @@
1
1
  <div onClick="toggle('ignored_table');"> <h2><%= warnings.length %> Ignored Warnings (click to see them)</h2 ></div>
2
- <div>
3
- <table style="display:none" id="ignored_table">
2
+ <div style="display:none; width:100%" id="ignored_table">
3
+ <table>
4
4
  <thead>
5
5
  <tr>
6
6
  <th>Confidence</th>
@@ -8,7 +8,7 @@
8
8
  <th>Warning Type</th>
9
9
  <th>CWE ID</th>
10
10
  <th>Message</th>
11
- <th>Note</th>
11
+ <th width="auto">Note</th>
12
12
  </tr>
13
13
  </thead>
14
14
  <tbody>
@@ -441,4 +441,10 @@ class Brakeman::Tracker
441
441
 
442
442
  @call_index.remove_indexes_by_file path
443
443
  end
444
+
445
+ # Call this to be able to marshal the Tracker
446
+ def marshallable
447
+ @app_tree.marshallable
448
+ self
449
+ end
444
450
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "7.0.2"
2
+ Version = "7.1.1"
3
3
  end
data/lib/brakeman.rb CHANGED
@@ -24,6 +24,10 @@ module Brakeman
24
24
  #--ensure-ignore-notes is set
25
25
  Empty_Ignore_Note_Exit_Code = 8
26
26
 
27
+ # Exit code returned when at least one obsolete ignore entry is present
28
+ # and `--ensure-no-obsolete-ignore-entries` is set.
29
+ Obsolete_Ignore_Entries_Exit_Code = 9
30
+
27
31
  @debug = false
28
32
  @quiet = false
29
33
  @loaded_dependencies = []
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: 7.0.2
4
+ version: 7.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-04-04 00:00:00.000000000 Z
11
+ date: 2025-11-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -235,6 +235,7 @@ files:
235
235
  - lib/brakeman/messages.rb
236
236
  - lib/brakeman/options.rb
237
237
  - lib/brakeman/parsers/erubis_patch.rb
238
+ - lib/brakeman/parsers/haml6_embedded.rb
238
239
  - lib/brakeman/parsers/haml_embedded.rb
239
240
  - lib/brakeman/parsers/rails2_erubis.rb
240
241
  - lib/brakeman/parsers/rails2_xss_plugin_erubis.rb
@@ -250,6 +251,7 @@ files:
250
251
  - lib/brakeman/processors/erb_template_processor.rb
251
252
  - lib/brakeman/processors/erubis_template_processor.rb
252
253
  - lib/brakeman/processors/gem_processor.rb
254
+ - lib/brakeman/processors/haml6_template_processor.rb
253
255
  - lib/brakeman/processors/haml_template_processor.rb
254
256
  - lib/brakeman/processors/lib/basic_processor.rb
255
257
  - lib/brakeman/processors/lib/call_conversion_helper.rb