railroader 4.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGES.md +1091 -0
- data/FEATURES +16 -0
- data/README.md +174 -0
- data/bin/railroader +8 -0
- data/lib/railroader/app_tree.rb +191 -0
- data/lib/railroader/call_index.rb +219 -0
- data/lib/railroader/checks/base_check.rb +505 -0
- data/lib/railroader/checks/check_basic_auth.rb +88 -0
- data/lib/railroader/checks/check_basic_auth_timing_attack.rb +33 -0
- data/lib/railroader/checks/check_content_tag.rb +200 -0
- data/lib/railroader/checks/check_create_with.rb +74 -0
- data/lib/railroader/checks/check_cross_site_scripting.rb +381 -0
- data/lib/railroader/checks/check_default_routes.rb +86 -0
- data/lib/railroader/checks/check_deserialize.rb +56 -0
- data/lib/railroader/checks/check_detailed_exceptions.rb +55 -0
- data/lib/railroader/checks/check_digest_dos.rb +38 -0
- data/lib/railroader/checks/check_divide_by_zero.rb +42 -0
- data/lib/railroader/checks/check_dynamic_finders.rb +48 -0
- data/lib/railroader/checks/check_escape_function.rb +21 -0
- data/lib/railroader/checks/check_evaluation.rb +35 -0
- data/lib/railroader/checks/check_execute.rb +189 -0
- data/lib/railroader/checks/check_file_access.rb +71 -0
- data/lib/railroader/checks/check_file_disclosure.rb +35 -0
- data/lib/railroader/checks/check_filter_skipping.rb +31 -0
- data/lib/railroader/checks/check_forgery_setting.rb +81 -0
- data/lib/railroader/checks/check_header_dos.rb +31 -0
- data/lib/railroader/checks/check_i18n_xss.rb +48 -0
- data/lib/railroader/checks/check_jruby_xml.rb +36 -0
- data/lib/railroader/checks/check_json_encoding.rb +47 -0
- data/lib/railroader/checks/check_json_parsing.rb +107 -0
- data/lib/railroader/checks/check_link_to.rb +132 -0
- data/lib/railroader/checks/check_link_to_href.rb +146 -0
- data/lib/railroader/checks/check_mail_to.rb +49 -0
- data/lib/railroader/checks/check_mass_assignment.rb +196 -0
- data/lib/railroader/checks/check_mime_type_dos.rb +39 -0
- data/lib/railroader/checks/check_model_attr_accessible.rb +55 -0
- data/lib/railroader/checks/check_model_attributes.rb +119 -0
- data/lib/railroader/checks/check_model_serialize.rb +67 -0
- data/lib/railroader/checks/check_nested_attributes.rb +38 -0
- data/lib/railroader/checks/check_nested_attributes_bypass.rb +58 -0
- data/lib/railroader/checks/check_number_to_currency.rb +74 -0
- data/lib/railroader/checks/check_permit_attributes.rb +43 -0
- data/lib/railroader/checks/check_quote_table_name.rb +40 -0
- data/lib/railroader/checks/check_redirect.rb +256 -0
- data/lib/railroader/checks/check_regex_dos.rb +68 -0
- data/lib/railroader/checks/check_render.rb +97 -0
- data/lib/railroader/checks/check_render_dos.rb +37 -0
- data/lib/railroader/checks/check_render_inline.rb +53 -0
- data/lib/railroader/checks/check_response_splitting.rb +21 -0
- data/lib/railroader/checks/check_route_dos.rb +42 -0
- data/lib/railroader/checks/check_safe_buffer_manipulation.rb +31 -0
- data/lib/railroader/checks/check_sanitize_methods.rb +112 -0
- data/lib/railroader/checks/check_secrets.rb +40 -0
- data/lib/railroader/checks/check_select_tag.rb +59 -0
- data/lib/railroader/checks/check_select_vulnerability.rb +60 -0
- data/lib/railroader/checks/check_send.rb +47 -0
- data/lib/railroader/checks/check_send_file.rb +19 -0
- data/lib/railroader/checks/check_session_manipulation.rb +35 -0
- data/lib/railroader/checks/check_session_settings.rb +176 -0
- data/lib/railroader/checks/check_simple_format.rb +58 -0
- data/lib/railroader/checks/check_single_quotes.rb +101 -0
- data/lib/railroader/checks/check_skip_before_filter.rb +60 -0
- data/lib/railroader/checks/check_sql.rb +700 -0
- data/lib/railroader/checks/check_sql_cves.rb +106 -0
- data/lib/railroader/checks/check_ssl_verify.rb +48 -0
- data/lib/railroader/checks/check_strip_tags.rb +89 -0
- data/lib/railroader/checks/check_symbol_dos.rb +71 -0
- data/lib/railroader/checks/check_symbol_dos_cve.rb +30 -0
- data/lib/railroader/checks/check_translate_bug.rb +45 -0
- data/lib/railroader/checks/check_unsafe_reflection.rb +50 -0
- data/lib/railroader/checks/check_unscoped_find.rb +57 -0
- data/lib/railroader/checks/check_validation_regex.rb +116 -0
- data/lib/railroader/checks/check_weak_hash.rb +148 -0
- data/lib/railroader/checks/check_without_protection.rb +80 -0
- data/lib/railroader/checks/check_xml_dos.rb +45 -0
- data/lib/railroader/checks/check_yaml_parsing.rb +121 -0
- data/lib/railroader/checks.rb +209 -0
- data/lib/railroader/codeclimate/engine_configuration.rb +97 -0
- data/lib/railroader/commandline.rb +179 -0
- data/lib/railroader/differ.rb +66 -0
- data/lib/railroader/file_parser.rb +54 -0
- data/lib/railroader/format/style.css +133 -0
- data/lib/railroader/options.rb +339 -0
- data/lib/railroader/parsers/rails2_erubis.rb +6 -0
- data/lib/railroader/parsers/rails2_xss_plugin_erubis.rb +48 -0
- data/lib/railroader/parsers/rails3_erubis.rb +81 -0
- data/lib/railroader/parsers/template_parser.rb +108 -0
- data/lib/railroader/processor.rb +102 -0
- data/lib/railroader/processors/alias_processor.rb +1229 -0
- data/lib/railroader/processors/base_processor.rb +295 -0
- data/lib/railroader/processors/config_processor.rb +14 -0
- data/lib/railroader/processors/controller_alias_processor.rb +278 -0
- data/lib/railroader/processors/controller_processor.rb +249 -0
- data/lib/railroader/processors/erb_template_processor.rb +77 -0
- data/lib/railroader/processors/erubis_template_processor.rb +92 -0
- data/lib/railroader/processors/gem_processor.rb +64 -0
- data/lib/railroader/processors/haml_template_processor.rb +191 -0
- data/lib/railroader/processors/lib/basic_processor.rb +37 -0
- data/lib/railroader/processors/lib/call_conversion_helper.rb +90 -0
- data/lib/railroader/processors/lib/find_all_calls.rb +224 -0
- data/lib/railroader/processors/lib/find_call.rb +183 -0
- data/lib/railroader/processors/lib/find_return_value.rb +166 -0
- data/lib/railroader/processors/lib/module_helper.rb +111 -0
- data/lib/railroader/processors/lib/processor_helper.rb +88 -0
- data/lib/railroader/processors/lib/rails2_config_processor.rb +145 -0
- data/lib/railroader/processors/lib/rails2_route_processor.rb +313 -0
- data/lib/railroader/processors/lib/rails3_config_processor.rb +132 -0
- data/lib/railroader/processors/lib/rails3_route_processor.rb +308 -0
- data/lib/railroader/processors/lib/render_helper.rb +181 -0
- data/lib/railroader/processors/lib/render_path.rb +107 -0
- data/lib/railroader/processors/lib/route_helper.rb +68 -0
- data/lib/railroader/processors/lib/safe_call_helper.rb +16 -0
- data/lib/railroader/processors/library_processor.rb +74 -0
- data/lib/railroader/processors/model_processor.rb +91 -0
- data/lib/railroader/processors/output_processor.rb +144 -0
- data/lib/railroader/processors/route_processor.rb +17 -0
- data/lib/railroader/processors/slim_template_processor.rb +111 -0
- data/lib/railroader/processors/template_alias_processor.rb +118 -0
- data/lib/railroader/processors/template_processor.rb +85 -0
- data/lib/railroader/report/config/remediation.yml +71 -0
- data/lib/railroader/report/ignore/config.rb +153 -0
- data/lib/railroader/report/ignore/interactive.rb +362 -0
- data/lib/railroader/report/pager.rb +112 -0
- data/lib/railroader/report/renderer.rb +24 -0
- data/lib/railroader/report/report_base.rb +292 -0
- data/lib/railroader/report/report_codeclimate.rb +79 -0
- data/lib/railroader/report/report_csv.rb +55 -0
- data/lib/railroader/report/report_hash.rb +23 -0
- data/lib/railroader/report/report_html.rb +216 -0
- data/lib/railroader/report/report_json.rb +45 -0
- data/lib/railroader/report/report_markdown.rb +107 -0
- data/lib/railroader/report/report_table.rb +117 -0
- data/lib/railroader/report/report_tabs.rb +17 -0
- data/lib/railroader/report/report_text.rb +198 -0
- data/lib/railroader/report/templates/controller_overview.html.erb +22 -0
- data/lib/railroader/report/templates/controller_warnings.html.erb +21 -0
- data/lib/railroader/report/templates/error_overview.html.erb +29 -0
- data/lib/railroader/report/templates/header.html.erb +58 -0
- data/lib/railroader/report/templates/ignored_warnings.html.erb +25 -0
- data/lib/railroader/report/templates/model_warnings.html.erb +21 -0
- data/lib/railroader/report/templates/overview.html.erb +38 -0
- data/lib/railroader/report/templates/security_warnings.html.erb +23 -0
- data/lib/railroader/report/templates/template_overview.html.erb +21 -0
- data/lib/railroader/report/templates/view_warnings.html.erb +34 -0
- data/lib/railroader/report/templates/warning_overview.html.erb +17 -0
- data/lib/railroader/report.rb +88 -0
- data/lib/railroader/rescanner.rb +483 -0
- data/lib/railroader/scanner.rb +321 -0
- data/lib/railroader/tracker/collection.rb +93 -0
- data/lib/railroader/tracker/config.rb +154 -0
- data/lib/railroader/tracker/constants.rb +171 -0
- data/lib/railroader/tracker/controller.rb +161 -0
- data/lib/railroader/tracker/library.rb +17 -0
- data/lib/railroader/tracker/model.rb +90 -0
- data/lib/railroader/tracker/template.rb +33 -0
- data/lib/railroader/tracker.rb +362 -0
- data/lib/railroader/util.rb +503 -0
- data/lib/railroader/version.rb +3 -0
- data/lib/railroader/warning.rb +294 -0
- data/lib/railroader/warning_codes.rb +117 -0
- data/lib/railroader.rb +544 -0
- data/lib/ruby_parser/bm_sexp.rb +626 -0
- data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
- metadata +386 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'railroader/processors/base_processor'
|
2
|
+
require 'railroader/tracker/template'
|
3
|
+
|
4
|
+
#Base Processor for templates/views
|
5
|
+
class Railroader::TemplateProcessor < Railroader::BaseProcessor
|
6
|
+
|
7
|
+
#Initializes template information.
|
8
|
+
def initialize tracker, template_name, called_from = nil, file_name = nil
|
9
|
+
super(tracker)
|
10
|
+
@current_template = Railroader::Template.new template_name, called_from, file_name, tracker
|
11
|
+
@file_name = file_name
|
12
|
+
|
13
|
+
if called_from
|
14
|
+
template_name = (template_name.to_s + "." + called_from.to_s).to_sym
|
15
|
+
end
|
16
|
+
|
17
|
+
tracker.templates[template_name] = @current_template
|
18
|
+
|
19
|
+
@inside_concat = false
|
20
|
+
end
|
21
|
+
|
22
|
+
#Process the template Sexp.
|
23
|
+
def process exp
|
24
|
+
begin
|
25
|
+
super
|
26
|
+
rescue => e
|
27
|
+
except = e.exception("Error when processing #{@current_template.name}: #{e.message}")
|
28
|
+
except.set_backtrace(e.backtrace)
|
29
|
+
raise except
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#Ignore initial variable assignment
|
34
|
+
def process_lasgn exp
|
35
|
+
if exp.lhs == :_erbout and exp.rhs.node_type == :str #ignore
|
36
|
+
ignore
|
37
|
+
elsif exp.lhs == :_buf and exp.rhs.node_type == :str
|
38
|
+
ignore
|
39
|
+
else
|
40
|
+
exp.rhs = process exp.rhs
|
41
|
+
exp
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
#Adds output to the list of outputs.
|
46
|
+
def process_output exp
|
47
|
+
exp.value = process exp.value
|
48
|
+
@current_template.add_output exp unless exp.original_line
|
49
|
+
exp
|
50
|
+
end
|
51
|
+
|
52
|
+
def process_escaped_output exp
|
53
|
+
process_output exp
|
54
|
+
end
|
55
|
+
|
56
|
+
# Pull out actual output value from template
|
57
|
+
def normalize_output arg
|
58
|
+
if call? arg and [:to_s, :html_safe!, :freeze].include? arg.method
|
59
|
+
arg.target
|
60
|
+
elsif node_type? arg, :if
|
61
|
+
branches = [arg.then_clause, arg.else_clause].compact
|
62
|
+
|
63
|
+
if branches.empty?
|
64
|
+
s(:nil)
|
65
|
+
elsif branches.length == 2
|
66
|
+
Sexp.new(:or, *branches)
|
67
|
+
else
|
68
|
+
branches.first
|
69
|
+
end
|
70
|
+
else
|
71
|
+
arg
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def add_escaped_output output
|
76
|
+
add_output output, :escaped_output
|
77
|
+
end
|
78
|
+
|
79
|
+
def add_output output, type = :output
|
80
|
+
s = Sexp.new(type, output)
|
81
|
+
s.line(output.line)
|
82
|
+
@current_template.add_output s
|
83
|
+
s
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
---
|
2
|
+
basic_auth_password: 300000
|
3
|
+
cross_site_scripting: 300000
|
4
|
+
xss_content_tag: 300000
|
5
|
+
CVE_2014_3514_call: 600000
|
6
|
+
all_default_routes: 2000000
|
7
|
+
unsafe_deserialize: 2000000
|
8
|
+
local_request_config: 100000
|
9
|
+
CVE_2012_3424: 4000000
|
10
|
+
CVE_2011_2932: 8000000
|
11
|
+
code_eval: 2000000
|
12
|
+
command_injection: 2000000
|
13
|
+
file_access: 2000000
|
14
|
+
CVE_2014_7829: 4000000
|
15
|
+
CVE_2011_2929: 4000000
|
16
|
+
csrf_protection_disabled: 4000000
|
17
|
+
CVE_2013_6414: 4000000
|
18
|
+
CVE_2013_4491: 4000000
|
19
|
+
CVE_2013_1856: 4000000
|
20
|
+
CVE_2015_3226: 4000000
|
21
|
+
CVE_2013_0333: 4000000
|
22
|
+
xss_link_to: 300000
|
23
|
+
xss_link_to_href: 300000
|
24
|
+
CVE_2011_0446: 300000
|
25
|
+
mass_assign_call: 2000000
|
26
|
+
dangerous_attr_accessible: 2000000
|
27
|
+
no_attr_accessible: 2000000
|
28
|
+
CVE_2013_0277: 2000000
|
29
|
+
CVE_2010_3933: 4000000
|
30
|
+
CVE_2014_0081: 300000
|
31
|
+
CVE_2011_2930: 600000
|
32
|
+
open_redirect: 300000
|
33
|
+
regex_dos: 600000
|
34
|
+
dynamic_render_path: 4000000
|
35
|
+
CVE_2014_0082: 4000000
|
36
|
+
cross_site_scripting_inline: 600000
|
37
|
+
CVE_2011_3186: 2000000
|
38
|
+
safe_buffer_vuln: 4000000
|
39
|
+
CVE_2013_1855: 4000000
|
40
|
+
CVE_2013_1857: 4000000
|
41
|
+
CVE_2012_3463: 600000
|
42
|
+
select_options_vuln: 4000000
|
43
|
+
dangerous_send: 600000
|
44
|
+
session_key_manipulation: 600000
|
45
|
+
http_cookies: 600000
|
46
|
+
session_secret: 600000
|
47
|
+
secure_cookies: 600000
|
48
|
+
CVE_2013_6416: 600000
|
49
|
+
CVE_2012_3464: 4000000
|
50
|
+
csrf_blacklist: 300000
|
51
|
+
auth_blacklist: 300000
|
52
|
+
sql_injection: 1200000
|
53
|
+
CVE-2012-2660: 4000000
|
54
|
+
CVE-2012-2661: 4000000
|
55
|
+
CVE-2012-2695: 4000000
|
56
|
+
CVE-2012-5664: 4000000
|
57
|
+
CVE-2013-0155: 4000000
|
58
|
+
CVE-2013-6417: 4000000
|
59
|
+
CVE-2014-3482: 4000000
|
60
|
+
CVE-2014-3483: 4000000
|
61
|
+
ssl_verification_bypass: 2500000
|
62
|
+
CVE_2011_2931: 4000000
|
63
|
+
unsafe_symbol_creation: 300000
|
64
|
+
translate_vuln: 300000
|
65
|
+
unsafe_constantize: 600000
|
66
|
+
unscoped_find: 300000
|
67
|
+
validation_regex: 300000
|
68
|
+
mass_assign_without_protection: 600000
|
69
|
+
CVE_2015_3227: 4000000
|
70
|
+
CVE_2013_0156: 4000000
|
71
|
+
weak_hash_digest: 800000
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
module Railroader
|
5
|
+
class IgnoreConfig
|
6
|
+
attr_reader :shown_warnings, :ignored_warnings
|
7
|
+
attr_accessor :file
|
8
|
+
|
9
|
+
def initialize file, new_warnings
|
10
|
+
@file = file
|
11
|
+
@new_warnings = new_warnings
|
12
|
+
@already_ignored = []
|
13
|
+
@ignored_fingerprints = Set.new
|
14
|
+
@used_fingerprints = Set.new
|
15
|
+
@notes = {}
|
16
|
+
@shown_warnings = @ignored_warnings = nil
|
17
|
+
@changed = false
|
18
|
+
end
|
19
|
+
|
20
|
+
# Populate ignored_warnings and shown_warnings based on ignore
|
21
|
+
# configuration
|
22
|
+
def filter_ignored
|
23
|
+
@shown_warnings = []
|
24
|
+
@ignored_warnings = []
|
25
|
+
|
26
|
+
@new_warnings.each do |w|
|
27
|
+
if ignored? w
|
28
|
+
@ignored_warnings << w
|
29
|
+
else
|
30
|
+
@shown_warnings << w
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
@shown_warnings
|
35
|
+
end
|
36
|
+
|
37
|
+
# Remove warning from ignored list
|
38
|
+
def unignore warning
|
39
|
+
@ignored_fingerprints.delete warning.fingerprint
|
40
|
+
if @already_ignored.reject! { |w|w[:fingerprint] == warning.fingerprint }
|
41
|
+
@changed = true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Determine if warning should be ignored
|
46
|
+
def ignored? warning
|
47
|
+
@used_fingerprints << warning.fingerprint
|
48
|
+
@ignored_fingerprints.include? warning.fingerprint
|
49
|
+
end
|
50
|
+
|
51
|
+
def ignore warning
|
52
|
+
@changed = true unless ignored? warning
|
53
|
+
@ignored_fingerprints << warning.fingerprint
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add note for warning
|
57
|
+
def add_note warning, note
|
58
|
+
@changed = true
|
59
|
+
@notes[warning.fingerprint] = note
|
60
|
+
end
|
61
|
+
|
62
|
+
# Retrieve note for warning if it exists. Returns nil if no
|
63
|
+
# note is found
|
64
|
+
def note_for warning
|
65
|
+
if warning.is_a? Warning
|
66
|
+
fingerprint = warning.fingerprint
|
67
|
+
else
|
68
|
+
fingerprint = warning[:fingerprint]
|
69
|
+
end
|
70
|
+
|
71
|
+
@already_ignored.each do |w|
|
72
|
+
if fingerprint == w[:fingerprint]
|
73
|
+
return w[:note]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
# The set of unused ignore entries
|
81
|
+
def obsolete_fingerprints
|
82
|
+
(@ignored_fingerprints - @used_fingerprints).to_a
|
83
|
+
end
|
84
|
+
|
85
|
+
def prune_obsolete
|
86
|
+
obsolete = obsolete_fingerprints.to_set
|
87
|
+
@ignored_fingerprints -= obsolete
|
88
|
+
|
89
|
+
@already_ignored.reject! do |w|
|
90
|
+
if obsolete.include? w[:fingerprint]
|
91
|
+
@changed = true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Read configuration to file
|
97
|
+
def read_from_file file = @file
|
98
|
+
if File.exist? file
|
99
|
+
@already_ignored = JSON.parse(File.read(file), :symbolize_names => true)[:ignored_warnings]
|
100
|
+
else
|
101
|
+
Railroader.notify "[Notice] Could not find ignore configuration in #{file}"
|
102
|
+
@already_ignored = []
|
103
|
+
end
|
104
|
+
|
105
|
+
@already_ignored.each do |w|
|
106
|
+
@ignored_fingerprints << w[:fingerprint]
|
107
|
+
@notes[w[:fingerprint]] = w[:note]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Save configuration to file
|
112
|
+
def save_to_file warnings, file = @file
|
113
|
+
warnings = warnings.map do |w|
|
114
|
+
if w.is_a? Warning
|
115
|
+
w_hash = w.to_hash
|
116
|
+
w_hash[:file] = w.relative_path
|
117
|
+
w = w_hash
|
118
|
+
end
|
119
|
+
|
120
|
+
w[:note] = @notes[w[:fingerprint]] || ""
|
121
|
+
w
|
122
|
+
end.sort_by { |w| w[:fingerprint] }
|
123
|
+
|
124
|
+
output = {
|
125
|
+
:ignored_warnings => warnings,
|
126
|
+
:updated => Time.now.to_s,
|
127
|
+
:railroader_version => Railroader::Version
|
128
|
+
}
|
129
|
+
|
130
|
+
File.open file, "w" do |f|
|
131
|
+
f.puts JSON.pretty_generate(output)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Save old ignored warnings and newly ignored ones
|
136
|
+
def save_with_old
|
137
|
+
warnings = @ignored_warnings.dup
|
138
|
+
|
139
|
+
# Only add ignored warnings not already ignored
|
140
|
+
@already_ignored.each do |w|
|
141
|
+
fingerprint = w[:fingerprint]
|
142
|
+
|
143
|
+
unless @ignored_warnings.find { |ignored_warning| ignored_warning.fingerprint == fingerprint }
|
144
|
+
warnings << w
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
if @changed
|
149
|
+
save_to_file warnings
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,362 @@
|
|
1
|
+
Railroader.load_railroader_dependency 'highline'
|
2
|
+
|
3
|
+
module Railroader
|
4
|
+
class InteractiveIgnorer
|
5
|
+
def initialize file, warnings
|
6
|
+
@ignore_config = Railroader::IgnoreConfig.new(file, warnings)
|
7
|
+
@new_warnings = warnings
|
8
|
+
@skip_ignored = false
|
9
|
+
@skip_rest = false
|
10
|
+
@ignore_rest = false
|
11
|
+
@quit = false
|
12
|
+
@restart = false
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
file_menu
|
17
|
+
initial_menu
|
18
|
+
|
19
|
+
@ignore_config.filter_ignored
|
20
|
+
|
21
|
+
unless @quit
|
22
|
+
penultimate_menu
|
23
|
+
final_menu
|
24
|
+
end
|
25
|
+
|
26
|
+
if @restart
|
27
|
+
@restart = false
|
28
|
+
start
|
29
|
+
end
|
30
|
+
|
31
|
+
@ignore_config
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def file_menu
|
37
|
+
loop do
|
38
|
+
@ignore_config.file = HighLine.new.ask "Input file: " do |q|
|
39
|
+
if @ignore_config.file and not @ignore_config.file.empty?
|
40
|
+
q.default = @ignore_config.file
|
41
|
+
else
|
42
|
+
q.default = "config/railroader.ignore"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if File.exist? @ignore_config.file
|
47
|
+
@ignore_config.read_from_file
|
48
|
+
return
|
49
|
+
else
|
50
|
+
if yes_or_no "No such file. Continue with empty config? "
|
51
|
+
return
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def initial_menu
|
58
|
+
HighLine.new.choose do |m|
|
59
|
+
m.choice "Inspect all warnings" do
|
60
|
+
@skip_ignored = false
|
61
|
+
pre_show_help
|
62
|
+
process_warnings
|
63
|
+
end
|
64
|
+
|
65
|
+
m.choice "Hide previously ignored warnings" do
|
66
|
+
@skip_ignored = true
|
67
|
+
pre_show_help
|
68
|
+
process_warnings
|
69
|
+
end
|
70
|
+
|
71
|
+
m.choice "Prune obsolete ignored warnings" do
|
72
|
+
prune_obsolete
|
73
|
+
end
|
74
|
+
|
75
|
+
m.choice "Skip - use current ignore configuration" do
|
76
|
+
@quit = true
|
77
|
+
@ignore_config.filter_ignored
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def warning_menu
|
83
|
+
HighLine.new.choose do |m|
|
84
|
+
m.prompt = "Action: "
|
85
|
+
m.layout = :one_line
|
86
|
+
m.list_option = ", "
|
87
|
+
m.select_by = :name
|
88
|
+
|
89
|
+
m.choice "i"
|
90
|
+
m.choice "n"
|
91
|
+
m.choice "k"
|
92
|
+
m.choice "u"
|
93
|
+
m.choice "a"
|
94
|
+
m.choice "s"
|
95
|
+
m.choice "q"
|
96
|
+
m.choice "?" do
|
97
|
+
show_help
|
98
|
+
"?"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def pre_show_help
|
104
|
+
say "-" * 30
|
105
|
+
say "Actions:", :cyan
|
106
|
+
show_help
|
107
|
+
end
|
108
|
+
|
109
|
+
def show_help
|
110
|
+
say <<-HELP
|
111
|
+
i - Add warning to ignore list
|
112
|
+
n - Add warning to ignore list and add note
|
113
|
+
s - Skip this warning (will remain ignored or shown)
|
114
|
+
u - Remove this warning from ignore list
|
115
|
+
a - Ignore this warning and all remaining warnings
|
116
|
+
k - Skip this warning and all remaining warnings
|
117
|
+
q - Quit, do not update ignored warnings
|
118
|
+
? - Display this help
|
119
|
+
HELP
|
120
|
+
end
|
121
|
+
|
122
|
+
def penultimate_menu
|
123
|
+
obsolete = @ignore_config.obsolete_fingerprints
|
124
|
+
return unless obsolete.any?
|
125
|
+
|
126
|
+
if obsolete.length > 1
|
127
|
+
plural = 's'
|
128
|
+
verb = 'are'
|
129
|
+
else
|
130
|
+
plural = ''
|
131
|
+
verb = 'is'
|
132
|
+
end
|
133
|
+
|
134
|
+
say "\n#{obsolete.length} fingerprint#{plural} #{verb} unused:", :green
|
135
|
+
obsolete.each do |obs|
|
136
|
+
say obs
|
137
|
+
end
|
138
|
+
|
139
|
+
if yes_or_no "\nRemove fingerprint#{plural}?"
|
140
|
+
@ignore_config.prune_obsolete
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def prune_obsolete
|
145
|
+
@ignore_config.filter_ignored
|
146
|
+
obsolete = @ignore_config.obsolete_fingerprints
|
147
|
+
@ignore_config.prune_obsolete
|
148
|
+
|
149
|
+
say "Removed #{obsolete.length} obsolete fingerprint#{'s' if obsolete.length > 1} from ignore config.", :yellow
|
150
|
+
end
|
151
|
+
|
152
|
+
def final_menu
|
153
|
+
summarize_changes
|
154
|
+
|
155
|
+
HighLine.new.choose do |m|
|
156
|
+
m.choice "Save changes" do
|
157
|
+
save
|
158
|
+
end
|
159
|
+
|
160
|
+
m.choice "Start over" do
|
161
|
+
start_over
|
162
|
+
end
|
163
|
+
|
164
|
+
m.choice "Quit, do not save changes" do
|
165
|
+
quit
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def save
|
171
|
+
@ignore_config.file = HighLine.new.ask "Output file: " do |q|
|
172
|
+
if @ignore_config.file and not @ignore_config.file.empty?
|
173
|
+
q.default = @ignore_config.file
|
174
|
+
else
|
175
|
+
q.default = "config/railroader.ignore"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
@ignore_config.save_with_old
|
180
|
+
end
|
181
|
+
|
182
|
+
def start_over
|
183
|
+
reset_config
|
184
|
+
@restart = true
|
185
|
+
end
|
186
|
+
|
187
|
+
def reset_config
|
188
|
+
@ignore_config = Railroader::IgnoreConfig.new(@ignore_config.file, @new_warnings)
|
189
|
+
end
|
190
|
+
|
191
|
+
def process_warnings
|
192
|
+
@warning_count = @new_warnings.length
|
193
|
+
|
194
|
+
@new_warnings.each_with_index do |w, index|
|
195
|
+
@current_index = index
|
196
|
+
|
197
|
+
if skip_ignored? w or @skip_rest
|
198
|
+
next
|
199
|
+
elsif @ignore_rest
|
200
|
+
ignore w
|
201
|
+
elsif @quit or @restart
|
202
|
+
return
|
203
|
+
else
|
204
|
+
ask_about w
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def ask_about warning
|
210
|
+
pretty_display warning
|
211
|
+
warning_action warning_menu, warning
|
212
|
+
end
|
213
|
+
|
214
|
+
def warning_action action, warning
|
215
|
+
case action
|
216
|
+
when "i"
|
217
|
+
ignore warning
|
218
|
+
when "n"
|
219
|
+
ignore_and_note warning
|
220
|
+
when "s"
|
221
|
+
# do nothing
|
222
|
+
when "u"
|
223
|
+
unignore warning
|
224
|
+
when "a"
|
225
|
+
ignore_rest warning
|
226
|
+
when "k"
|
227
|
+
skip_rest warning
|
228
|
+
when "q"
|
229
|
+
quit
|
230
|
+
when "?"
|
231
|
+
ask_about warning
|
232
|
+
else
|
233
|
+
raise "Unexpected action"
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def ignore warning
|
238
|
+
@ignore_config.ignore warning
|
239
|
+
end
|
240
|
+
|
241
|
+
def ignore_and_note warning
|
242
|
+
note = HighLine.new.ask("Note: ")
|
243
|
+
@ignore_config.ignore warning
|
244
|
+
@ignore_config.add_note warning, note
|
245
|
+
end
|
246
|
+
|
247
|
+
def unignore warning
|
248
|
+
@ignore_config.unignore warning
|
249
|
+
end
|
250
|
+
|
251
|
+
def skip_rest warning
|
252
|
+
@skip_rest = true
|
253
|
+
end
|
254
|
+
|
255
|
+
def ignore_rest warning
|
256
|
+
ignore warning
|
257
|
+
@ignore_rest = true
|
258
|
+
end
|
259
|
+
|
260
|
+
def quit
|
261
|
+
reset_config
|
262
|
+
@ignore_config.read_from_file
|
263
|
+
@ignore_config.filter_ignored
|
264
|
+
@quit = true
|
265
|
+
end
|
266
|
+
|
267
|
+
def pretty_display warning
|
268
|
+
progress = "#{@current_index + 1}/#{@warning_count}"
|
269
|
+
say "-------- #{progress} #{"-" * (20 - progress.length)}", :cyan
|
270
|
+
show_confidence warning
|
271
|
+
|
272
|
+
label "Category"
|
273
|
+
say warning.warning_type
|
274
|
+
|
275
|
+
label "Message"
|
276
|
+
say warning.message
|
277
|
+
|
278
|
+
if warning.code
|
279
|
+
label "Code"
|
280
|
+
say warning.format_code
|
281
|
+
end
|
282
|
+
|
283
|
+
if warning.relative_path
|
284
|
+
label "File"
|
285
|
+
say warning.relative_path
|
286
|
+
end
|
287
|
+
|
288
|
+
if warning.line
|
289
|
+
label "Line"
|
290
|
+
say warning.line
|
291
|
+
end
|
292
|
+
|
293
|
+
if already_ignored? warning
|
294
|
+
show_note warning
|
295
|
+
say "Already ignored", :red
|
296
|
+
end
|
297
|
+
|
298
|
+
say ""
|
299
|
+
end
|
300
|
+
|
301
|
+
def already_ignored? warning
|
302
|
+
@ignore_config.ignored? warning
|
303
|
+
end
|
304
|
+
|
305
|
+
def skip_ignored? warning
|
306
|
+
@skip_ignored and already_ignored? warning
|
307
|
+
end
|
308
|
+
|
309
|
+
def summarize_changes
|
310
|
+
say "-" * 30
|
311
|
+
|
312
|
+
say "Ignoring #{@ignore_config.ignored_warnings.length} warnings", :yellow
|
313
|
+
say "Showing #{@ignore_config.shown_warnings.length} warnings", :green
|
314
|
+
end
|
315
|
+
|
316
|
+
def label name
|
317
|
+
say "#{name}: ", :green
|
318
|
+
end
|
319
|
+
|
320
|
+
def show_confidence warning
|
321
|
+
label "Confidence"
|
322
|
+
|
323
|
+
case warning.confidence
|
324
|
+
when 0
|
325
|
+
say "High", :red
|
326
|
+
when 1
|
327
|
+
say "Medium", :yellow
|
328
|
+
when 2
|
329
|
+
say "Weak", :cyan
|
330
|
+
else
|
331
|
+
say "Unknown"
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def show_note warning
|
336
|
+
note = @ignore_config.note_for warning
|
337
|
+
|
338
|
+
if note
|
339
|
+
label "Note"
|
340
|
+
say note
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def say text, color = nil
|
345
|
+
text = text.to_s
|
346
|
+
|
347
|
+
if color
|
348
|
+
HighLine.new.say HighLine.new.color(text, color)
|
349
|
+
else
|
350
|
+
HighLine.new.say text
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def yes_or_no message
|
355
|
+
answer = HighLine.new.ask message do |q|
|
356
|
+
q.in = ["y", "n", "yes", "no"]
|
357
|
+
end
|
358
|
+
|
359
|
+
answer.match /^y/i
|
360
|
+
end
|
361
|
+
end
|
362
|
+
end
|