romanbsd-tarantula 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. data/CHANGELOG +47 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +162 -0
  4. data/Rakefile +69 -0
  5. data/VERSION.yml +4 -0
  6. data/examples/example_helper.rb +48 -0
  7. data/examples/relevance/core_extensions/ellipsize_example.rb +19 -0
  8. data/examples/relevance/core_extensions/file_example.rb +8 -0
  9. data/examples/relevance/core_extensions/response_example.rb +29 -0
  10. data/examples/relevance/core_extensions/test_case_example.rb +20 -0
  11. data/examples/relevance/tarantula/attack_form_submission_example.rb +79 -0
  12. data/examples/relevance/tarantula/attack_handler_example.rb +29 -0
  13. data/examples/relevance/tarantula/crawler_example.rb +386 -0
  14. data/examples/relevance/tarantula/form_example.rb +50 -0
  15. data/examples/relevance/tarantula/form_submission_example.rb +71 -0
  16. data/examples/relevance/tarantula/html_document_handler_example.rb +43 -0
  17. data/examples/relevance/tarantula/html_report_helper_example.rb +46 -0
  18. data/examples/relevance/tarantula/html_reporter_example.rb +82 -0
  19. data/examples/relevance/tarantula/invalid_html_handler_example.rb +33 -0
  20. data/examples/relevance/tarantula/io_reporter_example.rb +11 -0
  21. data/examples/relevance/tarantula/link_example.rb +67 -0
  22. data/examples/relevance/tarantula/log_grabber_example.rb +26 -0
  23. data/examples/relevance/tarantula/rails_integration_proxy_example.rb +88 -0
  24. data/examples/relevance/tarantula/result_example.rb +85 -0
  25. data/examples/relevance/tarantula/tidy_handler_example.rb +58 -0
  26. data/examples/relevance/tarantula/transform_example.rb +20 -0
  27. data/examples/relevance/tarantula/w3c_validator_example.rb +71 -0
  28. data/examples/relevance/tarantula_example.rb +23 -0
  29. data/laf/images/button_active.png +0 -0
  30. data/laf/images/button_hover.png +0 -0
  31. data/laf/images/button_inactive.png +0 -0
  32. data/laf/images/header_bg.jpg +0 -0
  33. data/laf/images/logo.png +0 -0
  34. data/laf/images/tagline.png +0 -0
  35. data/laf/javascripts/jquery-1.2.3.js +3408 -0
  36. data/laf/javascripts/jquery-ui-tabs.js +890 -0
  37. data/laf/javascripts/jquery.tablesorter.js +861 -0
  38. data/laf/javascripts/niftyLayout.js +11 -0
  39. data/laf/javascripts/niftycube-details.js +298 -0
  40. data/laf/javascripts/niftycube.js +298 -0
  41. data/laf/javascripts/tarantula.js +10 -0
  42. data/laf/stylesheets/tarantula.css +345 -0
  43. data/laf/v2/detail.html +59 -0
  44. data/laf/v2/images/button_active.png +0 -0
  45. data/laf/v2/images/button_hover.png +0 -0
  46. data/laf/v2/images/button_inactive.png +0 -0
  47. data/laf/v2/images/header_bg.jpg +0 -0
  48. data/laf/v2/images/logo.png +0 -0
  49. data/laf/v2/images/tagline.png +0 -0
  50. data/laf/v2/index.html +77 -0
  51. data/laf/v2/stylesheets/tarantula.v2.css +324 -0
  52. data/lib/relevance/core_extensions/ellipsize.rb +34 -0
  53. data/lib/relevance/core_extensions/file.rb +9 -0
  54. data/lib/relevance/core_extensions/metaclass.rb +78 -0
  55. data/lib/relevance/core_extensions/response.rb +9 -0
  56. data/lib/relevance/core_extensions/string_chars_fix.rb +11 -0
  57. data/lib/relevance/core_extensions/test_case.rb +19 -0
  58. data/lib/relevance/tarantula/attack.rb +15 -0
  59. data/lib/relevance/tarantula/attack_form_submission.rb +75 -0
  60. data/lib/relevance/tarantula/attack_handler.rb +37 -0
  61. data/lib/relevance/tarantula/crawler.rb +264 -0
  62. data/lib/relevance/tarantula/detail.html.erb +82 -0
  63. data/lib/relevance/tarantula/form.rb +21 -0
  64. data/lib/relevance/tarantula/form_submission.rb +70 -0
  65. data/lib/relevance/tarantula/html_document_handler.rb +36 -0
  66. data/lib/relevance/tarantula/html_report_helper.rb +39 -0
  67. data/lib/relevance/tarantula/html_reporter.rb +105 -0
  68. data/lib/relevance/tarantula/index.html.erb +37 -0
  69. data/lib/relevance/tarantula/invalid_html_handler.rb +18 -0
  70. data/lib/relevance/tarantula/io_reporter.rb +34 -0
  71. data/lib/relevance/tarantula/link.rb +56 -0
  72. data/lib/relevance/tarantula/log_grabber.rb +16 -0
  73. data/lib/relevance/tarantula/rails_integration_proxy.rb +68 -0
  74. data/lib/relevance/tarantula/recording.rb +12 -0
  75. data/lib/relevance/tarantula/response.rb +13 -0
  76. data/lib/relevance/tarantula/result.rb +66 -0
  77. data/lib/relevance/tarantula/test_report.html.erb +32 -0
  78. data/lib/relevance/tarantula/tidy_handler.rb +32 -0
  79. data/lib/relevance/tarantula/transform.rb +17 -0
  80. data/lib/relevance/tarantula/w3c_validator.rb +33 -0
  81. data/lib/relevance/tarantula.rb +59 -0
  82. data/tasks/tarantula_tasks.rake +36 -0
  83. data/template/tarantula_test.rb +22 -0
  84. data/vendor/w3c_validators/CHANGELOG +14 -0
  85. data/vendor/w3c_validators/LICENSE +60 -0
  86. data/vendor/w3c_validators/README +120 -0
  87. data/vendor/w3c_validators/README.svn +4 -0
  88. data/vendor/w3c_validators/lib/w3c_validators/constants.rb +80 -0
  89. data/vendor/w3c_validators/lib/w3c_validators/css_validator.rb +149 -0
  90. data/vendor/w3c_validators/lib/w3c_validators/exceptions.rb +4 -0
  91. data/vendor/w3c_validators/lib/w3c_validators/feed_validator.rb +110 -0
  92. data/vendor/w3c_validators/lib/w3c_validators/markup_validator.rb +227 -0
  93. data/vendor/w3c_validators/lib/w3c_validators/message.rb +82 -0
  94. data/vendor/w3c_validators/lib/w3c_validators/results.rb +62 -0
  95. data/vendor/w3c_validators/lib/w3c_validators/validator.rb +157 -0
  96. data/vendor/w3c_validators/lib/w3c_validators.rb +5 -0
  97. data/vendor/w3c_validators/rakefile.rb +53 -0
  98. data/vendor/w3c_validators/test/fixtures/invalid_css.css +2 -0
  99. data/vendor/w3c_validators/test/fixtures/invalid_encoding.html +10 -0
  100. data/vendor/w3c_validators/test/fixtures/invalid_feed.xml +19 -0
  101. data/vendor/w3c_validators/test/fixtures/invalid_html5.html +16 -0
  102. data/vendor/w3c_validators/test/fixtures/invalid_markup.html +11 -0
  103. data/vendor/w3c_validators/test/fixtures/valid_css.css +2 -0
  104. data/vendor/w3c_validators/test/fixtures/valid_feed.xml +20 -0
  105. data/vendor/w3c_validators/test/fixtures/valid_html5.html +16 -0
  106. data/vendor/w3c_validators/test/fixtures/valid_markup.html +11 -0
  107. data/vendor/w3c_validators/test/test_css_validator.rb +51 -0
  108. data/vendor/w3c_validators/test/test_exceptions.rb +35 -0
  109. data/vendor/w3c_validators/test/test_feed_validator.rb +61 -0
  110. data/vendor/w3c_validators/test/test_helper.rb +6 -0
  111. data/vendor/w3c_validators/test/test_html5_validator.rb +64 -0
  112. data/vendor/w3c_validators/test/test_markup_validator.rb +94 -0
  113. data/vendor/xss-shield/MIT-LICENSE +20 -0
  114. data/vendor/xss-shield/README +76 -0
  115. data/vendor/xss-shield/init.rb +16 -0
  116. data/vendor/xss-shield/lib/xss_shield/erb_hacks.rb +111 -0
  117. data/vendor/xss-shield/lib/xss_shield/haml_hacks.rb +42 -0
  118. data/vendor/xss-shield/lib/xss_shield/safe_string.rb +47 -0
  119. data/vendor/xss-shield/lib/xss_shield/secure_helpers.rb +40 -0
  120. data/vendor/xss-shield/lib/xss_shield.rb +6 -0
  121. data/vendor/xss-shield/test/test_actionview_integration.rb +40 -0
  122. data/vendor/xss-shield/test/test_erb.rb +44 -0
  123. data/vendor/xss-shield/test/test_haml.rb +43 -0
  124. data/vendor/xss-shield/test/test_helpers.rb +25 -0
  125. data/vendor/xss-shield/test/test_safe_string.rb +55 -0
  126. metadata +218 -0
