tarantula 0.3.3 → 0.4.0

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 (74) hide show
  1. data/.autotest +14 -0
  2. data/.gitignore +9 -0
  3. data/.rvmrc +1 -0
  4. data/DSL_EXAMPLES.md +120 -0
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +108 -0
  7. data/{MIT-LICENSE → LICENSE} +0 -0
  8. data/README.rdoc +3 -28
  9. data/Rakefile +27 -59
  10. data/lib/relevance/core_extensions/ellipsize.rb +23 -19
  11. data/lib/relevance/core_extensions/file.rb +10 -4
  12. data/lib/relevance/core_extensions/response.rb +9 -6
  13. data/lib/relevance/core_extensions/test_case.rb +14 -12
  14. data/lib/relevance/tarantula.rb +24 -25
  15. data/lib/relevance/tarantula/attack.rb +19 -15
  16. data/lib/relevance/tarantula/attack_handler.rb +32 -26
  17. data/lib/relevance/tarantula/basic_attack.rb +36 -32
  18. data/lib/relevance/tarantula/crawler.rb +222 -216
  19. data/lib/relevance/tarantula/form.rb +27 -21
  20. data/lib/relevance/tarantula/form_submission.rb +79 -73
  21. data/lib/relevance/tarantula/html_document_handler.rb +37 -31
  22. data/lib/relevance/tarantula/html_report_helper.rb +36 -29
  23. data/lib/relevance/tarantula/html_reporter.rb +105 -99
  24. data/lib/relevance/tarantula/invalid_html_handler.rb +21 -15
  25. data/lib/relevance/tarantula/io_reporter.rb +37 -31
  26. data/lib/relevance/tarantula/link.rb +97 -73
  27. data/lib/relevance/tarantula/log_grabber.rb +20 -14
  28. data/lib/relevance/tarantula/rails_integration_proxy.rb +64 -58
  29. data/lib/relevance/tarantula/response.rb +16 -10
  30. data/lib/relevance/tarantula/result.rb +69 -63
  31. data/lib/relevance/tarantula/tidy_handler.rb +22 -17
  32. data/lib/relevance/tarantula/transform.rb +18 -14
  33. data/lib/relevance/tarantula/version.rb +5 -0
  34. data/{tasks → lib/relevance/tasks}/tarantula_tasks.rake +1 -1
  35. data/lib/tarantula-rails3.rb +9 -0
  36. data/{examples/relevance/core_extensions/ellipsize_example.rb → spec/relevance/core_extensions/ellipsize_spec.rb} +2 -2
  37. data/{examples/relevance/core_extensions/file_example.rb → spec/relevance/core_extensions/file_spec.rb} +2 -2
  38. data/{examples/relevance/core_extensions/response_example.rb → spec/relevance/core_extensions/response_spec.rb} +2 -2
  39. data/{examples/relevance/core_extensions/test_case_example.rb → spec/relevance/core_extensions/test_case_spec.rb} +1 -1
  40. data/{examples/relevance/tarantula/attack_handler_example.rb → spec/relevance/tarantula/attack_handler_spec.rb} +1 -1
  41. data/{examples/relevance/tarantula/basic_attack_example.rb → spec/relevance/tarantula/basic_attack_spec.rb} +2 -2
  42. data/{examples/relevance/tarantula/crawler_example.rb → spec/relevance/tarantula/crawler_spec.rb} +2 -2
  43. data/{examples/relevance/tarantula/form_example.rb → spec/relevance/tarantula/form_spec.rb} +2 -2
  44. data/{examples/relevance/tarantula/form_submission_example.rb → spec/relevance/tarantula/form_submission_spec.rb} +3 -3
  45. data/{examples/relevance/tarantula/html_document_handler_example.rb → spec/relevance/tarantula/html_document_handler_spec.rb} +1 -1
  46. data/{examples/relevance/tarantula/html_report_helper_example.rb → spec/relevance/tarantula/html_report_helper_spec.rb} +1 -1
  47. data/{examples/relevance/tarantula/html_reporter_example.rb → spec/relevance/tarantula/html_reporter_spec.rb} +1 -1
  48. data/{examples/relevance/tarantula/invalid_html_handler_example.rb → spec/relevance/tarantula/invalid_html_handler_spec.rb} +1 -1
  49. data/{examples/relevance/tarantula/io_reporter_example.rb → spec/relevance/tarantula/io_reporter_spec.rb} +1 -1
  50. data/{examples/relevance/tarantula/link_example.rb → spec/relevance/tarantula/link_spec.rb} +5 -5
  51. data/{examples/relevance/tarantula/log_grabber_example.rb → spec/relevance/tarantula/log_grabber_spec.rb} +1 -1
  52. data/{examples/relevance/tarantula/rails_integration_proxy_example.rb → spec/relevance/tarantula/rails_integration_proxy_spec.rb} +1 -1
  53. data/{examples/relevance/tarantula/result_example.rb → spec/relevance/tarantula/result_spec.rb} +1 -1
  54. data/{examples/relevance/tarantula/tidy_handler_example.rb → spec/relevance/tarantula/tidy_handler_spec.rb} +1 -1
  55. data/{examples/relevance/tarantula/transform_example.rb → spec/relevance/tarantula/transform_spec.rb} +2 -2
  56. data/{examples/relevance/tarantula_example.rb → spec/relevance/tarantula_spec.rb} +1 -1
  57. data/{examples/example_helper.rb → spec/spec_helper.rb} +6 -14
  58. data/tarantula.gemspec +31 -0
  59. data/template/tarantula_test.rb +1 -1
  60. data/vendor/xss-shield/MIT-LICENSE +20 -0
  61. data/vendor/xss-shield/README +76 -0
  62. data/vendor/xss-shield/init.rb +16 -0
  63. data/vendor/xss-shield/lib/xss_shield.rb +6 -0
  64. data/vendor/xss-shield/lib/xss_shield/erb_hacks.rb +111 -0
  65. data/vendor/xss-shield/lib/xss_shield/haml_hacks.rb +42 -0
  66. data/vendor/xss-shield/lib/xss_shield/safe_string.rb +47 -0
  67. data/vendor/xss-shield/lib/xss_shield/secure_helpers.rb +40 -0
  68. data/vendor/xss-shield/test/test_actionview_integration.rb +40 -0
  69. data/vendor/xss-shield/test/test_erb.rb +44 -0
  70. data/vendor/xss-shield/test/test_haml.rb +43 -0
  71. data/vendor/xss-shield/test/test_helpers.rb +25 -0
  72. data/vendor/xss-shield/test/test_safe_string.rb +55 -0
  73. metadata +170 -99
  74. data/VERSION.yml +0 -4
