brakeman-lib 4.8.0 → 4.10.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 +4 -4
- data/CHANGES.md +39 -1
- data/README.md +12 -4
- data/lib/brakeman.rb +20 -0
- data/lib/brakeman/checks/base_check.rb +1 -1
- data/lib/brakeman/checks/check_basic_auth.rb +2 -0
- data/lib/brakeman/checks/check_csrf_token_forgery_cve.rb +28 -0
- data/lib/brakeman/checks/check_deserialize.rb +21 -1
- data/lib/brakeman/checks/check_json_entity_escape.rb +38 -0
- data/lib/brakeman/checks/check_mass_assignment.rb +33 -3
- data/lib/brakeman/checks/check_model_attr_accessible.rb +1 -1
- data/lib/brakeman/checks/check_model_attributes.rb +1 -1
- data/lib/brakeman/checks/check_page_caching_cve.rb +37 -0
- data/lib/brakeman/checks/check_permit_attributes.rb +1 -1
- data/lib/brakeman/checks/check_skip_before_filter.rb +4 -4
- data/lib/brakeman/checks/check_sql.rb +1 -1
- data/lib/brakeman/checks/check_template_injection.rb +32 -0
- data/lib/brakeman/commandline.rb +25 -1
- data/lib/brakeman/options.rb +21 -1
- data/lib/brakeman/processors/alias_processor.rb +2 -3
- data/lib/brakeman/processors/lib/call_conversion_helper.rb +1 -1
- data/lib/brakeman/processors/lib/find_all_calls.rb +28 -13
- data/lib/brakeman/processors/lib/render_helper.rb +3 -1
- data/lib/brakeman/report.rb +7 -0
- data/lib/brakeman/report/ignore/config.rb +4 -0
- data/lib/brakeman/report/report_sarif.rb +114 -0
- data/lib/brakeman/report/report_text.rb +37 -16
- data/lib/brakeman/scanner.rb +4 -1
- data/lib/brakeman/tracker.rb +3 -1
- data/lib/brakeman/tracker/config.rb +6 -4
- data/lib/brakeman/tracker/constants.rb +8 -7
- data/lib/brakeman/util.rb +16 -0
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning_codes.rb +7 -0
- metadata +25 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0b264d50410107be24af470596fa3b5511eb8f174707f571f9884a6aea932d87
|
|
4
|
+
data.tar.gz: 4a8ab18c5e077e4b192ea52db29b52c7dd6006f66163862f0d4b1fd9973ba366
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 524c94b3b25e13273dea5707e315fde68fe5ad984433e3c0a11674bc7baf1c133a9e4becc528fabb092536ba9b5d02f05714dd10dd32014067cff8e301c37096
|
|
7
|
+
data.tar.gz: 349db7828699760d574a0534f21361eee617454647033f27ed7440e16a9ef38b7807f76b62fbd62fd1746d024c70a557f3d3fa5970cc390f3e92eaf3ca004f0c
|
data/CHANGES.md
CHANGED
|
@@ -1,4 +1,42 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 4.10.0 - 2020-09-28
|
|
2
|
+
|
|
3
|
+
* Add SARIF report format (Steve Winton)
|
|
4
|
+
|
|
5
|
+
# 4.9.1 - 2020-09-04
|
|
6
|
+
|
|
7
|
+
* Check `chomp`ed strings for SQL injection
|
|
8
|
+
* Use version from `active_record` for non-Rails apps (Ulysse Buonomo)
|
|
9
|
+
* Always set line number for joined arrays
|
|
10
|
+
* Avoid warning about missing `attr_accessible` if `protected_attributes` gem is used
|
|
11
|
+
|
|
12
|
+
# 4.9.0 - 2020-08-04
|
|
13
|
+
|
|
14
|
+
* Add check for CVE-2020-8166 (Jamie Finnigan)
|
|
15
|
+
* Avoid warning when `safe_yaml` is used via `YAML.load(..., safe: true)`
|
|
16
|
+
* Add check for user input in `ERB.new` (Matt Hickman)
|
|
17
|
+
* Add `--ensure-ignore-notes` (Eli Block)
|
|
18
|
+
* Remove whitelist/blacklist language, add clarifications
|
|
19
|
+
* Do not warn about mass assignment with `params.permit!.slice`
|
|
20
|
+
* Add "full call" information to call index results
|
|
21
|
+
* Ignore `params.permit!` in path helpers
|
|
22
|
+
* Treat `Dir.glob` as safe source of values in guards
|
|
23
|
+
* Always scan `environment.rb`
|
|
24
|
+
|
|
25
|
+
# 4.8.2 - 2020-05-12
|
|
26
|
+
|
|
27
|
+
* Add check for CVE-2020-8159
|
|
28
|
+
* Fix `authenticate_or_request_with_http_basic` check for passed blocks (Hugo Corbucci)
|
|
29
|
+
* Add `--text-fields` option
|
|
30
|
+
* Add check for escaping HTML entities in JSON configuration
|
|
31
|
+
|
|
32
|
+
# 4.8.1 - 2020-04-06
|
|
33
|
+
|
|
34
|
+
* Check SQL query strings using `String#strip` or `String.squish`
|
|
35
|
+
* Handle non-symbol keys in locals hash for render()
|
|
36
|
+
* Warn about global(!) mass assignment
|
|
37
|
+
* Index calls in render arguments
|
|
38
|
+
|
|
39
|
+
# 4.8.0 - 2020-02-18
|
|
2
40
|
|
|
3
41
|
* Add JUnit-XML report format (Naoki Kimura)
|
|
4
42
|
* Sort ignore files by fingerprint and line (Ngan Pham)
|
data/README.md
CHANGED
|
@@ -16,9 +16,11 @@ Using RubyGems:
|
|
|
16
16
|
|
|
17
17
|
Using Bundler:
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
```ruby
|
|
20
|
+
group :development do
|
|
21
|
+
gem 'brakeman'
|
|
22
|
+
end
|
|
23
|
+
```
|
|
22
24
|
|
|
23
25
|
Using Docker:
|
|
24
26
|
|
|
@@ -74,12 +76,16 @@ To specify an output file for the results:
|
|
|
74
76
|
|
|
75
77
|
brakeman -o output_file
|
|
76
78
|
|
|
77
|
-
The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `markdown`, `csv`, and `codeclimate`.
|
|
79
|
+
The output format is determined by the file extension or by using the `-f` option. Current options are: `text`, `html`, `tabs`, `json`, `junit`, `markdown`, `csv`, and `codeclimate`.
|
|
78
80
|
|
|
79
81
|
Multiple output files can be specified:
|
|
80
82
|
|
|
81
83
|
brakeman -o output.html -o output.json
|
|
82
84
|
|
|
85
|
+
To output to both a file and to the console, with color:
|
|
86
|
+
|
|
87
|
+
brakeman --color -o /dev/stdout -o output.json
|
|
88
|
+
|
|
83
89
|
To suppress informational warnings and just output the report:
|
|
84
90
|
|
|
85
91
|
brakeman -q
|
|
@@ -167,6 +173,8 @@ There is a [plugin available](http://brakemanscanner.org/docs/jenkins/) for Jenk
|
|
|
167
173
|
|
|
168
174
|
For even more continuous testing, try the [Guard plugin](https://github.com/guard/guard-brakeman).
|
|
169
175
|
|
|
176
|
+
There are a couple [Github Actions](https://github.com/marketplace?type=actions&query=brakeman) available.
|
|
177
|
+
|
|
170
178
|
# Building
|
|
171
179
|
|
|
172
180
|
git clone git://github.com/presidentbeef/brakeman.git
|
data/lib/brakeman.rb
CHANGED
|
@@ -20,6 +20,10 @@ module Brakeman
|
|
|
20
20
|
#option is set
|
|
21
21
|
Errors_Found_Exit_Code = 7
|
|
22
22
|
|
|
23
|
+
#Exit code returned when an ignored warning has no note and
|
|
24
|
+
#--ensure-ignore-notes is set
|
|
25
|
+
Empty_Ignore_Note_Exit_Code = 8
|
|
26
|
+
|
|
23
27
|
@debug = false
|
|
24
28
|
@quiet = false
|
|
25
29
|
@loaded_dependencies = []
|
|
@@ -233,6 +237,8 @@ module Brakeman
|
|
|
233
237
|
[:to_table]
|
|
234
238
|
when :junit, :to_junit
|
|
235
239
|
[:to_junit]
|
|
240
|
+
when :sarif, :to_sarif
|
|
241
|
+
[:to_sarif]
|
|
236
242
|
else
|
|
237
243
|
[:to_text]
|
|
238
244
|
end
|
|
@@ -262,6 +268,8 @@ module Brakeman
|
|
|
262
268
|
:to_table
|
|
263
269
|
when /\.junit$/i
|
|
264
270
|
:to_junit
|
|
271
|
+
when /\.sarif$/i
|
|
272
|
+
:to_sarif
|
|
265
273
|
else
|
|
266
274
|
:to_text
|
|
267
275
|
end
|
|
@@ -498,6 +506,18 @@ module Brakeman
|
|
|
498
506
|
end
|
|
499
507
|
end
|
|
500
508
|
|
|
509
|
+
# Returns an array of alert fingerprints for any ignored warnings without
|
|
510
|
+
# notes found in the specified ignore file (if it exists).
|
|
511
|
+
def self.ignore_file_entries_with_empty_notes file
|
|
512
|
+
return [] unless file
|
|
513
|
+
|
|
514
|
+
require 'brakeman/report/ignore/config'
|
|
515
|
+
|
|
516
|
+
config = IgnoreConfig.new(file, nil)
|
|
517
|
+
config.read_from_file
|
|
518
|
+
config.already_ignored_entries_with_empty_notes.map { |i| i[:fingerprint] }
|
|
519
|
+
end
|
|
520
|
+
|
|
501
521
|
def self.filter_warnings tracker, options
|
|
502
522
|
require 'brakeman/report/ignore/config'
|
|
503
523
|
|
|
@@ -467,7 +467,7 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
|
467
467
|
end
|
|
468
468
|
|
|
469
469
|
def gemfile_or_environment gem_name = :rails
|
|
470
|
-
if gem_name and info = tracker.config.get_gem(gem_name)
|
|
470
|
+
if gem_name and info = tracker.config.get_gem(gem_name.to_sym)
|
|
471
471
|
info
|
|
472
472
|
elsif @app_tree.exists?("Gemfile")
|
|
473
473
|
@app_tree.file_path "Gemfile"
|
|
@@ -57,6 +57,8 @@ class Brakeman::CheckBasicAuth < Brakeman::BaseCheck
|
|
|
57
57
|
|
|
58
58
|
# Check if the block of a result contains a comparison of password to string
|
|
59
59
|
def include_password_literal? result
|
|
60
|
+
return false if result[:block_args].nil?
|
|
61
|
+
|
|
60
62
|
@password_var = result[:block_args].last
|
|
61
63
|
@include_password = false
|
|
62
64
|
process result[:block]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckCSRFTokenForgeryCVE < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Checks for versions with CSRF token forgery vulnerability (CVE-2020-8166)"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
fix_version = case
|
|
10
|
+
when version_between?('0.0.0', '5.2.4.2')
|
|
11
|
+
'5.2.4.3'
|
|
12
|
+
when version_between?('6.0.0', '6.0.3')
|
|
13
|
+
'6.0.3.1'
|
|
14
|
+
else
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if fix_version
|
|
19
|
+
warn :warning_type => "Cross-Site Request Forgery",
|
|
20
|
+
:warning_code => :CVE_2020_8166,
|
|
21
|
+
:message => msg(msg_version(rails_version), " has a vulnerability that may allow CSRF token forgery. Upgrade to ", msg_version(fix_version), " or patch"),
|
|
22
|
+
:confidence => :medium,
|
|
23
|
+
:gem_info => gemfile_or_environment,
|
|
24
|
+
:link => "https://groups.google.com/g/rubyonrails-security/c/NOjKiGeXUgw"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
@@ -13,7 +13,23 @@ class Brakeman::CheckDeserialize < Brakeman::BaseCheck
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def check_yaml
|
|
16
|
-
check_methods :YAML, :
|
|
16
|
+
check_methods :YAML, :load_documents, :load_stream, :parse_documents, :parse_stream
|
|
17
|
+
|
|
18
|
+
# Check for safe_yaml gem use with YAML.load(..., safe: true)
|
|
19
|
+
if uses_safe_yaml?
|
|
20
|
+
tracker.find_call(target: :YAML, method: :load).each do |result|
|
|
21
|
+
call = result[:call]
|
|
22
|
+
options = call.second_arg
|
|
23
|
+
|
|
24
|
+
if hash? options and true? hash_access(options, :safe)
|
|
25
|
+
next
|
|
26
|
+
else
|
|
27
|
+
check_deserialize result, :YAML
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
check_methods :YAML, :load
|
|
32
|
+
end
|
|
17
33
|
end
|
|
18
34
|
|
|
19
35
|
def check_csv
|
|
@@ -102,4 +118,8 @@ class Brakeman::CheckDeserialize < Brakeman::BaseCheck
|
|
|
102
118
|
|
|
103
119
|
false
|
|
104
120
|
end
|
|
121
|
+
|
|
122
|
+
def uses_safe_yaml?
|
|
123
|
+
tracker.config.has_gem? :safe_yaml
|
|
124
|
+
end
|
|
105
125
|
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckJSONEntityEscape < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Check if HTML escaping is disabled for JSON output"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
check_config_setting
|
|
10
|
+
check_manual_disable
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def check_config_setting
|
|
14
|
+
if false? tracker.config.rails.dig(:active_support, :escape_html_entities_in_json)
|
|
15
|
+
warn :warning_type => "Cross-Site Scripting",
|
|
16
|
+
:warning_code => :json_html_escape_config,
|
|
17
|
+
:message => msg("HTML entities in JSON are not escaped by default"),
|
|
18
|
+
:confidence => :medium,
|
|
19
|
+
:file => "config/environments/production.rb",
|
|
20
|
+
:line => 1
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def check_manual_disable
|
|
25
|
+
tracker.find_call(targets: [:ActiveSupport, :'ActiveSupport::JSON::Encoding'], method: :escape_html_entities_in_json=).each do |result|
|
|
26
|
+
setting = result[:call].first_arg
|
|
27
|
+
|
|
28
|
+
if false? setting
|
|
29
|
+
warn :result => result,
|
|
30
|
+
:warning_type => "Cross-Site Scripting",
|
|
31
|
+
:warning_code => :json_html_escape_module,
|
|
32
|
+
:message => msg("HTML entities in JSON are not escaped by default"),
|
|
33
|
+
:confidence => :medium,
|
|
34
|
+
:file => "config/environments/production.rb"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -17,6 +17,7 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
|
|
|
17
17
|
def run_check
|
|
18
18
|
check_mass_assignment
|
|
19
19
|
check_permit!
|
|
20
|
+
check_permit_all_parameters
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def find_mass_assign_calls
|
|
@@ -159,12 +160,27 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
|
|
|
159
160
|
# Look for and warn about uses of Parameters#permit! for mass assignment
|
|
160
161
|
def check_permit!
|
|
161
162
|
tracker.find_call(:method => :permit!, :nested => true).each do |result|
|
|
162
|
-
if params? result[:call].target
|
|
163
|
-
|
|
163
|
+
if params? result[:call].target
|
|
164
|
+
unless inside_safe_method? result or calls_slice? result
|
|
165
|
+
warn_on_permit! result
|
|
166
|
+
end
|
|
164
167
|
end
|
|
165
168
|
end
|
|
166
169
|
end
|
|
167
170
|
|
|
171
|
+
# Ignore blah_some_path(params.permit!)
|
|
172
|
+
def inside_safe_method? result
|
|
173
|
+
parent_call = result.dig(:parent, :call)
|
|
174
|
+
|
|
175
|
+
call? parent_call and
|
|
176
|
+
parent_call.method.match(/_path$/)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def calls_slice? result
|
|
180
|
+
result[:chain].include? :slice or
|
|
181
|
+
(result[:full_call] and result[:full_call][:chain].include? :slice)
|
|
182
|
+
end
|
|
183
|
+
|
|
168
184
|
# Look for actual use of params in mass assignment to avoid
|
|
169
185
|
# warning about uses of Parameters#permit! without any mass assignment
|
|
170
186
|
# or when mass assignment is restricted by model instead.
|
|
@@ -190,7 +206,21 @@ class Brakeman::CheckMassAssignment < Brakeman::BaseCheck
|
|
|
190
206
|
warn :result => result,
|
|
191
207
|
:warning_type => "Mass Assignment",
|
|
192
208
|
:warning_code => :mass_assign_permit!,
|
|
193
|
-
:message =>
|
|
209
|
+
:message => msg('Specify exact keys allowed for mass assignment instead of using ', msg_code('permit!'), ' which allows any keys'),
|
|
194
210
|
:confidence => confidence
|
|
195
211
|
end
|
|
212
|
+
|
|
213
|
+
def check_permit_all_parameters
|
|
214
|
+
tracker.find_call(target: :"ActionController::Parameters", method: :permit_all_parameters=).each do |result|
|
|
215
|
+
call = result[:call]
|
|
216
|
+
|
|
217
|
+
if true? call.first_arg
|
|
218
|
+
warn :result => result,
|
|
219
|
+
:warning_type => "Mass Assignment",
|
|
220
|
+
:warning_code => :mass_assign_permit_all,
|
|
221
|
+
:message => msg('Mass assignment is globally enabled. Disable and specify exact keys using ', msg_code('params.permit'), ' instead'),
|
|
222
|
+
:confidence => :high
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
196
226
|
end
|
|
@@ -8,7 +8,7 @@ require 'brakeman/checks/base_check'
|
|
|
8
8
|
class Brakeman::CheckModelAttrAccessible < Brakeman::BaseCheck
|
|
9
9
|
Brakeman::Checks.add self
|
|
10
10
|
|
|
11
|
-
@description = "Reports models which have dangerous attributes defined
|
|
11
|
+
@description = "Reports models which have dangerous attributes defined via attr_accessible"
|
|
12
12
|
|
|
13
13
|
SUSP_ATTRS = [
|
|
14
14
|
[:admin, :high], # Very dangerous unless some Rails authorization used
|
|
@@ -8,7 +8,7 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
|
|
|
8
8
|
@description = "Reports models which do not use attr_restricted and warns on models that use attr_protected"
|
|
9
9
|
|
|
10
10
|
def run_check
|
|
11
|
-
return if mass_assign_disabled?
|
|
11
|
+
return if mass_assign_disabled? or tracker.config.has_gem?(:protected_attributes)
|
|
12
12
|
|
|
13
13
|
#Roll warnings into one warning for all models
|
|
14
14
|
if tracker.options[:collapse_mass_assignment]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckPageCachingCVE < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Check for page caching vulnerability (CVE-2020-8159)"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
gem_name = 'actionpack-page_caching'
|
|
10
|
+
gem_version = tracker.config.gem_version(gem_name.to_sym)
|
|
11
|
+
upgrade_version = '1.2.2'
|
|
12
|
+
cve = 'CVE-2020-8159'
|
|
13
|
+
|
|
14
|
+
return unless gem_version and version_between?('0.0.0', '1.2.1', gem_version)
|
|
15
|
+
|
|
16
|
+
message = msg("Directory traversal vulnerability in ", msg_version(gem_version, gem_name), " ", msg_cve(cve), ". Upgrade to ", msg_version(upgrade_version, gem_name))
|
|
17
|
+
|
|
18
|
+
if uses_caches_page?
|
|
19
|
+
confidence = :high
|
|
20
|
+
else
|
|
21
|
+
confidence = :weak
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
warn :warning_type => 'Directory Traversal',
|
|
25
|
+
:warning_code => :CVE_2020_8159,
|
|
26
|
+
:message => message,
|
|
27
|
+
:confidence => confidence,
|
|
28
|
+
:link_path => 'https://groups.google.com/d/msg/rubyonrails-security/CFRVkEytdP8/c5gmICECAgAJ',
|
|
29
|
+
:gem_info => gemfile_or_environment(gem_name)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def uses_caches_page?
|
|
33
|
+
tracker.controllers.any? do |name, controller|
|
|
34
|
+
controller.options.has_key? :caches_page
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -3,7 +3,7 @@ require 'brakeman/checks/base_check'
|
|
|
3
3
|
class Brakeman::CheckPermitAttributes < Brakeman::BaseCheck
|
|
4
4
|
Brakeman::Checks.add self
|
|
5
5
|
|
|
6
|
-
@description = "Warn on potentially dangerous attributes
|
|
6
|
+
@description = "Warn on potentially dangerous attributes allowed via permit"
|
|
7
7
|
|
|
8
8
|
SUSPICIOUS_KEYS = {
|
|
9
9
|
admin: :high,
|
|
@@ -4,8 +4,8 @@ require 'brakeman/checks/base_check'
|
|
|
4
4
|
#
|
|
5
5
|
# skip_before_filter :verify_authenticity_token, :except => [...]
|
|
6
6
|
#
|
|
7
|
-
#which is essentially a
|
|
8
|
-
#ones listed) versus a
|
|
7
|
+
#which is essentially a skip-by-default approach (no actions are checked EXCEPT the
|
|
8
|
+
#ones listed) versus a enforce-by-default approach (ONLY the actions listed will skip
|
|
9
9
|
#the check)
|
|
10
10
|
class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
|
|
11
11
|
Brakeman::Checks.add self
|
|
@@ -26,7 +26,7 @@ class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
|
|
|
26
26
|
warn :class => controller.name, #ugh this should be a controller warning, too
|
|
27
27
|
:warning_type => "Cross-Site Request Forgery",
|
|
28
28
|
:warning_code => :csrf_blacklist,
|
|
29
|
-
:message => msg("
|
|
29
|
+
:message => msg("List specific actions (", msg_code(":only => [..]"), ") when skipping CSRF check"),
|
|
30
30
|
:code => filter,
|
|
31
31
|
:confidence => :medium,
|
|
32
32
|
:file => controller.file
|
|
@@ -35,7 +35,7 @@ class Brakeman::CheckSkipBeforeFilter < Brakeman::BaseCheck
|
|
|
35
35
|
warn :controller => controller.name,
|
|
36
36
|
:warning_code => :auth_blacklist,
|
|
37
37
|
:warning_type => "Authentication",
|
|
38
|
-
:message => msg("
|
|
38
|
+
:message => msg("List specific actions (", msg_code(":only => [..]"), ") when skipping authentication"),
|
|
39
39
|
:code => filter,
|
|
40
40
|
:confidence => :medium,
|
|
41
41
|
:link_path => "authentication_whitelist",
|
|
@@ -393,7 +393,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
|
393
393
|
nil
|
|
394
394
|
end
|
|
395
395
|
|
|
396
|
-
TO_STRING_METHODS = [:to_s, :strip_heredoc]
|
|
396
|
+
TO_STRING_METHODS = [:chomp, :to_s, :squish, :strip, :strip_heredoc]
|
|
397
397
|
|
|
398
398
|
#Returns value if interpolated value is not something safe
|
|
399
399
|
def unsafe_string_interp? exp
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckTemplateInjection < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Searches for evaluation of user input through template injection"
|
|
7
|
+
|
|
8
|
+
#Process calls
|
|
9
|
+
def run_check
|
|
10
|
+
Brakeman.debug "Finding ERB.new calls"
|
|
11
|
+
erb_calls = tracker.find_call :target => :ERB, :method => :new, :nested => true
|
|
12
|
+
|
|
13
|
+
Brakeman.debug "Processing ERB.new calls"
|
|
14
|
+
erb_calls.each do |call|
|
|
15
|
+
process_result call
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
#Warns if eval includes user input
|
|
20
|
+
def process_result result
|
|
21
|
+
return unless original? result
|
|
22
|
+
|
|
23
|
+
if input = include_user_input?(result[:call].arglist)
|
|
24
|
+
warn :result => result,
|
|
25
|
+
:warning_type => "Template Injection",
|
|
26
|
+
:warning_code => :erb_template_injection,
|
|
27
|
+
:message => msg(msg_input(input), " used directly in ", msg_code("ERB"), " template, which might enable remote code execution"),
|
|
28
|
+
:user_input => input,
|
|
29
|
+
:confidence => :high
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/brakeman/commandline.rb
CHANGED
|
@@ -102,6 +102,13 @@ module Brakeman
|
|
|
102
102
|
app_path = "."
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
+
if options[:ensure_ignore_notes] and options[:previous_results_json]
|
|
106
|
+
warn '[Notice] --ensure-ignore-notes may not be used at the same ' \
|
|
107
|
+
'time as --compare. Deactivating --ensure-ignore-notes. ' \
|
|
108
|
+
'Please see `brakeman --help` for valid options'
|
|
109
|
+
options[:ensure_ignore_notes] = false
|
|
110
|
+
end
|
|
111
|
+
|
|
105
112
|
return options, app_path
|
|
106
113
|
end
|
|
107
114
|
|
|
@@ -115,7 +122,20 @@ module Brakeman
|
|
|
115
122
|
|
|
116
123
|
# Runs a regular report based on the options provided.
|
|
117
124
|
def regular_report options
|
|
118
|
-
tracker = run_brakeman options
|
|
125
|
+
tracker = run_brakeman options
|
|
126
|
+
|
|
127
|
+
ensure_ignore_notes_failed = false
|
|
128
|
+
if tracker.options[:ensure_ignore_notes]
|
|
129
|
+
fingerprints = Brakeman::ignore_file_entries_with_empty_notes tracker.ignored_filter&.file
|
|
130
|
+
|
|
131
|
+
unless fingerprints.empty?
|
|
132
|
+
ensure_ignore_notes_failed = true
|
|
133
|
+
warn '[Error] Notes required for all ignored warnings when ' \
|
|
134
|
+
'--ensure-ignore-notes is set. No notes provided for these ' \
|
|
135
|
+
'warnings: '
|
|
136
|
+
fingerprints.each { |f| warn f }
|
|
137
|
+
end
|
|
138
|
+
end
|
|
119
139
|
|
|
120
140
|
if tracker.options[:exit_on_warn] and not tracker.filtered_warnings.empty?
|
|
121
141
|
quit Brakeman::Warnings_Found_Exit_Code
|
|
@@ -124,6 +144,10 @@ module Brakeman
|
|
|
124
144
|
if tracker.options[:exit_on_error] and tracker.errors.any?
|
|
125
145
|
quit Brakeman::Errors_Found_Exit_Code
|
|
126
146
|
end
|
|
147
|
+
|
|
148
|
+
if ensure_ignore_notes_failed
|
|
149
|
+
quit Brakeman::Empty_Ignore_Note_Exit_Code
|
|
150
|
+
end
|
|
127
151
|
end
|
|
128
152
|
|
|
129
153
|
# Actually run Brakeman.
|
data/lib/brakeman/options.rb
CHANGED
|
@@ -67,6 +67,10 @@ module Brakeman::Options
|
|
|
67
67
|
options[:ensure_latest] = true
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
opts.on "--ensure-ignore-notes", "Fail when an ignored warnings does not include a note" do
|
|
71
|
+
options[:ensure_ignore_notes] = true
|
|
72
|
+
end
|
|
73
|
+
|
|
70
74
|
opts.on "-3", "--rails3", "Force Rails 3 mode" do
|
|
71
75
|
options[:rails3] = true
|
|
72
76
|
end
|
|
@@ -225,7 +229,7 @@ module Brakeman::Options
|
|
|
225
229
|
|
|
226
230
|
opts.on "-f",
|
|
227
231
|
"--format TYPE",
|
|
228
|
-
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit],
|
|
232
|
+
[:pdf, :text, :html, :csv, :tabs, :json, :markdown, :codeclimate, :cc, :plain, :table, :junit, :sarif],
|
|
229
233
|
"Specify output formats. Default is text" do |type|
|
|
230
234
|
|
|
231
235
|
type = "s" if type == :text
|
|
@@ -301,6 +305,22 @@ module Brakeman::Options
|
|
|
301
305
|
options[:github_repo] = repo
|
|
302
306
|
end
|
|
303
307
|
|
|
308
|
+
opts.on "--text-fields field1,field2,etc.", Array, "Specify fields for text report format" do |format|
|
|
309
|
+
valid_options = [:category, :category_id, :check, :code, :confidence, :file, :fingerprint, :line, :link, :message, :render_path]
|
|
310
|
+
|
|
311
|
+
options[:text_fields] = format.map(&:to_sym)
|
|
312
|
+
|
|
313
|
+
if options[:text_fields] == [:all]
|
|
314
|
+
options[:text_fields] = valid_options
|
|
315
|
+
else
|
|
316
|
+
invalid_options = (options[:text_fields] - valid_options)
|
|
317
|
+
|
|
318
|
+
unless invalid_options.empty?
|
|
319
|
+
raise OptionParser::ParseError, "\nInvalid format options: #{invalid_options.inspect}"
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
304
324
|
opts.on "-w",
|
|
305
325
|
"--confidence-level LEVEL",
|
|
306
326
|
["1", "2", "3"],
|
|
@@ -82,7 +82,6 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
|
82
82
|
def replace exp, int = 0
|
|
83
83
|
return exp if int > 3
|
|
84
84
|
|
|
85
|
-
|
|
86
85
|
if replacement = env[exp] and not duplicate? replacement
|
|
87
86
|
replace(replacement.deep_clone(exp.line), int + 1)
|
|
88
87
|
elsif tracker and replacement = tracker.constant_lookup(exp) and not duplicate? replacement
|
|
@@ -731,14 +730,14 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
|
731
730
|
def array_include_all_literals? exp
|
|
732
731
|
call? exp and
|
|
733
732
|
exp.method == :include? and
|
|
734
|
-
all_literals? exp.target
|
|
733
|
+
(all_literals? exp.target or dir_glob? exp.target)
|
|
735
734
|
end
|
|
736
735
|
|
|
737
736
|
def array_detect_all_literals? exp
|
|
738
737
|
call? exp and
|
|
739
738
|
[:detect, :find].include? exp.method and
|
|
740
739
|
exp.first_arg.nil? and
|
|
741
|
-
all_literals? exp.target
|
|
740
|
+
(all_literals? exp.target or dir_glob? exp.target)
|
|
742
741
|
end
|
|
743
742
|
|
|
744
743
|
#Sets @inside_if = true
|
|
@@ -10,7 +10,7 @@ module Brakeman
|
|
|
10
10
|
def join_arrays lhs, rhs, original_exp = nil
|
|
11
11
|
if array? lhs and array? rhs
|
|
12
12
|
result = Sexp.new(:array)
|
|
13
|
-
result.line(lhs.line || rhs.line)
|
|
13
|
+
result.line(lhs.line || rhs.line || 1)
|
|
14
14
|
result.concat lhs[1..-1]
|
|
15
15
|
result.concat rhs[1..-1]
|
|
16
16
|
result
|
|
@@ -20,6 +20,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
|
20
20
|
@current_template = opts[:template]
|
|
21
21
|
@current_file = opts[:file]
|
|
22
22
|
@current_call = nil
|
|
23
|
+
@full_call = nil
|
|
23
24
|
process exp
|
|
24
25
|
end
|
|
25
26
|
|
|
@@ -89,7 +90,7 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
|
89
90
|
#Calls to render() are converted to s(:render, ...) but we would
|
|
90
91
|
#like them in the call cache still for speed
|
|
91
92
|
def process_render exp
|
|
92
|
-
|
|
93
|
+
process_all exp
|
|
93
94
|
|
|
94
95
|
add_simple_call :render, exp
|
|
95
96
|
|
|
@@ -137,7 +138,8 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
|
137
138
|
:call => exp,
|
|
138
139
|
:nested => false,
|
|
139
140
|
:location => make_location,
|
|
140
|
-
:parent => @current_call
|
|
141
|
+
:parent => @current_call,
|
|
142
|
+
:full_call => @full_call }.freeze
|
|
141
143
|
end
|
|
142
144
|
|
|
143
145
|
#Gets the target of a call as a Symbol
|
|
@@ -214,34 +216,47 @@ class Brakeman::FindAllCalls < Brakeman::BasicProcessor
|
|
|
214
216
|
#Return info hash for a call Sexp
|
|
215
217
|
def create_call_hash exp
|
|
216
218
|
target = get_target exp.target
|
|
217
|
-
|
|
218
|
-
if call? target or node_type? target, :dxstr # need to index `` even if target of a call
|
|
219
|
-
already_in_target = @in_target
|
|
220
|
-
@in_target = true
|
|
221
|
-
process target
|
|
222
|
-
@in_target = already_in_target
|
|
223
|
-
|
|
224
|
-
target = get_target(target, :include_calls)
|
|
225
|
-
end
|
|
219
|
+
target_symbol = get_target(target, :include_calls)
|
|
226
220
|
|
|
227
221
|
method = exp.method
|
|
228
222
|
|
|
229
223
|
call_hash = {
|
|
230
|
-
:target =>
|
|
224
|
+
:target => target_symbol,
|
|
231
225
|
:method => method,
|
|
232
226
|
:call => exp,
|
|
233
227
|
:nested => @in_target,
|
|
234
228
|
:chain => get_chain(exp),
|
|
235
229
|
:location => make_location,
|
|
236
|
-
:parent => @current_call
|
|
230
|
+
:parent => @current_call,
|
|
231
|
+
:full_call => @full_call
|
|
237
232
|
}
|
|
238
233
|
|
|
234
|
+
unless @in_target
|
|
235
|
+
@full_call = call_hash
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Process up the call chain
|
|
239
|
+
if call? target or node_type? target, :dxstr # need to index `` even if target of a call
|
|
240
|
+
already_in_target = @in_target
|
|
241
|
+
@in_target = true
|
|
242
|
+
process target
|
|
243
|
+
@in_target = already_in_target
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Process call arguments
|
|
247
|
+
# but add the current call as the 'parent'
|
|
248
|
+
# to any calls in the arguments
|
|
239
249
|
old_parent = @current_call
|
|
240
250
|
@current_call = call_hash
|
|
241
251
|
|
|
252
|
+
# Do not set @full_call when processing arguments
|
|
253
|
+
old_full_call = @full_call
|
|
254
|
+
@full_call = nil
|
|
255
|
+
|
|
242
256
|
process_call_args exp
|
|
243
257
|
|
|
244
258
|
@current_call = old_parent
|
|
259
|
+
@full_call = old_full_call
|
|
245
260
|
|
|
246
261
|
call_hash
|
|
247
262
|
end
|
|
@@ -98,7 +98,9 @@ module Brakeman::RenderHelper
|
|
|
98
98
|
|
|
99
99
|
if hash? options[:locals]
|
|
100
100
|
hash_iterate options[:locals] do |key, value|
|
|
101
|
-
|
|
101
|
+
if symbol? key
|
|
102
|
+
template_env[Sexp.new(:call, nil, key.value)] = value
|
|
103
|
+
end
|
|
102
104
|
end
|
|
103
105
|
end
|
|
104
106
|
|
data/lib/brakeman/report.rb
CHANGED
|
@@ -43,6 +43,8 @@ class Brakeman::Report
|
|
|
43
43
|
when :to_junit
|
|
44
44
|
require_report 'junit'
|
|
45
45
|
Brakeman::Report::JUnit
|
|
46
|
+
when :to_sarif
|
|
47
|
+
return self.to_sarif
|
|
46
48
|
else
|
|
47
49
|
raise "Invalid format: #{format}. Should be one of #{VALID_FORMATS.inspect}"
|
|
48
50
|
end
|
|
@@ -85,6 +87,11 @@ class Brakeman::Report
|
|
|
85
87
|
alias to_plain to_text
|
|
86
88
|
alias to_s to_text
|
|
87
89
|
|
|
90
|
+
def to_sarif
|
|
91
|
+
require_report 'sarif'
|
|
92
|
+
generate Brakeman::Report::SARIF
|
|
93
|
+
end
|
|
94
|
+
|
|
88
95
|
def generate reporter
|
|
89
96
|
reporter.new(@tracker).generate_report
|
|
90
97
|
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
class Brakeman::Report::SARIF < Brakeman::Report::Base
|
|
2
|
+
def generate_report
|
|
3
|
+
sarif_log = {
|
|
4
|
+
:version => '2.1.0',
|
|
5
|
+
:$schema => 'https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json',
|
|
6
|
+
:runs => runs,
|
|
7
|
+
}
|
|
8
|
+
JSON.pretty_generate sarif_log
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def runs
|
|
12
|
+
[
|
|
13
|
+
{
|
|
14
|
+
:tool => {
|
|
15
|
+
:driver => {
|
|
16
|
+
:name => 'Brakeman',
|
|
17
|
+
:informationUri => 'https://brakemanscanner.org',
|
|
18
|
+
:semanticVersion => Brakeman::Version,
|
|
19
|
+
:rules => rules,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
:results => results,
|
|
23
|
+
},
|
|
24
|
+
]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def rules
|
|
28
|
+
@rules ||= unique_warnings_by_warning_code.map do |warning|
|
|
29
|
+
rule_id = render_id warning
|
|
30
|
+
check_name = warning.check.gsub(/^Brakeman::Check/, '')
|
|
31
|
+
check_description = render_message check_descriptions[check_name]
|
|
32
|
+
{
|
|
33
|
+
:id => rule_id,
|
|
34
|
+
:name => "#{check_name}/#{warning.warning_type}",
|
|
35
|
+
:fullDescription => {
|
|
36
|
+
:text => check_description,
|
|
37
|
+
},
|
|
38
|
+
:helpUri => warning.link,
|
|
39
|
+
:help => {
|
|
40
|
+
:text => "More info: #{warning.link}.",
|
|
41
|
+
:markdown => "[More info](#{warning.link}).",
|
|
42
|
+
},
|
|
43
|
+
:properties => {
|
|
44
|
+
:tags => [check_name],
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def results
|
|
51
|
+
@results ||= all_warnings.map do |warning|
|
|
52
|
+
rule_id = render_id warning
|
|
53
|
+
result_level = infer_level warning
|
|
54
|
+
message_text = render_message warning.message.to_s
|
|
55
|
+
result = {
|
|
56
|
+
:ruleId => rule_id,
|
|
57
|
+
:ruleIndex => rules.index { |r| r[:id] == rule_id },
|
|
58
|
+
:level => result_level,
|
|
59
|
+
:message => {
|
|
60
|
+
:text => message_text,
|
|
61
|
+
},
|
|
62
|
+
:locations => [
|
|
63
|
+
:physicalLocation => {
|
|
64
|
+
:artifactLocation => {
|
|
65
|
+
:uri => warning.file.relative,
|
|
66
|
+
:uriBaseId => '%SRCROOT%',
|
|
67
|
+
},
|
|
68
|
+
:region => {
|
|
69
|
+
:startLine => warning.line.is_a?(Integer) ? warning.line : 1,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
result
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Returns a hash of all check descriptions, keyed by check namne
|
|
80
|
+
def check_descriptions
|
|
81
|
+
@check_descriptions ||= Brakeman::Checks.checks.map do |check|
|
|
82
|
+
[check.name.gsub(/^Check/, ''), check.description]
|
|
83
|
+
end.to_h
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Returns a de-duplicated set of warnings, used to generate rules
|
|
87
|
+
def unique_warnings_by_warning_code
|
|
88
|
+
@unique_warnings_by_warning_code ||= all_warnings.uniq { |w| w.warning_code }
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def render_id warning
|
|
92
|
+
# Include alpha prefix to provide 'compiler error' appearance
|
|
93
|
+
"BRAKE#{'%04d' % warning.warning_code}" # 46 becomes BRAKE0046, for example
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def render_message message
|
|
97
|
+
# Ensure message ends with a period
|
|
98
|
+
if message.end_with? "."
|
|
99
|
+
message
|
|
100
|
+
else
|
|
101
|
+
"#{message}."
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def infer_level warning
|
|
106
|
+
# Infer result level from warning confidence
|
|
107
|
+
@@levels_from_confidence ||= Hash.new('warning').update({
|
|
108
|
+
0 => 'error', # 0 represents 'high confidence', which we infer as 'error'
|
|
109
|
+
1 => 'warning', # 1 represents 'medium confidence' which we infer as 'warning'
|
|
110
|
+
2 => 'note', # 2 represents 'weak, or low, confidence', which we infer as 'note'
|
|
111
|
+
})
|
|
112
|
+
@@levels_from_confidence[warning.confidence]
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -145,24 +145,45 @@ class Brakeman::Report::Text < Brakeman::Report::Base
|
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
def output_warning w
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
148
|
+
text_format = tracker.options[:text_fields] ||
|
|
149
|
+
[:confidence, :category, :check, :message, :code, :file, :line]
|
|
150
|
+
|
|
151
|
+
text_format.map do |option|
|
|
152
|
+
format_line(w, option)
|
|
153
|
+
end.compact
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def format_line w, option
|
|
157
|
+
case option
|
|
158
|
+
when :confidence
|
|
159
|
+
label('Confidence', confidence(w.confidence))
|
|
160
|
+
when :category
|
|
161
|
+
label('Category', w.warning_type.to_s)
|
|
162
|
+
when :check
|
|
163
|
+
label('Check', w.check.gsub(/^Brakeman::Check/, ''))
|
|
164
|
+
when :message
|
|
152
165
|
label('Message', w.message)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
166
|
+
when :code
|
|
167
|
+
if w.code
|
|
168
|
+
label('Code', format_code(w))
|
|
169
|
+
end
|
|
170
|
+
when :file
|
|
171
|
+
label('File', warning_file(w))
|
|
172
|
+
when :line
|
|
173
|
+
if w.line
|
|
174
|
+
label('Line', w.line)
|
|
175
|
+
end
|
|
176
|
+
when :link
|
|
177
|
+
label('Link', w.link)
|
|
178
|
+
when :fingerprint
|
|
179
|
+
label('Fingerprint', w.fingerprint)
|
|
180
|
+
when :category_id
|
|
181
|
+
label('Category ID', w.warning_code)
|
|
182
|
+
when :render_path
|
|
183
|
+
if w.called_from
|
|
184
|
+
label('Render Path', w.called_from.join(" > "))
|
|
185
|
+
end
|
|
163
186
|
end
|
|
164
|
-
|
|
165
|
-
out
|
|
166
187
|
end
|
|
167
188
|
|
|
168
189
|
def double_space title, values
|
data/lib/brakeman/scanner.rb
CHANGED
|
@@ -94,11 +94,14 @@ class Brakeman::Scanner
|
|
|
94
94
|
#
|
|
95
95
|
#Stores parsed information in tracker.config
|
|
96
96
|
def process_config
|
|
97
|
+
# Sometimes folks like to put constants in environment.rb
|
|
98
|
+
# so let's always process it even for newer Rails versions
|
|
99
|
+
process_config_file "environment.rb"
|
|
100
|
+
|
|
97
101
|
if options[:rails3] or options[:rails4] or options[:rails5] or options[:rails6]
|
|
98
102
|
process_config_file "application.rb"
|
|
99
103
|
process_config_file "environments/production.rb"
|
|
100
104
|
else
|
|
101
|
-
process_config_file "environment.rb"
|
|
102
105
|
process_config_file "gems.rb"
|
|
103
106
|
end
|
|
104
107
|
|
data/lib/brakeman/tracker.rb
CHANGED
|
@@ -198,8 +198,10 @@ class Brakeman::Tracker
|
|
|
198
198
|
@constants.add name, value, context unless @options[:disable_constant_tracking]
|
|
199
199
|
end
|
|
200
200
|
|
|
201
|
+
# This method does not return all constants at this time,
|
|
202
|
+
# just ones with "simple" values.
|
|
201
203
|
def constant_lookup name
|
|
202
|
-
@constants.
|
|
204
|
+
@constants.get_simple_value name unless @options[:disable_constant_tracking]
|
|
203
205
|
end
|
|
204
206
|
|
|
205
207
|
def find_class name
|
|
@@ -54,7 +54,7 @@ module Brakeman
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def gem_version name
|
|
57
|
-
extract_version @gems.dig(name, :version)
|
|
57
|
+
extract_version @gems.dig(name.to_sym, :version)
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def add_gem name, version, file, line
|
|
@@ -67,11 +67,11 @@ module Brakeman
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def has_gem? name
|
|
70
|
-
!!@gems[name]
|
|
70
|
+
!!@gems[name.to_sym]
|
|
71
71
|
end
|
|
72
72
|
|
|
73
73
|
def get_gem name
|
|
74
|
-
@gems[name]
|
|
74
|
+
@gems[name.to_sym]
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
def set_rails_version version = nil
|
|
@@ -79,7 +79,9 @@ module Brakeman
|
|
|
79
79
|
# Only used by Rails2ConfigProcessor right now
|
|
80
80
|
extract_version(version)
|
|
81
81
|
else
|
|
82
|
-
gem_version(:rails) ||
|
|
82
|
+
gem_version(:rails) ||
|
|
83
|
+
gem_version(:railties) ||
|
|
84
|
+
gem_version(:activerecord)
|
|
83
85
|
end
|
|
84
86
|
|
|
85
87
|
if version
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
require 'brakeman/processors/output_processor'
|
|
2
|
+
require 'brakeman/util'
|
|
2
3
|
|
|
3
4
|
module Brakeman
|
|
4
5
|
class Constant
|
|
6
|
+
include Brakeman::Util
|
|
7
|
+
|
|
5
8
|
attr_reader :name, :name_array, :file, :value, :context
|
|
6
9
|
|
|
7
10
|
def initialize name, value, context = {}
|
|
@@ -107,13 +110,11 @@ module Brakeman
|
|
|
107
110
|
@constants[base_name] << Constant.new(name, value, context)
|
|
108
111
|
end
|
|
109
112
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def get_literal name
|
|
116
|
-
if x = self[name] and literal? x
|
|
113
|
+
# Returns constant values that are not too complicated.
|
|
114
|
+
# Right now that means literal values (string, array, etc.)
|
|
115
|
+
# or calls on Dir.glob(..).whatever.
|
|
116
|
+
def get_simple_value name
|
|
117
|
+
if x = self[name] and (literal? x or dir_glob? x)
|
|
117
118
|
x
|
|
118
119
|
else
|
|
119
120
|
nil
|
data/lib/brakeman/util.rb
CHANGED
|
@@ -293,6 +293,22 @@ module Brakeman::Util
|
|
|
293
293
|
exp.is_a? Sexp and types.include? exp.node_type
|
|
294
294
|
end
|
|
295
295
|
|
|
296
|
+
LITERALS = [:lit, :false, :str, :true, :array, :hash]
|
|
297
|
+
|
|
298
|
+
def literal? exp
|
|
299
|
+
exp.is_a? Sexp and LITERALS.include? exp.node_type
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
DIR_CONST = s(:const, :Dir)
|
|
303
|
+
|
|
304
|
+
# Dir.glob(...).whatever
|
|
305
|
+
def dir_glob? exp
|
|
306
|
+
exp = exp.block_call if node_type? exp, :iter
|
|
307
|
+
return unless call? exp
|
|
308
|
+
|
|
309
|
+
(exp.target == DIR_CONST and exp.method == :glob) or dir_glob? exp.target
|
|
310
|
+
end
|
|
311
|
+
|
|
296
312
|
#Returns true if the given _exp_ contains a :class node.
|
|
297
313
|
#
|
|
298
314
|
#Useful for checking if a module is just a module or if it is a namespace.
|
data/lib/brakeman/version.rb
CHANGED
|
@@ -113,6 +113,13 @@ module Brakeman::WarningCodes
|
|
|
113
113
|
:force_ssl_disabled => 109,
|
|
114
114
|
:unsafe_cookie_serialization => 110,
|
|
115
115
|
:reverse_tabnabbing => 111,
|
|
116
|
+
:mass_assign_permit_all => 112,
|
|
117
|
+
:json_html_escape_config => 113,
|
|
118
|
+
:json_html_escape_module => 114,
|
|
119
|
+
:CVE_2020_8159 => 115,
|
|
120
|
+
:CVE_2020_8166 => 116,
|
|
121
|
+
:erb_template_injection => 117,
|
|
122
|
+
|
|
116
123
|
:custom_check => 9090,
|
|
117
124
|
}
|
|
118
125
|
|
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.
|
|
4
|
+
version: 4.10.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: 2020-
|
|
11
|
+
date: 2020-09-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|
|
@@ -52,6 +52,20 @@ dependencies:
|
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: simplecov-html
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - '='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 0.10.2
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - '='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 0.10.2
|
|
55
69
|
- !ruby/object:Gem::Dependency
|
|
56
70
|
name: ruby_parser
|
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -170,14 +184,14 @@ dependencies:
|
|
|
170
184
|
requirements:
|
|
171
185
|
- - "~>"
|
|
172
186
|
- !ruby/object:Gem::Version
|
|
173
|
-
version:
|
|
187
|
+
version: 5.1.0
|
|
174
188
|
type: :runtime
|
|
175
189
|
prerelease: false
|
|
176
190
|
version_requirements: !ruby/object:Gem::Requirement
|
|
177
191
|
requirements:
|
|
178
192
|
- - "~>"
|
|
179
193
|
- !ruby/object:Gem::Version
|
|
180
|
-
version:
|
|
194
|
+
version: 5.1.0
|
|
181
195
|
- !ruby/object:Gem::Dependency
|
|
182
196
|
name: slim
|
|
183
197
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -187,7 +201,7 @@ dependencies:
|
|
|
187
201
|
version: 1.3.6
|
|
188
202
|
- - "<="
|
|
189
203
|
- !ruby/object:Gem::Version
|
|
190
|
-
version: 4.
|
|
204
|
+
version: '4.1'
|
|
191
205
|
type: :runtime
|
|
192
206
|
prerelease: false
|
|
193
207
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -197,7 +211,7 @@ dependencies:
|
|
|
197
211
|
version: 1.3.6
|
|
198
212
|
- - "<="
|
|
199
213
|
- !ruby/object:Gem::Version
|
|
200
|
-
version: 4.
|
|
214
|
+
version: '4.1'
|
|
201
215
|
description: Brakeman detects security vulnerabilities in Ruby on Rails applications
|
|
202
216
|
via static analysis. This package declares gem dependencies instead of bundling
|
|
203
217
|
them.
|
|
@@ -222,6 +236,7 @@ files:
|
|
|
222
236
|
- lib/brakeman/checks/check_cookie_serialization.rb
|
|
223
237
|
- lib/brakeman/checks/check_create_with.rb
|
|
224
238
|
- lib/brakeman/checks/check_cross_site_scripting.rb
|
|
239
|
+
- lib/brakeman/checks/check_csrf_token_forgery_cve.rb
|
|
225
240
|
- lib/brakeman/checks/check_default_routes.rb
|
|
226
241
|
- lib/brakeman/checks/check_deserialize.rb
|
|
227
242
|
- lib/brakeman/checks/check_detailed_exceptions.rb
|
|
@@ -240,6 +255,7 @@ files:
|
|
|
240
255
|
- lib/brakeman/checks/check_i18n_xss.rb
|
|
241
256
|
- lib/brakeman/checks/check_jruby_xml.rb
|
|
242
257
|
- lib/brakeman/checks/check_json_encoding.rb
|
|
258
|
+
- lib/brakeman/checks/check_json_entity_escape.rb
|
|
243
259
|
- lib/brakeman/checks/check_json_parsing.rb
|
|
244
260
|
- lib/brakeman/checks/check_link_to.rb
|
|
245
261
|
- lib/brakeman/checks/check_link_to_href.rb
|
|
@@ -252,6 +268,7 @@ files:
|
|
|
252
268
|
- lib/brakeman/checks/check_nested_attributes.rb
|
|
253
269
|
- lib/brakeman/checks/check_nested_attributes_bypass.rb
|
|
254
270
|
- lib/brakeman/checks/check_number_to_currency.rb
|
|
271
|
+
- lib/brakeman/checks/check_page_caching_cve.rb
|
|
255
272
|
- lib/brakeman/checks/check_permit_attributes.rb
|
|
256
273
|
- lib/brakeman/checks/check_quote_table_name.rb
|
|
257
274
|
- lib/brakeman/checks/check_redirect.rb
|
|
@@ -281,6 +298,7 @@ files:
|
|
|
281
298
|
- lib/brakeman/checks/check_strip_tags.rb
|
|
282
299
|
- lib/brakeman/checks/check_symbol_dos.rb
|
|
283
300
|
- lib/brakeman/checks/check_symbol_dos_cve.rb
|
|
301
|
+
- lib/brakeman/checks/check_template_injection.rb
|
|
284
302
|
- lib/brakeman/checks/check_translate_bug.rb
|
|
285
303
|
- lib/brakeman/checks/check_unsafe_reflection.rb
|
|
286
304
|
- lib/brakeman/checks/check_unscoped_find.rb
|
|
@@ -350,6 +368,7 @@ files:
|
|
|
350
368
|
- lib/brakeman/report/report_json.rb
|
|
351
369
|
- lib/brakeman/report/report_junit.rb
|
|
352
370
|
- lib/brakeman/report/report_markdown.rb
|
|
371
|
+
- lib/brakeman/report/report_sarif.rb
|
|
353
372
|
- lib/brakeman/report/report_table.rb
|
|
354
373
|
- lib/brakeman/report/report_tabs.rb
|
|
355
374
|
- lib/brakeman/report/report_text.rb
|