tarantula 0.2.0 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,47 +4,64 @@ describe "Relevance::Tarantula::Link" do
4
4
  include ActionView::Helpers::UrlHelper
5
5
 
6
6
  it "does not raise an error when initializing without href attribtue" do
7
- link = Relevance::Tarantula::Link.new(Hpricot('<a="/foo">foo</a>').at('a'))
7
+ link = make_link(Hpricot('<a="/foo">foo</a>').at('a'))
8
8
  link.href.should == nil
9
9
  link.method.should == :get
10
10
  end
11
11
 
12
12
  it "parses anchor tags" do
13
- link = Relevance::Tarantula::Link.new(Hpricot('<a href="/foo">foo</a>').at('a'))
13
+ link = make_link(Hpricot('<a href="/foo">foo</a>').at('a'))
14
14
  link.href.should == '/foo'
15
15
  link.method.should == :get
16
16
  end
17
17
 
18
18
  it "parses anchor tags with POST 'method'" do
19
- link = Relevance::Tarantula::Link.new(Hpricot(%Q{<a href="/foo" onclick="#{method_javascript_function(:post)}">foo</a>}).at('a'))
19
+ link = make_link(Hpricot(%Q{<a href="/foo" onclick="#{method_javascript_function(:post)}">foo</a>}).at('a'))
20
20
  link.href.should == '/foo'
21
21
  link.method.should == :post
22
22
  end
23
23
 
24
24
  it "parses anchor tags with PUT 'method'" do
25
- link = Relevance::Tarantula::Link.new(Hpricot(%Q{<a href="/foo" onclick="#{method_javascript_function(:put)}">foo</a>}).at('a'))
25
+ link = make_link(Hpricot(%Q{<a href="/foo" onclick="#{method_javascript_function(:put)}">foo</a>}).at('a'))
26
26
  link.href.should == '/foo'
27
27
  link.method.should == :put
28
28
  end
29
29
 
30
30
  it "parses anchor tags with DELETE 'method'" do
31
- link = Relevance::Tarantula::Link.new(Hpricot(%Q{<a href="/foo" onclick="#{method_javascript_function(:delete)}">foo</a>}).at('a'))
31
+ link = make_link(Hpricot(%Q{<a href="/foo" onclick="#{method_javascript_function(:delete)}">foo</a>}).at('a'))
32
32
  link.href.should == '/foo'
33
33
  link.method.should == :delete
34
34
  end
35
35
 
36
36
  it "parses link tags with text" do
37
- link = Relevance::Tarantula::Link.new(Hpricot('<link href="/bar">bar</a>').at('link'))
37
+ link = make_link(Hpricot('<link href="/bar">bar</a>').at('link'))
38
38
  link.href.should == '/bar'
39
39
  link.method.should == :get
40
40
  end
41
41
 
42
42
  it "parses link tags without text" do
43
- link = Relevance::Tarantula::Link.new(Hpricot('<link href="/bar" />').at('link'))
43
+ link = make_link(Hpricot('<link href="/bar" />').at('link'))
44
44
  link.href.should == '/bar'
45
45
  link.method.should == :get
46
46
  end
47
47
 
48
+ it 'remembers link referrer if there is one' do
49
+ link = make_link('/url', stub_everything, '/some-referrer')
50
+ link.referrer.should == '/some-referrer'
51
+ end
52
+
53
+ it "does two things when crawled: follow, log, and handle" do
54
+ crawler = Relevance::Tarantula::Crawler.new
55
+ link = make_link('/foo', crawler)
56
+
57
+ response = stub(:code => "200")
58
+ crawler.expects(:follow).returns(response)
59
+ link.expects(:log)
60
+ crawler.expects(:handle_link_results)
61
+
62
+ link.crawl
63
+ end
64
+
48
65
  # method_javascript_function needs this method
49
66
  def protect_against_forgery?
50
67
  false
@@ -41,7 +41,7 @@ describe "Relevance::Tarantula::RailsIntegrationProxy" do
41
41
  it "adds a response accessor to its delegate rails integration test" do
42
42
  o = Object.new
43
43
  Relevance::Tarantula::RailsIntegrationProxy.new(o)
