owasp-esapi-ruby 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/AUTHORS +5 -0
- data/ChangeLog +69 -0
- data/ISSUES +0 -0
- data/LICENSE +24 -0
- data/README +51 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/lib/codec/base_codec.rb +99 -0
- data/lib/codec/css_codec.rb +101 -0
- data/lib/codec/encoder.rb +330 -0
- data/lib/codec/html_codec.rb +424 -0
- data/lib/codec/javascript_codec.rb +119 -0
- data/lib/codec/mysql_codec.rb +131 -0
- data/lib/codec/oracle_codec.rb +46 -0
- data/lib/codec/os_codec.rb +78 -0
- data/lib/codec/percent_codec.rb +53 -0
- data/lib/codec/pushable_string.rb +114 -0
- data/lib/codec/vbscript_codec.rb +64 -0
- data/lib/codec/xml_codec.rb +173 -0
- data/lib/esapi.rb +68 -0
- data/lib/exceptions.rb +37 -0
- data/lib/executor.rb +20 -0
- data/lib/owasp-esapi-ruby.rb +13 -0
- data/lib/sanitizer/xss.rb +59 -0
- data/lib/validator/base_rule.rb +90 -0
- data/lib/validator/date_rule.rb +92 -0
- data/lib/validator/email.rb +29 -0
- data/lib/validator/float_rule.rb +76 -0
- data/lib/validator/generic_validator.rb +26 -0
- data/lib/validator/integer_rule.rb +61 -0
- data/lib/validator/string_rule.rb +146 -0
- data/lib/validator/validator_error_list.rb +48 -0
- data/lib/validator/zipcode.rb +27 -0
- data/spec/codec/css_codec_spec.rb +61 -0
- data/spec/codec/html_codec_spec.rb +87 -0
- data/spec/codec/javascript_codec_spec.rb +45 -0
- data/spec/codec/mysql_codec_spec.rb +44 -0
- data/spec/codec/oracle_codec_spec.rb +23 -0
- data/spec/codec/os_codec_spec.rb +51 -0
- data/spec/codec/percent_codec_spec.rb +34 -0
- data/spec/codec/vbcript_codec_spec.rb +23 -0
- data/spec/codec/xml_codec_spec.rb +83 -0
- data/spec/owasp_esapi_encoder_spec.rb +226 -0
- data/spec/owasp_esapi_executor_spec.rb +9 -0
- data/spec/owasp_esapi_ruby_email_validator_spec.rb +39 -0
- data/spec/owasp_esapi_ruby_xss_sanitizer_spec.rb +66 -0
- data/spec/owasp_esapi_ruby_zipcode_validator_spec.rb +42 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/validator/base_rule_spec.rb +29 -0
- data/spec/validator/date_rule_spec.rb +40 -0
- data/spec/validator/float_rule_spec.rb +31 -0
- data/spec/validator/integer_rule_spec.rb +51 -0
- data/spec/validator/string_rule_spec.rb +103 -0
- data/spec/validator_skeleton.rb +150 -0
- metadata +235 -0
@@ -0,0 +1,131 @@
|
|
1
|
+
#
|
2
|
+
# Codec to provide for MySQL string support
|
3
|
+
# http://mirror.yandex.ru/mirrors/ftp.mysql.com/doc/refman/5.0/en/string-syntax.html for details
|
4
|
+
module Owasp
|
5
|
+
module Esapi
|
6
|
+
module Codec
|
7
|
+
class MySQLCodec < BaseCodec
|
8
|
+
MYSQL_MODE = 0 # MySQL standard mode
|
9
|
+
ANSI_MODE = 1; # ANSI escape mode
|
10
|
+
|
11
|
+
# create a mysql codec.
|
12
|
+
# mode must be either MYSQL_MODE or ANSI_MODE
|
13
|
+
# The mode sets wether to use ansi mode in mysql or not
|
14
|
+
# defaults to MYSQL_MODE
|
15
|
+
def initialize(mode = 0)
|
16
|
+
if mode < MYSQL_MODE or mode > ANSI_MODE
|
17
|
+
raise RangeError.new()
|
18
|
+
end
|
19
|
+
@mode = mode
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns quote-encoded *character*
|
23
|
+
def encode_char(immune,input)
|
24
|
+
return input if immune.include?(input)
|
25
|
+
hex = hex(input)
|
26
|
+
return input if hex.nil?
|
27
|
+
return to_ansi(input) if @mode == ANSI_MODE
|
28
|
+
return to_mysql(input) if @mode == MYSQL_MODE
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the decoded version of the character starting at index, or
|
32
|
+
# nil if no decoding is possible.
|
33
|
+
#
|
34
|
+
# Formats all are legal (case sensitive)
|
35
|
+
# In ANSI_MODE '' decodes to '
|
36
|
+
# In MYSQL_MODE \x decodes to x (or a small list of specials)
|
37
|
+
def decode_char(input)
|
38
|
+
return from_ansi(input) if @mode == ANSI_MODE
|
39
|
+
return from_mysql(input) if @mode == MYSQL_MODE
|
40
|
+
end
|
41
|
+
|
42
|
+
# encode ' only
|
43
|
+
def to_ansi(input) #:nodoc:
|
44
|
+
return "\'\'" if input == "\'"
|
45
|
+
input
|
46
|
+
end
|
47
|
+
|
48
|
+
# encode for NO_BACKLASH_MODE
|
49
|
+
def to_mysql(input) # :nodoc:
|
50
|
+
c = input.ord
|
51
|
+
return "\\0" if c == 0x00
|
52
|
+
return "\\b" if c == 0x08
|
53
|
+
return "\\t" if c == 0x09
|
54
|
+
return "\\n" if c == 0x0a
|
55
|
+
return "\\r" if c == 0x0d
|
56
|
+
return "\\Z" if c == 0x1a
|
57
|
+
return "\\\"" if c == 0x22
|
58
|
+
return "\\%" if c == 0x25
|
59
|
+
return "\\'" if c == 0x27
|
60
|
+
return "\\\\" if c == 0x5c
|
61
|
+
return "\\_" if c == 0x5f
|
62
|
+
"\\#{input}"
|
63
|
+
end
|
64
|
+
|
65
|
+
# decode a char with ansi only compliane i.e. apostrohpe only
|
66
|
+
def from_ansi(input) # :nodoc:
|
67
|
+
input.mark
|
68
|
+
first = input.next
|
69
|
+
|
70
|
+
# check first char
|
71
|
+
if first.nil?
|
72
|
+
input.reset
|
73
|
+
return nil
|
74
|
+
end
|
75
|
+
|
76
|
+
unless first == "\'"
|
77
|
+
input.reset
|
78
|
+
return nil
|
79
|
+
end
|
80
|
+
|
81
|
+
# check second char
|
82
|
+
second = input.next
|
83
|
+
if second.nil?
|
84
|
+
input.reset
|
85
|
+
return nil
|
86
|
+
end
|
87
|
+
|
88
|
+
# if second isnt an encoded char return nil
|
89
|
+
unless second == "\'"
|
90
|
+
input.reset
|
91
|
+
return nil
|
92
|
+
end
|
93
|
+
"\'"
|
94
|
+
end
|
95
|
+
|
96
|
+
# decode a char using mysql NO_BACKSLAH_QUOTE rules
|
97
|
+
def from_mysql(input) # :nodoc:
|
98
|
+
input.mark
|
99
|
+
# check first
|
100
|
+
first = input.next
|
101
|
+
if first.nil?
|
102
|
+
input.reset
|
103
|
+
return nil
|
104
|
+
end
|
105
|
+
|
106
|
+
# check second
|
107
|
+
second = input.next
|
108
|
+
if second.nil?
|
109
|
+
input.reset
|
110
|
+
return nil
|
111
|
+
end
|
112
|
+
|
113
|
+
return 0x00.chr if second == "0"
|
114
|
+
return 0x08.chr if second == "b"
|
115
|
+
return 0x08.chr if second == "t"
|
116
|
+
return 0x0a.chr if second == "n"
|
117
|
+
return 0x0d.chr if second == "r"
|
118
|
+
return 0x1a.chr if second == "z"
|
119
|
+
return 0x22.chr if second == "\""
|
120
|
+
return 0x25.chr if second == "%"
|
121
|
+
return 0x27.chr if second == "\'"
|
122
|
+
return 0x5c.chr if second == "\\"
|
123
|
+
return 0x5f.chr if second == "_"
|
124
|
+
# not an escape
|
125
|
+
second
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# Codec to provide for Oracle string support
|
3
|
+
# see http://oraqa.com/2006/03/20/how-to-escape-single-quotes-in-strings for details
|
4
|
+
# This will only prevent SQLinjection in the case of user data being placed within an
|
5
|
+
# Oracle quoted string such as select * from table where field = ' USERDATA '
|
6
|
+
module Owasp
|
7
|
+
module Esapi
|
8
|
+
module Codec
|
9
|
+
class OracleCodec < BaseCodec
|
10
|
+
|
11
|
+
# Encodes ' to ''
|
12
|
+
def encode_char(immune,input)
|
13
|
+
return "\'\'" if input == "\'"
|
14
|
+
input
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the decoded version of the character starting at index, or
|
18
|
+
# nil if no decoding is possible.
|
19
|
+
#
|
20
|
+
# Formats all are legal
|
21
|
+
# '' decodes to '
|
22
|
+
def decode_char(input)
|
23
|
+
# check first *char*
|
24
|
+
input.mark
|
25
|
+
first = input.next
|
26
|
+
if first.nil?
|
27
|
+
input.reset
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
# if it isnt an encoded string return nil
|
31
|
+
unless first == "\'"
|
32
|
+
input.reset
|
33
|
+
return nil
|
34
|
+
end
|
35
|
+
# if second isnt an encoded marker return nil
|
36
|
+
second = input.next
|
37
|
+
unless second == "\'"
|
38
|
+
input.reset
|
39
|
+
return nil
|
40
|
+
end
|
41
|
+
return "\'"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
# Operating system codec for escape characters for HOST commands
|
4
|
+
# We look at Unix style (max, linux) and Windows style
|
5
|
+
module Owasp
|
6
|
+
module Esapi
|
7
|
+
module Codec
|
8
|
+
class OsCodec < BaseCodec
|
9
|
+
# Window Host flag
|
10
|
+
WINDOWS_HOST = :Windows
|
11
|
+
# Unix Host flag
|
12
|
+
UNIX_HOST = :Unix
|
13
|
+
|
14
|
+
# Setup the code, if no os is passed in the codec
|
15
|
+
# will guess the OS based on the ruby host_os variable
|
16
|
+
def initialize(os = nil)
|
17
|
+
@host = nil
|
18
|
+
@escape_char = ''
|
19
|
+
host_os = os
|
20
|
+
if os.nil?
|
21
|
+
host_os = case Config::CONFIG['host_os']
|
22
|
+
when /mswin|windows/i then WINDOWS_HOST
|
23
|
+
when /linux/i then UNIX_HOST
|
24
|
+
when /darwin/i then UNIX_HOST
|
25
|
+
when /sunos|solaris/i then UNIX_HOST
|
26
|
+
else UNIX_HOST
|
27
|
+
end
|
28
|
+
end
|
29
|
+
if host_os == WINDOWS_HOST
|
30
|
+
@host = WINDOWS_HOST
|
31
|
+
@escape_char = '^'
|
32
|
+
elsif host_os == UNIX_HOST
|
33
|
+
@host = UNIX_HOST
|
34
|
+
@escape_char = '\\'
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# get the configured OS
|
39
|
+
def os
|
40
|
+
@host
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns shell encoded character
|
44
|
+
# ^ - for windows
|
45
|
+
# \\ - for unix
|
46
|
+
def encode_char(immune,input)
|
47
|
+
return input if immune.include?(input)
|
48
|
+
return input if hex(input).nil?
|
49
|
+
return "#{@escape_char}#{input}"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the decoded version of the character starting at index, or
|
53
|
+
# nil if no decoding is possible.
|
54
|
+
# <p>
|
55
|
+
# Formats all are legal both upper/lower case:
|
56
|
+
# ^x - all special characters when configured for WINDOWS
|
57
|
+
# \\ - all special characters when configured for UNIX
|
58
|
+
def decode_char(input)
|
59
|
+
input.mark
|
60
|
+
first = input.next
|
61
|
+
# check first char
|
62
|
+
if first.nil?
|
63
|
+
input.reset
|
64
|
+
return nil
|
65
|
+
end
|
66
|
+
# if it isnt escape return nil
|
67
|
+
if first != @escape_char
|
68
|
+
input.reset
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
# get teh escape value
|
72
|
+
return input.next
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Implementation of the Codec interface for percent encoding (aka URL encoding).
|
2
|
+
module Owasp
|
3
|
+
module Esapi
|
4
|
+
module Codec
|
5
|
+
class PercentCodec < BaseCodec
|
6
|
+
|
7
|
+
# Encode a character for URLs
|
8
|
+
def encode_char(immune,input)
|
9
|
+
return input if input =~ /[a-zA-Z0-9_.-]/
|
10
|
+
# RFC compliance
|
11
|
+
return "+" if input == " "
|
12
|
+
val = ''
|
13
|
+
input.each_byte do |b|
|
14
|
+
val << '%' << b.ord.to_h.upcase
|
15
|
+
end
|
16
|
+
val
|
17
|
+
end
|
18
|
+
|
19
|
+
# Formats all are legal both upper/lower case:
|
20
|
+
# %hh;
|
21
|
+
def decode_char(input)
|
22
|
+
input.mark
|
23
|
+
first = input.next
|
24
|
+
if first.nil?
|
25
|
+
input.reset
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
# check if this is an encoded character
|
29
|
+
if first != '%'
|
30
|
+
input.reset
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
# search for 2 hex digits
|
34
|
+
tmp = ''
|
35
|
+
for i in 0..1 do
|
36
|
+
c = input.next_hex
|
37
|
+
tmp << c unless c.nil?
|
38
|
+
end
|
39
|
+
# we found 2, convert to a number
|
40
|
+
if tmp.size == 2
|
41
|
+
i = tmp.hex
|
42
|
+
begin
|
43
|
+
return i.chr(Encoding::UTF_8) if i >= START_CODE_POINT and i <= END_CODE_POINT
|
44
|
+
rescue Exception => e
|
45
|
+
end
|
46
|
+
end
|
47
|
+
input.reset
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# The pushback string is used by Codecs to allow them to push decoded characters back onto a string
|
2
|
+
# for further decoding. This is necessary to detect double-encoding.
|
3
|
+
|
4
|
+
module Owasp
|
5
|
+
module Esapi
|
6
|
+
module Codec
|
7
|
+
class PushableString
|
8
|
+
attr :index
|
9
|
+
|
10
|
+
#
|
11
|
+
# Setup a pushable string
|
12
|
+
# stream will setup UTF_8 encoding on the input
|
13
|
+
def initialize(string)
|
14
|
+
@input = string.force_encoding(Encoding::UTF_8)
|
15
|
+
@index = 0
|
16
|
+
@mark = 0
|
17
|
+
@temp = nil
|
18
|
+
@push = nil
|
19
|
+
end
|
20
|
+
|
21
|
+
# Get the next token off of the stream
|
22
|
+
def next
|
23
|
+
unless @push.nil?
|
24
|
+
t = @push
|
25
|
+
@push = nil
|
26
|
+
return t
|
27
|
+
end
|
28
|
+
return nil if @input.nil?
|
29
|
+
return nil if @input.size == 0
|
30
|
+
return nil if @index >= @input.size
|
31
|
+
|
32
|
+
t = @input[@index]
|
33
|
+
@index += 1
|
34
|
+
t
|
35
|
+
end
|
36
|
+
|
37
|
+
# fetch the next hex token in the string or nil
|
38
|
+
def next_hex
|
39
|
+
c = self.next
|
40
|
+
return nil if c.nil?
|
41
|
+
return c if hex?(c)
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
# Fetch the next octal token int eh string or nil
|
46
|
+
def next_octal
|
47
|
+
c = self.next
|
48
|
+
return nil if c.nil?
|
49
|
+
return c if octal?(c)
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
|
53
|
+
# Check to see if we have another token on the stream
|
54
|
+
def next?
|
55
|
+
!@push.nil? ? true : @input.nil? ? false : @input.empty? ? false : @index >= @input.length ? false : true
|
56
|
+
end
|
57
|
+
|
58
|
+
# Push a character back onto the string, this is a unread operation
|
59
|
+
def push(c)
|
60
|
+
@push = c
|
61
|
+
end
|
62
|
+
|
63
|
+
# Peek into teh stream and see if the next character is the one in question
|
64
|
+
def peek?(c)
|
65
|
+
return true if !@push.nil? and @push == c
|
66
|
+
return false if @input.empty?
|
67
|
+
return false if @input.nil?
|
68
|
+
return false if @index >= @input.size
|
69
|
+
@input[@index] == c
|
70
|
+
end
|
71
|
+
|
72
|
+
# Peek into the stream and fetch teh next character without moving the index
|
73
|
+
def peek
|
74
|
+
return @push if !@push.nil?
|
75
|
+
return nil if @input.nil?
|
76
|
+
return nil if @input.empty?
|
77
|
+
return nil if @index >= @input.size
|
78
|
+
@input[@index]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Mark the stream for rewind
|
82
|
+
def mark
|
83
|
+
@temp = @push
|
84
|
+
@mark = @index
|
85
|
+
end
|
86
|
+
|
87
|
+
# Check if a given character is a hexadecimal character
|
88
|
+
def hex?(c)
|
89
|
+
return false if c.nil?
|
90
|
+
c =~ /[a-fA-F0-9]/
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if a given character is an octal character
|
94
|
+
def octal?(c)
|
95
|
+
return false if c.nil?
|
96
|
+
c =~ /[0-7]/
|
97
|
+
end
|
98
|
+
|
99
|
+
# Reset the index back to the mark
|
100
|
+
def reset
|
101
|
+
@push = @temp
|
102
|
+
@index = @mark
|
103
|
+
end
|
104
|
+
|
105
|
+
# Fetch the rest of the string from the current index
|
106
|
+
def remainder
|
107
|
+
t = @input.slice(@index,@input.size-@index)
|
108
|
+
return @push + t unless @push.nil?
|
109
|
+
t
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Implementation of the Codec interface for 'quote' encoding from VBScript.
|
2
|
+
module Owasp
|
3
|
+
module Esapi
|
4
|
+
module Codec
|
5
|
+
class VbScriptCodec < BaseCodec
|
6
|
+
|
7
|
+
# Encode a String so that it can be safely used in a specific context.
|
8
|
+
def encode(immune, input)
|
9
|
+
encoded_string = ''
|
10
|
+
encoding = false
|
11
|
+
inquotes = false
|
12
|
+
encoded_string.encode!(Encoding::UTF_8)
|
13
|
+
i = 0
|
14
|
+
input.encode(Encoding::UTF_8).chars do |c|
|
15
|
+
if Owasp::Esapi::Encoder::CHAR_ALPHANUMERIC.include?(c) or immune.include?(c)
|
16
|
+
encoded_string << "&" if encoding and i > 0
|
17
|
+
encoded_string << "\"" if !inquotes and i > 0
|
18
|
+
encoded_string << c
|
19
|
+
inquotes = true
|
20
|
+
encoding = false
|
21
|
+
else
|
22
|
+
encoded_string << "\"" if inquotes and i < input.size
|
23
|
+
encoded_string << "&" if i > 0
|
24
|
+
encoded_string << encode_char(immune,c)
|
25
|
+
inquotes = false
|
26
|
+
encoding = true
|
27
|
+
end
|
28
|
+
i += 1
|
29
|
+
end
|
30
|
+
encoded_string
|
31
|
+
end
|
32
|
+
# Returns quote-encoded character
|
33
|
+
def encode_char(immune,input)
|
34
|
+
return input if immune.include?(input)
|
35
|
+
hex = hex(input)
|
36
|
+
return input if hex.nil?
|
37
|
+
return "chrw(#{input.ord})"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the decoded version of the character starting at index, or
|
41
|
+
# nil if no decoding is possible.
|
42
|
+
#
|
43
|
+
# Formats all are legal both upper/lower case:
|
44
|
+
# "x - all special characters
|
45
|
+
# " + chr(x) + " - not supported
|
46
|
+
|
47
|
+
def decode_char(input)
|
48
|
+
input.mark();
|
49
|
+
first = input.next
|
50
|
+
if first.nil?
|
51
|
+
input.reset
|
52
|
+
return nil;
|
53
|
+
end
|
54
|
+
# if this is not an encoded character, return null
|
55
|
+
if first != "\""
|
56
|
+
input.reset
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
input.next
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|