@@ -1,9 +1,15 @@
1
- module Relevance::CoreExtensions::File
2
- def extension(path)
3
- extname(path)[1..-1]
1
+ module Relevance
2
+ module CoreExtensions
3
+
4
+ module File
5
+ def extension(path)
6
+ extname(path)[1..-1]
7
+ end
8
+ end
9
+
4
10
  end
5
11
  end
6
12
 
7
13
  class File
8
14
  extend Relevance::CoreExtensions::File
9
- end
15
+ end
@@ -1,9 +1,12 @@
1
1
  # dynamically mixed in to response objects
2
- module Relevance::CoreExtensions::Response
3
- def html?
4
- # some versions of Rails integration tests don't set content type
5
- # so we are treating nil as html. A better fix would be welcome here.
6
- ((content_type =~ %r{^text/html}) != nil) || content_type == nil
2
+ module Relevance
3
+ module CoreExtensions
4
+ module Response
5
+ def html?
6
+ # some versions of Rails integration tests don't set content type
7
+ # so we are treating nil as html. A better fix would be welcome here.
8
+ ((content_type =~ %r{^text/html}) != nil) || content_type == nil
9
+ end
10
+ end
7
11
  end
8
12
  end
9
-
@@ -1,19 +1,21 @@
1
- require 'action_controller/integration'
1
+ module Relevance
2
+ module CoreExtensions
2
3
 