44
- o.methods(false).sort.should == %w{response response=}
44
+ o.methods(false).map(&:to_s).sort.should == %w{response response=}
45
45
  end
46
46
 
47
47
  end
@@ -10,6 +10,9 @@ class Relevance::Tarantula::Attack
10
10
  def ==(other)
11
11
  Relevance::Tarantula::Attack === other && HASHABLE_ATTRS.all? { |attr| send(attr) == other.send(attr)}
12
12
  end
13
+ def input(input_field=nil)
14
+ @input
15
+ end
13
16
  end
14
17
 
15
18
 
@@ -4,7 +4,7 @@ class Relevance::Tarantula::AttackHandler
4
4
  include ERB::Util
5
5
 
6
6
  def attacks
7
- Relevance::Tarantula::AttackFormSubmission.attacks.select(&:output)
7
+ Relevance::Tarantula::FormSubmission.attacks.select(&:output)
8
8
  end
9
9
 
10
10
  def handle(result)
@@ -0,0 +1,40 @@
1
+ class Relevance::Tarantula::BasicAttack
2
+ ATTRS = [:name, :output, :description]
3
+
4
+ attr_reader *ATTRS
5
+
6
+ def initialize
7
+ @name = "Tarantula Basic Fuzzer"
8
+ @output = nil
9
+ @description = "Supplies purely random but simplistically generated form input."
10
+ end
11
+
12
+ def ==(other)
13
+ Relevance::Tarantula::BasicAttack === other && ATTRS.all? { |attr| send(attr) == other.send(attr)}
14
+ end
15
+
16
+ def input(input_field)
17
+ case input_field['name']
18
+ when /amount/ then random_int
19
+ when /_id$/ then random_whole_number
20
+ when /uploaded_data/ then nil
21
+ when nil then input['value']
22
+ else
23
+ random_int
24
+ end
25
+ end
26
+
27
+ def big_number
28
+ 10000 # arbitrary
29
+ end
30
+
31
+ def random_int
32
+ rand(big_number) - (big_number/2)
33
+ end
34
+
35
+ def random_whole_number
36
+ rand(big_number)
37
+ end
38
+ end
39
+
40
+
@@ -10,7 +10,7 @@ class Relevance::Tarantula::Crawler
10
10
  class CrawlTimeout < RuntimeError; end
11
11
 
12
12
  attr_accessor :proxy, :handlers, :skip_uri_patterns, :log_grabber,
13
- :reporters, :links_to_crawl, :links_queued, :forms_to_crawl,
13
+ :reporters, :crawl_queue, :links_queued,
14
14
  :form_signatures_queued, :max_url_length, :response_code_handler,
15
15
  :times_to_crawl, :fuzzers, :test_name, :crawl_timeout
16
16
  attr_reader :transform_url_patterns, :referrers, :failures, :successes, :crawl_start_times, :crawl_end_times
@@ -22,8 +22,7 @@ class Relevance::Tarantula::Crawler
22
22
  @handlers = [@response_code_handler = Result]
23
23
  @links_queued = Set.new
24
24
  @form_signatures_queued = Set.new
25
- @links_to_crawl = []
26
- @forms_to_crawl = []
25
+ @crawl_queue = []
27
26
  @crawl_start_times, @crawl_end_times = [], []
28
27
  @crawl_timeout = 20.minutes
29
28
  @referrers = {}
@@ -39,6 +38,8 @@ class Relevance::Tarantula::Crawler
39
38
  @decoder = HTMLEntities.new
40
39
  @times_to_crawl = 1
41
40
  @fuzzers = [Relevance::Tarantula::FormSubmission]
41
+
42
+ @stdout_tty = $stdout.tty?
42
43
  end
43
44
 
44
45
  def method_missing(meth, *args)
@@ -55,14 +56,14 @@ class Relevance::Tarantula::Crawler
55
56
  def crawl(url = "/")
56
57
  orig_links_queued = @links_queued.dup
57
58
  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
59
+ orig_crawl_queue = @crawl_queue.dup
60
60
  @times_to_crawl.times do |num|
61
61
  queue_link url
62
62
 
63
63
  begin
