railroader 4.3.4
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 +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,112 @@
|
|
|
1
|
+
module Railroader
|
|
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
|
+
Railroader.load_railroader_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
|
+
return unless @tracker
|
|
106
|
+
|
|
107
|
+
unless less_options.include? "-R " or @tracker.options[:output_color] == :force
|
|
108
|
+
@tracker.options[:output_color] = false
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'erb'
|
|
2
|
+
|
|
3
|
+
class Railroader::Report
|
|
4
|
+
class Renderer
|
|
5
|
+
def initialize(template_file, hash = {})
|
|
6
|
+
hash[:locals] ||= {}
|
|
7
|
+
singleton = class << self; self end
|
|
8
|
+
|
|
9
|
+
hash[:locals].each do |attribute_name, attribute_value|
|
|
10
|
+
singleton.send(:define_method, attribute_name) { attribute_value }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# There are last, so as to make overwriting these using locals impossible.
|
|
14
|
+
singleton.send(:define_method, 'template_file') { template_file }
|
|
15
|
+
singleton.send(:define_method, 'template') {
|
|
16
|
+
File.read(File.expand_path("templates/#{template_file}.html.erb", File.dirname(__FILE__)))
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render
|
|
21
|
+
ERB.new(template).result(binding)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
require 'railroader/util'
|
|
3
|
+
require 'railroader/version'
|
|
4
|
+
require 'railroader/report/renderer'
|
|
5
|
+
require 'railroader/processors/output_processor'
|
|
6
|
+
require 'railroader/warning'
|
|
7
|
+
|
|
8
|
+
# Base class for report formats
|
|
9
|
+
class Railroader::Report::Base
|
|
10
|
+
include Railroader::Util
|
|
11
|
+
|
|
12
|
+
attr_reader :tracker, :checks
|
|
13
|
+
|
|
14
|
+
TEXT_CONFIDENCE = Railroader::Warning::TEXT_CONFIDENCE
|
|
15
|
+
|
|
16
|
+
def initialize app_tree, tracker
|
|
17
|
+
@app_tree = app_tree
|
|
18
|
+
@tracker = tracker
|
|
19
|
+
@checks = tracker.checks
|
|
20
|
+
@ignore_filter = tracker.ignored_filter
|
|
21
|
+
@highlight_user_input = tracker.options[:highlight_user_input]
|
|
22
|
+
@warnings_summary = nil
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
#Generate table of how many warnings of each warning type were reported
|
|
26
|
+
def generate_warning_overview
|
|
27
|
+
types = warnings_summary.keys
|
|
28
|
+
types.delete :high_confidence
|
|
29
|
+
values = types.sort.collect{|warning_type| [warning_type, warnings_summary[warning_type]] }
|
|
30
|
+
locals = {:types => types, :warnings_summary => warnings_summary}
|
|
31
|
+
|
|
32
|
+
render_array('warning_overview', ['Warning Type', 'Total'], values, locals)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
#Generate table of controllers and routes found for those controllers
|
|
36
|
+
def generate_controllers
|
|
37
|
+
controller_rows = []
|
|
38
|
+
|
|
39
|
+
tracker.controllers.keys.map{|k| k.to_s}.sort.each do |name|
|
|
40
|
+
name = name.to_sym
|
|
41
|
+
c = tracker.controllers[name]
|
|
42
|
+
|
|
43
|
+
if tracker.routes.include? :allow_all_actions or (tracker.routes[name] and tracker.routes[name].include? :allow_all_actions)
|
|
44
|
+
routes = c.methods_public.keys.map{|e| e.to_s}.sort.join(", ")
|
|
45
|
+
elsif tracker.routes[name].nil?
|
|
46
|
+
#No routes defined for this controller.
|
|
47
|
+
#This can happen when it is only a parent class
|
|
48
|
+
#for other controllers, for example.
|
|
49
|
+
routes = "[None]"
|
|
50
|
+
|
|
51
|
+
else
|
|
52
|
+
routes = (Set.new(c.methods_public.keys) & tracker.routes[name.to_sym]).
|
|
53
|
+
to_a.
|
|
54
|
+
map {|e| e.to_s}.
|
|
55
|
+
sort.
|
|
56
|
+
join(", ")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if routes == ""
|
|
60
|
+
routes = "[None]"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
controller_rows << { "Name" => name.to_s,
|
|
64
|
+
"Parent" => c.parent.to_s,
|
|
65
|
+
"Includes" => c.includes.join(", "),
|
|
66
|
+
"Routes" => routes
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
cols = ['Name', 'Parent', 'Includes', 'Routes']
|
|
71
|
+
|
|
72
|
+
locals = {:controller_rows => controller_rows}
|
|
73
|
+
values = controller_rows.collect{|row| row.values_at(*cols) }
|
|
74
|
+
render_array('controller_overview', cols, values, locals)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
#Generate table of errors or return nil if no errors
|
|
78
|
+
def generate_errors
|
|
79
|
+
values = tracker.errors.collect{|error| [error[:error], error[:backtrace][0]]}
|
|
80
|
+
render_array('error_overview', ['Error', 'Location'], values, {:tracker => tracker})
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def generate_obsolete
|
|
84
|
+
values = tracker.unused_fingerprints.collect{|fingerprint| [fingerprint] }
|
|
85
|
+
render_array('obsolete_ignore_entries', ['fingerprint'], values, {:tracker => tracker})
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def generate_warnings
|
|
89
|
+
render_warnings generic_warnings,
|
|
90
|
+
:warning,
|
|
91
|
+
'security_warnings',
|
|
92
|
+
["Confidence", "Class", "Method", "Warning Type", "Message"],
|
|
93
|
+
'Class'
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
#Generate table of template warnings or return nil if no warnings
|
|
97
|
+
def generate_template_warnings
|
|
98
|
+
render_warnings template_warnings,
|
|
99
|
+
:template,
|
|
100
|
+
'view_warnings',
|
|
101
|
+
['Confidence', 'Template', 'Warning Type', 'Message'],
|
|
102
|
+
'Template'
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
#Generate table of model warnings or return nil if no warnings
|
|
107
|
+
def generate_model_warnings
|
|
108
|
+
render_warnings model_warnings,
|
|
109
|
+
:model,
|
|
110
|
+
'model_warnings',
|
|
111
|
+
['Confidence', 'Model', 'Warning Type', 'Message'],
|
|
112
|
+
'Model'
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
#Generate table of controller warnings or nil if no warnings
|
|
116
|
+
def generate_controller_warnings
|
|
117
|
+
render_warnings controller_warnings,
|
|
118
|
+
:controller,
|
|
119
|
+
'controller_warnings',
|
|
120
|
+
['Confidence', 'Controller', 'Warning Type', 'Message'],
|
|
121
|
+
'Controller'
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def generate_ignored_warnings
|
|
125
|
+
render_warnings ignored_warnings,
|
|
126
|
+
:ignored,
|
|
127
|
+
'ignored_warnings',
|
|
128
|
+
['Confidence', 'Warning Type', 'File', 'Message'],
|
|
129
|
+
'Warning Type'
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def render_warnings warnings, type, template, cols, sort_col
|
|
133
|
+
unless warnings.empty?
|
|
134
|
+
rows = sort(convert_to_rows(warnings, type), sort_col)
|
|
135
|
+
|
|
136
|
+
values = rows.collect { |row| row.values_at(*cols) }
|
|
137
|
+
|
|
138
|
+
locals = { :warnings => rows }
|
|
139
|
+
|
|
140
|
+
render_array(template, cols, values, locals)
|
|
141
|
+
else
|
|
142
|
+
nil
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def convert_to_rows warnings, type = :warning
|
|
147
|
+
warnings.map do |warning|
|
|
148
|
+
w = warning.to_row type
|
|
149
|
+
|
|
150
|
+
case type
|
|
151
|
+
when :warning
|
|
152
|
+
convert_warning w, warning
|
|
153
|
+
when :template
|
|
154
|
+
convert_template_warning w, warning
|
|
155
|
+
when :model
|
|
156
|
+
convert_model_warning w, warning
|
|
157
|
+
when :controller
|
|
158
|
+
convert_controller_warning w, warning
|
|
159
|
+
when :ignored
|
|
160
|
+
convert_ignored_warning w, warning
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def convert_warning warning, original
|
|
166
|
+
warning["Confidence"] = TEXT_CONFIDENCE[warning["Confidence"]]
|
|
167
|
+
warning["Message"] = text_message original, warning["Message"]
|
|
168
|
+
warning
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def convert_template_warning warning, original
|
|
172
|
+
convert_warning warning, original
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def convert_model_warning warning, original
|
|
176
|
+
convert_warning warning, original
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def convert_controller_warning warning, original
|
|
180
|
+
convert_warning warning, original
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def convert_ignored_warning warning, original
|
|
184
|
+
convert_warning warning, original
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def sort rows, sort_col
|
|
188
|
+
stabilizer = 0
|
|
189
|
+
rows.sort_by do |row|
|
|
190
|
+
stabilizer += 1
|
|
191
|
+
|
|
192
|
+
row.values_at("Confidence", "Warning Type", sort_col) << stabilizer
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
#Return summary of warnings in hash and store in @warnings_summary
|
|
197
|
+
def warnings_summary
|
|
198
|
+
return @warnings_summary if @warnings_summary
|
|
199
|
+
|
|
200
|
+
summary = Hash.new(0)
|
|
201
|
+
high_confidence_warnings = 0
|
|
202
|
+
|
|
203
|
+
[all_warnings].each do |warnings|
|
|
204
|
+
warnings.each do |warning|
|
|
205
|
+
summary[warning.warning_type.to_s] += 1
|
|
206
|
+
high_confidence_warnings += 1 if warning.confidence == 0
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
summary[:high_confidence] = high_confidence_warnings
|
|
211
|
+
@warnings_summary = summary
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def all_warnings
|
|
215
|
+
if @ignore_filter
|
|
216
|
+
@all_warnings ||= @ignore_filter.shown_warnings
|
|
217
|
+
else
|
|
218
|
+
@all_warnings ||= tracker.checks.all_warnings
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def filter_warnings warnings
|
|
223
|
+
if @ignore_filter
|
|
224
|
+
warnings.reject do |w|
|
|
225
|
+
@ignore_filter.ignored? w
|
|
226
|
+
end
|
|
227
|
+
else
|
|
228
|
+
warnings
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def generic_warnings
|
|
233
|
+
filter_warnings tracker.checks.warnings
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def template_warnings
|
|
237
|
+
filter_warnings tracker.checks.template_warnings
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def model_warnings
|
|
241
|
+
filter_warnings tracker.checks.model_warnings
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def controller_warnings
|
|
245
|
+
filter_warnings tracker.checks.controller_warnings
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def ignored_warnings
|
|
249
|
+
if @ignore_filter
|
|
250
|
+
@ignore_filter.ignored_warnings
|
|
251
|
+
else
|
|
252
|
+
[]
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def number_of_templates tracker
|
|
257
|
+
Set.new(tracker.templates.map {|k,v| v.name.to_s[/[^.]+/]}).length
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def warning_file warning, absolute = @tracker.options[:absolute_paths]
|
|
261
|
+
return nil if warning.file.nil?
|
|
262
|
+
|
|
263
|
+
if absolute
|
|
264
|
+
warning.file
|
|
265
|
+
else
|
|
266
|
+
relative_path warning.file
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def rails_version
|
|
271
|
+
case
|
|
272
|
+
when tracker.config.rails_version
|
|
273
|
+
tracker.config.rails_version
|
|
274
|
+
when tracker.options[:rails4]
|
|
275
|
+
"4.x"
|
|
276
|
+
when tracker.options[:rails3]
|
|
277
|
+
"3.x"
|
|
278
|
+
else
|
|
279
|
+
"Unknown"
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
#Escape warning message and highlight user input in text output
|
|
284
|
+
def text_message warning, message
|
|
285
|
+
if @highlight_user_input and warning.user_input
|
|
286
|
+
user_input = warning.format_user_input
|
|
287
|
+
message.gsub(user_input, "+#{user_input}+")
|
|
288
|
+
else
|
|
289
|
+
message
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require "yaml"
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
class Railroader::Report::CodeClimate < Railroader::Report::Base
|
|
6
|
+
DOCUMENTATION_PATH = File.expand_path("../../../../docs/warning_types", __FILE__)
|
|
7
|
+
REMEDIATION_POINTS_CONFIG_PATH = File.expand_path("../config/remediation.yml", __FILE__)
|
|
8
|
+
REMEDIATION_POINTS_DEFAULT = 300_000
|
|
9
|
+
|
|
10
|
+
def generate_report
|
|
11
|
+
all_warnings.map { |warning| issue_json(warning) }.join("\0")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def issue_json(warning)
|
|
17
|
+
warning_code_name = name_for(warning.warning_code)
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
type: "Issue",
|
|
21
|
+
check_name: warning_code_name,
|
|
22
|
+
description: warning.message,
|
|
23
|
+
fingerprint: warning.fingerprint,
|
|
24
|
+
categories: ["Security"],
|
|
25
|
+
severity: severity_level_for(warning.confidence),
|
|
26
|
+
remediation_points: remediation_points_for(warning_code_name),
|
|
27
|
+
location: {
|
|
28
|
+
path: file_path(warning),
|
|
29
|
+
lines: {
|
|
30
|
+
begin: warning.line || 1,
|
|
31
|
+
end: warning.line || 1,
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
content: {
|
|
35
|
+
body: content_for(warning.warning_code, warning.link)
|
|
36
|
+
}
|
|
37
|
+
}.to_json
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def severity_level_for(confidence)
|
|
41
|
+
if confidence == 0
|
|
42
|
+
"critical"
|
|
43
|
+
else
|
|
44
|
+
"normal"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def remediation_points_for(warning_code)
|
|
49
|
+
@remediation_points ||= YAML.load_file(REMEDIATION_POINTS_CONFIG_PATH)
|
|
50
|
+
@remediation_points.fetch(name_for(warning_code), REMEDIATION_POINTS_DEFAULT)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def name_for(warning_code)
|
|
54
|
+
@warning_codes ||= Railroader::WarningCodes::Codes.invert
|
|
55
|
+
@warning_codes[warning_code].to_s
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def content_for(warning_code, link)
|
|
59
|
+
@contents ||= {}
|
|
60
|
+
unless link.nil?
|
|
61
|
+
@contents[warning_code] ||= local_content_for(link) || "Read more: #{link}"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def local_content_for(link)
|
|
66
|
+
directory = link.split("/").last
|
|
67
|
+
filename = File.join(DOCUMENTATION_PATH, directory, "index.markdown")
|
|
68
|
+
|
|
69
|
+
File.read(filename) if File.exist?(filename)
|
|
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
|
|
79
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
require "railroader/report/report_table"
|
|
3
|
+
|
|
4
|
+
class Railroader::Report::CSV < Railroader::Report::Table
|
|
5
|
+
def generate_report
|
|
6
|
+
output = csv_header
|
|
7
|
+
output << "\nSUMMARY\n"
|
|
8
|
+
|
|
9
|
+
output << table_to_csv(generate_overview) << "\n"
|
|
10
|
+
|
|
11
|
+
output << table_to_csv(generate_warning_overview) << "\n"
|
|
12
|
+
|
|
13
|
+
#Return output early if only summarizing
|
|
14
|
+
if tracker.options[:summary_only]
|
|
15
|
+
return output
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
if tracker.options[:report_routes] or tracker.options[:debug]
|
|
19
|
+
output << "CONTROLLERS\n"
|
|
20
|
+
output << table_to_csv(generate_controllers) << "\n"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if tracker.options[:debug]
|
|
24
|
+
output << "TEMPLATES\n\n"
|
|
25
|
+
output << table_to_csv(generate_templates) << "\n"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
res = generate_errors
|
|
29
|
+
output << "ERRORS\n" << table_to_csv(res) << "\n" if res
|
|
30
|
+
|
|
31
|
+
res = generate_warnings
|
|
32
|
+
output << "SECURITY WARNINGS\n" << table_to_csv(res) << "\n" if res
|
|
33
|
+
|
|
34
|
+
output << "Controller Warnings\n"
|
|
35
|
+
res = generate_controller_warnings
|
|
36
|
+
output << table_to_csv(res) << "\n" if res
|
|
37
|
+
|
|
38
|
+
output << "Model Warnings\n"
|
|
39
|
+
res = generate_model_warnings
|
|
40
|
+
output << table_to_csv(res) << "\n" if res
|
|
41
|
+
|
|
42
|
+
res = generate_template_warnings
|
|
43
|
+
output << "Template Warnings\n"
|
|
44
|
+
output << table_to_csv(res) << "\n" if res
|
|
45
|
+
|
|
46
|
+
output
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
#Generate header for CSV output
|
|
50
|
+
def csv_header
|
|
51
|
+
header = CSV.generate_line(["Application Path", "Report Generation Time", "Checks Performed", "Rails Version"])
|
|
52
|
+
header << CSV.generate_line([File.expand_path(tracker.app_path), Time.now.to_s, checks.checks_run.sort.join(", "), rails_version])
|
|
53
|
+
"BRAKEMAN REPORT\n\n" + header
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Generates a hash table for use in Railroader tests
|
|
2
|
+
class Railroader::Report::Hash < Railroader::Report::Base
|
|
3
|
+
def generate_report
|
|
4
|
+
report = { :errors => tracker.errors,
|
|
5
|
+
:controllers => tracker.controllers,
|
|
6
|
+
:models => tracker.models,
|
|
7
|
+
:templates => tracker.templates
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
[:generic_warnings, :controller_warnings, :model_warnings, :template_warnings].each do |meth|
|
|
11
|
+
report[meth] = self.send(meth)
|
|
12
|
+
report[meth].each do |w|
|
|
13
|
+
w.message = w.format_message
|
|
14
|
+
w.context = context_for(@app_tree, w).join("\n")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
report[:config] = tracker.config
|
|
19
|
+
report[:checks_run] = tracker.checks.checks_run
|
|
20
|
+
|
|
21
|
+
report
|
|
22
|
+
end
|
|
23
|
+
end
|