@@ -0,0 +1,264 @@
1
+ require 'active_record'
2
+ require 'active_record/base'
3
+ require File.expand_path(File.join(File.dirname(__FILE__), "rails_integration_proxy"))
4
+ require File.expand_path(File.join(File.dirname(__FILE__), "html_document_handler.rb"))
5
+
6
+ class Relevance::Tarantula::Crawler
7
+ extend Forwardable
8
+ include Relevance::Tarantula
9
+
10
+ class CrawlTimeout < RuntimeError; end
11
+
12
+ attr_accessor :proxy, :handlers, :skip_uri_patterns, :log_grabber,
13
+ :reporters, :links_to_crawl, :links_queued, :forms_to_crawl,
14
+ :form_signatures_queued, :max_url_length, :response_code_handler,
15
+ :times_to_crawl, :fuzzers, :test_name, :crawl_timeout
16
+ attr_reader :transform_url_patterns, :referrers, :failures, :successes, :crawl_start_times, :crawl_end_times
17
+
18
+ def initialize
19
+ @max_url_length = 1024
20
+ @successes = []
21
+ @failures = []
22
+ @handlers = [@response_code_handler = Result]
23
+ @links_queued = Set.new
24
+ @form_signatures_queued = Set.new
25
+ @links_to_crawl = []
26
+ @forms_to_crawl = []
27
+ @crawl_start_times, @crawl_end_times = [], []
28
+ @crawl_timeout = 20.minutes
29
+ @referrers = {}
30
+ @skip_uri_patterns = [
31
+ /^javascript/,
32
+ /^mailto/,
33
+ /^http/,
34
+ ]
35
+ self.transform_url_patterns = [
36
+ [/#.*$/, '']
37
+ ]
38
+ @reporters = [Relevance::Tarantula::IOReporter.new($stderr)]
39
+ @decoder = HTMLEntities.new
40
+ @times_to_crawl = 1
41
+ @fuzzers = [Relevance::Tarantula::FormSubmission]
42
+ end
43
+
44
+ def method_missing(meth, *args)
45
+ super unless Result::ALLOW_NNN_FOR =~ meth.to_s
46
+ @response_code_handler.send(meth, *args)
47
+ end
48
+
49
+ def transform_url_patterns=(patterns)
50
+ @transform_url_patterns = patterns.map do |pattern|
51
+ Array === pattern ? Relevance::Tarantula::Transform.new(*pattern) : pattern
52
+ end
53
+ end
54
+
55
+ def crawl(url = "/")
56
+ orig_links_queued = @links_queued.dup
57
+ orig_form_signatures_queued = @form_signatures_queued.dup
58
+ orig_links_to_crawl = @links_to_crawl.dup
59
+ orig_forms_to_crawl = @forms_to_crawl.dup
60
+ @times_to_crawl.times do |num|
61
+ queue_link url
62
+
63
+ begin
64
+ do_crawl num
65
+ rescue CrawlTimeout => e
66
+ puts e.message
67
+ end
68
+
69
+ puts "#{(num+1).ordinalize} crawl" if @times_to_crawl > 1
70
+
71
+ if num + 1 < @times_to_crawl
72
+ @links_queued = orig_links_queued
73
+ @form_signatures_queued = orig_form_signatures_queued
74
+ @links_to_crawl = orig_links_to_crawl
75
+ @forms_to_crawl = orig_forms_to_crawl
76
+ @referrers = {}
77
+ end
78
+ end
79
+ rescue Interrupt
80
+ $stderr.puts "CTRL-C"
81
+ ensure
82
+ report_results
83
+ end
84
+
85
+ def finished?
86
+ @links_to_crawl.empty? && @forms_to_crawl.empty?
87
+ end
88
+
89
+ def do_crawl(number)
90
+ while (!finished?)
91
+ @crawl_start_times << Time.now
92
+ crawl_queued_links(number)
93
+ crawl_queued_forms(number)
94
+ @crawl_end_times << Time.now
95
+ end
96
+ end
97
+
98
+ def crawl_queued_links(number = 0)
99
+ while (link = @links_to_crawl.pop)
100
+ response = proxy.send(link.method, link.href)
101
+ log "Response #{response.code} for #{link}"
102
+ handle_link_results(link, response)
103
+ blip(number)
104
+ end
105
+ end
106
+
107
+ def save_result(result)
108
+ reporters.each do |reporter|
109
+ reporter.report(result)
110
+ end
111
+ end
112
+
113
+ def handle_link_results(link, response)
114
+ handlers.each do |h|
115
+ begin
116
+ save_result h.handle(Result.new(:method => link.method,
117
+ :url => link.href,
118
+ :response => response,
119
+ :log => grab_log!,
120
+ :referrer => referrers[link],
121
+ :test_name => test_name).freeze)
122
+ rescue Exception => e
123
+ log "error handling #{link} #{e.message}"
124
+ # TODO: pass to results
125
+ end
126
+ end
127
+ end
128
+
129
+ def crawl_form(form)
130
+ response = proxy.send(form.method, form.action, form.data)
131
+ log "Response #{response.code} for #{form}"
132
+ response
133
+ rescue ActiveRecord::RecordNotFound => e
134
+ log "Skipping #{form.action}, presumed ok that record is missing"
135
+ Relevance::Tarantula::Response.new(:code => "404", :body => e.message, :content_type => "text/plain")
136
+ end
137
+
138
+ def crawl_queued_forms(number = 0)
139
+ while (form = @forms_to_crawl.pop)
140
+ response = crawl_form(form)
141
+ handle_form_results(form, response)
142
+ blip(number)
143
+ end
144
+ end
145
+
146
+ def elasped_time_for_pass(num)
147
+ Time.now - crawl_start_times[num]
148
+ end
149
+
150
+ def grab_log!
151
+ @log_grabber && @log_grabber.grab!
152
+ end
153
+
154
+ def handle_form_results(form, response)
155
+ handlers.each do |h|
156
+ save_result h.handle(Result.new(:method => form.method,
157
+ :url => form.action,
158
+ :response => response,
159
+ :log => grab_log!,
160
+ :referrer => form.action,
161
+ :data => form.data.inspect,
162
+ :test_name => test_name).freeze)
163
+ end
164
+ end
165
+
166
+ def should_skip_url?(url)
167
+ return true if url.blank?
168
+ if @skip_uri_patterns.any? {|pattern| pattern =~ url}
169
+ log "Skipping #{url}"
170
+ return true
171
+ end
172
+ if url.length > max_url_length
173
+ log "Skipping long url #{url}"
174
+ return true
175
+ end
176
+ end
177
+
178
+ def should_skip_link?(link)
179
+ should_skip_url?(link.href) || @links_queued.member?(link)
180
+ end
181
+
182
+ def should_skip_form_submission?(fs)
183
+ should_skip_url?(fs.action) || @form_signatures_queued.member?(fs.signature)
184
+ end
185
+
186
+ def transform_url(url)
187
+ return unless url
188
+ url = @decoder.decode(url)
189
+ @transform_url_patterns.each do |pattern|
190
+ url = pattern[url]
191
+ end
192
+ url
193
+ end
194
+
195
+ def queue_link(dest, referrer = nil)
196
+ dest = Link.new(dest)
197
+ dest.href = transform_url(dest.href)
198
+ return if should_skip_link?(dest)
199
+ @referrers[dest] = referrer if referrer
200
+ @links_to_crawl << dest
201
+ @links_queued << dest
202
+ dest
203
+ end
204
+
205
+ def queue_form(form, referrer = nil)
206
+ fuzzers.each do |fuzzer|
207
+ fuzzer.mutate(Form.new(form)).each do |fs|
208
+ # fs = fuzzer.new(Form.new(form))
209
+ fs.action = transform_url(fs.action)
210
+ return if should_skip_form_submission?(fs)
211
+ @referrers[fs.action] = referrer if referrer
212
+ @forms_to_crawl << fs
213
+ @form_signatures_queued << fs.signature
214
+ end
215
+ end
216
+ end
217
+
218
+ def report_dir
219
+ File.join(rails_root, "tmp", "tarantula")
220
+ end
221
+
222
+ def generate_reports
223
+ errors = []
224
+ reporters.each do |reporter|
225
+ begin
226
+ reporter.finish_report(test_name)
227
+ rescue RuntimeError => e
228
+ errors << e
229
+ end
230
+ end
231
+ unless errors.empty?
232
+ raise errors.map(&:message).join("\n")
233
+ end
234
+ end
235
+
236
+ def report_results
237
+ generate_reports
238
+ end
239
+
240
+ def total_links_count
241
+ @links_queued.size + @form_signatures_queued.size
242
+ end
243
+
244
+ def links_remaining_count
245
+ @links_to_crawl.size + @forms_to_crawl.size
246
+ end
247
+
248
+ def links_completed_count
249
+ total_links_count - links_remaining_count
250
+ end
251
+
252
+ def blip(number = 0)
253
+ unless verbose
254
+ print "\r #{links_completed_count} of #{total_links_count} links completed "
255
+ timeout_if_too_long(number)
256
+ end
257
+ end
258
+
259
+ def timeout_if_too_long(number = 0)
260
+ if elasped_time_for_pass(number) > crawl_timeout
261
+ raise CrawlTimeout, "Exceeded crawl timeout of #{crawl_timeout} seconds - skipping to the next crawl..."
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,82 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+ <link rel="stylesheet" href="../stylesheets/tarantula.css" type="text/css" media="screen" title="no title" charset="utf-8" />
8
+ <title>Detail</title>
9
+ </head>
10
+ <body>
11
+ <div id="container">
12
+ <div id="header">
13
+ <h1>Tarantula by Relevance</h1>
14
+ <h2>Eight legs, two fangs ... and an attitude</h2>
15
+ <p>Tarantula is an open source tool for testing Rails web
16
+ applications. Tarantula is developed by <a href="http://thinkrelevance.com">Relevance, Inc.</a>
17
+ and lives at <a href="http://github.com/relevance/tarantula">http://github.com/relevance/tarantula</a>.</p>
18
+ <hr/>
19
+ </div>
20
+ <div id="page">
21
+ <ul class="tabs">
22
+ <li><a href="../index.html">&laquo; Back</a></li>
23
+ <!-- TODO dynamically hide/show body/log sections based on selected "tab" - dynamically assign the "active" class to the selected tab -->
24
+ <li><a href="#fragment-1" class="active">Body</a></li>
25
+ <li><a href="#fragment-2" class="active">Log</a></li>
26
+ </ul>
27
+
28
+ <div id="report">
29
+ <h3>Detail of <%= short_description %> <em>Generated on <%= Time.now %></em></h3>
30
+ <p><b>Resource</b> <a href="<%= full_url %>"><%= full_url %></a></p>
31
+ <p><b>Response</b> <span class="r<%= code.first %>"><%= code %></span></p>
32
+ <p><b>Referrer</b> <%= referrer || "" %></p>
33
+
34
+ <table class="output">
35
+ <tbody>
36
+ <tr>
37
+ <th colspan="2">#&nbsp;&nbsp;Data</th>
38
+ </tr>
39
+ <% if data %>
40
+ <%= wrap_in_line_number_table_row(data) %>
41
+ <% else %>
42
+ <tr>
43
+ <td colspan="2">No Data</td>
44
+ </tr>
45
+ <% end %>
46
+ </tbody>
47
+ </table>
48
+
49
+ <table class="output" id="fragment-1">
50
+ <tbody>
51
+ <tr>
52
+ <th colspan="2">#&nbsp;&nbsp;Body</th>
53
+ </tr>
54
+ <% if body %>
55
+ <%= wrap_in_line_number_table_row(body) %>
56
+ <% else %>
57
+ <tr>
58
+ <td colspan="2">No Body</td>
59
+ </tr>
60
+ <% end %>
61
+ </tbody>
62
+ </table>
63
+
64
+ <table class="output" id="fragment-2">
65
+ <tbody>
66
+ <tr>
67
+ <th colspan="2">#&nbsp;&nbsp;Log</th>
68
+ </tr>
69
+ <% if log %>
70
+ <%= wrap_in_line_number_table_row(log) {|line| wrap_stack_trace_line(line)} %>
71
+ <% else %>
72
+ <tr>
73
+ <td colspan="2">No Log</td>
74
+ </tr>
75
+ <% end %>
76
+ </tbody>
77
+ </table>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ </body>
82
+ </html>
@@ -0,0 +1,21 @@
1
+ class Relevance::Tarantula::Form
2
+ extend Forwardable
3
+ def_delegators("@tag", :search)
4
+
5
+ def initialize(tag)
6
+ @tag = tag
7
+ end
8
+
9
+ def action
10
+ @tag['action'].downcase
11
+ end
12
+
13
+ def method
14
+ (rails_method_hack or @tag['method'] or 'get').downcase
15
+ end
16
+
17
+ def rails_method_hack
18
+ (tag = @tag.at('input[@name="_method"]')) && tag["value"]
19
+ end
20
+
21
+ end
@@ -0,0 +1,70 @@
1
+ class Relevance::Tarantula::FormSubmission
2
+ attr_accessor :method, :action, :data
3
+ def initialize(form)
4
+ @method = form.method
5
+ @action = form.action
6
+ @data = mutate_selects(form).merge(mutate_text_areas(form)).merge(mutate_inputs(form))
7
+ end
8
+
9
+ def self.mutate(form)
10
+ [self.new(form)]
11
+ end
12
+
13
+ def to_s
14
+ "#{action} #{method} #{data.inspect}"
15
+ end
16
+
17
+ # a form's signature is what makes it unique (e.g. action + fields)
18
+ # used to keep track of which forms we have submitted already
19
+ def signature
20
+ [action, data.keys.sort]
21
+ end
22
+
23
+ def create_random_data_for(form, tag_selector)
24
+ form.search(tag_selector).inject({}) do |form_args, input|
25
+ # TODO: test
26
+ form_args[input['name']] = random_data(input) if input['name']
27
+ form_args
28
+ end
29
+ end
30
+
31
+ def mutate_inputs(form)
32
+ create_random_data_for(form, 'input')
33
+ end
34
+
35
+ def mutate_text_areas(form)
36
+ create_random_data_for(form, 'textarea')
37
+ end
38
+
39
+ def mutate_selects(form)
40
+ form.search('select').inject({}) do |form_args, select|
41
+ options = select.search('option')
42
+ option = options.rand
43
+ form_args[select['name']] = option['value']
44
+ form_args
45
+ end
46
+ end
47
+
48
+ def random_data(input)
49
+ case input['name']
50
+ when /amount/ : random_int
51
+ when /_id$/ : random_whole_number
52
+ when /uploaded_data/ : nil
53
+ when /^_method$/ : input['value']
54
+ when nil : input['value']
55
+ else random_int
56
+ end
57
+ end
58
+
59
+ def big_number
60
+ 10000 # arbitrary
61
+ end
62
+
63
+ def random_int
64
+ rand(big_number) - (big_number/2)
65
+ end
66
+
67
+ def random_whole_number
68
+ rand(big_number)
69
+ end
70
+ end
@@ -0,0 +1,36 @@
1
+ require 'hpricot'
2
+
3
+ class Relevance::Tarantula::HtmlDocumentHandler
4
+ extend Forwardable
5
+ def_delegators("@crawler", :queue_link, :queue_form)
6
+
7
+ def initialize(crawler)
8
+ @crawler = crawler
9
+ end
10
+ # HTML::Document shouts to stderr when it sees ugly HTML
11
+ # We don't want this -- the InvalidHtmlHandler will deal with it
12
+ def html_doc_without_stderr_noise(html)
13
+ body = nil
14
+ Recording.stderr do
15
+ body = Hpricot html
16
+ end
17
+ body
18
+ end
19
+ def handle(result)
20
+ response = result.response
21
+ url = result.url
22
+ return unless response.html?
23
+ body = html_doc_without_stderr_noise(response.body)
24
+ body.search('a').each do |tag|
25
+ queue_link(tag, url)
26
+ end
27
+ body.search('link').each do |tag|
28
+ queue_link(tag, url)
29
+ end
30
+ body.search('form').each do |form|
31
+ form['action'] = url unless form['action']
32
+ queue_form(form, url)
33
+ end
34
+ nil
35
+ end
36
+ end
@@ -0,0 +1,39 @@
1
+ require "erb"
2
+ module Relevance::Tarantula::HtmlReportHelper
3
+ include ERB::Util
4
+ include Relevance::Tarantula
5
+ def wrap_in_line_number_table_row(text, &blk)
6
+ x = Builder::XmlMarkup.new
7
+
8
+ x.tr do
9
+ lines = text.split("\n")
10
+ x.td(:class => "numbers") do
11
+ lines.size.times do |index|
12
+ x.span(index+1, :class => "line number")
13
+ end
14
+ end
15
+ x.td(:class => "lines") do
16
+ lines.each do |line|
17
+ x.span(line, :class => "line")
18
+ end
19
+ end
20
+ end
21
+
22
+ x.target!
23
+ end
24
+
25
+ def textmate_url(file, line_no)
26
+ "txmt://open?url=file://#{File.expand_path(File.join(rails_root,file))}&line_no=#{line_no}"
27
+ end
28
+
29
+ def wrap_stack_trace_line(text)
30
+ if text =~ %r{^\s*(/[^:]+):(\d+):([^:]+)$}
31
+ file = h($1) # .to_s_xss_protected
32
+ line_number = $2
33
+ message = h($3) # .to_s_xss_protected
34
+ "<a href='#{textmate_url(file, line_number)}'>#{file}:#{line_number}</a>:#{message}" # .mark_as_xss_protected
35
+ else
36
+ h(text) # .to_s_xss_protected
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,105 @@
1
+ class Relevance::Tarantula::HtmlReporter
2
+
3
+ include Relevance::Tarantula
4
+ attr_accessor :basedir, :results
5
+ delegate :successes, :failures, :to => :results
6
+
7
+ HtmlResultOverview = Struct.new(:code, :url, :description, :method, :referrer, :file_name)
8
+
9
+ def initialize(basedir)
10
+ @basedir = basedir
11
+ @results = Struct.new(:successes, :failures).new([], [])
12
+ FileUtils.mkdir_p(@basedir)
13
+ end
14
+
15
+ def report(result)
16
+ return if result.nil?
17
+
18
+ create_detail_report(result)
19
+
20
+ collection = result.success ? results.successes : results.failures
21
+ collection << HtmlResultOverview.new(
22
+ result.code, result.url, result.description, result.method, result.referrer, result.file_name
23
+ )
24
+ end
25
+
26
+ def finish_report(test_name)
27
+ puts "Writing results to #{basedir}"
28
+ copy_styles unless styles_exist?
29
+ create_index unless index_exists?
30
+ update_index(test_name)
31
+ end
32
+
33
+ def create_detail_report(result)
34
+ template = ERB.new(template("detail.html.erb"))
35
+ output(result.file_name, template.result(result.send(:binding)), result.test_name)
36
+ end
37
+
38
+ def copy_styles
39
+ # not using cp_r because it picks up .svn crap
40
+ FileUtils.mkdir_p(File.join(basedir, "stylesheets"))
41
+ Dir.glob("#{tarantula_home}/laf/stylesheets/*.css").each do |file|
42
+ FileUtils.cp(file, File.join(basedir, "stylesheets"))
43
+ end
44
+ FileUtils.mkdir_p(File.join(basedir, "images"))
45
+ Dir.glob("#{tarantula_home}/laf/images/*.{jpg,gif,png}").each do |file|
46
+ FileUtils.cp(file, File.join(basedir, "images"))
47
+ end
48
+ FileUtils.mkdir_p(File.join(basedir, "javascripts"))
49
+ Dir.glob("#{tarantula_home}/laf/javascripts/*.js").each do |file|
50
+ FileUtils.cp(file, File.join(basedir, "javascripts"))
51
+ end
52
+ end
53
+
54
+ def create_index
55
+ template = ERB.new(template("index.html.erb"))
56
+ output("index.html", template.result(binding))
57
+ end
58
+
59
+ def update_index(test_name)
60
+ File.open(File.join(basedir, "index.html"), "r+") do |file|
61
+ doc = Hpricot file.read
62
+ tabs_container = doc.search "#tabs-container ul"
63
+ results_container = doc.search "#results-container"
64
+ tabs_container.append tab_html(test_name)
65
+ results_container.append results_html(test_name)
66
+ file.rewind
67
+ file.write doc.to_s
68
+ end
69
+ end
70
+
71
+ def index_exists?
72
+ File.exists?(File.join(basedir, "index.html"))
73
+ end
74
+
75
+ def styles_exist?
76
+ File.exists?(File.join(basedir, "stylesheets", "tarantula.css"))
77
+ end
78
+
79
+ def tab_html(test_name)
80
+ "<li><a href='##{test_name}'><span>#{test_name}</span></a></li>"
81
+ end
82
+
83
+ def results_html(test_name)
84
+ template = ERB.new(template("test_report.html.erb"))
85
+ template.result(binding)
86
+ end
87
+
88
+ def template(name)
89
+ File.read(File.join(File.dirname(__FILE__), name))
90
+ end
91
+
92
+ def output(name, body, subdir = '')
93
+ FileUtils.mkdir_p(File.join(basedir, subdir)) unless subdir.empty?
94
+ File.open(File.join(basedir, subdir, name), "w") do |file|
95
+ file.write body
96
+ end
97
+ end
98
+
99
+ # CSS class for HTML status codes
100
+ def class_for_code(code)
101
+ "r#{Integer(code)/100}"
102
+ end
103
+
104
+
105
+ end
@@ -0,0 +1,37 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
7
+ <link rel="stylesheet" href="stylesheets/tarantula.css" type="text/css" media="screen" title="no title" charset="utf-8">
8
+ <script type="text/javascript" src="javascripts/jquery-1.2.3.js"></script>
9
+ <script type="text/javascript" src="javascripts/jquery.tablesorter.js"></script>
10
+ <script type="text/javascript" src="javascripts/jquery-ui-tabs.js"></script>
11
+ <script type="text/javascript" src="javascripts/tarantula.js"></script>
12
+
13
+ <title>Tarantula</title>
14
+ </head>
15
+
16
+ <body>
17
+ <div id="container">
18
+ <div id="header">
19
+ <h1>Tarantula by Relevance</h1>
20
+ <h2>Eight legs, two fangs ... and an attitude</h2>
21
+ <p>Tarantula is an open source tool for testing Rails web
22
+ applications. Tarantula is developed by <a href="http://thinkrelevance.com">Relevance, Inc.</a>
23
+ and lives at <a href="http://github.com/relevance/tarantula">http://github.com/relevance/tarantula</a>.</p>
24
+ <hr/>
25
+ </div>
26
+ <div id="page">
27
+ <div id="tabs-container">
28
+ <ul class="tabs"> </ul>
29
+ </div>
30
+
31
+ <div id="results-container">
32
+
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </body>
37
+ </html>
@@ -0,0 +1,18 @@
1
+ class Relevance::Tarantula::InvalidHtmlHandler
2
+ include Relevance::Tarantula
3
+ def handle(result)
4
+ response = result.response
5
+ return unless response.html?
6
+ begin
7
+ body = HTML::Document.new(response.body, true)
8
+ rescue Exception => e
9
+ error_result = result.dup
10
+ error_result.success = false
11
+ error_result.description = "Bad HTML (Scanner)"
12
+ error_result.data = e.message
13
+ error_result
14
+ else
15
+ nil
16
+ end
17
+ end
18
+ end