3
- module Relevance::CoreExtensions::TestCaseExtensions
4
+ module TestCaseExtensions
5
+ def tarantula_crawl(integration_test, options = {})
6
+ url = options[:url] || "/"
7
+ t = tarantula_crawler(integration_test, options)
8
+ t.crawl url
9
+ end
10
+
11
+ def tarantula_crawler(integration_test, options = {})
12
+ Relevance::Tarantula::RailsIntegrationProxy.rails_integration_test(integration_test, options)
13
+ end
14
+ end
4
15
 
5
- def tarantula_crawl(integration_test, options = {})
6
- url = options[:url] || "/"
7
- t = tarantula_crawler(integration_test, options)
8
- t.crawl url
9
- end
10
-
11
- def tarantula_crawler(integration_test, options = {})
12
- Relevance::Tarantula::RailsIntegrationProxy.rails_integration_test(integration_test, options)
13
16
  end
14
-
15
17
  end
16
18
 
17
19
  if defined? ActionController::IntegrationTest
18
20
  ActionController::IntegrationTest.class_eval { include Relevance::CoreExtensions::TestCaseExtensions }
19
- end
21
+ end
@@ -4,7 +4,6 @@ require 'forwardable'
4
4
  require 'erb'
5
5
  require 'active_support'
6
6
  require 'action_controller'
7
-
8
7
  # bringing in xss-shield requires a bunch of other dependencies
9
8
  # still not certain about this, if it ruins your world please let me know
10
9
  #xss_shield_path = File.join(TARANTULA_ROOT, %w{vendor xss-shield})
@@ -24,7 +23,7 @@ module Relevance
24
23
  puts msg if verbose
25
24
  end
26
25
  def rails_root
27
- ::RAILS_ROOT
26
+ ::Rails.root.to_s
28
27
  end
29
28
  def verbose
30
29
  ENV["VERBOSE"]
@@ -32,28 +31,28 @@ module Relevance
32
31
  end
33
32
  end
34
33
 
35
- require File.expand_path(File.join(File.dirname(__FILE__), "core_extensions", "test_case"))
36
- require File.expand_path(File.join(File.dirname(__FILE__), "core_extensions", "ellipsize"))
37
- require File.expand_path(File.join(File.dirname(__FILE__), "core_extensions", "file"))
38
- require File.expand_path(File.join(File.dirname(__FILE__), "core_extensions", "response"))
39
- require File.expand_path(File.join(File.dirname(__FILE__), "core_extensions", "metaclass"))
40
- require File.expand_path(File.join(File.dirname(__FILE__), "core_extensions", "string_chars_fix"))
34
+ require "relevance/core_extensions/test_case"
35
+ require "relevance/core_extensions/ellipsize"
36
+ require "relevance/core_extensions/file"
37
+ require "relevance/core_extensions/response"
38
+ require "relevance/core_extensions/metaclass"
39
+ require "relevance/core_extensions/string_chars_fix"
41
40
 
42
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "html_reporter"))
43
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "html_report_helper"))
44
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "io_reporter"))
45
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "recording"))
46
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "response"))
47
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "result"))
48
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "log_grabber"))
49
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "invalid_html_handler"))
50
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "transform"))
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"))
53
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "form"))
54
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "form_submission"))
55
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "attack"))
56
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "attack_handler"))
57
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "link"))
41
+ require "relevance/tarantula/html_reporter"
42
+ require "relevance/tarantula/html_report_helper"
43
+ require "relevance/tarantula/io_reporter"
44
+ require "relevance/tarantula/recording"
45
+ require "relevance/tarantula/response"
46
+ require "relevance/tarantula/result"
47
+ require "relevance/tarantula/log_grabber"
48
+ require "relevance/tarantula/invalid_html_handler"
49
+ require "relevance/tarantula/transform"
50
+ require "relevance/tarantula/crawler"
51
+ require "relevance/tarantula/basic_attack"
52
+ require "relevance/tarantula/form"
53
+ require "relevance/tarantula/form_submission"
54
+ require "relevance/tarantula/attack"
55
+ require "relevance/tarantula/attack_handler"
56
+ require "relevance/tarantula/link"
58
57
 
