brakeman-min 7.1.2 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.md +15 -0
- data/README.md +1 -1
- data/lib/brakeman/app_tree.rb +7 -2
- data/lib/brakeman/checks/check_model_attributes.rb +1 -1
- data/lib/brakeman/checks/check_render.rb +1 -27
- data/lib/brakeman/checks/check_render_rce.rb +43 -0
- data/lib/brakeman/checks/check_session_settings.rb +1 -1
- data/lib/brakeman/checks.rb +31 -25
- data/lib/brakeman/commandline.rb +8 -1
- data/lib/brakeman/file_parser.rb +3 -2
- data/lib/brakeman/logger.rb +264 -0
- data/lib/brakeman/options.rb +0 -9
- data/lib/brakeman/parsers/rails_erubi.rb +82 -0
- data/lib/brakeman/parsers/template_parser.rb +7 -15
- data/lib/brakeman/processor.rb +2 -2
- data/lib/brakeman/processors/controller_alias_processor.rb +1 -1
- data/lib/brakeman/processors/controller_processor.rb +3 -3
- data/lib/brakeman/processors/{erubis_template_processor.rb → erubi_template_procesor.rb} +3 -3
- data/lib/brakeman/processors/lib/rails2_config_processor.rb +4 -3
- data/lib/brakeman/processors/lib/rails2_route_processor.rb +1 -1
- data/lib/brakeman/processors/lib/render_helper.rb +1 -1
- data/lib/brakeman/processors/lib/render_path.rb +1 -1
- data/lib/brakeman/processors/model_processor.rb +1 -1
- data/lib/brakeman/report/ignore/config.rb +1 -1
- data/lib/brakeman/scanner.rb +25 -49
- data/lib/brakeman/tracker/collection.rb +12 -2
- data/lib/brakeman/tracker/config.rb +17 -13
- data/lib/brakeman/tracker/constants.rb +17 -2
- data/lib/brakeman/tracker/controller.rb +1 -1
- data/lib/brakeman/tracker.rb +7 -15
- data/lib/brakeman/version.rb +1 -1
- data/lib/brakeman.rb +89 -49
- metadata +6 -7
- data/lib/brakeman/parsers/erubis_patch.rb +0 -11
- data/lib/brakeman/parsers/rails2_erubis.rb +0 -9
- data/lib/brakeman/parsers/rails2_xss_plugin_erubis.rb +0 -52
- data/lib/brakeman/parsers/rails3_erubis.rb +0 -85
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dddb2b335dd5a7b607653373eb3991dd57b63f57479e4eb0802973c3be80acdd
|
|
4
|
+
data.tar.gz: 0f83d5677d9b28d9e95030972502371dbbc281bd4b9b281b0b127cd4ceeec274
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ba3ebe3e2ab37afd21acedb9172aa5a5fbced8d9b2cfd79f1bb44bc6944d86e889e4d6ce8671c845dead1e6e0c8b35b73133865a8036339b6c30c7050bcadd82
|
|
7
|
+
data.tar.gz: 4ffad27ed4985e51342536f6edcd4c7140cfa90b4bcfc4a89236a7fd76f0cfffaf6894e9ae1548478bfbcce74c77177d961571de4b248b60c6494bd491cacc2d
|
data/CHANGES.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# 8.0.1 - 2026-01-29
|
|
2
|
+
|
|
3
|
+
* Make sure to reset the cursor even when exit code is 0
|
|
4
|
+
|
|
5
|
+
# 8.0.0 - 2026-01-29
|
|
6
|
+
|
|
7
|
+
* No longer produce weak dynamic render path warnings
|
|
8
|
+
* `--skip-libs` removed
|
|
9
|
+
* `--index-libs` removed
|
|
10
|
+
* Revamp of scan progress output and logging
|
|
11
|
+
* Faster file globbing for templates (Mikael Henriksson)
|
|
12
|
+
* Fix singleton method prefixes (viralpraxis)
|
|
13
|
+
* Fix qualified constant lookup to respect module/class context (Mike Dalessio)
|
|
14
|
+
* Replace Erubis with Erubi
|
|
15
|
+
|
|
1
16
|
# 7.1.2 - 2025-12-25
|
|
2
17
|
|
|
3
18
|
* Update `ruby_parser` to remove version restriction (Chedli Bourguiba)
|
data/README.md
CHANGED
|
@@ -75,7 +75,7 @@ To specify an output file for the results:
|
|
|
75
75
|
|
|
76
76
|
brakeman -o output_file
|
|
77
77
|
|
|
78
|
-
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`, `codeclimate`, `github` and `sonar`.
|
|
78
|
+
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`, `codeclimate`, `github`, `sarif`, and `sonar`.
|
|
79
79
|
|
|
80
80
|
Multiple output files can be specified:
|
|
81
81
|
|
data/lib/brakeman/app_tree.rb
CHANGED
|
@@ -120,7 +120,7 @@ module Brakeman
|
|
|
120
120
|
|
|
121
121
|
def template_paths
|
|
122
122
|
@template_paths ||= find_paths(".", "*.{#{VIEW_EXTENSIONS}}") +
|
|
123
|
-
find_paths("
|
|
123
|
+
find_paths(".", "*.{erb,haml,slim}").reject { |path| File.basename(path).count(".") > 1 }
|
|
124
124
|
end
|
|
125
125
|
|
|
126
126
|
def layout_exists?(name)
|
|
@@ -213,13 +213,17 @@ module Brakeman
|
|
|
213
213
|
end
|
|
214
214
|
|
|
215
215
|
def reject_directories(paths)
|
|
216
|
-
paths.reject
|
|
216
|
+
paths.reject do |path|
|
|
217
|
+
Brakeman.logger.spin
|
|
218
|
+
File.directory?(path)
|
|
219
|
+
end
|
|
217
220
|
end
|
|
218
221
|
|
|
219
222
|
def select_only_files(paths)
|
|
220
223
|
return paths unless @only_files
|
|
221
224
|
|
|
222
225
|
paths.select do |path|
|
|
226
|
+
Brakeman.logger.spin
|
|
223
227
|
match_path @only_files, path
|
|
224
228
|
end
|
|
225
229
|
end
|
|
@@ -228,6 +232,7 @@ module Brakeman
|
|
|
228
232
|
return paths unless @skip_files
|
|
229
233
|
|
|
230
234
|
paths.reject do |path|
|
|
235
|
+
Brakeman.logger.spin
|
|
231
236
|
match_path @skip_files, path
|
|
232
237
|
end
|
|
233
238
|
end
|
|
@@ -12,7 +12,7 @@ class Brakeman::CheckModelAttributes < Brakeman::BaseCheck
|
|
|
12
12
|
|
|
13
13
|
#Roll warnings into one warning for all models
|
|
14
14
|
if tracker.options[:collapse_mass_assignment]
|
|
15
|
-
Brakeman.
|
|
15
|
+
Brakeman.alert "The `collapse_mass_assignment` option has been removed."
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
check_models do |name, model|
|
|
@@ -17,8 +17,7 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
|
17
17
|
|
|
18
18
|
case result[:call].render_type
|
|
19
19
|
when :partial, :template, :action, :file
|
|
20
|
-
|
|
21
|
-
check_for_dynamic_path(result)
|
|
20
|
+
check_for_dynamic_path(result)
|
|
22
21
|
when :inline
|
|
23
22
|
when :js
|
|
24
23
|
when :json
|
|
@@ -41,8 +40,6 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
|
41
40
|
else
|
|
42
41
|
confidence = :high
|
|
43
42
|
end
|
|
44
|
-
elsif input = include_user_input?(view)
|
|
45
|
-
confidence = :weak
|
|
46
43
|
else
|
|
47
44
|
return
|
|
48
45
|
end
|
|
@@ -62,29 +59,6 @@ class Brakeman::CheckRender < Brakeman::BaseCheck
|
|
|
62
59
|
end
|
|
63
60
|
end
|
|
64
61
|
|
|
65
|
-
def check_for_rce result
|
|
66
|
-
return unless version_between? "0.0.0", "3.2.22" or
|
|
67
|
-
version_between? "4.0.0", "4.1.14" or
|
|
68
|
-
version_between? "4.2.0", "4.2.5"
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
view = result[:call][2]
|
|
72
|
-
if sexp? view and not duplicate? result
|
|
73
|
-
if params? view
|
|
74
|
-
add_result result
|
|
75
|
-
return if safe_param? view
|
|
76
|
-
|
|
77
|
-
warn :result => result,
|
|
78
|
-
:warning_type => "Remote Code Execution",
|
|
79
|
-
:warning_code => :dynamic_render_path_rce,
|
|
80
|
-
:message => msg("Passing query parameters to ", msg_code("render"), " is vulnerable in ", msg_version(rails_version), " ", msg_cve("CVE-2016-0752")),
|
|
81
|
-
:user_input => view,
|
|
82
|
-
:confidence => :high,
|
|
83
|
-
:cwe_id => [22]
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
62
|
def safe_param? exp
|
|
89
63
|
if params? exp and call? exp
|
|
90
64
|
method_name = exp.method
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'brakeman/checks/check_render'
|
|
2
|
+
|
|
3
|
+
class Brakeman::CheckRenderRCE < Brakeman::CheckRender
|
|
4
|
+
Brakeman::Checks.add self
|
|
5
|
+
|
|
6
|
+
@description = "Finds calls to render that might be vulnerable to CVE-2016-0752"
|
|
7
|
+
|
|
8
|
+
def run_check
|
|
9
|
+
tracker.find_call(:target => nil, :method => :render).each do |result|
|
|
10
|
+
process_render_result result
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def process_render_result result
|
|
15
|
+
return unless node_type? result[:call], :render
|
|
16
|
+
|
|
17
|
+
case result[:call].render_type
|
|
18
|
+
when :partial, :template, :action, :file
|
|
19
|
+
check_for_rce(result)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def check_for_rce result
|
|
24
|
+
return unless version_between? "0.0.0", "3.2.22" or
|
|
25
|
+
version_between? "4.0.0", "4.1.14" or
|
|
26
|
+
version_between? "4.2.0", "4.2.5"
|
|
27
|
+
|
|
28
|
+
view = result[:call][2]
|
|
29
|
+
if sexp? view and not duplicate? result
|
|
30
|
+
if params? view and not safe_param? view
|
|
31
|
+
add_result result
|
|
32
|
+
|
|
33
|
+
warn :result => result,
|
|
34
|
+
:warning_type => "Remote Code Execution",
|
|
35
|
+
:warning_code => :dynamic_render_path_rce,
|
|
36
|
+
:message => msg("Passing query parameters to ", msg_code("render"), " is vulnerable in ", msg_version(rails_version), " ", msg_cve("CVE-2016-0752")),
|
|
37
|
+
:user_input => view,
|
|
38
|
+
:confidence => :high,
|
|
39
|
+
:cwe_id => [22]
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -120,7 +120,7 @@ class Brakeman::CheckSessionSettings < Brakeman::BaseCheck
|
|
|
120
120
|
begin
|
|
121
121
|
secrets = YAML.safe_load yaml, aliases: true
|
|
122
122
|
rescue Psych::SyntaxError, RuntimeError => e
|
|
123
|
-
Brakeman.
|
|
123
|
+
Brakeman.alert "#{self.class}: Unable to parse `#{secrets_file}`"
|
|
124
124
|
Brakeman.debug "Failed to parse #{secrets_file}: #{e.inspect}"
|
|
125
125
|
return
|
|
126
126
|
end
|
data/lib/brakeman/checks.rb
CHANGED
|
@@ -121,37 +121,43 @@ class Brakeman::Checks
|
|
|
121
121
|
parallel = tracker.options[:parallel_checks]
|
|
122
122
|
error_mutex = Mutex.new
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
message = if parallel
|
|
125
|
+
"Running #{checks.length} checks in parallel"
|
|
126
|
+
else
|
|
127
|
+
"Running #{checks.length} checks"
|
|
128
|
+
end
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
Brakeman.process_step(message) do
|
|
131
|
+
checks.each do |c|
|
|
132
|
+
check_name = get_check_name c
|
|
133
|
+
Brakeman.debug " - #{check_name}"
|
|
134
|
+
|
|
135
|
+
if parallel
|
|
136
|
+
threads << Thread.new do
|
|
137
|
+
self.run_a_check(c, error_mutex, tracker)
|
|
138
|
+
end
|
|
139
|
+
else
|
|
140
|
+
results << self.run_a_check(c, error_mutex, tracker)
|
|
131
141
|
end
|
|
132
|
-
else
|
|
133
|
-
results << self.run_a_check(c, error_mutex, tracker)
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
#Maintain list of which checks were run
|
|
137
|
-
#mainly for reporting purposes
|
|
138
|
-
check_runner.checks_run << check_name[5..-1]
|
|
139
|
-
end
|
|
140
142
|
|
|
141
|
-
|
|
143
|
+
#Maintain list of which checks were run
|
|
144
|
+
#mainly for reporting purposes
|
|
145
|
+
check_runner.checks_run << check_name[5..-1]
|
|
146
|
+
end
|
|
142
147
|
|
|
143
|
-
|
|
148
|
+
threads.each { |t| t.join }
|
|
144
149
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
150
|
+
if parallel
|
|
151
|
+
threads.each do |thread|
|
|
152
|
+
thread.value.each do |warning|
|
|
153
|
+
check_runner.add_warning warning
|
|
154
|
+
end
|
|
149
155
|
end
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
156
|
+
else
|
|
157
|
+
results.each do |warnings|
|
|
158
|
+
warnings.each do |warning|
|
|
159
|
+
check_runner.add_warning warning
|
|
160
|
+
end
|
|
155
161
|
end
|
|
156
162
|
end
|
|
157
163
|
end
|
data/lib/brakeman/commandline.rb
CHANGED
|
@@ -33,6 +33,8 @@ module Brakeman
|
|
|
33
33
|
set_options options, default_app_path
|
|
34
34
|
check_latest if options[:ensure_latest]
|
|
35
35
|
run_report options
|
|
36
|
+
|
|
37
|
+
quit
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
# Check for the latest version.
|
|
@@ -54,11 +56,13 @@ module Brakeman
|
|
|
54
56
|
f.puts JSON.pretty_generate(vulns)
|
|
55
57
|
end
|
|
56
58
|
|
|
57
|
-
Brakeman.
|
|
59
|
+
Brakeman.announce "Comparison saved in '#{options[:comparison_output_file]}'"
|
|
58
60
|
else
|
|
59
61
|
puts JSON.pretty_generate(vulns)
|
|
60
62
|
end
|
|
61
63
|
|
|
64
|
+
Brakeman.cleanup(false)
|
|
65
|
+
|
|
62
66
|
if options[:exit_on_warn] && vulns[:new].count > 0
|
|
63
67
|
quit Brakeman::Warnings_Found_Exit_Code
|
|
64
68
|
end
|
|
@@ -117,6 +121,7 @@ module Brakeman
|
|
|
117
121
|
# Override this method for different behavior.
|
|
118
122
|
def quit exit_code = 0, message = nil
|
|
119
123
|
warn message if message
|
|
124
|
+
Brakeman.cleanup
|
|
120
125
|
exit exit_code
|
|
121
126
|
end
|
|
122
127
|
|
|
@@ -186,6 +191,8 @@ module Brakeman
|
|
|
186
191
|
warn caller
|
|
187
192
|
end
|
|
188
193
|
|
|
194
|
+
Brakeman.cleanup
|
|
195
|
+
|
|
189
196
|
exit!
|
|
190
197
|
end
|
|
191
198
|
end
|
data/lib/brakeman/file_parser.rb
CHANGED
|
@@ -13,9 +13,9 @@ module Brakeman
|
|
|
13
13
|
if @use_prism
|
|
14
14
|
begin
|
|
15
15
|
require 'prism'
|
|
16
|
-
Brakeman.debug '
|
|
16
|
+
Brakeman.debug 'Using Prism parser'
|
|
17
17
|
rescue LoadError => e
|
|
18
|
-
Brakeman.debug "
|
|
18
|
+
Brakeman.debug "Asked to use Prism, but failed to load: #{e}"
|
|
19
19
|
@use_prism = false
|
|
20
20
|
end
|
|
21
21
|
end
|
|
@@ -46,6 +46,7 @@ module Brakeman
|
|
|
46
46
|
#
|
|
47
47
|
# Note this method no longer uses read_files
|
|
48
48
|
@file_list, new_errors = Parallel.map(list, parallel_options) do |file_name|
|
|
49
|
+
Brakeman.logger.spin
|
|
49
50
|
file_path = @app_tree.file_path(file_name)
|
|
50
51
|
contents = file_path.read
|
|
51
52
|
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
module Brakeman
|
|
2
|
+
module Logger
|
|
3
|
+
def self.get_logger options, dest = $stderr
|
|
4
|
+
case
|
|
5
|
+
when options[:debug]
|
|
6
|
+
Debug.new(options, dest)
|
|
7
|
+
when options[:quiet]
|
|
8
|
+
Quiet.new(options, dest)
|
|
9
|
+
when options[:report_progress] == false
|
|
10
|
+
Plain.new(options, dest)
|
|
11
|
+
when dest.tty?
|
|
12
|
+
Console.new(options, dest)
|
|
13
|
+
else
|
|
14
|
+
Plain.new(options, dest)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
class Base
|
|
19
|
+
def initialize(options, log_destination = $stderr)
|
|
20
|
+
@dest = log_destination
|
|
21
|
+
@show_timing = options[:debug] || options[:show_timing]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Output a message to the log.
|
|
25
|
+
# If newline is `false`, does not output a newline after message.
|
|
26
|
+
def log(message, newline: true)
|
|
27
|
+
if newline
|
|
28
|
+
@dest.puts message
|
|
29
|
+
else
|
|
30
|
+
@dest.write message
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Notify about important information - use sparingly
|
|
35
|
+
def announce(message); end
|
|
36
|
+
|
|
37
|
+
# Notify regarding errors - use sparingly
|
|
38
|
+
def alert(message); end
|
|
39
|
+
|
|
40
|
+
# Output debug information
|
|
41
|
+
def debug(message); end
|
|
42
|
+
|
|
43
|
+
# Wraps a step in the scanning process
|
|
44
|
+
def context(description, &)
|
|
45
|
+
yield self
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Wraps a substep (e.g. processing one file)
|
|
49
|
+
def single_context(description, &)
|
|
50
|
+
yield
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Update progress towards a known total
|
|
54
|
+
def update_progress(current, total, type = 'files'); end
|
|
55
|
+
|
|
56
|
+
# Show a spinner
|
|
57
|
+
def spin; end
|
|
58
|
+
|
|
59
|
+
# Called on exit
|
|
60
|
+
def cleanup(newline); end
|
|
61
|
+
|
|
62
|
+
def show_timing? = @show_timing
|
|
63
|
+
|
|
64
|
+
# Use ANSI codes to color a string
|
|
65
|
+
def color(message, *)
|
|
66
|
+
if @highline
|
|
67
|
+
@highline.color(message, *)
|
|
68
|
+
else
|
|
69
|
+
message
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def color?
|
|
74
|
+
@highline and @highline.use_color?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def load_highline(output_color)
|
|
80
|
+
if @dest.tty? or output_color == :force
|
|
81
|
+
Brakeman.load_brakeman_dependency 'highline'
|
|
82
|
+
@highline = HighLine.new
|
|
83
|
+
@highline.use_color = !!output_color
|
|
84
|
+
else
|
|
85
|
+
@highline = nil
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class Plain < Base
|
|
91
|
+
def initialize(options, *)
|
|
92
|
+
super
|
|
93
|
+
|
|
94
|
+
load_highline(options[:output_color])
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def announce(message)
|
|
98
|
+
log color(message, :bold, :green)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def alert(message)
|
|
102
|
+
log color(message, :red)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def context(description, &)
|
|
106
|
+
log "#{color(description, :green)}..."
|
|
107
|
+
|
|
108
|
+
if show_timing?
|
|
109
|
+
time_step(description, &)
|
|
110
|
+
else
|
|
111
|
+
yield
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def time_step(description, &)
|
|
116
|
+
start_t = Time.now
|
|
117
|
+
yield
|
|
118
|
+
duration = Time.now - start_t
|
|
119
|
+
|
|
120
|
+
log color(("Completed #{description.to_s.downcase} in %0.2fs" % duration), :gray)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class Quiet < Base
|
|
125
|
+
def initialize(*)
|
|
126
|
+
super
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
class Debug < Plain
|
|
131
|
+
def debug(message)
|
|
132
|
+
log color(message, :gray)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def context(description, &)
|
|
136
|
+
log "#{description}..."
|
|
137
|
+
|
|
138
|
+
time_step(description, &)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def single_context(description, &)
|
|
142
|
+
debug "Processing #{description}"
|
|
143
|
+
|
|
144
|
+
if show_timing?
|
|
145
|
+
# Even in debug, only show timing for each file if asked
|
|
146
|
+
time_step(description, &)
|
|
147
|
+
else
|
|
148
|
+
yield
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
class Console < Base
|
|
154
|
+
attr_reader :prefix
|
|
155
|
+
|
|
156
|
+
def initialize(options, *)
|
|
157
|
+
super
|
|
158
|
+
|
|
159
|
+
load_highline(options[:output_color])
|
|
160
|
+
require 'reline'
|
|
161
|
+
require 'reline/io/ansi'
|
|
162
|
+
|
|
163
|
+
@prefix = ''
|
|
164
|
+
@post_fix_pos = 0
|
|
165
|
+
@reline = Reline::ANSI.new
|
|
166
|
+
@report_progress = options[:report_progress]
|
|
167
|
+
@spinner = ["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"]
|
|
168
|
+
@percenter = ["⣀", "⣤", "⣶", "⣿"]
|
|
169
|
+
@spindex = 0
|
|
170
|
+
@last_spin = Time.now
|
|
171
|
+
@reline.hide_cursor
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def announce message
|
|
175
|
+
clear_line
|
|
176
|
+
log color(message, :bold, :green)
|
|
177
|
+
rewrite_prefix
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def alert message
|
|
181
|
+
clear_line
|
|
182
|
+
log color(message, :red)
|
|
183
|
+
rewrite_prefix
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def context(description, &)
|
|
187
|
+
write_prefix description
|
|
188
|
+
|
|
189
|
+
time_step(description, &)
|
|
190
|
+
ensure
|
|
191
|
+
clear_prefix
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def time_step(description, &)
|
|
195
|
+
if show_timing?
|
|
196
|
+
start_t = Time.now
|
|
197
|
+
yield
|
|
198
|
+
duration = Time.now - start_t
|
|
199
|
+
|
|
200
|
+
write_after color(('%0.2fs' % duration), :gray)
|
|
201
|
+
log ''
|
|
202
|
+
else
|
|
203
|
+
yield
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def update_progress current, total, type = 'files'
|
|
208
|
+
percent = ((current / total.to_f) * 100).to_i
|
|
209
|
+
tenths = [(percent / 10), 0].max
|
|
210
|
+
|
|
211
|
+
lead = color(@percenter[percent % 10 / 3], :bold, :red)
|
|
212
|
+
done_blocks = color("⣿" * tenths, :red)
|
|
213
|
+
remaining = color("⣀" * (9 - tenths), :gray)
|
|
214
|
+
write_after "#{done_blocks}#{lead}#{remaining}"
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def write_prefix pref
|
|
218
|
+
set_prefix pref
|
|
219
|
+
rewrite_prefix
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
# If an alert was written, redo prefix on next line
|
|
223
|
+
def rewrite_prefix
|
|
224
|
+
log(@prefix, newline: false)
|
|
225
|
+
@reline.erase_after_cursor
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def write_after message
|
|
229
|
+
@reline.move_cursor_column(@post_fix_pos)
|
|
230
|
+
log(message, newline: false)
|
|
231
|
+
@reline.erase_after_cursor
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def set_prefix message
|
|
235
|
+
@prefix = "#{color('»', :bold, :cyan)} #{color(message, :green)}"
|
|
236
|
+
@post_fix_pos = HighLine::Wrapper.actual_length(@prefix) + 1
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def clear_prefix
|
|
240
|
+
@prefix = ''
|
|
241
|
+
@post_fix_pos = 0
|
|
242
|
+
clear_line
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def clear_line
|
|
246
|
+
@reline.move_cursor_column(0)
|
|
247
|
+
@reline.erase_after_cursor
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def spin
|
|
251
|
+
return unless (Time.now - @last_spin) > 0.2
|
|
252
|
+
|
|
253
|
+
write_after color(@spinner[@spindex], :bold, :red)
|
|
254
|
+
@spindex = (@spindex + 1) % @spinner.length
|
|
255
|
+
@last_spin = Time.now
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def cleanup(newline = true)
|
|
259
|
+
@reline.show_cursor
|
|
260
|
+
log('') if newline
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
data/lib/brakeman/options.rb
CHANGED
|
@@ -131,7 +131,6 @@ module Brakeman::Options
|
|
|
131
131
|
|
|
132
132
|
opts.on "--faster", "Faster, but less accurate scan" do
|
|
133
133
|
options[:ignore_ifs] = true
|
|
134
|
-
options[:skip_libs] = true
|
|
135
134
|
options[:disable_constant_tracking] = true
|
|
136
135
|
end
|
|
137
136
|
|
|
@@ -143,10 +142,6 @@ module Brakeman::Options
|
|
|
143
142
|
options[:ignore_attr_protected] = true
|
|
144
143
|
end
|
|
145
144
|
|
|
146
|
-
opts.on "--[no-]index-libs", "Add libraries to call index (Default)" do |index|
|
|
147
|
-
options[:index_libs] = index
|
|
148
|
-
end
|
|
149
|
-
|
|
150
145
|
opts.on "--interprocedural", "Process method calls to known methods" do
|
|
151
146
|
options[:interprocedural] = true
|
|
152
147
|
end
|
|
@@ -212,10 +207,6 @@ module Brakeman::Options
|
|
|
212
207
|
options[:skip_vendor] = skip
|
|
213
208
|
end
|
|
214
209
|
|
|
215
|
-
opts.on "--skip-libs", "Skip processing lib directory" do
|
|
216
|
-
options[:skip_libs] = true
|
|
217
|
-
end
|
|
218
|
-
|
|
219
210
|
opts.on "--add-libs-path path1,path2,etc", Array, "An application relative lib directory (ex. app/mailers) to process" do |paths|
|
|
220
211
|
options[:additional_libs_path] ||= Set.new
|
|
221
212
|
options[:additional_libs_path].merge paths
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# Copied almost verbatim from Rails
|
|
3
|
+
# https://github.com/rails/rails/blob/5359cf8a5b093b04170e884ee8da5a1e076b8a0d/actionview/lib/action_view/template/handlers/erb/erubi.rb#L9
|
|
4
|
+
|
|
5
|
+
Brakeman.load_brakeman_dependency "erubi"
|
|
6
|
+
|
|
7
|
+
module Brakeman
|
|
8
|
+
class Erubi < ::Erubi::Engine
|
|
9
|
+
# :nodoc: all
|
|
10
|
+
def initialize(input, properties = {})
|
|
11
|
+
@newline_pending = 0
|
|
12
|
+
|
|
13
|
+
# Dup properties so that we don't modify argument
|
|
14
|
+
properties = Hash[properties]
|
|
15
|
+
|
|
16
|
+
properties[:bufvar] ||= "@output_buffer"
|
|
17
|
+
properties[:preamble] ||= ""
|
|
18
|
+
properties[:postamble] ||= "#{properties[:bufvar]}"
|
|
19
|
+
|
|
20
|
+
# Tell Erubi whether the template will be compiled with `frozen_string_literal: true`
|
|
21
|
+
# properties[:freeze_template_literals] = !Template.frozen_string_literal
|
|
22
|
+
properties[:freeze_template_literals] = false
|
|
23
|
+
|
|
24
|
+
properties[:escapefunc] = ""
|
|
25
|
+
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
def add_text(text)
|
|
31
|
+
return if text.empty?
|
|
32
|
+
|
|
33
|
+
if text == "\n"
|
|
34
|
+
@newline_pending += 1
|
|
35
|
+
else
|
|
36
|
+
with_buffer do
|
|
37
|
+
src << ".safe_append='"
|
|
38
|
+
src << "\n" * @newline_pending if @newline_pending > 0
|
|
39
|
+
src << text.gsub(/['\\]/, '\\\\\&') << @text_end
|
|
40
|
+
end
|
|
41
|
+
@newline_pending = 0
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
BLOCK_EXPR = /((\s|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
|
46
|
+
|
|
47
|
+
def add_expression(indicator, code)
|
|
48
|
+
flush_newline_if_pending(src)
|
|
49
|
+
|
|
50
|
+
with_buffer do
|
|
51
|
+
if (indicator == "==") || @escape
|
|
52
|
+
src << ".safe_expr_append="
|
|
53
|
+
else
|
|
54
|
+
src << ".append="
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if BLOCK_EXPR.match?(code)
|
|
58
|
+
src << " " << code
|
|
59
|
+
else
|
|
60
|
+
src << "(" << code << ")"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def add_code(code)
|
|
66
|
+
flush_newline_if_pending(src)
|
|
67
|
+
super
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def add_postamble(_)
|
|
71
|
+
flush_newline_if_pending(src)
|
|
72
|
+
super
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def flush_newline_if_pending(src)
|
|
76
|
+
if @newline_pending > 0
|
|
77
|
+
with_buffer { src << ".safe_append='#{"\n" * @newline_pending}" << @text_end }
|
|
78
|
+
@newline_pending = 0
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|