brakeman-lib 8.0.5 → 8.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
  SHA256:
3
- metadata.gz: '081816b1dce310635af2372f9bd944fe3efc4e28a89e5f32be39a83b7abff137'
4
- data.tar.gz: a1984f13bf1ffedb4999f5f3b761788f6fd01eb3abbedcfb72fd2be0aa6a5b00
3
+ metadata.gz: d76a4590b3df146050c07df100680cc1a72821ca5c3e3e6bdf50de904603529a
4
+ data.tar.gz: 03de5ab61f3f56d0f99e6d6c833b671ddda0c6721214853cc3ddaa909fa01e5d
5
5
  SHA512:
6
- metadata.gz: a9f099212f13d0897ed9a17a5281da9b8b2f0a983123f0204d72990c7747c1a3ef11db38bbe846cb48a9c99c8e84adb8d20763377fa9279bae17371629d74b9c
7
- data.tar.gz: 88d203ec8ce92493f2192658589ba47c9a41543c1e4b7bc78da3aacaf485eaa005fb5beef49ce0f04d02ad3dffbe7c40b65b57aed35bc35baf150928fc3fea25
6
+ metadata.gz: 39fb331fc88e7a4360eac71cebdca9aebc8a3082ee6293ed52ae1b5d419e5685a7eddb1eccf5f80db7914f8cd2aefc2738f6b020d2f9a55bb6d8c9a5a2938c91
7
+ data.tar.gz: 4adf39f28d7a5e8ddc64f3e4d539bde4b0dd77f482be347601d4e2cf4846da63cfe37046a7ca66c1ebadcd6156bc2b2726c47b54f7bc0bdacbf5d0d70f044456
data/CHANGES.md CHANGED
@@ -1,3 +1,22 @@
1
+ # 8.1.0 - 2026-09-30
2
+
3
+ * Better help and error message for `--ensure-latest`
4
+ * Fix frozen string error (Shai Coleman)
5
+ * Update Sonar report format (fangxing)
6
+ * Fix frozen `src` string error
7
+ * Recognize `Haml::AttributeBuilder.build_class` as an escaped output (Yuriy Tumanov)
8
+ * Include regex code in validation warnings (Eliot Sykes)
9
+ * Check validation regexes in non-activerecord models (Eliot Sykes)
10
+ * Skip top-level vendor directory before recursive globbing (Conor O'Donnell)
11
+ * Support Rails 7.1+ positional enum syntax in SQL injection check (Michael Vogl/Yuriy Tumanov)
12
+
13
+ # 8.0.6 - 2026-08-13
14
+
15
+ * Fix EOL date for Rails 8.0 (yeaseul-kim)
16
+ * Add EOL dates for Rails 8.1 and Ruby 4.0
17
+ * Fix command injection false positives (Jacob Evelyn)
18
+ * Fix unused variable warning (viralpraxis)
19
+
1
20
  # 8.0.5 - 2026-06-12
2
21
 
3
22
  * Add `quote_schema_name` to safe quote method list (Zsolt Kozaroczy)
@@ -64,6 +64,8 @@ module Brakeman
64
64
  @only_files = init_options[:only_files]
65
65
  @additional_libs_path = init_options[:additional_libs_path] || []
66
66
  @engine_paths = init_options[:engine_paths] || []
67
+ @additional_libs_path_pairs = @additional_libs_path.map { |p| [p, expand_path(p)] }
68
+ @engine_path_pairs = @engine_paths.map { |p| [p, expand_path(p)] }
67
69
  @absolute_engine_paths = @engine_paths.select { |path| path.start_with?(File::SEPARATOR) }
68
70
  @relative_engine_paths = @engine_paths - @absolute_engine_paths
69
71
  @skip_vendor = init_options[:skip_vendor]
@@ -251,7 +253,10 @@ module Brakeman
251
253
  paths.reject do |path|
252
254
  relative_path = path.relative
253
255
 
254
- if @skip_vendor and relative_path.include? 'vendor/' and !in_engine_paths?(path) and !in_add_libs_paths?(path)
256
+ if @skip_vendor and
257
+ (relative_path == 'vendor' || relative_path.include?('vendor/')) and
258
+ !in_engine_paths?(path) and
259
+ !in_add_libs_paths?(path)
255
260
  true
256
261
  else
257
262
  match_path EXCLUDED_PATHS, path
@@ -260,11 +265,17 @@ module Brakeman
260
265
  end
261
266
 
262
267
  def in_engine_paths?(path)
263
- @engine_paths.any? { |p| path.absolute.include?(p) }
268
+ @engine_path_pairs.any? do |original, expanded|
269
+ path.absolute.include?(original) ||
270
+ expanded.start_with?("#{path.absolute}#{File::SEPARATOR}")
271
+ end
264
272
  end
265
273
 
266
274
  def in_add_libs_paths?(path)
267
- @additional_libs_path.any? { |p| path.absolute.include?(p) }
275
+ @additional_libs_path_pairs.any? do |original, expanded|
276
+ path.absolute.include?(original) ||
277
+ expanded.start_with?("#{path.absolute}#{File::SEPARATOR}")
278
+ end
268
279
  end
269
280
 
270
281
  def match_path files, path
@@ -25,6 +25,7 @@ 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
+ ['8.0.0', '8.0.99'] => Date.new(2026, 11, 7),
29
+ ['8.1.0', '8.1.99'] => Date.new(2027, 10, 10),
29
30
  }