64
64
  do_crawl num
65
65
  rescue CrawlTimeout => e
66
+ puts
66
67
  puts e.message
67
68
  end
68
69
 
@@ -71,8 +72,7 @@ class Relevance::Tarantula::Crawler
71
72
  if num + 1 < @times_to_crawl
72
73
  @links_queued = orig_links_queued
73
74
  @form_signatures_queued = orig_form_signatures_queued
74
- @links_to_crawl = orig_links_to_crawl
75
- @forms_to_crawl = orig_forms_to_crawl
75
+ @crawl_queue = orig_crawl_queue
76
76
  @referrers = {}
77
77
  end
78
78
  end
@@ -83,23 +83,20 @@ class Relevance::Tarantula::Crawler
83
83
  end
84
84
 
85
85
  def finished?
86
- @links_to_crawl.empty? && @forms_to_crawl.empty?
86
+ @crawl_queue.empty?
87
87
  end
88
88
 
89
89
  def do_crawl(number)
90
90
  while (!finished?)
91
91
  @crawl_start_times << Time.now
92
- crawl_queued_links(number)
93
- crawl_queued_forms(number)
92
+ crawl_the_queue(number)
94
93
  @crawl_end_times << Time.now
95
94
  end
96
95
  end
97
96
 
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)
97
+ def crawl_the_queue(number = 0)
98
+ while (request = @crawl_queue.pop)
99
+ request.crawl
103
100
  blip(number)
104
101
  end
105
102
  end
@@ -110,15 +107,10 @@ class Relevance::Tarantula::Crawler
110
107
  end
111
108
  end
112
109
 
113
- def handle_link_results(link, response)
110
+ def handle_link_results(link, result)
114
111
  handlers.each do |h|
115
112
  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)
113
+ save_result h.handle(result)
122
114
  rescue Exception => e
123
115
  log "error handling #{link} #{e.message}"
124
116
  # TODO: pass to results
@@ -126,23 +118,14 @@ class Relevance::Tarantula::Crawler
126
118
  end
127
119
  end
128
120
 
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
121
+ def follow(method, url, data=nil)
122
+ proxy.send(method, url, data)
144
123
  end
145
124
 
125
+ def submit(method, action, data)
126
+ proxy.send(method, action, data)
127
+ end
128
+
146
129
  def elasped_time_for_pass(num)
147
130
  Time.now - crawl_start_times[num]
148
131
  end
@@ -150,6 +133,14 @@ class Relevance::Tarantula::Crawler
150
133
  def grab_log!
151
134
  @log_grabber && @log_grabber.grab!
152
135
  end
136
+
137
+ def make_result(options)
138
+ defaults = {
139
+ :log => grab_log!,
140
+ :test_name => test_name
141
+ }
142
+ Result.new(defaults.merge(options)).freeze
143
+ end
153
144
 
154
145
  def handle_form_results(form, response)
155
146
  handlers.each do |h|
@@ -193,23 +184,21 @@ class Relevance::Tarantula::Crawler
193
184
  end
194
185
 
195
186
  def queue_link(dest, referrer = nil)
196
- dest = Link.new(dest)
197
- dest.href = transform_url(dest.href)
187
+ dest = Link.new(dest, self, referrer)
198
188
  return if should_skip_link?(dest)
199
- @referrers[dest] = referrer if referrer
200
- @links_to_crawl << dest
189
+ @crawl_queue << dest
201
190
  @links_queued << dest
202
191
  dest
203
192
  end
204
193
 
205
194
  def queue_form(form, referrer = nil)
206
195
  fuzzers.each do |fuzzer|
207
- fuzzer.mutate(Form.new(form)).each do |fs|
208
- # fs = fuzzer.new(Form.new(form))
196
+ fuzzer.mutate(Form.new(form, self, referrer)).each do |fs|
197
+ # fs = fuzzer.new(Form.new(form, self, referrer))
209
198
  fs.action = transform_url(fs.action)
210
199
  return if should_skip_form_submission?(fs)
211
200
  @referrers[fs.action] = referrer if referrer
212
- @forms_to_crawl << fs
201
+ @crawl_queue << fs
213
202
  @form_signatures_queued << fs.signature
