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.
- data/.autotest +14 -0
- data/.gitignore +9 -0
- data/.rvmrc +1 -0
- data/DSL_EXAMPLES.md +120 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +108 -0
- data/{MIT-LICENSE → LICENSE} +0 -0
- data/README.rdoc +3 -28
- data/Rakefile +27 -59
- data/lib/relevance/core_extensions/ellipsize.rb +23 -19
- data/lib/relevance/core_extensions/file.rb +10 -4
- data/lib/relevance/core_extensions/response.rb +9 -6
- data/lib/relevance/core_extensions/test_case.rb +14 -12
- data/lib/relevance/tarantula.rb +24 -25
- data/lib/relevance/tarantula/attack.rb +19 -15
- data/lib/relevance/tarantula/attack_handler.rb +32 -26
- data/lib/relevance/tarantula/basic_attack.rb +36 -32
- data/lib/relevance/tarantula/crawler.rb +222 -216
- data/lib/relevance/tarantula/form.rb +27 -21
- data/lib/relevance/tarantula/form_submission.rb +79 -73
- data/lib/relevance/tarantula/html_document_handler.rb +37 -31
- data/lib/relevance/tarantula/html_report_helper.rb +36 -29
- data/lib/relevance/tarantula/html_reporter.rb +105 -99
- data/lib/relevance/tarantula/invalid_html_handler.rb +21 -15
- data/lib/relevance/tarantula/io_reporter.rb +37 -31
- data/lib/relevance/tarantula/link.rb +97 -73
- data/lib/relevance/tarantula/log_grabber.rb +20 -14
- data/lib/relevance/tarantula/rails_integration_proxy.rb +64 -58
- data/lib/relevance/tarantula/response.rb +16 -10
- data/lib/relevance/tarantula/result.rb +69 -63
- data/lib/relevance/tarantula/tidy_handler.rb +22 -17
- data/lib/relevance/tarantula/transform.rb +18 -14
- data/lib/relevance/tarantula/version.rb +5 -0
- data/{tasks → lib/relevance/tasks}/tarantula_tasks.rake +1 -1
- data/lib/tarantula-rails3.rb +9 -0
- data/{examples/relevance/core_extensions/ellipsize_example.rb → spec/relevance/core_extensions/ellipsize_spec.rb} +2 -2
- data/{examples/relevance/core_extensions/file_example.rb → spec/relevance/core_extensions/file_spec.rb} +2 -2
- data/{examples/relevance/core_extensions/response_example.rb → spec/relevance/core_extensions/response_spec.rb} +2 -2
- data/{examples/relevance/core_extensions/test_case_example.rb → spec/relevance/core_extensions/test_case_spec.rb} +1 -1
- data/{examples/relevance/tarantula/attack_handler_example.rb → spec/relevance/tarantula/attack_handler_spec.rb} +1 -1
- data/{examples/relevance/tarantula/basic_attack_example.rb → spec/relevance/tarantula/basic_attack_spec.rb} +2 -2
- data/{examples/relevance/tarantula/crawler_example.rb → spec/relevance/tarantula/crawler_spec.rb} +2 -2
- data/{examples/relevance/tarantula/form_example.rb → spec/relevance/tarantula/form_spec.rb} +2 -2
- data/{examples/relevance/tarantula/form_submission_example.rb → spec/relevance/tarantula/form_submission_spec.rb} +3 -3
- data/{examples/relevance/tarantula/html_document_handler_example.rb → spec/relevance/tarantula/html_document_handler_spec.rb} +1 -1
- data/{examples/relevance/tarantula/html_report_helper_example.rb → spec/relevance/tarantula/html_report_helper_spec.rb} +1 -1
- data/{examples/relevance/tarantula/html_reporter_example.rb → spec/relevance/tarantula/html_reporter_spec.rb} +1 -1
- data/{examples/relevance/tarantula/invalid_html_handler_example.rb → spec/relevance/tarantula/invalid_html_handler_spec.rb} +1 -1
- data/{examples/relevance/tarantula/io_reporter_example.rb → spec/relevance/tarantula/io_reporter_spec.rb} +1 -1
- data/{examples/relevance/tarantula/link_example.rb → spec/relevance/tarantula/link_spec.rb} +5 -5
- data/{examples/relevance/tarantula/log_grabber_example.rb → spec/relevance/tarantula/log_grabber_spec.rb} +1 -1
- data/{examples/relevance/tarantula/rails_integration_proxy_example.rb → spec/relevance/tarantula/rails_integration_proxy_spec.rb} +1 -1
- data/{examples/relevance/tarantula/result_example.rb → spec/relevance/tarantula/result_spec.rb} +1 -1
- data/{examples/relevance/tarantula/tidy_handler_example.rb → spec/relevance/tarantula/tidy_handler_spec.rb} +1 -1
- data/{examples/relevance/tarantula/transform_example.rb → spec/relevance/tarantula/transform_spec.rb} +2 -2
- data/{examples/relevance/tarantula_example.rb → spec/relevance/tarantula_spec.rb} +1 -1
- data/{examples/example_helper.rb → spec/spec_helper.rb} +6 -14
- data/tarantula.gemspec +31 -0
- data/template/tarantula_test.rb +1 -1
- data/vendor/xss-shield/MIT-LICENSE +20 -0
- data/vendor/xss-shield/README +76 -0
- data/vendor/xss-shield/init.rb +16 -0
- data/vendor/xss-shield/lib/xss_shield.rb +6 -0
- data/vendor/xss-shield/lib/xss_shield/erb_hacks.rb +111 -0
- data/vendor/xss-shield/lib/xss_shield/haml_hacks.rb +42 -0
- data/vendor/xss-shield/lib/xss_shield/safe_string.rb +47 -0
- data/vendor/xss-shield/lib/xss_shield/secure_helpers.rb +40 -0
- data/vendor/xss-shield/test/test_actionview_integration.rb +40 -0
- data/vendor/xss-shield/test/test_erb.rb +44 -0
- data/vendor/xss-shield/test/test_haml.rb +43 -0
- data/vendor/xss-shield/test/test_helpers.rb +25 -0
- data/vendor/xss-shield/test/test_safe_string.rb +55 -0
- metadata +170 -99
- data/VERSION.yml +0 -4
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
module Relevance
|
|
2
|
-
|
|
3
|
-
|
|
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
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
1
|
+
module Relevance
|
|
2
|
+
module CoreExtensions
|
|
2
3
|
|
|
3
|
-
module
|
|
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
|
data/lib/relevance/tarantula.rb
CHANGED
|
@@ -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
|
-
::
|
|
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
|
|
36
|
-
require
|
|
37
|
-
require
|
|
38
|
-
require
|
|
39
|
-
require
|
|
40
|
-
require
|
|
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
|
|
43
|
-
require
|
|
44
|
-
require
|
|
45
|
-
require
|
|
46
|
-
require
|
|
47
|
-
require
|
|
48
|
-
require
|
|
49
|
-
require
|
|
50
|
-
require
|
|
51
|
-
require
|
|
52
|
-
require
|
|
53
|
-
require
|
|
54
|
-
require
|
|
55
|
-
require
|
|
56
|
-
require
|
|
57
|
-
require
|
|
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
|
|
58
|
+
require "relevance/tarantula/tidy_handler" if ENV['TIDY_PATH']
|
|
@@ -1,18 +1,22 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
2
|
-
|
|
1
|
+
module Relevance
|
|
2
|
+
module Tarantula
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
class BasicAttack
|
|
5
|
+
ATTRS = [:name, :output, :description]
|
|
5
6
|
|
|
6
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
9
|
+
class Crawler
|
|
10
|
+
extend Forwardable
|
|
11
|
+
include Relevance::Tarantula
|
|
49
12
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
124
|
+
def follow(method, url, data=nil)
|
|
125
|
+
proxy.send(method, url, data)
|
|
126
|
+
end
|
|
172
127
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
128
|
+
def submit(method, action, data)
|
|
129
|
+
proxy.send(method, action, data)
|
|
130
|
+
end
|
|
176
131
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
end
|
|
172
|
+
def should_skip_link?(link)
|
|
173
|
+
should_skip_url?(link.href) || @links_queued.member?(link)
|
|
174
|
+
end
|
|
229
175
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
176
|
+
def should_skip_form_submission?(fs)
|
|
177
|
+
should_skip_url?(fs.action) || @form_signatures_queued.member?(fs.signature)
|
|
178
|
+
end
|
|
233
179
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|