30
31
  end
@@ -26,5 +26,6 @@ class Brakeman::CheckEOLRuby < Brakeman::EOLCheck
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
28
  ['3.4.0', '3.4.99'] => Date.new(2028, 3, 31),
29
+ ['4.0.0', '4.0.99'] => Date.new(2029, 3, 31),
29
30
  }
30
31
  end
@@ -55,6 +55,13 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
55
55
  first_arg = call.first_arg
56
56
  failure = nil
57
57
 
58
+ # All spawn-family methods optionally accept an env hash as the first
59
+ # argument. Strip it so first_arg always points to the command name.
60
+ if hash?(first_arg)
61
+ args.delete_at(1)
62
+ first_arg = args[1]
63
+ end
64
+
58
65
  case call.method
59
66
  when :popen
60
67
  # Normally, if we're in a `popen` call, we only are worried about shell
@@ -99,14 +106,17 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
99
106
 
100
107
  break if failure
101
108
  end
102
- when :system, :exec
103
- # Normally, if we're in a `system` or `exec` call, we only are worried
104
- # about shell injection when there's a single argument, because comma-
105
- # separated arguments are always escaped by Ruby. However, an exception is
106
- # when the first two arguments are something like "bash -c" because then
107
- # the third argument is effectively the command being run and might be
108
- # a malicious executable if it comes (partially or fully) from user input.
109
- if dash_c_shell_command?(first_arg, call.second_arg)
109
+ when :system, :exec, :spawn, :capture2, :capture2e, :capture3,
110
+ :popen2, :popen2e, :popen3
111
+ # Comma-separated arguments are passed directly to execve without a
112
+ # shell, so only the first argument (the command name) needs checking.
113
+ # Exception: `bash -c <script>` — the script is the injection vector.
114
+ #
115
+ # All of these methods accept an optional trailing options hash
116
+ # (e.g. stdin_data:, chdir:); strip it before checking.
117
+ args.pop if hash?(args.last) && args.length > 2
118
+
119
+ if dash_c_shell_command?(first_arg, args[2])
110
120
  failure = include_user_input?(args[3]) ||
111
121
  dangerous_interp?(args[3]) ||
112
122
  dangerous_string_building?(args[3])
@@ -115,21 +125,6 @@ class Brakeman::CheckExecute < Brakeman::BaseCheck
115
125
  dangerous_interp?(first_arg) ||
116
126
  dangerous_string_building?(first_arg)
117
127
  end
118
- when :capture2, :capture2e, :capture3
119
- # Open3 capture methods can take a :stdin_data argument which is used as the
120
- # the input to the called command so it is not succeptable to command injection.
121
- # As such if the last argument is a hash (and therefore execution options) it
122
- # should be ignored
123
-
124
- args.pop if hash?(args.last) && args.length > 2
125
-
126
- args.each_sexp do |arg|
127
- failure = include_user_input?(arg) ||
128
- dangerous_interp?(arg) ||
129
- dangerous_string_building?(arg)
130
-
131
- break if failure
132
- end
133
128
  else
134
129
  failure = include_user_input?(args) ||
135
130
  dangerous_interp?(args) ||
@@ -16,7 +16,7 @@ class Brakeman::CheckValidationRegex < Brakeman::BaseCheck
16
16
  FORMAT = Sexp.new(:lit, :format)
17
17
 
18
18
  def run_check
19
- active_record_models.each do |name, model|
19
+ tracker.models.each do |name, model|
20
20
  @current_model = model
