miketracy-wwmd 0.2.11

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 (70) hide show
  1. data/History.txt +3 -0
  2. data/README +62 -0
  3. data/README.txt +62 -0
  4. data/Rakefile +34 -0
  5. data/examples/config_example.yaml +24 -0
  6. data/examples/wwmd_example.rb +73 -0
  7. data/lib/wwmd.rb +78 -0
  8. data/lib/wwmd/encoding.rb +40 -0
  9. data/lib/wwmd/form.rb +110 -0
  10. data/lib/wwmd/form_array.rb +273 -0
  11. data/lib/wwmd/guid.rb +155 -0
  12. data/lib/wwmd/hpricot_html2text.rb +76 -0
  13. data/lib/wwmd/mixins.rb +318 -0
  14. data/lib/wwmd/mixins_extends.rb +188 -0
  15. data/lib/wwmd/mixins_external.rb +18 -0
  16. data/lib/wwmd/nokogiri_html2text.rb +41 -0
  17. data/lib/wwmd/page.rb +414 -0
  18. data/lib/wwmd/page/auth.rb +183 -0
  19. data/lib/wwmd/page/config.rb +44 -0
  20. data/lib/wwmd/page/constants.rb +60 -0
  21. data/lib/wwmd/page/headers.rb +107 -0
  22. data/lib/wwmd/page/inputs.rb +47 -0
  23. data/lib/wwmd/page/irb_helpers.rb +90 -0
  24. data/lib/wwmd/page/scrape.rb +202 -0
  25. data/lib/wwmd/page/spider.rb +127 -0
  26. data/lib/wwmd/page/urlparse.rb +79 -0
  27. data/lib/wwmd/page/utils.rb +30 -0
  28. data/lib/wwmd/viewstate.rb +118 -0
  29. data/lib/wwmd/viewstate/viewstate_class_helpers.rb +35 -0
  30. data/lib/wwmd/viewstate/viewstate_deserializer_methods.rb +213 -0
  31. data/lib/wwmd/viewstate/viewstate_from_xml.rb +126 -0
  32. data/lib/wwmd/viewstate/viewstate_types.rb +51 -0
  33. data/lib/wwmd/viewstate/viewstate_utils.rb +157 -0
  34. data/lib/wwmd/viewstate/viewstate_yaml.rb +25 -0
  35. data/lib/wwmd/viewstate/vs_array.rb +36 -0
  36. data/lib/wwmd/viewstate/vs_binary_serialized.rb +28 -0
  37. data/lib/wwmd/viewstate/vs_hashtable.rb +40 -0
  38. data/lib/wwmd/viewstate/vs_hybrid_dict.rb +40 -0
  39. data/lib/wwmd/viewstate/vs_indexed_string.rb +6 -0
  40. data/lib/wwmd/viewstate/vs_indexed_string_ref.rb +22 -0
  41. data/lib/wwmd/viewstate/vs_int_enum.rb +25 -0
  42. data/lib/wwmd/viewstate/vs_list.rb +32 -0
  43. data/lib/wwmd/viewstate/vs_pair.rb +27 -0
  44. data/lib/wwmd/viewstate/vs_read_types.rb +11 -0
  45. data/lib/wwmd/viewstate/vs_read_value.rb +33 -0
  46. data/lib/wwmd/viewstate/vs_sparse_array.rb +56 -0
  47. data/lib/wwmd/viewstate/vs_string.rb +29 -0
  48. data/lib/wwmd/viewstate/vs_string_array.rb +37 -0
  49. data/lib/wwmd/viewstate/vs_string_formatted.rb +30 -0
  50. data/lib/wwmd/viewstate/vs_triplet.rb +29 -0
  51. data/lib/wwmd/viewstate/vs_type.rb +21 -0
  52. data/lib/wwmd/viewstate/vs_unit.rb +28 -0
  53. data/lib/wwmd/viewstate/vs_value.rb +33 -0
  54. data/spec/README +3 -0
  55. data/spec/form_array.spec +49 -0
  56. data/spec/spider_csrf_test.spec +28 -0
  57. data/spec/urlparse_test.spec +89 -0
  58. data/tasks/ann.rake +80 -0
  59. data/tasks/bones.rake +20 -0
  60. data/tasks/gem.rake +201 -0
  61. data/tasks/git.rake +40 -0
  62. data/tasks/notes.rake +27 -0
  63. data/tasks/post_load.rake +34 -0
  64. data/tasks/rdoc.rake +51 -0
  65. data/tasks/rubyforge.rake +55 -0
  66. data/tasks/setup.rb +292 -0
  67. data/tasks/spec.rake +54 -0
  68. data/tasks/test.rake +40 -0
  69. data/tasks/zentest.rake +36 -0
  70. metadata +164 -0
