codez-tarantula 0.5.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 +12 -0
- data/.travis.yml +7 -0
- data/CHANGELOG +64 -0
- data/DSL_EXAMPLES.md +120 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.rdoc +136 -0
- data/Rakefile +36 -0
- data/ci/rails2.gemfile +4 -0
- data/ci/rails3.gemfile +4 -0
- data/laf/images/header_bg.jpg +0 -0
- data/laf/images/logo.png +0 -0
- data/laf/images/tagline.png +0 -0
- data/laf/javascripts/jquery-1.2.3.js +3408 -0
- data/laf/javascripts/jquery-ui-tabs.js +890 -0
- data/laf/javascripts/jquery.tablesorter.js +861 -0
- data/laf/javascripts/tarantula.js +10 -0
- data/laf/stylesheets/tarantula.css +346 -0
- data/lib/relevance/core_extensions/ellipsize.rb +38 -0
- data/lib/relevance/core_extensions/file.rb +15 -0
- data/lib/relevance/core_extensions/metaclass.rb +78 -0
- data/lib/relevance/core_extensions/response.rb +14 -0
- data/lib/relevance/core_extensions/test_case.rb +21 -0
- data/lib/relevance/tarantula.rb +55 -0
- data/lib/relevance/tarantula/attack.rb +22 -0
- data/lib/relevance/tarantula/attack_handler.rb +43 -0
- data/lib/relevance/tarantula/basic_attack.rb +44 -0
- data/lib/relevance/tarantula/crawler.rb +271 -0
- data/lib/relevance/tarantula/detail.html.erb +81 -0
- data/lib/relevance/tarantula/form.rb +29 -0
- data/lib/relevance/tarantula/form_submission.rb +98 -0
- data/lib/relevance/tarantula/html_document_handler.rb +42 -0
- data/lib/relevance/tarantula/html_report_helper.rb +46 -0
- data/lib/relevance/tarantula/html_reporter.rb +111 -0
- data/lib/relevance/tarantula/index.html.erb +37 -0
- data/lib/relevance/tarantula/invalid_html_handler.rb +27 -0
- data/lib/relevance/tarantula/io_reporter.rb +40 -0
- data/lib/relevance/tarantula/link.rb +105 -0
- data/lib/relevance/tarantula/log_grabber.rb +22 -0
- data/lib/relevance/tarantula/rails_integration_proxy.rb +90 -0
- data/lib/relevance/tarantula/recording.rb +12 -0
- data/lib/relevance/tarantula/response.rb +19 -0
- data/lib/relevance/tarantula/result.rb +83 -0
- data/lib/relevance/tarantula/test_report.html.erb +32 -0
- data/lib/relevance/tarantula/tidy_handler.rb +35 -0
- data/lib/relevance/tarantula/transform.rb +21 -0
- data/lib/relevance/tarantula/version.rb +5 -0
- data/lib/relevance/tasks/tarantula_tasks.rake +42 -0
- data/lib/tarantula-rails3.rb +9 -0
- data/spec/relevance/core_extensions/ellipsize_spec.rb +19 -0
- data/spec/relevance/core_extensions/file_spec.rb +7 -0
- data/spec/relevance/core_extensions/response_spec.rb +48 -0
- data/spec/relevance/core_extensions/test_case_spec.rb +19 -0
- data/spec/relevance/tarantula/attack_handler_spec.rb +29 -0
- data/spec/relevance/tarantula/basic_attack_spec.rb +12 -0
- data/spec/relevance/tarantula/crawler_spec.rb +409 -0
- data/spec/relevance/tarantula/form_spec.rb +50 -0
- data/spec/relevance/tarantula/form_submission_spec.rb +171 -0
- data/spec/relevance/tarantula/html_document_handler_spec.rb +43 -0
- data/spec/relevance/tarantula/html_report_helper_spec.rb +46 -0
- data/spec/relevance/tarantula/html_reporter_spec.rb +82 -0
- data/spec/relevance/tarantula/invalid_html_handler_spec.rb +33 -0
- data/spec/relevance/tarantula/io_reporter_spec.rb +11 -0
- data/spec/relevance/tarantula/link_spec.rb +132 -0
- data/spec/relevance/tarantula/log_grabber_spec.rb +26 -0
- data/spec/relevance/tarantula/rails_integration_proxy_spec.rb +100 -0
- data/spec/relevance/tarantula/result_spec.rb +85 -0
- data/spec/relevance/tarantula/tidy_handler_spec.rb +58 -0
- data/spec/relevance/tarantula/transform_spec.rb +20 -0
- data/spec/relevance/tarantula_spec.rb +23 -0
- data/spec/spec_helper.rb +43 -0
- data/tarantula.gemspec +25 -0
- data/template/tarantula_test.rb +22 -0
- 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 +247 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
class XSSProtectedERB < ERB
|
|
2
|
+
class Compiler < ::ERB::Compiler
|
|
3
|
+
def compile(s)
|
|
4
|
+
out = Buffer.new(self)
|
|
5
|
+
|
|
6
|
+
content = ''
|
|
7
|
+
scanner = make_scanner(s)
|
|
8
|
+
scanner.scan do |token|
|
|
9
|
+
if scanner.stag.nil?
|
|
10
|
+
case token
|
|
11
|
+
when PercentLine
|
|
12
|
+
out.push("#{@put_cmd} #{content.dump}") if content.size > 0
|
|
13
|
+
content = ''
|
|
14
|
+
out.push(token.to_s)
|
|
15
|
+
out.cr
|
|
16
|
+
when :cr
|
|
17
|
+
out.cr
|
|
18
|
+
when '<%', '<%=', '<%#'
|
|
19
|
+
scanner.stag = token
|
|
20
|
+
out.push("#{@put_cmd} #{content.dump}") if content.size > 0
|
|
21
|
+
content = ''
|
|
22
|
+
when "\n"
|
|
23
|
+
content << "\n"
|
|
24
|
+
out.push("#{@put_cmd} #{content.dump}")
|
|
25
|
+
out.cr
|
|
26
|
+
content = ''
|
|
27
|
+
when '<%%'
|
|
28
|
+
content << '<%'
|
|
29
|
+
else
|
|
30
|
+
content << token
|
|
31
|
+
end
|
|
32
|
+
else
|
|
33
|
+
case token
|
|
34
|
+
when '%>'
|
|
35
|
+
case scanner.stag
|
|
36
|
+
when '<%'
|
|
37
|
+
if content[-1] == ?\n
|
|
38
|
+
content.chop!
|
|
39
|
+
out.push(content)
|
|
40
|
+
out.cr
|
|
41
|
+
else
|
|
42
|
+
out.push(content)
|
|
43
|
+
end
|
|
44
|
+
when '<%='
|
|
45
|
+
# NOTE: Changed lines
|
|
46
|
+
out.push("#{@insert_cmd}((#{content}).to_s_xss_protected)")
|
|
47
|
+
# NOTE: End changed lines
|
|
48
|
+
when '<%#'
|
|
49
|
+
# out.push("# #{content.dump}")
|
|
50
|
+
end
|
|
51
|
+
scanner.stag = nil
|
|
52
|
+
content = ''
|
|
53
|
+
when '%%>'
|
|
54
|
+
content << '%>'
|
|
55
|
+
else
|
|
56
|
+
content << token
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
out.push("#{@put_cmd} #{content.dump}") if content.size > 0
|
|
61
|
+
out.close
|
|
62
|
+
out.script
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
|
|
67
|
+
@safe_level = safe_level
|
|
68
|
+
compiler = XSSProtectedERB::Compiler.new(trim_mode)
|
|
69
|
+
set_eoutvar(compiler, eoutvar)
|
|
70
|
+
@src = compiler.compile(str)
|
|
71
|
+
@filename = nil
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
module ActionView
|
|
76
|
+
class Base
|
|
77
|
+
private
|
|
78
|
+
def create_template_source(extension, template, render_symbol, locals)
|
|
79
|
+
if template_requires_setup?(extension)
|
|
80
|
+
body = case extension.to_sym
|
|
81
|
+
when :rxml, :builder
|
|
82
|
+
content_type_handler = (controller.respond_to?(:response) ? "controller.response" : "controller")
|
|
83
|
+
"#{content_type_handler}.content_type ||= Mime::XML\n" +
|
|
84
|
+
"xml = Builder::XmlMarkup.new(:indent => 2)\n" +
|
|
85
|
+
template +
|
|
86
|
+
"\nxml.target!\n"
|
|
87
|
+
when :rjs
|
|
88
|
+
"controller.response.content_type ||= Mime::JS\n" +
|
|
89
|
+
"update_page do |page|\n#{template}\nend"
|
|
90
|
+
end
|
|
91
|
+
# NOTE: Changed lines
|
|
92
|
+
elsif extension.to_sym == :rhtml
|
|
93
|
+
body = XSSProtectedERB.new(template, nil, @@erb_trim_mode).src
|
|
94
|
+
# NOTE: End changed lines
|
|
95
|
+
else
|
|
96
|
+
body = ERB.new(template, nil, @@erb_trim_mode).src
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
@@template_args[render_symbol] ||= {}
|
|
100
|
+
locals_keys = @@template_args[render_symbol].keys | locals
|
|
101
|
+
@@template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h }
|
|
102
|
+
|
|
103
|
+
locals_code = ""
|
|
104
|
+
locals_keys.each do |key|
|
|
105
|
+
locals_code << "#{key} = local_assigns[:#{key}]\n"
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
"def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend"
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
raise "Haml not loaded" unless Haml::Engine.instance_method(:push_script)
|
|
2
|
+
|
|
3
|
+
module Haml
|
|
4
|
+
class Engine
|
|
5
|
+
def push_script(text, flattened)
|
|
6
|
+
unless options[:suppress_eval]
|
|
7
|
+
push_silent("haml_temp = #{text}", true)
|
|
8
|
+
push_silent("haml_temp = haml_temp.to_s_xss_protected", true)
|
|
9
|
+
out = "haml_temp = _hamlout.push_script(haml_temp, #{@output_tabs}, #{flattened})\n"
|
|
10
|
+
if @block_opened
|
|
11
|
+
push_and_tabulate([:loud, out])
|
|
12
|
+
else
|
|
13
|
+
@precompiled << out
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def build_attributes(attributes = {})
|
|
19
|
+
# We ignore @options[:attr_wrapper] because ERB::Util.h does not espace ' to '
|
|
20
|
+
# making ' as attribute quote not workable
|
|
21
|
+
result = attributes.map do |a,v|
|
|
22
|
+
v = v.to_s_xss_protected
|
|
23
|
+
unless v.blank?
|
|
24
|
+
" #{a}=\"#{v}\""
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
result.sort.join
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class Buffer
|
|
32
|
+
def build_attributes(attributes = {})
|
|
33
|
+
result = attributes.map do |a,v|
|
|
34
|
+
v = v.to_s_xss_protected
|
|
35
|
+
unless v.blank?
|
|
36
|
+
" #{a}=\"#{v}\""
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
result.sort.join
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
class SafeString < String
|
|
2
|
+
def to_s
|
|
3
|
+
self
|
|
4
|
+
end
|
|
5
|
+
def to_s_xss_protected
|
|
6
|
+
self
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class String
|
|
11
|
+
def mark_as_xss_protected
|
|
12
|
+
SafeString.new(self)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class NilClass
|
|
17
|
+
def mark_as_xss_protected
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# ERB::Util.h and (include ERB::Util; h) are different methods
|
|
23
|
+
module ERB::Util
|
|
24
|
+
class <<self
|
|
25
|
+
def h_with_xss_protection(*args)
|
|
26
|
+
h_without_xss_protection(*args).mark_as_xss_protected
|
|
27
|
+
end
|
|
28
|
+
alias_method_chain :h, :xss_protection
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def h_with_xss_protection(*args)
|
|
32
|
+
h_without_xss_protection(*args).mark_as_xss_protected
|
|
33
|
+
end
|
|
34
|
+
alias_method_chain :h, :xss_protection
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class Object
|
|
38
|
+
def to_s_xss_protected
|
|
39
|
+
ERB::Util.h(to_s).mark_as_xss_protected
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
class Array
|
|
44
|
+
def join_xss_protected(sep="")
|
|
45
|
+
map(&:to_s_xss_protected).join(sep.to_s_xss_protected).mark_as_xss_protected
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
class Module
|
|
2
|
+
def mark_helpers_as_xss_protected(*ms)
|
|
3
|
+
ms.each do |m|
|
|
4
|
+
begin
|
|
5
|
+
instance_method("#{m}_with_xss_protection")
|
|
6
|
+
rescue NameError
|
|
7
|
+
define_method :"#{m}_with_xss_protection" do |*args|
|
|
8
|
+
send(:"#{m}_without_xss_protection", *args).mark_as_xss_protected
|
|
9
|
+
end
|
|
10
|
+
alias_method_chain m, :xss_protection
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class ActionView::Base
|
|
17
|
+
mark_helpers_as_xss_protected :javascript_include_tag,
|
|
18
|
+
:stylesheet_link_tag,
|
|
19
|
+
:render,
|
|
20
|
+
:text_field_tag,
|
|
21
|
+
:submit_tag,
|
|
22
|
+
:radio_button,
|
|
23
|
+
:text_area,
|
|
24
|
+
:auto_discovery_link_tag,
|
|
25
|
+
:image_tag
|
|
26
|
+
|
|
27
|
+
def link_to_with_xss_protection(text, *args)
|
|
28
|
+
link_to_without_xss_protection(text.to_s_xss_protected, *args).mark_as_xss_protected
|
|
29
|
+
end
|
|
30
|
+
alias_method_chain :link_to, :xss_protection
|
|
31
|
+
|
|
32
|
+
def button_to_with_xss_protection(text, *args)
|
|
33
|
+
button_to_without_xss_protection(text.to_s_xss_protected, *args).mark_as_xss_protected
|
|
34
|
+
end
|
|
35
|
+
alias_method_chain :button_to, :xss_protection
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
module ActionView::Helpers::FormHelper
|
|
39
|
+
mark_helpers_as_xss_protected :text_field, :check_box
|
|
40
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Run from your Rails main directory
|
|
2
|
+
require 'test/test_helper'
|
|
3
|
+
|
|
4
|
+
class TestActionViewIntegration < Test::Unit::TestCase
|
|
5
|
+
def assert_renders(expected, input, extension)
|
|
6
|
+
base = ActionView::Base.new
|
|
7
|
+
actual = base.render_template(extension, input, "foo.#{extension}")
|
|
8
|
+
assert_equal expected, actual
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_erb
|
|
12
|
+
assert_renders <<OUT, <<IN, :erb
|
|
13
|
+
A & B
|
|
14
|
+
A & B
|
|
15
|
+
OUT
|
|
16
|
+
<%= "A & B" %>
|
|
17
|
+
<%= "A & B".mark_as_xss_protected %>
|
|
18
|
+
IN
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_rhtml
|
|
22
|
+
assert_renders <<OUT, <<IN, :rhtml
|
|
23
|
+
A & B
|
|
24
|
+
A & B
|
|
25
|
+
OUT
|
|
26
|
+
<%= "A & B" %>
|
|
27
|
+
<%= "A & B".mark_as_xss_protected %>
|
|
28
|
+
IN
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_haml
|
|
32
|
+
assert_renders <<OUT, <<IN, :haml
|
|
33
|
+
A & B
|
|
34
|
+
A & B
|
|
35
|
+
OUT
|
|
36
|
+
= "A & B"
|
|
37
|
+
= "A & B".mark_as_xss_protected
|
|
38
|
+
IN
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Run from your Rails main directory
|
|
2
|
+
require 'test/test_helper'
|
|
3
|
+
|
|
4
|
+
class TestERB < Test::Unit::TestCase
|
|
5
|
+
def assert_renders_erb(expected, input, shield=true)
|
|
6
|
+
erb_class = shield ? XSSProtectedERB : ERB
|
|
7
|
+
|
|
8
|
+
actual = eval(erb_class.new(input).src)
|
|
9
|
+
|
|
10
|
+
assert_equal expected, actual
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def test_erb_with_shield
|
|
14
|
+
assert_renders_erb <<OUT, <<IN, true
|
|
15
|
+
Foo &amp; Bar
|
|
16
|
+
Foo &amp; Bar
|
|
17
|
+
Foo & Bar
|
|
18
|
+
Foo & Bar
|
|
19
|
+
Foo & Bar
|
|
20
|
+
OUT
|
|
21
|
+
<%= "Foo & Bar" %>
|
|
22
|
+
<%= h("Foo & Bar") %>
|
|
23
|
+
<%= "Foo & Bar".mark_as_xss_protected %>
|
|
24
|
+
<%= h("Foo & Bar") %>
|
|
25
|
+
<%= "Foo & Bar" %>
|
|
26
|
+
IN
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_erb_without_shield
|
|
30
|
+
assert_renders_erb <<OUT, <<IN, false
|
|
31
|
+
Foo &amp; Bar
|
|
32
|
+
Foo & Bar
|
|
33
|
+
Foo & Bar
|
|
34
|
+
Foo & Bar
|
|
35
|
+
Foo & Bar
|
|
36
|
+
OUT
|
|
37
|
+
<%= h("Foo & Bar") %>
|
|
38
|
+
<%= "Foo & Bar" %>
|
|
39
|
+
<%= "Foo & Bar".mark_as_xss_protected %>
|
|
40
|
+
<%= h("Foo & Bar") %>
|
|
41
|
+
<%= "Foo & Bar" %>
|
|
42
|
+
IN
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Run from your Rails main directory
|
|
2
|
+
require 'test/test_helper'
|
|
3
|
+
|
|
4
|
+
class TestHaml < Test::Unit::TestCase
|
|
5
|
+
def setup
|
|
6
|
+
@base = ActionView::Base.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def assert_haml_renders(expected, input)
|
|
10
|
+
actual = Haml::Engine.new(input).to_html(@base)
|
|
11
|
+
assert_equal expected, actual
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_haml_engine
|
|
15
|
+
assert_haml_renders <<OUT, <<IN
|
|
16
|
+
A & B
|
|
17
|
+
C & D
|
|
18
|
+
E & F
|
|
19
|
+
G & H
|
|
20
|
+
I & J
|
|
21
|
+
OUT
|
|
22
|
+
A & B
|
|
23
|
+
= "C & D"
|
|
24
|
+
= h("E & F")
|
|
25
|
+
= "G & H".mark_as_xss_protected
|
|
26
|
+
= "I & J".to_s_xss_protected
|
|
27
|
+
IN
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_attribute_escaping_in_haml
|
|
31
|
+
@base.instance_eval {
|
|
32
|
+
@foo = "A < & > ' \" B"
|
|
33
|
+
}
|
|
34
|
+
assert_haml_renders <<OUT, <<IN
|
|
35
|
+
<div foo="A < & > ' " B" />
|
|
36
|
+
<div foo="A < & > ' " B" />
|
|
37
|
+
OUT
|
|
38
|
+
%div{:foo => @foo}/
|
|
39
|
+
%div{:foo => @foo.mark_as_xss_protected}/
|
|
40
|
+
IN
|
|
41
|
+
# Note that '/" explicitly marked as XSS-protected can break validity
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Run from your Rails main directory
|
|
2
|
+
require 'test/test_helper'
|
|
3
|
+
|
|
4
|
+
class TestHelpers < Test::Unit::TestCase
|
|
5
|
+
def setup
|
|
6
|
+
@base = ActionView::Base.new
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def assert_haml_renders(expected, input)
|
|
10
|
+
actual = Haml::Engine.new(input).to_html(@base)
|
|
11
|
+
assert_equal expected, actual
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_link_to
|
|
15
|
+
assert_haml_renders <<OUT, <<IN
|
|
16
|
+
<a href="/bar">Foo</a>
|
|
17
|
+
<a href="/bar">Foo & Bar</a>
|
|
18
|
+
<a href="/bar">Foo & Bar</a>
|
|
19
|
+
OUT
|
|
20
|
+
= link_to "Foo", "/bar"
|
|
21
|
+
= link_to "Foo & Bar", "/bar"
|
|
22
|
+
= link_to "Foo & Bar".mark_as_xss_protected, "/bar"
|
|
23
|
+
IN
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Run from your Rails main directory
|
|
2
|
+
require 'test/test_helper'
|
|
3
|
+
|
|
4
|
+
class TestSafeString < Test::Unit::TestCase
|
|
5
|
+
def test_safe_string
|
|
6
|
+
assert_equal "foo", "foo".to_s_xss_protected
|
|
7
|
+
assert_equal "foo & bar", "foo & bar".to_s_xss_protected
|
|
8
|
+
assert_equal "foo & bar", "foo & bar".to_s_xss_protected
|
|
9
|
+
assert_equal "foo &amp; bar", "foo & bar".to_s_xss_protected
|
|
10
|
+
assert_equal "foo & bar", "foo & bar".to_s_xss_protected.to_s_xss_protected
|
|
11
|
+
assert_equal "foo & bar", h("foo & bar").to_s_xss_protected
|
|
12
|
+
assert_equal "foo &amp; bar", h(h("foo & bar"))
|
|
13
|
+
|
|
14
|
+
assert_not_equal "foo".mark_as_xss_protected.object_id, "foo".mark_as_xss_protected.object_id
|
|
15
|
+
x = "foo & bar".mark_as_xss_protected
|
|
16
|
+
assert_equal x.mark_as_xss_protected, x
|
|
17
|
+
# Not sure if this makes sense
|
|
18
|
+
assert_not_equal x.mark_as_xss_protected.object_id, x.object_id
|
|
19
|
+
|
|
20
|
+
assert_equal x.to_s, x
|
|
21
|
+
assert_equal x.to_s.object_id, x.object_id
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_nonstring_objects
|
|
25
|
+
assert_equal "15", 15.to_s_xss_protected
|
|
26
|
+
assert_equal SafeString, 15.to_s_xss_protected.class
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_nil
|
|
30
|
+
assert_equal "", nil.to_s_xss_protected
|
|
31
|
+
assert_equal SafeString, nil.to_s_xss_protected.class
|
|
32
|
+
assert_equal nil, nil.mark_as_xss_protected
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_join
|
|
36
|
+
assert_equal "", [].join_xss_protected
|
|
37
|
+
assert_equal "", [].join_xss_protected(",")
|
|
38
|
+
assert_equal "a", ["a"].join_xss_protected
|
|
39
|
+
assert_equal "a", ["a"].join_xss_protected(",")
|
|
40
|
+
assert_equal "ab", ["a", "b"].join_xss_protected
|
|
41
|
+
assert_equal "a,b", ["a", "b"].join_xss_protected(",")
|
|
42
|
+
|
|
43
|
+
assert_equal "a&b", ["a", "b"].join_xss_protected("&")
|
|
44
|
+
assert_equal "a&amp;b", ["a", "b"].join_xss_protected("&")
|
|
45
|
+
assert_equal "a&b", ["a", "b"].join_xss_protected("&".mark_as_xss_protected)
|
|
46
|
+
|
|
47
|
+
assert_equal "<&>", ["<", ">"].join_xss_protected("&")
|
|
48
|
+
assert_equal "<&amp;>", ["<", ">"].join_xss_protected("&")
|
|
49
|
+
assert_equal "<&>", ["<", ">"].join_xss_protected("&".mark_as_xss_protected)
|
|
50
|
+
|
|
51
|
+
assert_equal "< & >", ["<".mark_as_xss_protected, ">"].join_xss_protected(" & ")
|
|
52
|
+
assert_equal "< & >", ["<", ">".mark_as_xss_protected].join_xss_protected(" & ")
|
|
53
|
+
assert_equal "< & >", ["<", ">"].join_xss_protected(" & ".mark_as_xss_protected)
|
|
54
|
+
end
|
|
55
|
+
end
|