21
21
  format_validations = model.options[:validates_format_of]
22
22
 
@@ -92,7 +92,8 @@ class Brakeman::CheckValidationRegex < Brakeman::BaseCheck
92
92
  :message => msg("Insufficient validation for ", msg_code(get_name validator), " using ", msg_code(regex.inspect), ". Use ", msg_code("\\A"), " and ", msg_code("\\z"), " as anchors"),
93
93
  :line => value.line,
94
94
  :confidence => :high,
95
- :cwe_id => [777]
95
+ :cwe_id => [777],
96
+ :code => validator
96
97
  end
97
98
  end
98
99
 
@@ -63,9 +63,9 @@ module Brakeman::Options
63
63
  options[:exit_on_error] = exit_on_error
64
64
  end
65
65
 
66
- opts.on "--ensure-latest [DAYS]", Integer, "Fail when Brakeman is outdated. Optionally set minimum age in days." do |days|
66
+ opts.on "--ensure-latest [DAYS]", Integer, "Fail when Brakeman is outdated. Optionally set minimum age in days (1-15)." do |days|
67
67
  if days and not (1..15).include? days
68
- raise OptionParser::InvalidArgument
68
+ raise OptionParser::InvalidArgument, "Minimum age must be 1-15 days."
69
69
  end
70
70
 
71
71
  options[:ensure_latest] = days || true
@@ -56,8 +56,15 @@ module Brakeman
56
56
  else
57
57
  ERB.new(text, nil, '-').src
58
58
  end
