brakeman-lib 4.0.1 → 4.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 +4 -4
- data/{CHANGES → CHANGES.md} +23 -0
- data/README.md +2 -2
- data/lib/brakeman.rb +3 -30
- data/lib/brakeman/checks/base_check.rb +5 -30
- data/lib/brakeman/checks/check_content_tag.rb +1 -1
- data/lib/brakeman/checks/check_divide_by_zero.rb +40 -0
- data/lib/brakeman/checks/check_file_access.rb +10 -1
- data/lib/brakeman/checks/check_forgery_setting.rb +2 -0
- data/lib/brakeman/checks/check_permit_attributes.rb +43 -0
- data/lib/brakeman/checks/check_redirect.rb +21 -2
- data/lib/brakeman/checks/check_sql.rb +5 -1
- data/lib/brakeman/codeclimate/engine_configuration.rb +97 -0
- data/lib/brakeman/parsers/template_parser.rb +7 -3
- data/lib/brakeman/processor.rb +1 -1
- data/lib/brakeman/processors/alias_processor.rb +34 -17
- data/lib/brakeman/report/pager.rb +110 -0
- data/lib/brakeman/report/report_codeclimate.rb +10 -1
- data/lib/brakeman/tracker/config.rb +44 -0
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman/warning.rb +2 -2
- data/lib/brakeman/warning_codes.rb +2 -0
- data/lib/ruby_parser/bm_sexp.rb +16 -0
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 25a56e16b745a1e4aff57461dc3b346db0c20a16
|
|
4
|
+
data.tar.gz: 998d68b4ab7e3ed1b288357ef101639eecc5c5bd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 623b9942d544ffa2671eef833f9cefebff610f1a1574f2e55b1c60336eb327b26f20413f900db982940c21f53d0ab91576afd0b1b0bb009dec9165392019d9dd
|
|
7
|
+
data.tar.gz: f3efcb07b4535768e48353bd72c36718137222ad8aeae2262b6fa69c88aef1899be5be6fbe28cd467c5bc43543b6924773ed5d3d73d6d439495e6fe17e64c457
|
data/{CHANGES → CHANGES.md}
RENAMED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
# 4.1.0
|
|
2
|
+
|
|
3
|
+
* Process models as root sexp instead of each sexp
|
|
4
|
+
* Avoid CSRF warning in Rails 5.2 default config
|
|
5
|
+
* Show better location for Sass errors (Andrew Bromwich)
|
|
6
|
+
* Warn about dynamic values in `Arel.sql`
|
|
7
|
+
* Fix `include_paths` for Code Climate engine (Will Fleming)
|
|
8
|
+
* Add check for dangerous keys in `permit`
|
|
9
|
+
* Try to guess options for `less` pager
|
|
10
|
+
* Better processing of op_asgn1 (e.g. x[:y] += 1)
|
|
11
|
+
* Add optional check for divide by zero
|
|
12
|
+
* Remove errors about divide by zero
|
|
13
|
+
* Avoid warning about file access for temp files
|
|
14
|
+
* Do not warn on params.permit with safe values
|
|
15
|
+
* Add Sexp#call_chain
|
|
16
|
+
* Use HTTPS for warning links
|
|
17
|
+
* Handle nested destructuring/multiple assignment
|
|
18
|
+
* Leave results on screen after paging
|
|
19
|
+
* Do not page if results fit on screen
|
|
20
|
+
* Support `app_path` configuration for Code Climate engine (Noah Davis)
|
|
21
|
+
* Refactor Code Climate engine options parsing (Noah Davis)
|
|
22
|
+
* Fix upgrade version for CVE-2016-6316
|
|
23
|
+
|
|
1
24
|
# 4.0.1
|
|
2
25
|
|
|
3
26
|
* Disable pager when `CI` environment variable is set
|
data/README.md
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
[](https://brakemanpro.com)
|
|
3
3
|
|
|
4
4
|
[](https://travis-ci.org/presidentbeef/brakeman)
|
|
5
|
-
[](https://codeclimate.com/github/presidentbeef/brakeman/maintainability)
|
|
6
|
+
[](https://codeclimate.com/github/presidentbeef/brakeman/test_coverage)
|
|
7
7
|
[](https://gitter.im/presidentbeef/brakeman)
|
|
8
8
|
|
|
9
9
|
# Brakeman
|
data/lib/brakeman.rb
CHANGED
|
@@ -402,39 +402,12 @@ module Brakeman
|
|
|
402
402
|
puts tracker.report.format(output_format)
|
|
403
403
|
end
|
|
404
404
|
else
|
|
405
|
-
|
|
406
|
-
end
|
|
407
|
-
end
|
|
408
|
-
private_class_method :write_report_to_formats
|
|
409
|
-
|
|
410
|
-
def self.page_output text
|
|
411
|
-
ci = ENV["CI"]
|
|
405
|
+
require "brakeman/report/pager"
|
|
412
406
|
|
|
413
|
-
|
|
414
|
-
puts text
|
|
415
|
-
elsif system("which less")
|
|
416
|
-
# Adapted from https://github.com/piotrmurach/tty-pager/
|
|
417
|
-
write_io = open("|less -R", 'w')
|
|
418
|
-
pid = write_io.pid
|
|
419
|
-
|
|
420
|
-
write_io.write(text)
|
|
421
|
-
write_io.close
|
|
422
|
-
|
|
423
|
-
Process.waitpid2(pid, Process::WNOHANG)
|
|
424
|
-
else
|
|
425
|
-
load_brakeman_dependency 'highline'
|
|
426
|
-
h = ::HighLine.new
|
|
427
|
-
h.page_at = :auto
|
|
428
|
-
h.say text
|
|
407
|
+
Brakeman::Pager.new(tracker).page_report(tracker.report, output_formats.first)
|
|
429
408
|
end
|
|
430
|
-
rescue Errno::ECHILD
|
|
431
|
-
# on jruby 9x waiting on pid raises (per tty-pager)
|
|
432
|
-
true
|
|
433
|
-
rescue => e
|
|
434
|
-
warn "[Error] #{e}"
|
|
435
|
-
warn "[Error] Could not use pager. Set --no-pager to avoid this issue."
|
|
436
|
-
puts text
|
|
437
409
|
end
|
|
410
|
+
private_class_method :write_report_to_formats
|
|
438
411
|
|
|
439
412
|
#Rescan a subset of files in a Rails application.
|
|
440
413
|
#
|
|
@@ -445,41 +445,16 @@ class Brakeman::BaseCheck < Brakeman::SexpProcessor
|
|
|
445
445
|
false
|
|
446
446
|
end
|
|
447
447
|
|
|
448
|
-
#Returns true if low_version <= RAILS_VERSION <= high_version
|
|
449
|
-
#
|
|
450
|
-
#If the Rails version is unknown, returns false.
|
|
451
|
-
def version_between? low_version, high_version, current_version = nil
|
|
452
|
-
current_version ||= rails_version
|
|
453
|
-
return false unless current_version
|
|
454
|
-
|
|
455
|
-
version = current_version.split(".").map! { |n| n.to_i }
|
|
456
|
-
low_version = low_version.split(".").map! { |n| n.to_i }
|
|
457
|
-
high_version = high_version.split(".").map! { |n| n.to_i }
|
|
458
|
-
|
|
459
|
-
version.each_with_index do |v, i|
|
|
460
|
-
if v < low_version.fetch(i, 0)
|
|
461
|
-
return false
|
|
462
|
-
elsif v > low_version.fetch(i, 0)
|
|
463
|
-
break
|
|
464
|
-
end
|
|
465
|
-
end
|
|
466
|
-
|
|
467
|
-
version.each_with_index do |v, i|
|
|
468
|
-
if v > high_version.fetch(i, 0)
|
|
469
|
-
return false
|
|
470
|
-
elsif v < high_version.fetch(i, 0)
|
|
471
|
-
break
|
|
472
|
-
end
|
|
473
|
-
end
|
|
474
|
-
|
|
475
|
-
true
|
|
476
|
-
end
|
|
477
|
-
|
|
478
448
|
def lts_version? version
|
|
479
449
|
tracker.config.has_gem? :'railslts-version' and
|
|
480
450
|
version_between? version, "2.3.18.99", tracker.config.gem_version(:'railslts-version')
|
|
481
451
|
end
|
|
482
452
|
|
|
453
|
+
|
|
454
|
+
def version_between? low_version, high_version, current_version = nil
|
|
455
|
+
tracker.config.version_between? low_version, high_version, current_version
|
|
456
|
+
end
|
|
457
|
+
|
|
483
458
|
def gemfile_or_environment gem_name = :rails
|
|
484
459
|
if gem_name and info = tracker.config.get_gem(gem_name)
|
|
485
460
|
info
|
|
@@ -170,7 +170,7 @@ class Brakeman::CheckContentTag < Brakeman::CheckCrossSiteScripting
|
|
|
170
170
|
when version_between?("4.0.0", "4.2.7.0")
|
|
171
171
|
"4.2.7.1"
|
|
172
172
|
when version_between?("5.0.0", "5.0.0")
|
|
173
|
-
"5.0.0"
|
|
173
|
+
"5.0.0.1"
|
|
174
174
|
when (version.nil? and tracker.options[:rails3])
|
|
175
175
|
"3.2.22.4"
|
|
176
176
|
when (version.nil? and tracker.options[:rails4])
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckDivideByZero < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add_optional self
|
|
5
|
+
|
|
6
|
+
@description = "Warns on potential division by zero"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
tracker.find_call(:method => :"/").each do |result|
|
|
10
|
+
check_division result
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def check_division result
|
|
15
|
+
call = result[:call]
|
|
16
|
+
|
|
17
|
+
denominator = call.first_arg
|
|
18
|
+
|
|
19
|
+
if number? denominator and denominator.value == 0
|
|
20
|
+
numerator = call.target
|
|
21
|
+
|
|
22
|
+
if number? numerator
|
|
23
|
+
if numerator.value.is_a? Float
|
|
24
|
+
return # 0.0 / 0 is NaN and 1.0 / 0 is Infinity
|
|
25
|
+
else
|
|
26
|
+
confidence = :medium
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
confidence = :weak
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
warn :result => result,
|
|
33
|
+
:warning_type => "Divide by Zero",
|
|
34
|
+
:warning_code => :divide_by_zero,
|
|
35
|
+
:message => "Potential division by zero",
|
|
36
|
+
:confidence => confidence,
|
|
37
|
+
:user_input => denominator
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -47,7 +47,8 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
|
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
-
if match
|
|
50
|
+
if match and not temp_file? match.match
|
|
51
|
+
|
|
51
52
|
message = "#{friendly_type_of(match).capitalize} used in file name"
|
|
52
53
|
|
|
53
54
|
warn :result => result,
|
|
@@ -59,4 +60,12 @@ class Brakeman::CheckFileAccess < Brakeman::BaseCheck
|
|
|
59
60
|
:user_input => match
|
|
60
61
|
end
|
|
61
62
|
end
|
|
63
|
+
|
|
64
|
+
def temp_file? exp
|
|
65
|
+
if call? exp
|
|
66
|
+
return true if exp.call_chain.include? :tempfile
|
|
67
|
+
|
|
68
|
+
params? exp.target and exp.method == :path
|
|
69
|
+
end
|
|
70
|
+
end
|
|
62
71
|
end
|
|
@@ -10,6 +10,8 @@ class Brakeman::CheckForgerySetting < Brakeman::BaseCheck
|
|
|
10
10
|
@description = "Verifies that protect_from_forgery is enabled in direct subclasses of ActionController::Base"
|
|
11
11
|
|
|
12
12
|
def run_check
|
|
13
|
+
return if tracker.config.default_protect_from_forgery?
|
|
14
|
+
|
|
13
15
|
tracker.controllers
|
|
14
16
|
.select { |_, controller| controller.parent == :"ActionController::Base" }
|
|
15
17
|
.each do |name, controller|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'brakeman/checks/base_check'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckPermitAttributes < Brakeman::BaseCheck
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Warn on potentially dangerous attributes whitelisted via permit"
|
|
7
|
+
|
|
8
|
+
SUSPICIOUS_KEYS = {
|
|
9
|
+
admin: :high,
|
|
10
|
+
account_id: :high,
|
|
11
|
+
role: :medium,
|
|
12
|
+
banned: :medium,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def run_check
|
|
16
|
+
tracker.find_call(:method => :permit).each do |result|
|
|
17
|
+
check_permit result
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def check_permit result
|
|
22
|
+
call = result[:call]
|
|
23
|
+
|
|
24
|
+
call.each_arg do |arg|
|
|
25
|
+
if symbol? arg
|
|
26
|
+
if SUSPICIOUS_KEYS.key? arg.value
|
|
27
|
+
warn_on_permit_key result, arg
|
|
28
|
+
elsif arg.value.match /_id$/
|
|
29
|
+
warn_on_permit_key result, arg, :medium
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def warn_on_permit_key result, key, confidence = nil
|
|
36
|
+
warn :result => result,
|
|
37
|
+
:warning_type => "Mass Assignment",
|
|
38
|
+
:warning_code => :dangerous_permit_key,
|
|
39
|
+
:message => "Potentially dangerous key allowed for mass assignment",
|
|
40
|
+
:confidence => (confidence || SUSPICIOUS_KEYS[key.value]),
|
|
41
|
+
:user_input => key
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -34,10 +34,13 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
|
34
34
|
call = result[:call]
|
|
35
35
|
method = call.method
|
|
36
36
|
|
|
37
|
+
opt = call.first_arg
|
|
38
|
+
|
|
37
39
|
if method == :redirect_to and
|
|
38
40
|
not only_path?(call) and
|
|
39
|
-
not explicit_host?(
|
|
40
|
-
not slice_call?(
|
|
41
|
+
not explicit_host?(opt) and
|
|
42
|
+
not slice_call?(opt) and
|
|
43
|
+
not safe_permit?(opt) and
|
|
41
44
|
res = include_user_input?(call)
|
|
42
45
|
|
|
43
46
|
if res.type == :immediate
|
|
@@ -232,4 +235,20 @@ class Brakeman::CheckRedirect < Brakeman::BaseCheck
|
|
|
232
235
|
return unless call? exp
|
|
233
236
|
exp.method == :slice
|
|
234
237
|
end
|
|
238
|
+
|
|
239
|
+
DANGEROUS_KEYS = [:host, :subdomain, :domain, :port]
|
|
240
|
+
|
|
241
|
+
def safe_permit? exp
|
|
242
|
+
if call? exp and params? exp.target and exp.method == :permit
|
|
243
|
+
exp.each_arg do |opt|
|
|
244
|
+
if symbol? opt and DANGEROUS_KEYS.include? opt.value
|
|
245
|
+
return false
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
return true
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
false
|
|
253
|
+
end
|
|
235
254
|
end
|
|
@@ -38,7 +38,7 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
|
38
38
|
@connection_calls.concat [:add_limit!, :add_offset_limit!, :add_lock!]
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
@expected_targets = active_record_models.keys + [:connection, :"ActiveRecord::Base"]
|
|
41
|
+
@expected_targets = active_record_models.keys + [:connection, :"ActiveRecord::Base", :Arel]
|
|
42
42
|
|
|
43
43
|
Brakeman.debug "Finding possible SQL calls on models"
|
|
44
44
|
calls = tracker.find_call(:methods => @sql_targets, :nested => true)
|
|
@@ -53,6 +53,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
|
53
53
|
|
|
54
54
|
calls.concat tracker.find_call(:targets => @expected_targets, :methods => @connection_calls, :chained => true).select { |result| connect_call? result }
|
|
55
55
|
|
|
56
|
+
calls.concat tracker.find_call(:target => :Arel, :method => :sql)
|
|
57
|
+
|
|
56
58
|
Brakeman.debug "Finding calls to named_scope or scope"
|
|
57
59
|
calls.concat find_scope_calls
|
|
58
60
|
|
|
@@ -194,6 +196,8 @@ class Brakeman::CheckSQL < Brakeman::BaseCheck
|
|
|
194
196
|
check_lock_arguments call.first_arg
|
|
195
197
|
when :pluck
|
|
196
198
|
unsafe_sql? call.first_arg
|
|
199
|
+
when :sql
|
|
200
|
+
unsafe_sql? call.first_arg
|
|
197
201
|
when :update_all, :select
|
|
198
202
|
check_update_all_arguments call.args
|
|
199
203
|
when *@connection_calls
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
module Brakeman
|
|
4
|
+
module Codeclimate
|
|
5
|
+
class EngineConfiguration
|
|
6
|
+
|
|
7
|
+
def initialize(engine_config = {})
|
|
8
|
+
@engine_config = engine_config
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def options
|
|
12
|
+
default_options.merge(configured_options)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
attr_reader :engine_config
|
|
18
|
+
|
|
19
|
+
def default_options
|
|
20
|
+
@default_options = {
|
|
21
|
+
:output_format => :codeclimate,
|
|
22
|
+
:quiet => true,
|
|
23
|
+
:pager => false,
|
|
24
|
+
:app_path => Dir.pwd
|
|
25
|
+
}
|
|
26
|
+
if system("test -w /dev/stdout")
|
|
27
|
+
@default_options[:output_files] = ["/dev/stdout"]
|
|
28
|
+
end
|
|
29
|
+
@default_options
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def configured_options
|
|
33
|
+
@configured_options = {}
|
|
34
|
+
# ATM this gets parsed as a string instead of bool: "config":{ "debug":"true" }
|
|
35
|
+
if brakeman_configuration["debug"] && brakeman_configuration["debug"].to_s.downcase == "true"
|
|
36
|
+
@configured_options[:debug] = true
|
|
37
|
+
@configured_options[:report_progress] = false
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
if active_include_paths
|
|
41
|
+
@configured_options[:only_files] = active_include_paths
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if brakeman_configuration["app_path"]
|
|
45
|
+
@configured_options[:path_prefix] = brakeman_configuration["app_path"]
|
|
46
|
+
path = Pathname.new(Dir.pwd) + brakeman_configuration["app_path"]
|
|
47
|
+
@configured_options[:app_path] = path.to_s
|
|
48
|
+
end
|
|
49
|
+
@configured_options
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def brakeman_configuration
|
|
53
|
+
if engine_config["config"]
|
|
54
|
+
engine_config["config"]
|
|
55
|
+
else
|
|
56
|
+
{}
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def active_include_paths
|
|
61
|
+
@active_include_paths ||=
|
|
62
|
+
if brakeman_configuration["app_path"]
|
|
63
|
+
stripped_include_paths(brakeman_configuration["app_path"])
|
|
64
|
+
else
|
|
65
|
+
engine_config["include_paths"] && engine_config["include_paths"].compact
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def stripped_include_paths(prefix)
|
|
70
|
+
subprefixes = path_subprefixes(prefix)
|
|
71
|
+
engine_config["include_paths"] && engine_config["include_paths"].map do |path|
|
|
72
|
+
next unless path
|
|
73
|
+
stripped_include_path(prefix, subprefixes, path)
|
|
74
|
+
end.compact
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def path_subprefixes(path)
|
|
78
|
+
Pathname.new(path).each_filename.inject([]) do |memo, piece|
|
|
79
|
+
memo <<
|
|
80
|
+
if memo.any?
|
|
81
|
+
File.join(memo.last, piece)
|
|
82
|
+
else
|
|
83
|
+
File.join(piece)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def stripped_include_path(prefix, subprefixes, path)
|
|
89
|
+
if path.start_with?(prefix)
|
|
90
|
+
path.sub(%r{^#{prefix}/?}, "./")
|
|
91
|
+
elsif subprefixes.any? { |subprefix| path =~ %r{^#{subprefix}/?$} }
|
|
92
|
+
"./"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -36,9 +36,7 @@ module Brakeman
|
|
|
36
36
|
@file_parser.file_list[:templates] << TemplateFile.new(path, ast, name, type)
|
|
37
37
|
end
|
|
38
38
|
rescue Racc::ParseError => e
|
|
39
|
-
tracker.error e, "
|
|
40
|
-
rescue Haml::Error => e
|
|
41
|
-
tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
|
|
39
|
+
tracker.error e, "Could not parse #{path}"
|
|
42
40
|
rescue StandardError, LoadError => e
|
|
43
41
|
tracker.error e.exception(e.message + "\nWhile processing #{path}"), e.backtrace
|
|
44
42
|
end
|
|
@@ -78,6 +76,12 @@ module Brakeman
|
|
|
78
76
|
Haml::Engine.new(text,
|
|
79
77
|
:filename => path,
|
|
80
78
|
:escape_html => tracker.config.escape_html?).precompiled.gsub(/([^\\])\\n/, '\1')
|
|
79
|
+
rescue Haml::Error => e
|
|
80
|
+
tracker.error e, ["While compiling HAML in #{path}"] << e.backtrace
|
|
81
|
+
nil
|
|
82
|
+
rescue Sass::SyntaxError => e
|
|
83
|
+
tracker.error e, "While processing #{path}"
|
|
84
|
+
nil
|
|
81
85
|
end
|
|
82
86
|
|
|
83
87
|
def parse_slim path, text
|
data/lib/brakeman/processor.rb
CHANGED
|
@@ -54,7 +54,7 @@ module Brakeman
|
|
|
54
54
|
#Process a model source
|
|
55
55
|
def process_model src, file_name
|
|
56
56
|
result = ModelProcessor.new(@tracker).process_model src, file_name
|
|
57
|
-
AliasProcessor.new(@tracker).
|
|
57
|
+
AliasProcessor.new(@tracker).process result if result
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
#Process either an ERB or HAML template
|
|
@@ -235,14 +235,7 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
|
235
235
|
end
|
|
236
236
|
when :/
|
|
237
237
|
if number? target and number? first_arg
|
|
238
|
-
|
|
239
|
-
if @tracker
|
|
240
|
-
location = [@current_class, @current_method, "line #{first_arg.line}"].compact.join(' ')
|
|
241
|
-
require 'brakeman/processors/output_processor'
|
|
242
|
-
code = Brakeman::OutputProcessor.new.format(exp)
|
|
243
|
-
@tracker.error Exception.new("Potential divide by zero: #{code} (#{location})")
|
|
244
|
-
end
|
|
245
|
-
else
|
|
238
|
+
unless first_arg.value == 0 and not target.value.is_a? Float
|
|
246
239
|
exp = Sexp.new(:lit, target.value / first_arg.value)
|
|
247
240
|
end
|
|
248
241
|
end
|
|
@@ -500,6 +493,12 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
|
500
493
|
#
|
|
501
494
|
# x, y = z, w
|
|
502
495
|
def process_masgn exp
|
|
496
|
+
exp[2] = process exp[2] if sexp? exp[2]
|
|
497
|
+
|
|
498
|
+
if node_type? exp[2], :to_ary and array? exp[2][1]
|
|
499
|
+
exp[2] = exp[2][1]
|
|
500
|
+
end
|
|
501
|
+
|
|
503
502
|
unless array? exp[1] and array? exp[2] and exp[1].length == exp[2].length
|
|
504
503
|
return process_default(exp)
|
|
505
504
|
end
|
|
@@ -514,9 +513,20 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
|
514
513
|
vars.each_with_index do |var, i|
|
|
515
514
|
val = vals[i]
|
|
516
515
|
if val
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
516
|
+
|
|
517
|
+
# This happens with nested destructuring like
|
|
518
|
+
# x, (a, b) = blah
|
|
519
|
+
if node_type? var, :masgn
|
|
520
|
+
# Need to add value to masgn exp
|
|
521
|
+
m = var.dup
|
|
522
|
+
m[2] = s(:to_ary, val)
|
|
523
|
+
|
|
524
|
+
process_masgn m
|
|
525
|
+
else
|
|
526
|
+
assign = var.dup
|
|
527
|
+
assign.rhs = val
|
|
528
|
+
process assign
|
|
529
|
+
end
|
|
520
530
|
end
|
|
521
531
|
end
|
|
522
532
|
|
|
@@ -550,19 +560,26 @@ class Brakeman::AliasProcessor < Brakeman::SexpProcessor
|
|
|
550
560
|
#Assignments like this
|
|
551
561
|
# x[:y] ||= 1
|
|
552
562
|
def process_op_asgn1 exp
|
|
553
|
-
|
|
563
|
+
target_var = exp[1]
|
|
564
|
+
target_var &&= target_var.deep_clone
|
|
554
565
|
|
|
555
566
|
target = exp[1] = process(exp[1])
|
|
556
567
|
index = exp[2][1] = process(exp[2][1])
|
|
557
568
|
value = exp[4] = process(exp[4])
|
|
558
569
|
match = Sexp.new(:call, target, :[], index)
|
|
559
570
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
571
|
+
if exp[3] == :"||"
|
|
572
|
+
unless env[match]
|
|
573
|
+
if request_value? target
|
|
574
|
+
env[match] = match.combine(value)
|
|
575
|
+
else
|
|
576
|
+
env[match] = value
|
|
577
|
+
end
|
|
565
578
|
end
|
|
579
|
+
else
|
|
580
|
+
new_value = process s(:call, s(:call, target_var, :[], index), exp[3], value)
|
|
581
|
+
|
|
582
|
+
env[match] = new_value
|
|
566
583
|
end
|
|
567
584
|
|
|
568
585
|
exp
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module Brakeman
|
|
2
|
+
class Pager
|
|
3
|
+
def initialize tracker, pager = :less, output = $stdout
|
|
4
|
+
@tracker = tracker
|
|
5
|
+
@pager = pager
|
|
6
|
+
@output = output
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def page_report report, format
|
|
10
|
+
if @pager == :less
|
|
11
|
+
set_color
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
text = report.format(format)
|
|
15
|
+
|
|
16
|
+
if in_ci?
|
|
17
|
+
no_pager text
|
|
18
|
+
else
|
|
19
|
+
page_output text
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def page_output text
|
|
24
|
+
case @pager
|
|
25
|
+
when :none
|
|
26
|
+
no_pager text
|
|
27
|
+
when :highline
|
|
28
|
+
page_via_highline text
|
|
29
|
+
when :less
|
|
30
|
+
if less_available?
|
|
31
|
+
page_via_less text
|
|
32
|
+
else
|
|
33
|
+
page_via_highline text
|
|
34
|
+
end
|
|
35
|
+
else
|
|
36
|
+
no_pager text
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def no_pager text
|
|
41
|
+
@output.puts text
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def page_via_highline text
|
|
45
|
+
Brakeman.load_brakeman_dependency 'highline'
|
|
46
|
+
h = ::HighLine.new($stdin, @output)
|
|
47
|
+
h.page_at = :auto
|
|
48
|
+
h.say text
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def page_via_less text
|
|
52
|
+
# Adapted from https://github.com/piotrmurach/tty-pager/
|
|
53
|
+
|
|
54
|
+
write_io = open("|less #{less_options.join}", 'w')
|
|
55
|
+
pid = write_io.pid
|
|
56
|
+
|
|
57
|
+
write_io.write(text)
|
|
58
|
+
write_io.close
|
|
59
|
+
|
|
60
|
+
Process.waitpid2(pid, Process::WNOHANG)
|
|
61
|
+
rescue Errno::ECHILD
|
|
62
|
+
# on jruby 9x waiting on pid raises (per tty-pager)
|
|
63
|
+
true
|
|
64
|
+
rescue => e
|
|
65
|
+
warn "[Error] #{e}"
|
|
66
|
+
warn "[Error] Could not use pager. Set --no-pager to avoid this issue."
|
|
67
|
+
no_pager text
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def in_ci?
|
|
71
|
+
ci = ENV["CI"]
|
|
72
|
+
|
|
73
|
+
ci.is_a? String and ci.downcase == "true"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def less_available?
|
|
77
|
+
return @less_available unless @less_available.nil?
|
|
78
|
+
|
|
79
|
+
@less_available = system("which less > /dev/null")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def less_options
|
|
83
|
+
# -R show colors
|
|
84
|
+
# -F exit if output fits on one screen
|
|
85
|
+
# -X do not clear screen after less exits
|
|
86
|
+
|
|
87
|
+
return @less_options if @less_options
|
|
88
|
+
|
|
89
|
+
@less_options = []
|
|
90
|
+
|
|
91
|
+
if system("which less > /dev/null")
|
|
92
|
+
less_help = `less -?`
|
|
93
|
+
|
|
94
|
+
["-R ", "-F ", "-X "].each do |opt|
|
|
95
|
+
if less_help.include? opt
|
|
96
|
+
@less_options << opt
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
@less_options
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def set_color
|
|
105
|
+
unless @tracker and less_options.include? "-R "
|
|
106
|
+
@tracker.options[:output_color] = false
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require "yaml"
|
|
3
|
+
require "pathname"
|
|
3
4
|
|
|
4
5
|
class Brakeman::Report::CodeClimate < Brakeman::Report::Base
|
|
5
6
|
DOCUMENTATION_PATH = File.expand_path("../../../../docs/warning_types", __FILE__)
|
|
@@ -24,7 +25,7 @@ class Brakeman::Report::CodeClimate < Brakeman::Report::Base
|
|
|
24
25
|
severity: severity_level_for(warning.confidence),
|
|
25
26
|
remediation_points: remediation_points_for(warning_code_name),
|
|
26
27
|
location: {
|
|
27
|
-
path: warning
|
|
28
|
+
path: file_path(warning),
|
|
28
29
|
lines: {
|
|
29
30
|
begin: warning.line || 1,
|
|
30
31
|
end: warning.line || 1,
|
|
@@ -67,4 +68,12 @@ class Brakeman::Report::CodeClimate < Brakeman::Report::Base
|
|
|
67
68
|
|
|
68
69
|
File.read(filename) if File.exist?(filename)
|
|
69
70
|
end
|
|
71
|
+
|
|
72
|
+
def file_path(warning)
|
|
73
|
+
fp = Pathname.new(warning.relative_path)
|
|
74
|
+
if tracker.options[:path_prefix]
|
|
75
|
+
fp = Pathname.new(tracker.options[:path_prefix]) + fp
|
|
76
|
+
end
|
|
77
|
+
fp.to_s
|
|
78
|
+
end
|
|
70
79
|
end
|
|
@@ -24,6 +24,20 @@ module Brakeman
|
|
|
24
24
|
@rails[:action_controller][:allow_forgery_protection] == Sexp.new(:false)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
def default_protect_from_forgery?
|
|
28
|
+
if version_between? "5.2.0", "9.9.9"
|
|
29
|
+
if @rails[:action_controller] and
|
|
30
|
+
@rails[:action_controller][:default_protect_from_forgery] == Sexp.new(:false)
|
|
31
|
+
|
|
32
|
+
return false
|
|
33
|
+
else
|
|
34
|
+
return true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
false
|
|
39
|
+
end
|
|
40
|
+
|
|
27
41
|
def erubis?
|
|
28
42
|
@erubis
|
|
29
43
|
end
|
|
@@ -101,6 +115,36 @@ module Brakeman
|
|
|
101
115
|
end
|
|
102
116
|
end
|
|
103
117
|
|
|
118
|
+
#Returns true if low_version <= RAILS_VERSION <= high_version
|
|
119
|
+
#
|
|
120
|
+
#If the Rails version is unknown, returns false.
|
|
121
|
+
def version_between? low_version, high_version, current_version = nil
|
|
122
|
+
current_version ||= rails_version
|
|
123
|
+
return false unless current_version
|
|
124
|
+
|
|
125
|
+
version = current_version.split(".").map!(&:to_i)
|
|
126
|
+
low_version = low_version.split(".").map!(&:to_i)
|
|
127
|
+
high_version = high_version.split(".").map!(&:to_i)
|
|
128
|
+
|
|
129
|
+
version.each_with_index do |v, i|
|
|
130
|
+
if v < low_version.fetch(i, 0)
|
|
131
|
+
return false
|
|
132
|
+
elsif v > low_version.fetch(i, 0)
|
|
133
|
+
break
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
version.each_with_index do |v, i|
|
|
138
|
+
if v > high_version.fetch(i, 0)
|
|
139
|
+
return false
|
|
140
|
+
elsif v < high_version.fetch(i, 0)
|
|
141
|
+
break
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
true
|
|
146
|
+
end
|
|
147
|
+
|
|
104
148
|
def session_settings
|
|
105
149
|
@rails[:action_controller] &&
|
|
106
150
|
@rails[:action_controller][:session]
|
data/lib/brakeman/version.rb
CHANGED
data/lib/brakeman/warning.rb
CHANGED
|
@@ -196,11 +196,11 @@ class Brakeman::Warning
|
|
|
196
196
|
if @link_path.start_with? "http"
|
|
197
197
|
@link = @link_path
|
|
198
198
|
else
|
|
199
|
-
@link = "
|
|
199
|
+
@link = "https://brakemanscanner.org/docs/warning_types/#{@link_path}"
|
|
200
200
|
end
|
|
201
201
|
else
|
|
202
202
|
warning_path = self.warning_type.to_s.downcase.gsub(/\s+/, '_') + "/"
|
|
203
|
-
@link = "
|
|
203
|
+
@link = "https://brakemanscanner.org/docs/warning_types/#{warning_path}"
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
@link
|
data/lib/ruby_parser/bm_sexp.rb
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
class Sexp
|
|
5
5
|
attr_accessor :original_line, :or_depth
|
|
6
6
|
ASSIGNMENT_BOOL = [:gasgn, :iasgn, :lasgn, :cvdecl, :cvasgn, :cdecl, :or, :and, :colon2, :op_asgn_or]
|
|
7
|
+
CALLS = [:call, :attrasgn, :safe_call, :safe_attrasgn]
|
|
7
8
|
|
|
8
9
|
def method_missing name, *args
|
|
9
10
|
#Brakeman does not use this functionality,
|
|
@@ -307,6 +308,21 @@ class Sexp
|
|
|
307
308
|
end
|
|
308
309
|
end
|
|
309
310
|
|
|
311
|
+
def call_chain
|
|
312
|
+
expect :call, :attrasgn, :safe_call, :safe_attrasgn
|
|
313
|
+
|
|
314
|
+
chain = []
|
|
315
|
+
call = self
|
|
316
|
+
|
|
317
|
+
while call.class == Sexp and CALLS.include? call.first
|
|
318
|
+
chain << call.method
|
|
319
|
+
call = call.target
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
chain.reverse!
|
|
323
|
+
chain
|
|
324
|
+
end
|
|
325
|
+
|
|
310
326
|
#Returns condition of an if expression:
|
|
311
327
|
#
|
|
312
328
|
# s(:if,
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: brakeman-lib
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.0
|
|
4
|
+
version: 4.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Justin Collins
|
|
@@ -9,7 +9,7 @@ autorequire:
|
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain:
|
|
11
11
|
- brakeman-public_cert.pem
|
|
12
|
-
date: 2017-
|
|
12
|
+
date: 2017-12-14 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: minitest
|
|
@@ -198,7 +198,7 @@ executables:
|
|
|
198
198
|
extensions: []
|
|
199
199
|
extra_rdoc_files: []
|
|
200
200
|
files:
|
|
201
|
-
- CHANGES
|
|
201
|
+
- CHANGES.md
|
|
202
202
|
- FEATURES
|
|
203
203
|
- README.md
|
|
204
204
|
- bin/brakeman
|
|
@@ -216,6 +216,7 @@ files:
|
|
|
216
216
|
- lib/brakeman/checks/check_deserialize.rb
|
|
217
217
|
- lib/brakeman/checks/check_detailed_exceptions.rb
|
|
218
218
|
- lib/brakeman/checks/check_digest_dos.rb
|
|
219
|
+
- lib/brakeman/checks/check_divide_by_zero.rb
|
|
219
220
|
- lib/brakeman/checks/check_dynamic_finders.rb
|
|
220
221
|
- lib/brakeman/checks/check_escape_function.rb
|
|
221
222
|
- lib/brakeman/checks/check_evaluation.rb
|
|
@@ -240,6 +241,7 @@ files:
|
|
|
240
241
|
- lib/brakeman/checks/check_nested_attributes.rb
|
|
241
242
|
- lib/brakeman/checks/check_nested_attributes_bypass.rb
|
|
242
243
|
- lib/brakeman/checks/check_number_to_currency.rb
|
|
244
|
+
- lib/brakeman/checks/check_permit_attributes.rb
|
|
243
245
|
- lib/brakeman/checks/check_quote_table_name.rb
|
|
244
246
|
- lib/brakeman/checks/check_redirect.rb
|
|
245
247
|
- lib/brakeman/checks/check_regex_dos.rb
|
|
@@ -274,6 +276,7 @@ files:
|
|
|
274
276
|
- lib/brakeman/checks/check_without_protection.rb
|
|
275
277
|
- lib/brakeman/checks/check_xml_dos.rb
|
|
276
278
|
- lib/brakeman/checks/check_yaml_parsing.rb
|
|
279
|
+
- lib/brakeman/codeclimate/engine_configuration.rb
|
|
277
280
|
- lib/brakeman/commandline.rb
|
|
278
281
|
- lib/brakeman/differ.rb
|
|
279
282
|
- lib/brakeman/file_parser.rb
|
|
@@ -318,6 +321,7 @@ files:
|
|
|
318
321
|
- lib/brakeman/report/config/remediation.yml
|
|
319
322
|
- lib/brakeman/report/ignore/config.rb
|
|
320
323
|
- lib/brakeman/report/ignore/interactive.rb
|
|
324
|
+
- lib/brakeman/report/pager.rb
|
|
321
325
|
- lib/brakeman/report/renderer.rb
|
|
322
326
|
- lib/brakeman/report/report_base.rb
|
|
323
327
|
- lib/brakeman/report/report_codeclimate.rb
|