59
- require File.expand_path(File.join(File.dirname(__FILE__), "tarantula", "tidy_handler")) if ENV['TIDY_PATH']
58
+ require "relevance/tarantula/tidy_handler" if ENV['TIDY_PATH']
@@ -1,18 +1,22 @@
1
- class Relevance::Tarantula::Attack
2
- HASHABLE_ATTRS = [:name, :input, :output, :description]
3
- attr_accessor *HASHABLE_ATTRS
4
- def initialize(hash)
5
- hash.each do |k,v|
6
- raise ArgumentError, k unless HASHABLE_ATTRS.member?(k)
7
- self.instance_variable_set("@#{k}", v)
1
+ module Relevance
2
+ module Tarantula
3
+
4
+ class Attack
5
+ HASHABLE_ATTRS = [:name, :input, :output, :description]
6
+ attr_accessor *HASHABLE_ATTRS
7
+ def initialize(hash)
8
+ hash.each do |k,v|
9
+ raise ArgumentError, k unless HASHABLE_ATTRS.member?(k)
10
+ self.instance_variable_set("@#{k}", v)
11
+ end
12
+ end
13
+ def ==(other)
14
+ Relevance::Tarantula::Attack === other && HASHABLE_ATTRS.all? { |attr| send(attr) == other.send(attr)}
15
+ end
16
+ def input(input_field=nil)
17
+ @input
18
+ end
8
19
  end
9
- end
10
- def ==(other)
11
- Relevance::Tarantula::Attack === other && HASHABLE_ATTRS.all? { |attr| send(attr) == other.send(attr)}
12
- end
13
- def input(input_field=nil)
14
- @input
20
+
15
21
  end
16
22
  end
17
-
18
-
@@ -1,37 +1,43 @@
1
1
  require 'hpricot'
2
2
 