59
- src.sub!(/^#.*\n/, '')
60
- src
59
+
60
+ # Encoding comment at the beginning throws off line numbers
61
+ # but the string is frozen in ERB 6.0.2+, so we don't want to
62
+ # unnecessarily duplicate the string
63
+ if src.start_with? '#'
64
+ src.sub(/^#.*\n/, '')
65
+ else
66
+ src
67
+ end
61
68
  end
62
69
  end
63
70
 
@@ -171,7 +171,7 @@ class Brakeman::HamlTemplateProcessor < Brakeman::TemplateProcessor
171
171
 
172
172
  def escaped_builder_method? exp
173
173
  case exp.method
174
- when :build, :build_aria, :build_boolean, :build_data, :build_id, :escape_html
174
+ when :build, :build_aria, :build_boolean, :build_class, :build_data, :build_id, :escape_html
175
175
  true? exp.first_arg
176
176
  else
177
177
  false
@@ -91,12 +91,25 @@ class Brakeman::ModelProcessor < Brakeman::BaseProcessor
91
91
  end
92
92
 
93
93
  def add_enum_method call
94
- arg = call.first_arg
95
- return unless hash? arg
96
- return unless symbol? arg[1]
94
+ if call.second_arg.nil?
95
+ # Rails < 7.1 syntax: enum status: { active: 0, archived: 1 }
96
+ arg = call.first_arg
97
+ return unless hash? arg
98
+ return unless symbol? arg[1]
99
+
100
+ enum_name = arg[1].value # first key
101
+ enums = arg[2] # first value
102
+ else
103
+ # Rails >= 7.1 syntax: enum :status, { active: 0, archived: 1 }
104
+ first_arg = call.first_arg
105
+ second_arg = call.second_arg
106
+ return unless symbol? first_arg
107
+ return unless hash? second_arg or array? second_arg
108
+
109
+ enum_name = first_arg.value
110
+ enums = second_arg
111
+ end
97
112
 
98
- enum_name = arg[1].value # first key
99
- enums = arg[2] # first value
100
113
  enums_name = pluralize(enum_name.to_s).to_sym
101
114
 
102
115
  call_line = call.line
@@ -1,38 +1,72 @@
1
1
  class Brakeman::Report::Sonar < Brakeman::Report::Base
2
2
  def generate_report
3
+ warnings = all_warnings
3
4
  report_object = {
4
- issues: all_warnings.map { |warning| issue_json(warning) }
5
+ rules: rules_json(warnings),
6
+ issues: warnings.map { |warning| issue_json(warning) }
5
7
  }
6
- return JSON.pretty_generate report_object
8
+ JSON.pretty_generate report_object
7
9
  end
8
-
10
+
9
11
  private
10
-
12
+
13
+ def rules_json(warnings)
14
+ warnings
15
+ .each_with_object({}) do |w, unique_warnings|
16
+ unique_warnings[[w.warning_code, w.confidence]] ||= w
17
+ end
18
+ .values
19
+ .map do |w|
20
+ {
21
+ id: rule_id(w),
22
+ name: w.warning_type.to_s,
23
+ description: w.link,
24
+ engineId: "Brakeman",
25
+ cleanCodeAttribute: "TRUSTWORTHY",
26
+ type: "VULNERABILITY",
27
+ severity: rule_severity_for(w.confidence),
28
+ impacts: [
29
+ {
30
+ softwareQuality: "SECURITY",
31
+ severity: impact_severity_for(w.confidence)
32
+ }
33
+ ]
34
+ }
35
+ end
36
+ end
37
+
11
38
  def issue_json(warning)
12
39
  {
13
- engineId: "Brakeman",
14
- ruleId: warning.warning_code,
15
- type: "VULNERABILITY",
16
- severity: severity_level_for(warning.confidence),
40
+ ruleId: rule_id(warning),
41
+ effortMinutes: (4 - warning.confidence) * 15,
17
42
  primaryLocation: {
18
43
  message: warning.message,
19
44
  filePath: warning.file.relative,
20
45
  textRange: {
21
- "startLine": warning.line || 1,
22
- "endLine": warning.line || 1,
46
+ startLine: warning.line || 1,
47
+ endLine: warning.line || 1
23
48
  }
24
- },
25
- effortMinutes: (4 - warning.confidence) * 15
49
+ }
26
50
  }
27
51
  end
28
52
 
29
- def severity_level_for(confidence)
30
- if confidence == 0
31
- "CRITICAL"
32
- elsif confidence == 1
33
- "MAJOR"
34
- else
35
- "MINOR"
53
+ def rule_id(warning)
54
+ "BRAKEMAN_#{warning.warning_code}_#{rule_severity_for(warning.confidence)}"
55
+ end
56
+
57
+ def rule_severity_for(confidence)
58
+ case confidence
59
+ when 0 then "CRITICAL"
60
+ when 1 then "MAJOR"
61
+ else "MINOR"
62
+ end
63
+ end
64
+
65
+ def impact_severity_for(confidence)
66
+ case confidence
67
+ when 0 then "HIGH"
68
+ when 1 then "MEDIUM"
69
+ else "LOW"
36
70
  end
37
71
  end
38
72
  end
@@ -1,3 +1,3 @@
1
1
  module Brakeman
2
- Version = "8.0.5"
2
+ Version = "8.1.0"
3
3
  end
@@ -175,7 +175,7 @@ class Brakeman::Warning
175
175
  def format_with_user_input strip = true, &block
176
176
  if self.user_input
177
177
  formatted = Brakeman::OutputProcessor.new.format(code, self.user_input, &block)
178
- formatted.gsub!(/(\t|\r|\n)+/, " ") if strip
178
+ formatted = formatted.gsub(/(\t|\r|\n)+/, " ") if strip
179
179
  formatted
180
180
  else
181
181
  format_code
@@ -321,4 +321,3 @@ class Brakeman::Warning
321
321
  formatted
322
322
  end
323
323
  end
324
-
data/lib/brakeman.rb CHANGED
@@ -427,7 +427,7 @@ module Brakeman
427
427
  release_date = latest.date.to_date
428
428
  latest_version = latest.version.to_s
429
429
 
430
- if (Date.today - latest.date.to_date) >= days_old
430
+ if (Date.today - release_date) >= days_old
431
431
  if current != latest_version
432
432
  return "Brakeman #{current} is not the latest version #{latest_version}"
433
433
  else
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brakeman-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.5
4
+ version: 8.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Collins
8
+ autorequire:
8
9
  bindir: bin
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2026-09-30 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: minitest
@@ -481,6 +482,7 @@ metadata:
481
482
  mailing_list_uri: https://gitter.im/presidentbeef/brakeman
482
483
  source_code_uri: https://github.com/presidentbeef/brakeman
483
484
  wiki_uri: https://github.com/presidentbeef/brakeman/wiki
485
+ post_install_message:
484
486
  rdoc_options: []
485
487
  require_paths:
486
488
  - lib
@@ -495,7 +497,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
495
497
  - !ruby/object:Gem::Version
496
498
  version: '0'
497
499
  requirements: []
498
- rubygems_version: 4.0.3
500
+ rubygems_version: 3.5.11
501
+ signing_key:
499
502
  specification_version: 4
500
503
  summary: Security vulnerability scanner for Ruby on Rails.
501
504
  test_files: []