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.
Files changed (165) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES.md +1091 -0
  3. data/FEATURES +16 -0
  4. data/README.md +174 -0
  5. data/bin/railroader +8 -0
  6. data/lib/railroader/app_tree.rb +191 -0
  7. data/lib/railroader/call_index.rb +219 -0
  8. data/lib/railroader/checks/base_check.rb +505 -0
  9. data/lib/railroader/checks/check_basic_auth.rb +88 -0
  10. data/lib/railroader/checks/check_basic_auth_timing_attack.rb +33 -0
  11. data/lib/railroader/checks/check_content_tag.rb +200 -0
  12. data/lib/railroader/checks/check_create_with.rb +74 -0
  13. data/lib/railroader/checks/check_cross_site_scripting.rb +381 -0
  14. data/lib/railroader/checks/check_default_routes.rb +86 -0
  15. data/lib/railroader/checks/check_deserialize.rb +56 -0
  16. data/lib/railroader/checks/check_detailed_exceptions.rb +55 -0
  17. data/lib/railroader/checks/check_digest_dos.rb +38 -0
  18. data/lib/railroader/checks/check_divide_by_zero.rb +42 -0
  19. data/lib/railroader/checks/check_dynamic_finders.rb +48 -0
  20. data/lib/railroader/checks/check_escape_function.rb +21 -0
  21. data/lib/railroader/checks/check_evaluation.rb +35 -0
  22. data/lib/railroader/checks/check_execute.rb +189 -0
  23. data/lib/railroader/checks/check_file_access.rb +71 -0
  24. data/lib/railroader/checks/check_file_disclosure.rb +35 -0
  25. data/lib/railroader/checks/check_filter_skipping.rb +31 -0
  26. data/lib/railroader/checks/check_forgery_setting.rb +81 -0
  27. data/lib/railroader/checks/check_header_dos.rb +31 -0
  28. data/lib/railroader/checks/check_i18n_xss.rb +48 -0
  29. data/lib/railroader/checks/check_jruby_xml.rb +36 -0
  30. data/lib/railroader/checks/check_json_encoding.rb +47 -0
  31. data/lib/railroader/checks/check_json_parsing.rb +107 -0
  32. data/lib/railroader/checks/check_link_to.rb +132 -0
  33. data/lib/railroader/checks/check_link_to_href.rb +146 -0
  34. data/lib/railroader/checks/check_mail_to.rb +49 -0
  35. data/lib/railroader/checks/check_mass_assignment.rb +196 -0
  36. data/lib/railroader/checks/check_mime_type_dos.rb +39 -0
  37. data/lib/railroader/checks/check_model_attr_accessible.rb +55 -0
  38. data/lib/railroader/checks/check_model_attributes.rb +119 -0
  39. data/lib/railroader/checks/check_model_serialize.rb +67 -0
  40. data/lib/railroader/checks/check_nested_attributes.rb +38 -0
  41. data/lib/railroader/checks/check_nested_attributes_bypass.rb +58 -0
  42. data/lib/railroader/checks/check_number_to_currency.rb +74 -0
  43. data/lib/railroader/checks/check_permit_attributes.rb +43 -0
  44. data/lib/railroader/checks/check_quote_table_name.rb +40 -0
  45. data/lib/railroader/checks/check_redirect.rb +256 -0
  46. data/lib/railroader/checks/check_regex_dos.rb +68 -0
  47. data/lib/railroader/checks/check_render.rb +97 -0
  48. data/lib/railroader/checks/check_render_dos.rb +37 -0
  49. data/lib/railroader/checks/check_render_inline.rb +53 -0
  50. data/lib/railroader/checks/check_response_splitting.rb +21 -0
  51. data/lib/railroader/checks/check_route_dos.rb +42 -0
  52. data/lib/railroader/checks/check_safe_buffer_manipulation.rb +31 -0
  53. data/lib/railroader/checks/check_sanitize_methods.rb +112 -0
  54. data/lib/railroader/checks/check_secrets.rb +40 -0
  55. data/lib/railroader/checks/check_select_tag.rb +59 -0
  56. data/lib/railroader/checks/check_select_vulnerability.rb +60 -0
  57. data/lib/railroader/checks/check_send.rb +47 -0
  58. data/lib/railroader/checks/check_send_file.rb +19 -0
  59. data/lib/railroader/checks/check_session_manipulation.rb +35 -0
  60. data/lib/railroader/checks/check_session_settings.rb +176 -0
  61. data/lib/railroader/checks/check_simple_format.rb +58 -0
  62. data/lib/railroader/checks/check_single_quotes.rb +101 -0
  63. data/lib/railroader/checks/check_skip_before_filter.rb +60 -0
  64. data/lib/railroader/checks/check_sql.rb +700 -0
  65. data/lib/railroader/checks/check_sql_cves.rb +106 -0
  66. data/lib/railroader/checks/check_ssl_verify.rb +48 -0
  67. data/lib/railroader/checks/check_strip_tags.rb +89 -0
  68. data/lib/railroader/checks/check_symbol_dos.rb +71 -0
  69. data/lib/railroader/checks/check_symbol_dos_cve.rb +30 -0
  70. data/lib/railroader/checks/check_translate_bug.rb +45 -0
  71. data/lib/railroader/checks/check_unsafe_reflection.rb +50 -0
  72. data/lib/railroader/checks/check_unscoped_find.rb +57 -0
  73. data/lib/railroader/checks/check_validation_regex.rb +116 -0
  74. data/lib/railroader/checks/check_weak_hash.rb +148 -0
  75. data/lib/railroader/checks/check_without_protection.rb +80 -0
  76. data/lib/railroader/checks/check_xml_dos.rb +45 -0
  77. data/lib/railroader/checks/check_yaml_parsing.rb +121 -0
  78. data/lib/railroader/checks.rb +209 -0
  79. data/lib/railroader/codeclimate/engine_configuration.rb +97 -0
  80. data/lib/railroader/commandline.rb +179 -0
  81. data/lib/railroader/differ.rb +66 -0
  82. data/lib/railroader/file_parser.rb +54 -0
  83. data/lib/railroader/format/style.css +133 -0
  84. data/lib/railroader/options.rb +339 -0
  85. data/lib/railroader/parsers/rails2_erubis.rb +6 -0
  86. data/lib/railroader/parsers/rails2_xss_plugin_erubis.rb +48 -0
  87. data/lib/railroader/parsers/rails3_erubis.rb +81 -0
  88. data/lib/railroader/parsers/template_parser.rb +108 -0
  89. data/lib/railroader/processor.rb +102 -0
  90. data/lib/railroader/processors/alias_processor.rb +1229 -0
  91. data/lib/railroader/processors/base_processor.rb +295 -0
  92. data/lib/railroader/processors/config_processor.rb +14 -0
  93. data/lib/railroader/processors/controller_alias_processor.rb +278 -0
  94. data/lib/railroader/processors/controller_processor.rb +249 -0
  95. data/lib/railroader/processors/erb_template_processor.rb +77 -0
  96. data/lib/railroader/processors/erubis_template_processor.rb +92 -0
  97. data/lib/railroader/processors/gem_processor.rb +64 -0
  98. data/lib/railroader/processors/haml_template_processor.rb +191 -0
  99. data/lib/railroader/processors/lib/basic_processor.rb +37 -0
  100. data/lib/railroader/processors/lib/call_conversion_helper.rb +90 -0
  101. data/lib/railroader/processors/lib/find_all_calls.rb +224 -0
  102. data/lib/railroader/processors/lib/find_call.rb +183 -0
  103. data/lib/railroader/processors/lib/find_return_value.rb +166 -0
  104. data/lib/railroader/processors/lib/module_helper.rb +111 -0
  105. data/lib/railroader/processors/lib/processor_helper.rb +88 -0
  106. data/lib/railroader/processors/lib/rails2_config_processor.rb +145 -0
  107. data/lib/railroader/processors/lib/rails2_route_processor.rb +313 -0
  108. data/lib/railroader/processors/lib/rails3_config_processor.rb +132 -0
  109. data/lib/railroader/processors/lib/rails3_route_processor.rb +308 -0
  110. data/lib/railroader/processors/lib/render_helper.rb +181 -0
  111. data/lib/railroader/processors/lib/render_path.rb +107 -0
  112. data/lib/railroader/processors/lib/route_helper.rb +68 -0
  113. data/lib/railroader/processors/lib/safe_call_helper.rb +16 -0
  114. data/lib/railroader/processors/library_processor.rb +74 -0
  115. data/lib/railroader/processors/model_processor.rb +91 -0
  116. data/lib/railroader/processors/output_processor.rb +144 -0
  117. data/lib/railroader/processors/route_processor.rb +17 -0
  118. data/lib/railroader/processors/slim_template_processor.rb +111 -0
  119. data/lib/railroader/processors/template_alias_processor.rb +118 -0
  120. data/lib/railroader/processors/template_processor.rb +85 -0
  121. data/lib/railroader/report/config/remediation.yml +71 -0
  122. data/lib/railroader/report/ignore/config.rb +153 -0
  123. data/lib/railroader/report/ignore/interactive.rb +362 -0
  124. data/lib/railroader/report/pager.rb +112 -0
  125. data/lib/railroader/report/renderer.rb +24 -0
  126. data/lib/railroader/report/report_base.rb +292 -0
  127. data/lib/railroader/report/report_codeclimate.rb +79 -0
  128. data/lib/railroader/report/report_csv.rb +55 -0
  129. data/lib/railroader/report/report_hash.rb +23 -0
  130. data/lib/railroader/report/report_html.rb +216 -0
  131. data/lib/railroader/report/report_json.rb +45 -0
  132. data/lib/railroader/report/report_markdown.rb +107 -0
  133. data/lib/railroader/report/report_table.rb +117 -0
  134. data/lib/railroader/report/report_tabs.rb +17 -0
  135. data/lib/railroader/report/report_text.rb +198 -0
  136. data/lib/railroader/report/templates/controller_overview.html.erb +22 -0
  137. data/lib/railroader/report/templates/controller_warnings.html.erb +21 -0
  138. data/lib/railroader/report/templates/error_overview.html.erb +29 -0
  139. data/lib/railroader/report/templates/header.html.erb +58 -0
  140. data/lib/railroader/report/templates/ignored_warnings.html.erb +25 -0
  141. data/lib/railroader/report/templates/model_warnings.html.erb +21 -0
  142. data/lib/railroader/report/templates/overview.html.erb +38 -0
  143. data/lib/railroader/report/templates/security_warnings.html.erb +23 -0
  144. data/lib/railroader/report/templates/template_overview.html.erb +21 -0
  145. data/lib/railroader/report/templates/view_warnings.html.erb +34 -0
  146. data/lib/railroader/report/templates/warning_overview.html.erb +17 -0
  147. data/lib/railroader/report.rb +88 -0
  148. data/lib/railroader/rescanner.rb +483 -0
  149. data/lib/railroader/scanner.rb +321 -0
  150. data/lib/railroader/tracker/collection.rb +93 -0
  151. data/lib/railroader/tracker/config.rb +154 -0
  152. data/lib/railroader/tracker/constants.rb +171 -0
  153. data/lib/railroader/tracker/controller.rb +161 -0
  154. data/lib/railroader/tracker/library.rb +17 -0
  155. data/lib/railroader/tracker/model.rb +90 -0
  156. data/lib/railroader/tracker/template.rb +33 -0
  157. data/lib/railroader/tracker.rb +362 -0
  158. data/lib/railroader/util.rb +503 -0
  159. data/lib/railroader/version.rb +3 -0
  160. data/lib/railroader/warning.rb +294 -0
  161. data/lib/railroader/warning_codes.rb +117 -0
  162. data/lib/railroader.rb +544 -0
  163. data/lib/ruby_parser/bm_sexp.rb +626 -0
  164. data/lib/ruby_parser/bm_sexp_processor.rb +116 -0
  165. metadata +386 -0