3
- class Relevance::Tarantula::AttackHandler
4
- include ERB::Util
5
-
6
- def attacks
7
- Relevance::Tarantula::FormSubmission.attacks.select(&:output)
8
- end
9
-
10
- def handle(result)
11
- return unless attacks.size > 0
12
- regexp = '(' + attacks.map {|a| Regexp.escape a.output}.join('|') + ')'
13
- response = result.response
14
- return unless response.html?
15
- if n = (response.body =~ /#{regexp}/)
16
- error_result = result.dup
17
- error_result.success = false
18
- error_result.description = "XSS error found, match was: #{h($1)}"
19
- error_result.data = <<-STR
3
+ module Relevance
4
+ module Tarantula
5
+
6
+ class AttackHandler
7
+ include ERB::Util
8
+
9
+ def attacks
10
+ Relevance::Tarantula::FormSubmission.attacks.select(&:output)
11
+ end
12
+
13
+ def handle(result)
14
+ return unless attacks.size > 0
15
+ regexp = '(' + attacks.map {|a| Regexp.escape a.output}.join('|') + ')'
16
+ response = result.response
17
+ return unless response.html?
18
+ if n = (response.body =~ /#{regexp}/)
19
+ error_result = result.dup
20
+ error_result.success = false
21
+ error_result.description = "XSS error found, match was: #{h($1)}"
22
+ error_result.data = <<-STR
20
23
  ########################################################################
21
24
  # Text around unescaped string: #{$1}
22
25
  ########################################################################
23
- #{response.body[[0, n - 200].max , 400]}
24
-
25
-
26
-
27
-
28
-
26
+ #{response.body[[0, n - 200].max , 400]}
27
+
28
+
29
+
30
+
31
+
29
32
  ########################################################################
30
33
  # Attack information:
31
34
  ########################################################################
32
- #{attacks.select {|a| a.output == $1}[0].to_yaml}
33
- STR
34
- error_result
35
+ #{attacks.select {|a| a.output == $1}[0].to_yaml}
36
+ STR
37
+ error_result
38
+ end
39
+ end
35
40
  end
41
+
36
42
  end
37
43
  end
@@ -1,40 +1,44 @@
1
- class Relevance::Tarantula::BasicAttack
2
- ATTRS = [:name, :output, :description]
1
+ module Relevance
2
+ module Tarantula
3
3
 
4
- attr_reader *ATTRS
4
+ class BasicAttack
5
+ ATTRS = [:name, :output, :description]
5
6
 
6
- def initialize
7
- @name = "Tarantula Basic Fuzzer"
8
- @output = nil
9
- @description = "Supplies purely random but simplistically generated form input."
10
- end
7
+ attr_reader *ATTRS
11
8
 
12
- def ==(other)
13
- Relevance::Tarantula::BasicAttack === other && ATTRS.all? { |attr| send(attr) == other.send(attr)}
14
- end
9
+ def initialize
10
+ @name = "Tarantula Basic Fuzzer"
11
+ @output = nil
12
+ @description = "Supplies purely random but simplistically generated form input."
13
+ end
14
+
15
+ def ==(other)
16
+ Relevance::Tarantula::BasicAttack === other && ATTRS.all? { |attr| send(attr) == other.send(attr)}
17
+ end
18
+
19
+ def input(input_field)
20
+ case input_field['name']
21
+ when /amount/ then random_int
22
+ when /_id$/ then random_whole_number
23
+ when /uploaded_data/ then nil
24
+ when nil then input['value']
25
+ else
26
+ random_int
27
+ end
28
+ end
29
+
30
+ def big_number
31
+ 10000 # arbitrary
32
+ end
33
+
34
+ def random_int
35
+ rand(big_number) - (big_number/2)
36
+ end
15
37
 
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
38
+ def random_whole_number
39
+ rand(big_number)
40
+ end
24
41
  end
25
- end
26
42
 
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
43
  end
38
44
  end
39
-
40
-
@@ -3,252 +3,258 @@ require 'active_record/base'
3
3
  require File.expand_path(File.join(File.dirname(__FILE__), "rails_integration_proxy"))
4
4
  require File.expand_path(File.join(File.dirname(__FILE__), "html_document_handler.rb"))
5
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, :crawl_queue, :links_queued,
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
- @crawl_queue = []
26
- @crawl_start_times, @crawl_end_times = [], []
27
- @crawl_timeout = 20.minutes
28
- @referrers = {}
29
- @skip_uri_patterns = [
30
- /^javascript/,
31
- /^mailto/,
32
- /^http/,
33
- ]
34
- self.transform_url_patterns = [
35
- [/#.*$/, '']
36
- ]
37
- @reporters = [Relevance::Tarantula::IOReporter.new($stderr)]
38
- @decoder = HTMLEntities.new
39
- @times_to_crawl = 1
40
- @fuzzers = [Relevance::Tarantula::FormSubmission]
41
-
42
- @stdout_tty = $stdout.tty?
43
- end
6
+ module Relevance
7
+ module Tarantula
44
8
 
45
- def method_missing(meth, *args)
46
- super unless Result::ALLOW_NNN_FOR =~ meth.to_s
47
- @response_code_handler.send(meth, *args)
48
- end
9
+ class Crawler
10
+ extend Forwardable
11
+ include Relevance::Tarantula
49
12
 
50
- def transform_url_patterns=(patterns)
51
- @transform_url_patterns = patterns.map do |pattern|
52
- Array === pattern ? Relevance::Tarantula::Transform.new(*pattern) : pattern
53
- end
54
- end
13
+ class CrawlTimeout < RuntimeError; end
14
+
15
+ attr_accessor :proxy, :handlers, :skip_uri_patterns, :log_grabber,
16
+ :reporters, :crawl_queue, :links_queued,
17
+ :form_signatures_queued, :max_url_length, :response_code_handler,
18
+ :times_to_crawl, :fuzzers, :test_name, :crawl_timeout
19
+ attr_reader :transform_url_patterns, :referrers, :failures, :successes, :crawl_start_times, :crawl_end_times
55
20
 
56
- def crawl(url = "/")
57
- orig_links_queued = @links_queued.dup
58
- orig_form_signatures_queued = @form_signatures_queued.dup
59
- orig_crawl_queue = @crawl_queue.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
67
- puts e.message
68
- end
69
-
70
- puts "#{(num+1).ordinalize} crawl" if @times_to_crawl > 1
71
-
72
- if num + 1 < @times_to_crawl
73
- @links_queued = orig_links_queued
74
- @form_signatures_queued = orig_form_signatures_queued
75
- @crawl_queue = orig_crawl_queue
21
+ def initialize
22
+ @max_url_length = 1024
23
+ @successes = []
24
+ @failures = []
25
+ @handlers = [@response_code_handler = Result]
26
+ @links_queued = Set.new
27
+ @form_signatures_queued = Set.new
28
+ @crawl_queue = []
29
+ @crawl_start_times, @crawl_end_times = [], []
30
+ @crawl_timeout = 20.minutes
76
31
  @referrers = {}
32
+ @skip_uri_patterns = [
33
+ /^javascript/,
34
+ /^mailto/,
35
+ /^http/,
36
+ ]
37
+ self.transform_url_patterns = [
38
+ [/#.*$/, '']
39
+ ]
40
+ @reporters = [Relevance::Tarantula::IOReporter.new($stderr)]
41
+ @decoder = HTMLEntities.new
42
+ @times_to_crawl = 1
43
+ @fuzzers = [Relevance::Tarantula::FormSubmission]
44
+
45
+ @stdout_tty = $stdout.tty?
77
46
  end
78
- end
79
- rescue Interrupt
80
- $stderr.puts "CTRL-C"
81
- ensure
82
- report_results
83
- end
84
47
 
85
- def finished?
86
- @crawl_queue.empty?
87
- end
48
+ def method_missing(meth, *args)
49
+ super unless Result::ALLOW_NNN_FOR =~ meth.to_s
50
+ @response_code_handler.send(meth, *args)
51
+ end
88
52
 
89
- def do_crawl(number)
90
- while (!finished?)
91
- @crawl_start_times << Time.now
92
- crawl_the_queue(number)
93
- @crawl_end_times << Time.now
94
- end
95
- end
53
+ def transform_url_patterns=(patterns)
54
+ @transform_url_patterns = patterns.map do |pattern|
55
+ Array === pattern ? Relevance::Tarantula::Transform.new(*pattern) : pattern
56
+ end
57
+ end
96
58
 
97
- def crawl_the_queue(number = 0)
98
- while (request = @crawl_queue.pop)
99
- request.crawl
100
- blip(number)
101
- end
102
- end
59
+ def crawl(url = "/")
60
+ orig_links_queued = @links_queued.dup
61
+ orig_form_signatures_queued = @form_signatures_queued.dup
62
+ orig_crawl_queue = @crawl_queue.dup
63
+ @times_to_crawl.times do |num|
64
+ queue_link url
103
65
 
104
- def save_result(result)
105
- reporters.each do |reporter|
106
- reporter.report(result)
107
- end
108
- end
66
+ begin
67
+ do_crawl num
68
+ rescue CrawlTimeout => e
69
+ puts
70
+ puts e.message
71
+ end
72
+
73
+ puts "#{ActiveSupport::Inflector.ordinalize((num+1))} crawl" if @times_to_crawl > 1
109
74
 
110
- def handle_link_results(link, result)
111
- handlers.each do |h|
112
- begin
113
- save_result h.handle(result)
114
- rescue Exception => e
115
- log "error handling #{link} #{e.message}"
116
- # TODO: pass to results
75
+ if num + 1 < @times_to_crawl
76
+ @links_queued = orig_links_queued
77
+ @form_signatures_queued = orig_form_signatures_queued
78
+ @crawl_queue = orig_crawl_queue
79
+ @referrers = {}
80
+ end
81
+ end
82
+ rescue Interrupt
83
+ $stderr.puts "CTRL-C"
84
+ ensure
85
+ report_results
117
86
  end
118
- end
119
- end
120
87
 
121
- def follow(method, url, data=nil)
122
- proxy.send(method, url, data)
123
- end
124
-
125
- def submit(method, action, data)
126
- proxy.send(method, action, data)
127
- end
88
+ def finished?
89
+ @crawl_queue.empty?
90
+ end
128
91
 
129
- def elasped_time_for_pass(num)
130
- Time.now - crawl_start_times[num]
131
- end
92
+ def do_crawl(number)
93
+ while (!finished?)
94
+ @crawl_start_times << Time.now
95
+ crawl_the_queue(number)
96
+ @crawl_end_times << Time.now
97
+ end
98
+ end
132
99
 
133
- def grab_log!
134
- @log_grabber && @log_grabber.grab!
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
100
+ def crawl_the_queue(number = 0)
101
+ while (request = @crawl_queue.pop)
102
+ request.crawl
103
+ blip(number)
104
+ end
105
+ end
144
106
 
145
- def handle_form_results(form, response)
146
- handlers.each do |h|
147
- save_result h.handle(Result.new(:method => form.method,
148
- :url => form.action,
149
- :response => response,
150
- :log => grab_log!,
151
- :referrer => form.action,
152
- :data => form.data.inspect,
153
- :test_name => test_name).freeze)
154
- end
155
- end
107
+ def save_result(result)
108
+ reporters.each do |reporter|
109
+ reporter.report(result)
110
+ end
111
+ end
156
112
 
157
- def should_skip_url?(url)
158
- return true if url.blank?
159
- if @skip_uri_patterns.any? {|pattern| pattern =~ url}
160
- log "Skipping #{url}"
161
- return true
162
- end
163
- if url.length > max_url_length
164
- log "Skipping long url #{url}"
165
- return true
166
- end
167
- end
113
+ def handle_link_results(link, result)
114
+ handlers.each do |h|
115
+ begin
116
+ save_result h.handle(result)
117
+ rescue Exception => e
118
+ log "error handling #{link} #{e.message}"
119
+ # TODO: pass to results
120
+ end
121
+ end
122
+ end
168
123
 
169
- def should_skip_link?(link)
170
- should_skip_url?(link.href) || @links_queued.member?(link)
171
- end
124
+ def follow(method, url, data=nil)
125
+ proxy.send(method, url, data)
126
+ end
172
127
 
173
- def should_skip_form_submission?(fs)
174
- should_skip_url?(fs.action) || @form_signatures_queued.member?(fs.signature)
175
- end
128
+ def submit(method, action, data)
129
+ proxy.send(method, action, data)
130
+ end
176
131
 
177
- def transform_url(url)
178
- return unless url
179
- url = @decoder.decode(url)
180
- @transform_url_patterns.each do |pattern|
181
- url = pattern[url]
182
- end
183
- url
184
- end
132
+ def elasped_time_for_pass(num)
133
+ Time.now - crawl_start_times[num]
134
+ end
185
135
 
186
- def queue_link(dest, referrer = nil)
187
- dest = Link.new(dest, self, referrer)
188
- return if should_skip_link?(dest)
189
- @crawl_queue << dest
190
- @links_queued << dest
191
- dest
192
- end
136
+ def grab_log!
137
+ @log_grabber && @log_grabber.grab!
138
+ end
193
139
 
194
- def queue_form(form, referrer = nil)
195
- fuzzers.each do |fuzzer|
196
- fuzzer.mutate(Form.new(form, self, referrer)).each do |fs|
197
- # fs = fuzzer.new(Form.new(form, self, referrer))
198
- fs.action = transform_url(fs.action)
199
- return if should_skip_form_submission?(fs)
200
- @referrers[fs.action] = referrer if referrer
201
- @crawl_queue << fs
202
- @form_signatures_queued << fs.signature
140
+ def make_result(options)
141
+ defaults = {
142
+ :log => grab_log!,
143
+ :test_name => test_name
144
+ }
145
+ Result.new(defaults.merge(options)).freeze
203
146
  end
204
- end
205
- end
206
147
 
207
- def report_dir
208
- File.join(rails_root, "tmp", "tarantula")
209
- end
148
+ def handle_form_results(form, response)
149
+ handlers.each do |h|
150
+ save_result h.handle(Result.new(:method => form.method,
151
+ :url => form.action,
152
+ :response => response,
153
+ :log => grab_log!,
154
+ :referrer => form.action,
155
+ :data => form.data.inspect,
156
+ :test_name => test_name).freeze)
157
+ end
158
+ end
210
159
 
211
- def generate_reports
212
- errors = []
213
- reporters.each do |reporter|
214
- begin
215
- reporter.finish_report(test_name)
216
- rescue RuntimeError => e
217
- errors << e
160
+ def should_skip_url?(url)
161
+ return true if url.blank?
162
+ if @skip_uri_patterns.any? {|pattern| pattern =~ url}
163
+ log "Skipping #{url}"
164
+ return true
165
+ end
166
+ if url.length > max_url_length
167
+ log "Skipping long url #{url}"
168
+ return true
169
+ end
218
170
  end
219
- end
220
- unless errors.empty?
221
- raise errors.map(&:message).join("\n")
222
- end
223
- end
224
171
 
225
- def report_results
226
- puts "Crawled #{total_links_count} links and forms."
227
- generate_reports
228
- end
172
+ def should_skip_link?(link)
173
+ should_skip_url?(link.href) || @links_queued.member?(link)
174
+ end
229
175
 
230
- def total_links_count
231
- @links_queued.size + @form_signatures_queued.size
232
- end
176
+ def should_skip_form_submission?(fs)
177
+ should_skip_url?(fs.action) || @form_signatures_queued.member?(fs.signature)
178
+ end
233
179
 
234
- def links_remaining_count
235
- @crawl_queue.size
236
- end
180
+ def transform_url(url)
181
+ return unless url
182
+ url = @decoder.decode(url)
183
+ @transform_url_patterns.each do |pattern|
184
+ url = pattern[url]
185
+ end
186
+ url
187
+ end
237
188
 
238
- def links_completed_count
239
- total_links_count - links_remaining_count
240
- end
189
+ def queue_link(dest, referrer = nil)
190
+ dest = Link.new(dest, self, referrer)
191
+ return if should_skip_link?(dest)
192
+ @crawl_queue << dest
193
+ @links_queued << dest
194
+ dest
195
+ end
241
196
 
242
- def blip(number = 0)
243
- unless verbose
244
- print "\r #{links_completed_count} of #{total_links_count} links completed " if @stdout_tty
245
- timeout_if_too_long(number)
246
- end
247
- end
248
-
249
- def timeout_if_too_long(number = 0)
250
- if elasped_time_for_pass(number) > crawl_timeout
251
- raise CrawlTimeout, "Exceeded crawl timeout of #{crawl_timeout} seconds - skipping to the next crawl..."
197
+ def queue_form(form, referrer = nil)
198
+ fuzzers.each do |fuzzer|
199
+ fuzzer.mutate(Form.new(form, self, referrer)).each do |fs|
200
+ # fs = fuzzer.new(Form.new(form, self, referrer))
201
+ fs.action = transform_url(fs.action)
202
+ return if should_skip_form_submission?(fs)
203
+ @referrers[fs.action] = referrer if referrer
204
+ @crawl_queue << fs
205
+ @form_signatures_queued << fs.signature
206
+ end
207
+ end
208
+ end
209
+
210
+ def report_dir
211
+ File.join(rails_root, "tmp", "tarantula")
212
+ end
213
+
214
+ def generate_reports
215
+ errors = []
216
+ reporters.each do |reporter|
217
+ begin
218
+ reporter.finish_report(test_name)
219
+ rescue RuntimeError => e
220
+ errors << e
221
+ end
222
+ end
223
+ unless errors.empty?
224
+ raise errors.map(&:message).join("\n")
225
+ end
226
+ end
227
+
228
+ def report_results
229
+ puts "Crawled #{total_links_count} links and forms."
230
+ generate_reports
231
+ end
232
+
233
+ def total_links_count
234
+ @links_queued.size + @form_signatures_queued.size
235
+ end
236
+
237
+ def links_remaining_count
238
+ @crawl_queue.size
239
+ end
240
+
241
+ def links_completed_count
242
+ total_links_count - links_remaining_count
243
+ end
244
+
245
+ def blip(number = 0)
246
+ unless verbose
247
+ print "\r #{links_completed_count} of #{total_links_count} links completed " if @stdout_tty
248
+ timeout_if_too_long(number)
249
+ end
250
+ end
251
+
252
+ def timeout_if_too_long(number = 0)
253
+ if elasped_time_for_pass(number) > crawl_timeout
254
+ raise CrawlTimeout, "Exceeded crawl timeout of #{crawl_timeout} seconds - skipping to the next crawl..."
255
+ end
256
+ end
252
257
  end
258
+
253
259
  end
254
260
  end