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,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