214
203
  end
215
204
  end
@@ -234,6 +223,7 @@ class Relevance::Tarantula::Crawler
234
223
  end
235
224
 
236
225
  def report_results
226
+ puts "Crawled #{total_links_count} links and forms."
237
227
  generate_reports
238
228
  end
239
229
 
@@ -242,7 +232,7 @@ class Relevance::Tarantula::Crawler
242
232
  end
243
233
 
244
234
  def links_remaining_count
245
- @links_to_crawl.size + @forms_to_crawl.size
235
+ @crawl_queue.size
246
236
  end
247
237
 
248
238
  def links_completed_count
@@ -251,7 +241,7 @@ class Relevance::Tarantula::Crawler
251
241
 
252
242
  def blip(number = 0)
253
243
  unless verbose
254
- print "\r #{links_completed_count} of #{total_links_count} links completed "
244
+ print "\r #{links_completed_count} of #{total_links_count} links completed " if @stdout_tty
255
245
  timeout_if_too_long(number)
256
246
  end
257
247
  end
@@ -25,18 +25,18 @@
25
25
  </ul>
26
26
 
27
27
  <div id="report">
28
- <h3>Detail of <%= short_description %> <em>Generated on <%= Time.now %></em></h3>
29
- <p><b>Resource</b> <a href="<%= full_url %>"><%= full_url %></a></p>
30
- <p><b>Response</b> <span class="r<%= code.first %>"><%= code %></span></p>
31
- <p><b>Referrer</b> <%= referrer || "" %></p>
28
+ <h3>Detail of <%= result.short_description %> <em>Generated on <%= Time.now %></em></h3>
29
+ <p><b>Resource</b> <a href="<%= result.full_url %>"><%= result.full_url %></a></p>
30
+ <p><b>Response</b> <span class="r<%= result.code.first %>"><%= result.code %></span></p>
31
+ <p><b>Referrer</b> <%= result.referrer || "" %></p>
32
32
 
33
33
  <table class="output">
34
34
  <tbody>
35
35
  <tr>
36
36
  <th colspan="2">#&nbsp;&nbsp;Data</th>
37
37
  </tr>
38
- <% if data %>
39
- <%= wrap_in_line_number_table_row(data) %>
38
+ <% if result.data %>
39
+ <%= result.wrap_in_line_number_table_row(result.data) %>
40
40
  <% else %>
41
41
  <tr>
42
42
  <td colspan="2">No Data</td>
@@ -50,8 +50,8 @@
50
50
  <tr>
51
51
  <th colspan="2">#&nbsp;&nbsp;Body</th>
52
52
  </tr>
53
- <% if body %>
54
- <%= wrap_in_line_number_table_row(body) %>
53
+ <% if result.body %>
54
+ <%= result.wrap_in_line_number_table_row(result.body) %>
55
55
  <% else %>
56
56
  <tr>
57
57
  <td colspan="2">No Body</td>
@@ -65,8 +65,8 @@
65
65
  <tr>
66
66
  <th colspan="2">#&nbsp;&nbsp;Log</th>
67
67
  </tr>
68
- <% if log %>
69
- <%= wrap_in_line_number_table_row(log) {|line| wrap_stack_trace_line(line)} %>
68
+ <% if result.log %>
69
+ <%= result.wrap_in_line_number_table_row(result.log) {|line| wrap_stack_trace_line(line)} %>
70
70
  <% else %>
71
71
  <tr>
72
72
  <td colspan="2">No Log</td>
@@ -78,4 +78,4 @@
78
78
  </div>
79
79
  </div>
80
80
  </body>
81
- </html>
81
+ </html>
@@ -2,8 +2,10 @@ class Relevance::Tarantula::Form
2
2
  extend Forwardable
3
3
  def_delegators("@tag", :search)
4
4
 
5
- def initialize(tag)
6
- @tag = tag
5
+ attr_accessor :crawler, :referrer
6
+
7
+ def initialize(tag, crawler, referrer)
8
+ @tag, @crawler, @referrer = tag, crawler, referrer
7
9
  end
8
10
 
9
11
  def action