@@ -0,0 +1,273 @@
1
+ =begin rdoc
2
+ This is a weird kind of data structure for no other reason than
3
+ I wanted to keep the form inputs in order when they come in.
4
+
5
+ Accessing this either as a hash or an array (but => won't work)
6
+
7
+ Some of the methods in here are kept for backward compat before the refactor
8
+ and now everything in this array should be accessed with []= and []
9
+ =end
10
+
11
+ module WWMD
12
+ class FormArray < Array
13
+
14
+ def initialize(fields=nil)
15
+ if not fields.nil?
16
+ # this first one is an array of field objects
17
+ if fields.class == Array
18
+ fields.each do |f|
19
+ name = f['name']
20
+ if self.name_exists(name)
21
+ if f['type'] == "hidden"
22
+ self.set name,f.get_value
23
+ elsif f['type'] == "checkbox" and f.to_html.grep(/checked/) != ''
24
+ self[name] = f.get_value
25
+ end
26
+ else
27
+ self << [ f['name'],f.get_value ]
28
+ end
29
+ end
30
+ elsif fields.class == Hash
31
+ fields.each_pair { |k,v| self[k] = v }
32
+ elsif fields.class == String
33
+ fields.split("&").each do |f|
34
+ k,v = f.split("=",2)
35
+ self[k] = v
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ # "deep enough" copy of this object to make it a real copy
42
+ # instead of references to the arrays that already exist
43
+ def clone
44
+ ret = self.class.new
45
+ self.each { |r| ret << r.clone }
46
+ return ret
47
+ end
48
+
49
+ def clear
50
+ self.delete_if { |x| true }
51
+ end
52
+
53
+ # check if the passed name exists in the form
54
+ def include?(key)
55
+ self.map { |x| x.first }.flatten.include?(key)
56
+ end
57
+
58
+ alias_method :name_exists, :include?#:nodoc:
59
+ alias_method :name_exists?, :include?#:nodoc:
60
+ alias_method :has_key?, :include?#:nodoc:
61
+
62
+ # add key/value pairs to form
63
+ def add(key,value)
64
+ self << [key,value]
65
+ end
66
+
67
+ def clear_viewstate
68
+ self.each { |k,v|
69
+ self[k] = "" if k == "__VIEWSTATE"
70
+ }
71
+ end
72
+
73
+ alias_method :extend!, :add #:nodoc (this is here for backward compat)
74
+
75
+ # key = Fixnum set value at index key
76
+ # key = String find key named string and set value
77
+ def set_value!(key,value)
78
+ if key.class == Fixnum
79
+ self[key][1] = value
80
+ return [self[key][0], value]
81
+ end
82
+ self.each_index do |i|
83
+ if self[i][0] == key
84
+ self[i] = [key,value]
85
+ end
86
+ end
87
+ return [key,value]
88
+ end
89
+
90
+ alias_method :old_get, :[]#:nodoc:
91
+ def [](*args)
92
+ if args.first.class == Fixnum
93
+ self.old_get(args.first)
94
+ else
95
+ self.get_value(args.first)
96
+ end
97
+ end
98
+
99
+ alias_method :old_set, :[]=#:nodoc:
100
+ # set a key using its index, array key or add using a new key i.e.:
101
+ # if setting:
102
+ # form = [['key','value'],['foo','bar']]
103
+ # form[0] = ["replacekey","newalue"]
104
+ # form["replacekey"] = "newervalue"
105
+ # if adding:
106
+ # form["newkey"] = "value"
107
+ #
108
+ def []=(*args)
109
+ key,value = args
110
+ if args.first.kind_of?(Fixnum)
111
+ return self.old_set(*args)
112
+ elsif self.has_key?(key)
113
+ return self.set_value(key,value)
114
+ else
115
+ return self.add(key,value)
116
+ end
117
+ end
118
+
119
+ alias_method :set_value, :set_value!
120
+ alias_method :set, :set_value!
121
+
122
+ def get_value(key)
123
+ if key.class == Fixnum
124
+ return self[key][1]
125
+ end
126
+ self.each_index do |i|
127
+ if self[i][0] == key
128
+ return self[i][1]
129
+ end
130
+ end
131
+ return nil
132
+ end
133
+
134
+ alias_method :get, :get_value
135
+
136
+ def setall!(value)
137
+ self.each_index { |i| self.set_value!(i,value) }
138
+ end
139
+
140
+ alias_method :setall, :setall!#:nodoc:
141
+ alias_method :set_all!, :setall!#:nodoc:
142
+ alias_method :set_all, :setall!#:nodoc:
143
+
144
+ # delete all key = value pairs from self where key = key
145
+ def delete_key(key)
146
+ self.reject! { |x,y| x == key }
147
+ end
148
+
149
+ alias_method :delete_keys!, :delete_key #:nodoc:
150
+ alias_method :delete_key!, :delete_key #:nodoc:
151
+
152
+ # escape form keys in place
153
+ def escape_keys!(reg=WWMD::ESCAPE[:url])
154
+ return nil if reg == :none
155
+ self.map! { |x,y| [x.escape(reg),y] }
156
+ end
157
+
158
+ # unescape form keys in place
159
+ def unescape_keys!(reg=WWMD::ESCAPE[:url])
160
+ return nil if reg == :none
161
+ self.map! { |x,y| [x.unescape,y] }
162
+ end
163
+
164
+ # escape form values in place
165
+ def escape_all!(reg=WWMD::ESCAPE[:url])
166
+ return nil if reg == :none
167
+ self.map! { |x,y| [x,y.escape(reg)] }
168
+ end
169
+
170
+ alias_method :escape_all, :escape_all!#:nodoc:
171
+
172
+ # unescape all form values in place
173
+ def unescape_all!
174
+ self.map! { |x,y| [x,y.unescape] }
175
+ end
176
+
177
+ alias_method :unescape_all, :unescape_all!#:nodoc:
178
+
179
+ # convert form into a post parameters string
180
+ def to_post
181
+ ret = []
182
+ self.each do |i|
183
+ ret.push(i.join("="))
184
+ end
185
+ ret.join("&")
186
+ end
187
+
188
+ # convert form into a get parameters string
189
+ #
190
+ # pass me a base to get a full url to pass to Page.get
191
+ def to_get(base="")
192
+ ret = []
193
+ self.each do |i|
194
+ ret.push(i.join("="))
195
+ end
196
+ ret = ret.join("&")
197
+ return base.clip + "?" + ret.to_s
198
+ end
199
+
200
+ # IRB: puts the form in human readable format
201
+ # if you <tt>form.show(true)</tt> it will show unescaped values
202
+ def show(unescape=false)
203
+ if unescape
204
+ self.each_index { |i| puts i.to_s + " :: " + self[i][0].to_s + " = " + self[i][1].to_s.unescape }
205
+ else
206
+ self.each_index { |i| puts i.to_s + " :: " + self[i][0].to_s + " = " + self[i][1].to_s }
207
+ end
208
+ return nil
209
+ end
210
+
211
+ # meh
212
+ def add_viewstate#:nodoc:
213
+ self.insert(0,[ "__VIEWSTATE","" ])
214
+ self.insert(0,[ "__EVENTARGUMENT","" ])
215
+ self.insert(0,[ "__EVENTTARGET","" ])
216
+ self.insert(0,[ "__EVENTVALIDATION","" ])
217
+ return nil
218
+ end
219
+
220
+ # alias_method, :add_state, :add_viewstate#:nodoc:
221
+
222
+ # remove form elements with null values
223
+ def remove_nulls!
224
+ self.delete_if { |x| x[1].to_s.empty? || x[1].nil? }
225
+ end
226
+
227
+ alias_method :squeeze!, :remove_nulls!
228
+
229
+ # remove form elements with null keys (for housekeeping returns)
230
+ def remove_null_keys!
231
+ self.delete_if { |x,y| x.to_s.empty? || x.nil? }
232
+ end
233
+
234
+ alias_method :squeeze_keys!, :remove_null_keys!
235
+
236
+ # dump a web page containing a csrf example of the current FormArray
237
+ def to_csrf(action)
238
+ ret = ""
239
+ ret << "<html><body>\n"
240
+ ret << "<form method='post' id='wwmdtest' name='wwmdtest' action='#{action}'>\n"
241
+ self.each do |key,val|
242
+ val = val.unescape.gsub(/'/) { %q[\'] }
243
+ ret << "<input name='#{key.to_s.unescape}' type='hidden' value='#{val}' />\n"
244
+ # ret << "<input name='#{key.to_s.unescape}' type='hidden' value='#{val.to_s.unescape.gsub(/'/,"\\'")}' />\n"
245
+ end
246
+ ret << "</form>\n"
247
+ ret << "<script>document.wwmdtest.submit()</script>\n"
248
+ ret << "</body></html>\n"
249
+ return ret
250
+ end
251
+
252
+ def keys
253
+ self.map { |k,v| k }
254
+ end
255
+
256
+ def burpify #:nodoc:
257
+ ret = self.clone
258
+ ret.each_index do |i|
259
+ next if ret[i][0] =~ /^__/
260
+ ret.set_value!(i,"#{ret.get_value(i)}" + "\302\247" + "\302\247")
261
+ end
262
+ system("echo '#{ret.to_post}' | pbcopy")
263
+ return ret
264
+ end
265
+
266
+ # return md5 hash of sorted list of keys
267
+ def fingerprint
268
+ return self.map { |k,v| k }.sort.to_s.md5
269
+ end
270
+ alias_method :fp, :fingerprint #:nodoc:
271
+
272
+ end
273
+ end
data/lib/wwmd/guid.rb ADDED
@@ -0,0 +1,155 @@
1
+ =begin rdoc
2
+ Guid - Ruby library for portable GUID/UUID generation.
3
+
4
+ Copyright (c) 2004 David Garamond <davegaramond at icqmail com>
5
+
6
+ This library is free software; you can redistribute it and/or modify it
7
+ under the same terms as Ruby itself.
8
+
9
+ (small hack to fix for mac mtracy@matasano.com)
10
+ =end
11
+
12
+ if RUBY_PLATFORM =~ /win/i && ! RUBY_PLATFORM =~ /darwin/i
13
+ module Guid_Win32_#:nodoc:
14
+ require 'Win32API'
15
+
16
+ PROV_RSA_FULL = 1
17
+ CRYPT_VERIFYCONTEXT = 0xF0000000
18
+ FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200
19
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
20
+
21
+ CryptAcquireContext = Win32API.new("advapi32", "CryptAcquireContext",
22
+ 'PPPII', 'L')
23
+ CryptGenRandom = Win32API.new("advapi32", "CryptGenRandom",
24
+ 'LIP', 'L')
25
+ CryptReleaseContext = Win32API.new("advapi32", "CryptReleaseContext",
26
+ 'LI', 'L')
27
+ GetLastError = Win32API.new("kernel32", "GetLastError", '', 'L')
28
+ FormatMessageA = Win32API.new("kernel32", "FormatMessageA",
29
+ 'LPLLPLPPPPPPPP', 'L')
30
+
31
+ def lastErrorMessage
32
+ code = GetLastError.call
33
+ msg = "\0" * 1024
34
+ len = FormatMessageA.call(FORMAT_MESSAGE_IGNORE_INSERTS +
35
+ FORMAT_MESSAGE_FROM_SYSTEM, 0,
36
+ code, 0, msg, 1024, nil, nil,
37
+ nil, nil, nil, nil, nil, nil)
38
+ msg[0, len].tr("\r", '').chomp
39
+ end
40
+
41
+ def initialize
42
+ hProvStr = " " * 4
43
+ if CryptAcquireContext.call(hProvStr, nil, nil, PROV_RSA_FULL,
44
+ CRYPT_VERIFYCONTEXT) == 0
45
+ raise SystemCallError, "CryptAcquireContext failed: #{lastErrorMessage}"
46
+ end
47
+ hProv, = hProvStr.unpack('L')
48
+ @bytes = " " * 16
49
+ if CryptGenRandom.call(hProv, 16, @bytes) == 0
50
+ raise SystemCallError, "CryptGenRandom failed: #{lastErrorMessage}"
51
+ end
52
+ if CryptReleaseContext.call(hProv, 0) == 0
53
+ raise SystemCallError, "CryptReleaseContext failed: #{lastErrorMessage}"
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ module Guid_Unix_#:nodoc:
60
+ @@random_device = nil
61
+
62
+ def initialize
63
+ if !@@random_device
64
+ if File.exists? "/dev/urandom"
65
+ @@random_device = File.open "/dev/urandom", "r"
66
+ elsif File.exists? "/dev/random"
67
+ @@random_device = File.open "/dev/random", "r"
68
+ else
69
+ raise RuntimeError, "Can't find random device"
70
+ end
71
+ end
72
+
73
+ @bytes = @@random_device.read(16)
74
+ end
75
+ end
76
+
77
+ class Guid
78
+ if RUBY_PLATFORM =~ /win/ && ! RUBY_PLATFORM =~ /darwin/i
79
+ include Guid_Win32_
80
+ else
81
+ include Guid_Unix_
82
+ end
83
+
84
+ def hexdigest
85
+ @bytes.unpack("h*")[0]
86
+ end
87
+
88
+ def to_s
89
+ @bytes.unpack("h8 h4 h4 h4 h12").join "-"
90
+ end
91
+
92
+ def inspect
93
+ to_s
94
+ end
95
+
96
+ def raw
97
+ @bytes
98
+ end
99
+
100
+ def self.from_s(s)
101
+ raise ArgumentError, "Invalid GUID hexstring" unless
102
+ s =~ /\A[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}\z/i
103
+ guid = Guid.allocate
104
+ guid.instance_eval { @bytes = [s.gsub(/[^0-9a-f]+/i, '')].pack "h*" }
105
+ guid
106
+ end
107
+
108
+ def self.from_raw(bytes)
109
+ raise ArgumentError, "Invalid GUID raw bytes, length must be 16 bytes" unless
110
+ bytes.length == 16
111
+ guid = Guid.allocate
112
+ guid.instance_eval { @bytes = bytes }
113
+ guid
114
+ end
115
+
116
+ def ==(other)
117
+ @bytes == other.raw
118
+ end
119
+ end
120
+
121
+ if __FILE__ == $0
122
+ require 'test/unit'
123
+
124
+ class GuidTest < Test::Unit::TestCase#:nodoc:
125
+ def test_new
126
+ g = Guid.new
127
+
128
+ # different representations of guid: hexdigest, hex+dashes, raw bytes
129
+ assert_equal(0, g.to_s =~ /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/)
130
+ assert_equal(16, g.raw.length)
131
+ assert_equal(0, g.hexdigest =~ /\A[0-9a-f]{32}\z/)
132
+ assert_equal(g.hexdigest, g.to_s.gsub(/-/, ''))
133
+
134
+ # must be different each time we produce (this is just a simple test)
135
+ g2 = Guid.new
136
+ assert_equal(true, g != g2)
137
+ assert_equal(true, g.to_s != g2.to_s)
138
+ assert_equal(true, g.raw != g2.raw)
139
+ assert_equal(true, g.hexdigest != g2.hexdigest)
140
+ assert_equal(1000, (1..1000).select { |i| g != Guid.new }.length)
141
+ end
142
+
143
+ def test_from_s
144
+ g = Guid.new
145
+ g2 = Guid.from_s(g.to_s)
146
+ assert_equal(g, g2)
147
+ end
148
+
149
+ def test_from_raw
150
+ g = Guid.new
151
+ g2 = Guid.from_raw(g.raw)
152
+ assert_equal(g, g2)
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,76 @@
1
+ # Geoff Davis geoff at geoffdavis.net
2
+ # Wed May 2 20:08:44 EDT 2007
3
+ # http://rubyforge.org/pipermail/raleigh-rb-members/2007-May/000789.html
4
+ # modified by mtracy at matasano.com for WWMD
5
+
6
+ module WWMD
7
+ InlineTags = ['a','abbr','acronym','address','b','bdo','big','cite','code','del','dfn','em','font','i','ins','kbd','label','noframes','noscript','q','s','samp','small','span','strike','strong','sub','sup','td','th','tt','u','html','body','table']
8
+ BlockTags = ['blockquote','br','center','dd','div','fieldset','form','h1','h2','h3', 'h4','h5','h6','hr','p','pre','tr','var',]
9
+ ListTags = ['dir','dl','menu','ol','ul']
10
+ ItemTags = ['li','dt']
11
+ # AsciiEquivalents = {"amp"=>"&","bull"=>"*","copy"=>"(c)","laquo"=>"<<","raquo"=>">>","ge"=> ">=","le"=>"<=","mdash"=>"-","ndash"=>"-","plusmn"=>"+/-","times"=>"x"}
12
+
13
+ # NamedCharRegex = Regexp.new("(&("+Hpricot::NamedCharacters.keys.join("|")+");)")
14
+
15
+ class Page
16
+ def element_to_text(n)
17
+ tag = n.etag || n.stag
18
+ name = tag.name.downcase
19
+ s = ""
20
+ is_block = BlockTags.include?(name)
21
+ is_list = ListTags.include?(name)
22
+ is_item = ItemTags.include?(name)
23
+ is_inline = InlineTags.include?(name)
24
+ if is_block or is_list or is_item or is_inline
25
+ n.each_child do |c|
26
+ s += node_to_text(c)
27
+ end
28
+ if is_block or is_list
29
+ s += "\n"
30
+ elsif is_item
31
+ s = "* " + s + "\n"
32
+ end
33
+ end
34
+ s
35
+ end
36
+
37
+ def node_to_text(n)
38
+ return "" if n.comment?
39
+ return element_to_text(n) if n.elem?
40
+ return n.inner_text if n.text?
41
+
42
+ s = ""
43
+ begin
44
+ n.each_child do |c|
45
+ s += node_to_text(c)
46
+ end
47
+ rescue => e
48
+ # puts "WARNING: #{e.inspect}"
49
+ end
50
+ return s
51
+ end
52
+
53
+ # def lookup_named_char(s)
54
+ # c = Hpricot::NamedCharacters[s[1...-1]]
55
+ # c.chr if c
56
+ # end
57
+
58
+ def html2text
59
+ doc = self.scrape.hdoc
60
+ text = node_to_text(doc)
61
+ # text.gsub!(NamedCharRegex){|s| "#{lookup_named_char(s)}"}
62
+ # clean up white space
63
+ text.gsub!("\r"," ")
64
+ text.squeeze!(" ")
65
+ text.strip!
66
+ ret = ''
67
+ text.split(/\n/).each do |l|
68
+ l.strip!
69
+ next if l == ''
70
+ next if l =~ /^\?+$/
71
+ ret += "#{l}\n"
72
+ end
73
+ return ret
74
+ end
75
+ end
76
+ end