@@ -0,0 +1,216 @@
1
+ require 'cgi'
2
+
3
+ class Railroader::Report::HTML < Railroader::Report::Base
4
+ HTML_CONFIDENCE = [ "<span class='high-confidence'>High</span>",
5
+ "<span class='med-confidence'>Medium</span>",
6
+ "<span class='weak-confidence'>Weak</span>" ]
7
+
8
+ def initialize *args
9
+ super
10
+
11
+ @element_id = 0 #Used for HTML ids
12
+ end
13
+
14
+ def generate_report
15
+ out = html_header <<
16
+ generate_overview <<
17
+ generate_warning_overview.to_s
18
+
19
+ # Return early if only summarizing
20
+ return out if tracker.options[:summary_only]
21
+
22
+ out << generate_controllers.to_s if tracker.options[:report_routes] or tracker.options[:debug]
23
+ out << generate_templates.to_s if tracker.options[:debug]
24
+ out << generate_errors.to_s
25
+ out << generate_warnings.to_s
26
+ out << generate_controller_warnings.to_s
27
+ out << generate_model_warnings.to_s
28
+ out << generate_template_warnings.to_s
29
+ out << generate_ignored_warnings.to_s
30
+ out << "</body></html>"
31
+ end
32
+
33
+ def generate_overview
34
+ locals = {
35
+ :tracker => tracker,
36
+ :warnings => all_warnings.length,
37
+ :warnings_summary => warnings_summary,
38
+ :number_of_templates => number_of_templates(@tracker),
39
+ :ignored_warnings => ignored_warnings.length
40
+ }
41
+
42
+ Railroader::Report::Renderer.new('overview', :locals => locals).render
43
+ end
44
+
45
+ #Generate listings of templates and their output
46
+ def generate_templates
47
+ out_processor = Railroader::OutputProcessor.new
48
+ template_rows = {}
49
+ tracker.templates.each do |name, template|
50
+ template.each_output do |out|
51
+ out = CGI.escapeHTML(out_processor.format(out))
52
+ template_rows[name] ||= []
53
+ template_rows[name] << out.gsub("\n", ";").gsub(/\s+/, " ")
54
+ end
55
+ end
56
+
57
+ template_rows = template_rows.sort_by{|name, value| name.to_s}
58
+
59
+ Railroader::Report::Renderer.new('template_overview', :locals => {:template_rows => template_rows}).render
60
+ end
61
+
62
+ def render_array template, headings, value_array, locals
63
+ return if value_array.empty?
64
+
65
+ Railroader::Report::Renderer.new(template, :locals => locals).render
66
+ end
67
+
68
+ def convert_warning warning, original
69
+ warning["Confidence"] = HTML_CONFIDENCE[warning["Confidence"]]
70
+ warning["Message"] = with_context original, warning["Message"]
71
+ warning["Warning Type"] = with_link original, warning["Warning Type"]
72
+ warning
73
+ end
74
+
75
+ def with_link warning, message
76
+ "<a rel=\"no-referrer\" href=\"#{warning.link}\">#{message}</a>"
77
+ end
78
+
79
+ def convert_template_warning warning, original
80
+ warning["Confidence"] = HTML_CONFIDENCE[warning["Confidence"]]
81
+ warning["Message"] = with_context original, warning["Message"]
82
+ warning["Warning Type"] = with_link original, warning["Warning Type"]
83
+ warning["Called From"] = original.called_from
84
+ warning["Template Name"] = original.template.name
85
+ warning
86
+ end
87
+
88
+ def convert_ignored_warning warning, original
89
+ warning = convert_warning(warning, original)
90
+ warning['File'] = original.relative_path
91
+ warning['Note'] = CGI.escapeHTML(@ignore_filter.note_for(original) || "")
92
+ warning
93
+ end
94
+
95
+ #Return header for HTML output. Uses CSS from tracker.options[:html_style]
96
+ def html_header
97
+ if File.exist? tracker.options[:html_style]
98
+ css = File.read tracker.options[:html_style]
99
+ else
100
+ raise "Cannot find CSS stylesheet for HTML: #{tracker.options[:html_style]}"
101
+ end
102
+
103
+ locals = {
104
+ :css => css,
105
+ :tracker => tracker,
106
+ :checks => checks,
107
+ :rails_version => rails_version,
108
+ :railroader_version => Railroader::Version
109
+ }
110
+
111
+ Railroader::Report::Renderer.new('header', :locals => locals).render
112
+ end
113
+
114
+ #Generate HTML for warnings, including context show/hidden via Javascript
115
+ def with_context warning, message
116
+ context = context_for(@app_tree, warning)
117
+ full_message = nil
118
+
119
+ if tracker.options[:message_limit] and tracker.options[:message_limit] > 0 and message.length > tracker.options[:message_limit]
120
+ full_message = html_message(warning, message)
121
+ message = message[0..tracker.options[:message_limit]] << "..."
122
+ end
123
+
124
+ message = html_message(warning, message)
125
+ return message if context.empty? and not full_message
126
+
127
+ @element_id += 1
128
+ code_id = "context#@element_id"
129
+ message_id = "message#@element_id"
130
+ full_message_id = "full_message#@element_id"
131
+ alt = false
132
+ output = "<div class='warning_message' onClick=\"toggle('#{code_id}');toggle('#{message_id}');toggle('#{full_message_id}')\" >" <<
133
+ if full_message
134
+ "<span id='#{message_id}' style='display:block' >#{message}</span>" <<
135
+ "<span id='#{full_message_id}' style='display:none'>#{full_message}</span>"
136
+ else
137
+ message
138
+ end <<
139
+ "<table id='#{code_id}' class='context' style='display:none'>" <<
140
+ "<caption>#{CGI.escapeHTML warning_file(warning) || ''}</caption>"
141
+
142
+ output << <<-HTML
143
+ <thead style='display:none'>
144
+ <tr>
145
+ <th>line number</th>
146
+ <th>line content</th>
147
+ </tr>
148
+ </thead>
149
+ <tbody>
150
+ HTML
151
+
152
+ unless context.empty?
153
+ if warning.line - 1 == 1 or warning.line + 1 == 1
154
+ error = " near_error"
155
+ elsif 1 == warning.line
156
+ error = " error"
157
+ else
158
+ error = ""
159
+ end
160
+
161
+ output << <<-HTML
162
+ <tr class='context first#{error}'>
163
+ <td class='context_line'>
164
+ <pre class='context'>#{context.first[0]}</pre>
165
+ </td>
166
+ <td class='context'>
167
+ <pre class='context'>#{CGI.escapeHTML context.first[1].chomp}</pre>
168
+ </td>
169
+ </tr>
170
+ HTML
171
+
172
+ if context.length > 1
173
+ output << context[1..-1].map do |code|
174
+ alt = !alt
175
+ if code[0] == warning.line - 1 or code[0] == warning.line + 1
176
+ error = " near_error"
177
+ elsif code[0] == warning.line
178
+ error = " error"
179
+ else
180
+ error = ""
181
+ end
182
+
183
+ <<-HTML
184
+ <tr class='context#{alt ? ' alt' : ''}#{error}'>
185
+ <td class='context_line'>
186
+ <pre class='context'>#{code[0]}</pre>
187
+ </td>
188
+ <td class='context'>
189
+ <pre class='context'>#{CGI.escapeHTML code[1].chomp}</pre>
190
+ </td>
191
+ </tr>
192
+ HTML
193
+ end.join
194
+ end
195
+ end
196
+
197
+ output << "</tbody></table></div>"
198
+ end
199
+
200
+ #Escape warning message and highlight user input in HTML output
201
+ def html_message warning, message
202
+ message = CGI.escapeHTML(message)
203
+
204
+ if warning.file
205
+ github_url = github_url warning.file, warning.line
206
+ message.gsub!(/(near line \d+)/, "<a href=\"#{github_url}\" target='_blank'>\\1</a>") if github_url
207
+ end
208
+
209
+ if @highlight_user_input and warning.user_input
210
+ user_input = CGI.escapeHTML(warning.format_user_input)
211
+ message.gsub!(user_input, "<span class=\"user_input\">#{user_input}</span>")
212
+ end
213
+
214
+ message
215
+ end
216
+ end
@@ -0,0 +1,45 @@
1
+ class Railroader::Report::JSON < Railroader::Report::Base
2
+ def generate_report
3
+ errors = tracker.errors.map{|e| { :error => e[:error], :location => e[:backtrace][0] }}
4
+
5
+ obsolete = tracker.unused_fingerprints
6
+
7
+ warnings = convert_to_hashes all_warnings
8
+
9
+ ignored = convert_to_hashes ignored_warnings
10
+
11
+ scan_info = {
12
+ :app_path => tracker.app_path,
13
+ :rails_version => rails_version,
14
+ :security_warnings => all_warnings.length,
15
+ :start_time => tracker.start_time.to_s,
16
+ :end_time => tracker.end_time.to_s,
17
+ :duration => tracker.duration,
18
+ :checks_performed => checks.checks_run.sort,
19
+ :number_of_controllers => tracker.controllers.length,
20
+ # ignore the "fake" model
21
+ :number_of_models => tracker.models.length - 1,
22
+ :number_of_templates => number_of_templates(@tracker),
23
+ :ruby_version => RUBY_VERSION,
24
+ :railroader_version => Railroader::Version
25
+ }
26
+
27
+ report_info = {
28
+ :scan_info => scan_info,
29
+ :warnings => warnings,
30
+ :ignored_warnings => ignored,
31
+ :errors => errors,
32
+ :obsolete => obsolete
33
+ }
34
+
35
+ JSON.pretty_generate report_info
36
+ end
37
+
38
+ def convert_to_hashes warnings
39
+ warnings.map do |w|
40
+ hash = w.to_hash
41
+ hash[:file] = warning_file w
42
+ hash
43
+ end.sort_by { |w| "#{w[:fingerprint]}#{w[:line]}" }
44
+ end
45
+ end
@@ -0,0 +1,107 @@
1
+ require 'railroader/report/report_table'
2
+
3
+ class Railroader::Report::Markdown < Railroader::Report::Table
4
+
5
+ class MarkdownTable < Terminal::Table
6
+
7
+ def initialize options = {}, &block
8
+ options[:style] ||= {}
9
+ options[:style].merge!({
10
+ :border_x => '-',
11
+ :border_y => '|',
12
+ :border_i => '|'
13
+ })
14
+ super options, &block
15
+ end
16
+
17
+ def render
18
+ super.split("\n")[1...-1].join("\n")
19
+ end
20
+ alias :to_s :render
21
+
22
+ end
23
+
24
+ def initialize *args
25
+ super
26
+ @table = MarkdownTable
27
+ end
28
+
29
+ def generate_report
30
+ out = "# BRAKEMAN REPORT\n\n" <<
31
+ generate_metadata.to_s << "\n\n" <<
32
+ generate_checks.to_s << "\n\n" <<
33
+ "### SUMMARY\n\n" <<
34
+ generate_overview.to_s << "\n\n" <<
35
+ generate_warning_overview.to_s << "\n\n"
36
+
37
+ #Return output early if only summarizing
38
+ return out if tracker.options[:summary_only]
39
+
40
+ if tracker.options[:report_routes] or tracker.options[:debug]
41
+ out << "### CONTROLLERS" << "\n\n" <<
42
+ generate_controllers.to_s << "\n\n"
43
+ end
44
+
45
+ if tracker.options[:debug]
46
+ out << "### TEMPLATES\n\n" <<
47
+ generate_templates.to_s << "\n\n"
48
+ end
49
+
50
+ output_table("Errors", generate_errors, out)
51
+ output_table("SECURITY WARNINGS", generate_warnings, out)
52
+ output_table("Controller Warnings:", generate_controller_warnings, out)
53
+ output_table("Model Warnings:", generate_model_warnings, out)
54
+ output_table("View Warnings:", generate_template_warnings, out)
55
+
56
+ out
57
+ end
58
+
59
+ def output_table title, result, output
60
+ return unless result
61
+
62
+ output << "### #{title}\n\n#{result.to_s}\n\n"
63
+ end
64
+
65
+ def generate_metadata
66
+ MarkdownTable.new(
67
+ :headings =>
68
+ ['Application path', 'Rails version', 'Railroader version', 'Started at', 'Duration']
69
+ ) do |t|
70
+ t.add_row([
71
+ tracker.app_path,
72
+ rails_version,
73
+ Railroader::Version,
74
+ tracker.start_time,
75
+ "#{tracker.duration} seconds",
76
+ ])
77
+ end
78
+ end
79
+
80
+ def generate_checks
81
+ MarkdownTable.new(:headings => ['Checks performed']) do |t|
82
+ t.add_row([checks.checks_run.sort.join(", ")])
83
+ end
84
+ end
85
+
86
+ def convert_warning warning, original
87
+ warning["Confidence"] = TEXT_CONFIDENCE[warning["Confidence"]]
88
+ warning["Message"] = markdown_message original, warning["Message"]
89
+ warning["Warning Type"] = "[#{warning['Warning Type']}](#{original.link})" if original.link
90
+ warning
91
+ end
92
+
93
+ # Escape and code format warning message
94
+ def markdown_message warning, message
95
+ if warning.file
96
+ github_url = github_url warning.file, warning.line
97
+ message.gsub!(/(near line \d+)/, "[\\1](#{github_url})") if github_url
98
+ end
99
+ if warning.code
100
+ code = warning.format_code
101
+ message.gsub(code, "`#{code.gsub('`','``').gsub(/\A``|``\z/, '` `')}`")
102
+ else
103
+ message
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,117 @@
1
+ Railroader.load_railroader_dependency 'terminal-table'
2
+
3
+ class Railroader::Report::Table < Railroader::Report::Base
4
+ def initialize *args
5
+ super
6
+ @table = Terminal::Table
7
+ end
8
+
9
+ def generate_report
10
+ summary_option = tracker.options[:summary_only]
11
+ out = ""
12
+
13
+ unless summary_option == :no_summary
14
+ out << text_header <<
15
+ "\n\n+SUMMARY+\n\n" <<
16
+ truncate_table(generate_overview.to_s) << "\n\n" <<
17
+ truncate_table(generate_warning_overview.to_s) << "\n"
18
+ end
19
+
20
+ #Return output early if only summarizing
21
+ if summary_option == :summary_only or summary_option == true
22
+ return out
23
+ end
24
+
25
+ if tracker.options[:report_routes] or tracker.options[:debug]
26
+ out << "\n+CONTROLLERS+\n" <<
27
+ truncate_table(generate_controllers.to_s) << "\n"
28
+ end
29
+
30
+ if tracker.options[:debug]
31
+ out << "\n+TEMPLATES+\n\n" <<
32
+ truncate_table(generate_templates.to_s) << "\n"
33
+ end
34
+
35
+ output_table("+Obsolete Ignore Entries+", generate_obsolete, out)
36
+ output_table("+Errors+", generate_errors, out)
37
+ output_table("+SECURITY WARNINGS+", generate_warnings, out)
38
+ output_table("Controller Warnings:", generate_controller_warnings, out)
39
+ output_table("Model Warnings:", generate_model_warnings, out)
40
+ output_table("View Warnings:", generate_template_warnings, out)
41
+
42
+ out << "\n"
43
+ out
44
+ end
45
+
46
+ def output_table title, result, output
47
+ return unless result
48
+
49
+ output << "\n\n#{title}\n\n#{truncate_table(result.to_s)}"
50
+ end
51
+
52
+ def generate_overview
53
+ num_warnings = all_warnings.length
54
+
55
+ @table.new(:headings => ['Scanned/Reported', 'Total']) do |t|
56
+ t.add_row ['Controllers', tracker.controllers.length]
57
+ t.add_row ['Models', tracker.models.length - 1]
58
+ t.add_row ['Templates', number_of_templates(@tracker)]
59
+ t.add_row ['Errors', tracker.errors.length]
60
+ t.add_row ['Security Warnings', "#{num_warnings} (#{warnings_summary[:high_confidence]})"]
61
+ t.add_row ['Ignored Warnings', ignored_warnings.length] unless ignored_warnings.empty?
62
+ end
63
+ end
64
+
65
+ #Generate listings of templates and their output
66
+ def generate_templates
67
+ out_processor = Railroader::OutputProcessor.new
68
+ template_rows = {}
69
+ tracker.templates.each do |name, template|
70
+ template.each_output do |out|
71
+ out = out_processor.format out
72
+ template_rows[name] ||= []
73
+ template_rows[name] << out.gsub("\n", ";").gsub(/\s+/, " ")
74
+ end
75
+ end
76
+
77
+ template_rows = template_rows.sort_by{|name, value| name.to_s}
78
+
79
+ output = ''
80
+ template_rows.each do |template|
81
+ output << template.first.to_s << "\n\n"
82
+ table = @table.new(:headings => ['Output']) do |t|
83
+ # template[1] is an array of calls
84
+ template[1].each do |v|
85
+ t.add_row [v]
86
+ end
87
+ end
88
+
89
+ output << table.to_s << "\n\n"
90
+ end
91
+
92
+ output
93
+ end
94
+
95
+ def render_array template, headings, value_array, locals
96
+ return if value_array.empty?
97
+
98
+ @table.new(:headings => headings) do |t|
99
+ value_array.each { |value_row| t.add_row value_row }
100
+ end
101
+ end
102
+
103
+ #Generate header for text output
104
+ def text_header
105
+ <<-HEADER
106
+
107
+ +BRAKEMAN REPORT+
108
+
109
+ Application path: #{tracker.app_path}
110
+ Rails version: #{rails_version}
111
+ Railroader version: #{Railroader::Version}
112
+ Started at #{tracker.start_time}
113
+ Duration: #{tracker.duration} seconds
114
+ Checks run: #{checks.checks_run.sort.join(", ")}
115
+ HEADER
116
+ end
117
+ end
@@ -0,0 +1,17 @@
1
+ #Generated tab-separated output suitable for the Jenkins Railroader Plugin:
2
+ #https://github.com/presidentbeef/railroader-jenkins-plugin
3
+ class Railroader::Report::Tabs < Railroader::Report::Base
4
+ def generate_report
5
+ [[:generic_warnings, "General"], [:controller_warnings, "Controller"],
6
+ [:model_warnings, "Model"], [:template_warnings, "Template"]].map do |meth, category|
7
+
8
+ self.send(meth).map do |w|
9
+ line = w.line || 0
10
+ w.warning_type.gsub!(/[^\w\s]/, ' ')
11
+ "#{warning_file(w, :absolute)}\t#{line}\t#{w.warning_type}\t#{category}\t#{w.format_message}\t#{TEXT_CONFIDENCE[w.confidence]}"
12
+ end.join "\n"
13
+
14
+ end.join "\n"
15
+
16
+ end
17
+ end
@@ -0,0 +1,198 @@
1
+ Railroader.load_railroader_dependency 'highline'
2
+
3
+ class Railroader::Report::Text < Railroader::Report::Base
4
+ def generate_report
5
+ HighLine.use_color = !!tracker.options[:output_color]
6
+ summary_option = tracker.options[:summary_only]
7
+ @output_string = "\n"
8
+
9
+ unless summary_option == :no_summary
10
+ add_chunk generate_header
11
+ add_chunk generate_overview
12
+ add_chunk generate_warning_overview
13
+ end
14
+
15
+ if summary_option == :summary_only or summary_option == true
16
+ return @output_string
17
+ end
18
+
19
+ add_chunk generate_controllers if tracker.options[:debug] or tracker.options[:report_routes]
20
+ add_chunk generate_templates if tracker.options[:debug]
21
+ add_chunk generate_obsolete
22
+ add_chunk generate_errors
23
+ add_chunk generate_warnings
24
+ end
25
+
26
+ def add_chunk chunk, out = @output_string
27
+ if chunk and not chunk.empty?
28
+ if chunk.is_a? Array
29
+ chunk = chunk.join("\n")
30
+ end
31
+
32
+ out << chunk << "\n\n"
33
+ end
34
+ end
35
+
36
+ def generate_header
37
+ [
38
+ header("Railroader Report"),
39
+ label("Application Path", tracker.app_path),
40
+ label("Rails Version", rails_version),
41
+ label("Railroader Version", Railroader::Version),
42
+ label("Scan Date", tracker.start_time),
43
+ label("Duration", "#{tracker.duration} seconds"),
44
+ label("Checks Run", checks.checks_run.sort.join(", "))
45
+ ]
46
+ end
47
+
48
+ def generate_overview
49
+ overview = [
50
+ header("Overview"),
51
+ label('Controllers', tracker.controllers.length),
52
+ label('Models', tracker.models.length - 1),
53
+ label('Templates', number_of_templates(@tracker)),
54
+ label('Errors', tracker.errors.length),
55
+ label('Security Warnings', all_warnings.length)
56
+ ]
57
+
58
+ unless ignored_warnings.empty?
59
+ overview << label('Ignored Warnings', ignored_warnings.length)
60
+ end
61
+
62
+ overview
63
+ end
64
+
65
+ def generate_warning_overview
66
+ warning_types = warnings_summary
67
+ warning_types.delete :high_confidence
68
+
69
+ warning_types.sort_by { |t, c| t }.map do |type, count|
70
+ label(type, count)
71
+ end.unshift(header('Warning Types'))
72
+ end
73
+
74
+ def generate_warnings
75
+ if tracker.filtered_warnings.empty?
76
+ HighLine.color("No warnings found", :bold, :green)
77
+ else
78
+ warnings = tracker.filtered_warnings.sort_by do |w|
79
+ [w.confidence, w.warning_type, w.fingerprint]
80
+ end.map do |w|
81
+ output_warning w
82
+ end
83
+
84
+ double_space "Warnings", warnings
85
+ end
86
+ end
87
+
88
+ def generate_errors
89
+ return if tracker.errors.empty?
90
+ full_trace = tracker.options[:debug]
91
+
92
+ errors = tracker.errors.map do |e|
93
+ trace = if full_trace
94
+ e[:backtrace].join("\n")
95
+ else
96
+ e[:backtrace][0]
97
+ end
98
+
99
+ [
100
+ label("Error", e[:error]),
101
+ label("Location", trace)
102
+ ]
103
+ end
104
+
105
+ double_space "Errors", errors
106
+ end
107
+
108
+ def generate_obsolete
109
+ return if tracker.unused_fingerprints.empty?
110
+
111
+ [header("Obsolete Ignore Entries")] + tracker.unused_fingerprints
112
+ end
113
+
114
+ def generate_templates
115
+ out_processor = Railroader::OutputProcessor.new
116
+
117
+ template_rows = {}
118
+ tracker.templates.each do |name, template|
119
+ template.each_output do |out|
120
+ out = out_processor.format out
121
+ template_rows[name] ||= []
122
+ template_rows[name] << out.gsub("\n", ";").gsub(/\s+/, " ")
123
+ end
124
+ end
125
+
126
+ double_space "Template Output", template_rows.sort_by { |name, value| name.to_s }.map { |template|
127
+ [HighLine.new.color(template.first.to_s << "\n", :cyan)] + template[1]
128
+ }.compact
129
+ end
130
+
131
+ def output_warning w
132
+ out = [
133
+ label('Confidence', confidence(w.confidence)),
134
+ label('Category', w.warning_type.to_s),
135
+ label('Check', w.check.gsub(/^Railroader::Check/, '')),
136
+ label('Message', w.message)
137
+ ]
138
+
139
+ if w.code
140
+ out << label('Code', format_code(w))
141
+ end
142
+
143
+ out << label('File', warning_file(w))
144
+
145
+ if w.line
146
+ out << label('Line', w.line)
147
+ end
148
+
149
+ out
150
+ end
151
+
152
+ def double_space title, values
153
+ values = values.map { |v| v.join("\n") }.join("\n\n")
154
+ [header(title), values]
155
+ end
156
+
157
+ def format_code w
158
+ if @highlight_user_input and w.user_input
159
+ w.format_with_user_input do |exp, text|
160
+ HighLine.new.color(text, :yellow)
161
+ end
162
+ else
163
+ w.format_code
164
+ end
165
+ end
166
+
167
+ def confidence c
168
+ case c
169
+ when 0
170
+ HighLine.new.color("High", :red)
171
+ when 1
172
+ HighLine.new.color("Medium", :yellow)
173
+ when 2
174
+ HighLine.new.color("Weak", :none)
175
+ end
176
+ end
177
+
178
+ def label l, value, color = :green
179
+ "#{HighLine.new.color(l, color)}: #{value}"
180
+ end
181
+
182
+ def header text
183
+ HighLine.new.color("== #{text} ==\n", :bold, :magenta)
184
+ end
185
+
186
+ # ONLY used for generate_controllers to avoid duplication
187
+ def render_array name, cols, values, locals
188
+ controllers = values.map do |name, parent, includes, routes|
189
+ c = [ label("Controller", name) ]
190
+ c << label("Parent", parent) unless parent.empty?
191
+ c << label("Includes", includes) unless includes.empty?
192
+ c << label("Routes", routes)
193
+ end
194
+
195
+ double_space "Controller Overview", controllers
196
+ end
197
+ end
198
+