@@ -1,25 +1,58 @@
1
1
  class Relevance::Tarantula::FormSubmission
2
- attr_accessor :method, :action, :data
3
- def initialize(form)
2
+ include Relevance::Tarantula
3
+ attr_accessor :method, :action, :data, :attack, :form
4
+
5
+ class << self
6
+ def attacks
7
+ # normalize from hash input to Attack
8
+ @attacks = @attacks.map do |val|
9
+ Hash === val ? Relevance::Tarantula::Attack.new(val) : val
10
+ end
11
+ @attacks
12
+ end
13
+ def attacks=(atts)
14
+ # normalize from hash input to Attack
15
+ @attacks = atts.map do |val|
16
+ Hash === val ? Relevance::Tarantula::Attack.new(val) : val
17
+ end
18
+ end
19
+ end
20
+ @attacks = [Relevance::Tarantula::BasicAttack.new]
21
+
22
+ def initialize(form, attack = Relevance::Tarantula::BasicAttack.new)
23
+ @form = form
4
24
  @method = form.method
5
25
  @action = form.action
26
+ @attack = attack
6
27
  @data = mutate_selects(form).merge(mutate_text_areas(form)).merge(mutate_inputs(form))
7
28
  end
8
29
 
30
+ def crawl
31
+ begin
32
+ response = form.crawler.submit(method, action, data)
33
+ log "Response #{response.code} for #{self}"
34
+ rescue ActiveRecord::RecordNotFound => e
35
+ log "Skipping #{action}, presumed ok that record is missing"
36
+ response = Relevance::Tarantula::Response.new(:code => "404", :body => e.message, :content_type => "text/plain")
37
+ end
38
+ form.crawler.handle_form_results(self, response)
39
+ response
40
+ end
41
+
9
42
  def self.mutate(form)
10
- [self.new(form)]
43
+ attacks.map{|attack| new(form, attack)} if attacks
11
44
  end
12
-
45
+
13
46
  def to_s
14
- "#{action} #{method} #{data.inspect}"
47
+ "#{action} #{method} #{data.inspect} #{attack.inspect}"
15
48
  end
16
-
49
+
17
50
  # a form's signature is what makes it unique (e.g. action + fields)
18
51
  # used to keep track of which forms we have submitted already
19
52
  def signature
20
- [action, data.keys.sort]
53
+ [action, data.keys.sort, attack.name]
21
54
  end
22
-
55
+
23
56
  def create_random_data_for(form, tag_selector)
24
57
  form.search(tag_selector).inject({}) do |form_args, input|
25
58
  # TODO: test
@@ -35,36 +68,21 @@ class Relevance::Tarantula::FormSubmission
35
68
  def mutate_text_areas(form)
36
69
  create_random_data_for(form, 'textarea')
37
70
  end
38
-
71
+
39
72
  def mutate_selects(form)
40
73
  form.search('select').inject({}) do |form_args, select|
41
74
  options = select.search('option')
42
75
  option = options.rand
43
- form_args[select['name']] = option['value']
76
+ form_args[select['name']] = option['value']
44
77
  form_args
45
78
  end
46
79
  end
47
-
80
+
48
81
  def random_data(input)
49
82
  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
83
+ when /^_method$/ then input['value']
84
+ else
85
+ attack.input(input)
56
86
  end
57
87
  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
88
  end
@@ -1,4 +1,5 @@
1
1
  class Relevance::Tarantula::Link
2
+ include Relevance::Tarantula
2
3
 
3
4
  class << self
4
5
  include ActionView::Helpers::UrlHelper
@@ -17,18 +18,33 @@ class Relevance::Tarantula::Link
17
18
  METHOD_REGEXPS[m] = /#{s}/
18
19
  end
19
20
 
20
- attr_accessor :href
21
+ attr_accessor :href, :crawler, :referrer
21
22
 
22
- def initialize(link)
23
+ def initialize(link, crawler, referrer)
24
+ @crawler, @referrer = crawler, referrer
25
+
23
26
  if String === link || link.nil?
24
- @href = link
27
+ @href = transform_url(link)
25
28
  @method = :get
26
29
  else # should be a tag
27
- @href = link['href'] ? link['href'].downcase : nil
30
+ @href = link['href'] ? transform_url(link['href'].downcase) : nil
28
31
  @tag = link
29
32
  end
30
33
  end
31
34
 
35
+ def crawl
36
+ response = crawler.follow(method, href)
37
+ log "Response #{response.code} for #{self}"
38
+ crawler.handle_link_results(self, make_result(response))
39
+ end
40
+
41
+ def make_result(response)
42
+ crawler.make_result(:method => method,
43
+ :url => href,
44
+ :response => response,
45
+ :referrer => referrer)
46
+ end
47
+
32
48
  def method
33
49
  @method ||= begin
34
50
  (@tag &&
@@ -39,6 +55,10 @@ class Relevance::Tarantula::Link
39
55
  end
40
56
  end
41
57
 
58
+ def transform_url(link)
59
+ crawler.transform_url(link)
60
+ end
61
+
42
62
  def ==(obj)
43
63
  obj.respond_to?(:href) && obj.respond_to?(:method) &&
44
64
  self.href.to_s == obj.href.to_s && self.method.to_s == obj.method.to_s
@@ -41,7 +41,7 @@ class Relevance::Tarantula::RailsIntegrationProxy
41
41
  if response.code == '404'
42
42
  if File.exist?(static_content_path(url))
43
43
  case ext = File.extension(url)
44
- when /html|te?xt|css|js|jpe?g|gif|psd|png|eps|pdf/
44
+ when /html|te?xt|css|js|jpe?g|gif|psd|png|eps|pdf|ico/
45
45
  response.body = static_content_file(url)
46
46
  response.headers["type"] = "text/#{ext}" # readable as response.content_type
47
47
  response.meta.attr_accessor :code
@@ -11,33 +11,43 @@ class Relevance::Tarantula::Result
11
11
  self.instance_variable_set("@#{k}", v)
12
12
  end
13
13
  end
14
+
14
15
  def short_description
15
16
  [method,url].join(" ")
16
17
  end
18
+
17
19
  def sequence_number
18
20
  @sequence_number ||= (self.class.next_number += 1)
19
21
  end
22
+
20
23
  def file_name
21
24
  "#{sequence_number}.html"
22
25
  end
26
+
23
27
  def code
24
28
  response && response.code
25
29
  end
30
+
26
31
  def body
27
32
  response && response.body
28
33
  end
34
+
29
35
  def full_url
30
36
  "#{DEFAULT_LOCALHOST}#{url}"
31
37
  end
38
+
32
39
  ALLOW_NNN_FOR = /^allow_(\d\d\d)_for$/
40
+
33
41
  class << self
34
42
  attr_accessor :next_number
43
+
35
44
  def handle(result)
36
45
  retval = result.dup
37
46
  retval.success = successful?(result.response) || can_skip_error?(result)
38
47
  retval.description = "Bad HTTP Response" unless retval.success
39
48
  retval
40
49
  end
50
+
41
51
  def success_codes
42
52
  %w{200 201 302 401}
43
53
  end
@@ -51,16 +61,17 @@ class Relevance::Tarantula::Result
51
61
  return false unless coll
52
62
  coll.any? {|item| item === result.url}
53
63
  end
64
+
54
65
  def successful?(response)
55
66
  success_codes.member?(response.code)
56
67
  end
68
+
57
69
  def method_missing(meth, *args)
58
70
  super unless ALLOW_NNN_FOR =~ meth.to_s
59
71
  (allow_errors_for[$1] ||= []).push(*args)
60
72
  end
61
73
  end
74
+
62
75
  self.allow_errors_for = {}
63
76
  self.next_number = 0
64
-
65
-
66
- end
77
+ end
@@ -49,10 +49,10 @@ require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "log_gra
49
49
  require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "invalid_html_handler"))
50
50
  require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "transform"))
51
51
  require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "crawler"))
52
+ require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "basic_attack"))
52
53
  require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "form"))
53
54
  require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "form_submission"))
54
55
  require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "attack"))
55
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "attack_form_submission"))
56
56
  require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "attack_handler"))
57
57
  require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "link"))
58
58