ronin-web 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,305 @@
1
+ #
2
+ #--
3
+ # Ronin Web - A Ruby library for Ronin that provides support for web
4
+ # scraping and spidering functionality.
5
+ #
6
+ # Copyright (c) 2006-2009 Hal Brodigan (postmodern.mod3 at gmail.com)
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #++
22
+ #
23
+
24
+ require 'ronin/network/http'
25
+
26
+ require 'uri/http'
27
+ require 'nokogiri'
28
+ require 'mechanize'
29
+ require 'open-uri'
30
+
31
+ module Ronin
32
+ module Web
33
+ #
34
+ # Returns a Nokogiri::HTML::Document object for the specified _body_
35
+ # of html.
36
+ #
37
+ def Web.html(body)
38
+ Nokogiri::HTML(body)
39
+ end
40
+
41
+ #
42
+ # Returns a Nokogiri::XML::Document object for the specified _body_
43
+ # of xml.
44
+ #
45
+ def Web.xml(body)
46
+ Nokogiri::XML(body)
47
+ end
48
+
49
+ #
50
+ # Returns the default Ronin Web proxy port.
51
+ #
52
+ def Web.default_proxy_port
53
+ Network::HTTP.default_proxy_port
54
+ end
55
+
56
+ #
57
+ # Sets the default Ronin Web proxy port to the specified _port_.
58
+ #
59
+ def Web.default_proxy_port=(port)
60
+ Network::HTTP.default_proxy_port = port
61
+ end
62
+
63
+ #
64
+ # Returns the +Hash+ of the Ronin Web proxy information.
65
+ #
66
+ def Web.proxy
67
+ Network::HTTP.proxy
68
+ end
69
+
70
+ #
71
+ # Resets the Web proxy settings.
72
+ #
73
+ def Web.disable_proxy
74
+ Network::HTTP.disable_proxy
75
+ end
76
+
77
+ #
78
+ # Creates a HTTP URI based from the given _proxy_info_ hash. The
79
+ # _proxy_info_ hash defaults to Web.proxy, if not given.
80
+ #
81
+ def Web.proxy_url(proxy_info=Web.proxy)
82
+ if Web.proxy[:host]
83
+ userinfo = nil
84
+
85
+ if (Web.proxy[:user] || Web.proxy[:password])
86
+ userinfo = "#{Web.proxy[:user]}:#{Web.proxy[:password]}"
87
+ end
88
+
89
+ return URI::HTTP.build(
90
+ :host => Web.proxy[:host],
91
+ :port => Web.proxy[:port],
92
+ :userinfo => userinfo,
93
+ :path => '/'
94
+ )
95
+ end
96
+ end
97
+
98
+ #
99
+ # Returns the supported Web User-Agent Aliases.
100
+ #
101
+ def Web.user_agent_aliases
102
+ WWW::Mechanize::AGENT_ALIASES
103
+ end
104
+
105
+ #
106
+ # Returns the Ronin Web User-Agent
107
+ #
108
+ def Web.user_agent
109
+ Network::HTTP.user_agent
110
+ end
111
+
112
+ #
113
+ # Sets the Ronin Web User-Agent to the specified _new_agent_.
114
+ #
115
+ def Web.user_agent=(new_agent)
116
+ Network::HTTP.user_agent = new_agent
117
+ end
118
+
119
+ #
120
+ # Sets the Ronin Web User-Agent to the specified user agent alias
121
+ # _name_.
122
+ #
123
+ def Web.user_agent_alias=(name)
124
+ Network::HTTP.user_agent = Web.user_agent_aliases[name.to_s]
125
+ end
126
+
127
+ #
128
+ # Opens the _url_ with the given _options_. The contents of the _url_
129
+ # will be returned.
130
+ #
131
+ # _options_ may contain the following keys:
132
+ # <tt>:user_agent_alias</tt>:: The User-Agent Alias to use.
133
+ # <tt>:user_agent</tt>:: The User-Agent string to use.
134
+ # <tt>:proxy</tt>:: A +Hash+ of the proxy information to use.
135
+ # <tt>:user</tt>:: The HTTP Basic Authentication user name.
136
+ # <tt>:password</tt>:: The HTTP Basic Authentication password.
137
+ # <tt>:content_length_proc</tt>:: A callback which will be passed the
138
+ # content-length of the HTTP response.
139
+ # <tt>:progress_proc</tt>:: A callback which will be passed the size
140
+ # of each fragment, once received from the
141
+ # server.
142
+ #
143
+ # Web.open('http://www.hackety.org/')
144
+ #
145
+ # Web.open('http://tenderlovemaking.com/',
146
+ # :user_agent_alias => 'Linux Mozilla')
147
+ #
148
+ # Web.open('http://www.wired.com/', :user_agent => 'the future')
149
+ #
150
+ def Web.open(url,options={})
151
+ user_agent_alias = options.delete(:user_agent_alias)
152
+ proxy = (options.delete(:proxy) || Web.proxy)
153
+ user = options.delete(:user)
154
+ password = options.delete(:password)
155
+ content_length_proc = options.delete(:content_length_proc)
156
+ progress_proc = options.delete(:progress_proc)
157
+
158
+ headers = Network::HTTP.headers(options)
159
+
160
+ if user_agent_alias
161
+ headers['User-Agent'] = Web.user_agent_aliases[user_agent_alias]
162
+ end
163
+
164
+ if proxy[:host]
165
+ headers[:proxy] = Web.proxy_url(proxy)
166
+ end
167
+
168
+ if user
169
+ headers[:http_basic_authentication] = [user, password]
170
+ end
171
+
172
+ if content_length_proc
173
+ headers[:content_length_proc] = content_length_proc
174
+ end
175
+
176
+ if progress_proc
177
+ headers[:progress_proc] = progress_proc
178
+ end
179
+
180
+ return Kernel.open(url,headers)
181
+ end
182
+
183
+ #
184
+ # Creates a new Mechanize agent with the given _options_.
185
+ #
186
+ # _options_ may contain the following keys:
187
+ # <tt>:user_agent_alias</tt>:: The User-Agent Alias to use.
188
+ # <tt>:user_agent</tt>:: The User-Agent string to use.
189
+ # <tt>:proxy</tt>:: A +Hash+ of the proxy information to use.
190
+ #
191
+ # Web.agent
192
+ # Web.agent(:user_agent_alias => 'Linux Mozilla')
193
+ # Web.agent(:user_agent => 'wooden pants')
194
+ #
195
+ def Web.agent(options={},&block)
196
+ agent = WWW::Mechanize.new
197
+
198
+ if options[:user_agent_alias]
199
+ agent.user_agent_alias = options[:user_agent_alias]
200
+ elsif options[:user_agent]
201
+ agent.user_agent = options[:user_agent]
202
+ elsif Web.user_agent
203
+ agent.user_agent = Web.user_agent
204
+ end
205
+
206
+ proxy = (options[:proxy] || Web.proxy)
207
+ if proxy[:host]
208
+ agent.set_proxy(proxy[:host],proxy[:port],proxy[:user],proxy[:password])
209
+ end
210
+
211
+ block.call(agent) if block
212
+ return agent
213
+ end
214
+
215
+ #
216
+ # Gets the specified _url_ with the given _options_. If a _block_ is
217
+ # given, it will be passed the retrieved page.
218
+ #
219
+ # _options_ may contain the following keys:
220
+ # <tt>:user_agent_alias</tt>:: The User-Agent Alias to use.
221
+ # <tt>:user_agent</tt>:: The User-Agent string to use.
222
+ # <tt>:proxy</tt>:: A +Hash+ of the proxy information to use.
223
+ #
224
+ # Web.get('http://www.0x000000.com') # => WWW::Mechanize::Page
225
+ #
226
+ # Web.get('http://www.rubyinside.com') do |page|
227
+ # page.search('div.post/h2/a').each do |title|
228
+ # puts title.inner_text
229
+ # end
230
+ # end
231
+ #
232
+ def Web.get(url,options={},&block)
233
+ page = Web.agent(options).get(url)
234
+
235
+ block.call(page) if block
236
+ return page
237
+ end
238
+
239
+ #
240
+ # Gets the specified _url_ with the given _options_, returning the body
241
+ # of the requested page. If a _block_ is given, it will be passed the
242
+ # body of the retrieved page.
243
+ #
244
+ # _options_ may contain the following keys:
245
+ # <tt>:user_agent_alias</tt>:: The User-Agent Alias to use.
246
+ # <tt>:user_agent</tt>:: The User-Agent string to use.
247
+ # <tt>:proxy</tt>:: A +Hash+ of the proxy information to use.
248
+ #
249
+ # Web.get_body('http://www.rubyinside.com') # => String
250
+ #
251
+ # Web.get_body('http://www.rubyinside.com') do |body|
252
+ # puts body
253
+ # end
254
+ #
255
+ def Web.get_body(url,options={},&block)
256
+ body = Web.get(url,options).body
257
+
258
+ block.call(body) if block
259
+ return body
260
+ end
261
+
262
+ #
263
+ # Posts the specified _url_ with the given _options_. If a _block_ is
264
+ # given, it will be passed the posted page.
265
+ #
266
+ # _options_ may contain the following keys:
267
+ # <tt>:query</tt>:: The query parameters to post to the specified _url_.
268
+ # <tt>:user_agent_alias</tt>:: The User-Agent Alias to use.
269
+ # <tt>:user_agent</tt>:: The User-Agent string to use.
270
+ # <tt>:proxy</tt>:: A +Hash+ of the proxy information to use.
271
+ #
272
+ # Web.post('http://www.rubyinside.com') # => WWW::Mechanize::Page
273
+ #
274
+ def Web.post(url,options={},&block)
275
+ query = (options[:query] || {})
276
+ page = Web.agent(options).post(url,query)
277
+
278
+ block.call(page) if block
279
+ return page
280
+ end
281
+
282
+ #
283
+ # Poststhe specified _url_ with the given _options_, returning the body
284
+ # of the posted page. If a _block_ is given, it will be passed the
285
+ # body of the posted page.
286
+ #
287
+ # _options_ may contain the following keys:
288
+ # <tt>:user_agent_alias</tt>:: The User-Agent Alias to use.
289
+ # <tt>:user_agent</tt>:: The User-Agent string to use.
290
+ # <tt>:proxy</tt>:: A +Hash+ of the proxy information to use.
291
+ #
292
+ # Web.post_body('http://www.rubyinside.com') # => String
293
+ #
294
+ # Web.post_body('http://www.rubyinside.com') do |body|
295
+ # puts body
296
+ # end
297
+ #
298
+ def Web.post_body(url,options={},&block)
299
+ body = Web.post(url,options).body
300
+
301
+ block.call(body) if block
302
+ return body
303
+ end
304
+ end
305
+ end
data/lib/ronin/web.rb ADDED
@@ -0,0 +1,25 @@
1
+ #
2
+ #--
3
+ # Ronin Web - A Ruby library for Ronin that provides support for web
4
+ # scraping and spidering functionality.
5
+ #
6
+ # Copyright (c) 2006-2009 Hal Brodigan (postmodern.mod3 at gmail.com)
7
+ #
8
+ # This program is free software; you can redistribute it and/or modify
9
+ # it under the terms of the GNU General Public License as published by
10
+ # the Free Software Foundation; either version 2 of the License, or
11
+ # (at your option) any later version.
12
+ #
13
+ # This program is distributed in the hope that it will be useful,
14
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
+ # GNU General Public License for more details.
17
+ #
18
+ # You should have received a copy of the GNU General Public License
19
+ # along with this program; if not, write to the Free Software
20
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
+ #++
22
+ #
23
+
24
+ require 'ronin/web/extensions'
25
+ require 'ronin/web/web'
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>=1.1.3'
3
+ require 'spec'
4
+
5
+ require 'ronin/web/version'
6
+
7
+ include Ronin
@@ -0,0 +1,38 @@
1
+ require 'ronin/web/extensions/nokogiri'
2
+
3
+ require 'spec_helper'
4
+ require 'nokogiri'
5
+
6
+ describe Nokogiri::HTML do
7
+ before(:all) do
8
+ @doc = Nokogiri::HTML(%{<html><head><title>test</title></head><body><p><b>This is a test</b> html <i>page</i>.</p></div></body></html>})
9
+
10
+ @edited_doc = Nokogiri::HTML(%{<html><head><title>test</title></head><body><p><b>This is a test</b> html page.</p></div></body></html>})
11
+ end
12
+
13
+ it "should be able to test if two elements are similar" do
14
+ elem1 = @doc.at('b')
15
+ elem2 = @edited_doc.at('b')
16
+
17
+ elem1.similar?(elem2).should == true
18
+ end
19
+
20
+ it "should be able to test if two elements are not similar" do
21
+ elem1 = @doc.at('p').children.last
22
+ elem2 = @edited_doc.at('b')
23
+
24
+ elem1.similar?(elem2).should == false
25
+ end
26
+
27
+ it "should be able to traverse over every text node" do
28
+ text = []
29
+
30
+ @doc.traverse_text { |node| text << node.content }
31
+
32
+ text.should == ['test', 'This is a test', ' html ', 'page', '.']
33
+ end
34
+
35
+ it "should provide a count of all sub-children" do
36
+ @doc.total_children.should == 12
37
+ end
38
+ end
@@ -0,0 +1 @@
1
+ Index of files.
@@ -0,0 +1 @@
1
+ This is a test.
@@ -0,0 +1,2 @@
1
+ # Web Server root directory
2
+ WEB_SERVER_ROOT = File.expand_path(File.join(File.dirname(__FILE__),'root'))
@@ -0,0 +1,142 @@
1
+ require 'ronin/web/server'
2
+
3
+ require 'spec_helper'
4
+ require 'web/helpers/server'
5
+
6
+ describe Web::Server do
7
+ before(:all) do
8
+ @server = Web::Server.new do
9
+ default do |env|
10
+ response('This is default.')
11
+ end
12
+
13
+ bind('/test/bind.xml') do |env|
14
+ response('<secret/>', :content_type => 'text/xml')
15
+ end
16
+
17
+ paths_like(/path_patterns\/secret\./) do |env|
18
+ response('No secrets here.')
19
+ end
20
+
21
+ map('/test/map') do |env|
22
+ response('mapped')
23
+ end
24
+
25
+ file('/test/file.txt',File.join(WEB_SERVER_ROOT,'test.txt'))
26
+
27
+ mount('/test/mount/',WEB_SERVER_ROOT)
28
+ end
29
+
30
+ @virtual_host = Web::Server.new do
31
+ bind('/test/virtual_host.xml') do |env|
32
+ response('<virtual/>', :content_type => 'text/xml')
33
+ end
34
+ end
35
+
36
+ @server.host('virtual.host.com') do
37
+ bind('/test/virtual_host.xml') do |env|
38
+ response('<virtual/>', :content_type => 'text/xml')
39
+ end
40
+ end
41
+
42
+ @server.hosts_like(/^virtual[0-9]\./) do
43
+ bind('/test/virtual_host_patterns.xml') do |env|
44
+ response('<virtual-patterns/>', :content_type => 'text/xml')
45
+ end
46
+ end
47
+ end
48
+
49
+ it "should have a default host to listen on" do
50
+ Web::Server.default_host.should_not be_nil
51
+ end
52
+
53
+ it "should have a default port to listen on" do
54
+ Web::Server.default_port.should_not be_nil
55
+ end
56
+
57
+ it "should have built-in content types" do
58
+ Web::Server.content_types.should_not be_empty
59
+ end
60
+
61
+ it "should map file extensions to content-types" do
62
+ @server.content_type('html').should == 'text/html'
63
+ end
64
+
65
+ it "should have a default content-type for unknown files" do
66
+ @server.content_type('lol').should == 'application/x-unknown-content-type'
67
+ end
68
+
69
+ it "should find the index file for a directory" do
70
+ dir = WEB_SERVER_ROOT
71
+
72
+ @server.index_of(dir).should == File.join(dir,'index.html')
73
+ end
74
+
75
+ it "should have a default response for un-matched paths" do
76
+ path = '/test/default'
77
+
78
+ @server.route_path(path).body.should == ['This is default.']
79
+ end
80
+
81
+ it "should bind a path to a certain response" do
82
+ path = '/test/bind.xml'
83
+
84
+ @server.route_path(path).body.should == ['<secret/>']
85
+ end
86
+
87
+ it "should match paths with patterns" do
88
+ path = '/test/path_patterns/secret.pdf'
89
+
90
+ @server.route_path(path).body.should == ['No secrets here.']
91
+ end
92
+
93
+ it "should match paths to sub-directories" do
94
+ path = '/test/map/impossible.html'
95
+
96
+ @server.route_path(path).body.should == ['mapped']
97
+ end
98
+
99
+ it "should return a response for a file" do
100
+ path = '/test/file.txt'
101
+
102
+ @server.route_path(path).body.should == ["This is a test.\n"]
103
+ end
104
+
105
+ it "should return files from mounted directories" do
106
+ path = '/test/mount/test.txt'
107
+
108
+ @server.route_path(path).body.should == ["This is a test.\n"]
109
+ end
110
+
111
+ it "should return the index file for a mounted directory" do
112
+ path = '/test/mount/'
113
+
114
+ @server.route_path(path).body.should == ["Index of files.\n"]
115
+ end
116
+
117
+ it "should match virtual hosts" do
118
+ url = 'http://virtual.host.com/test/virtual_host.xml'
119
+
120
+ @server.route(url).body.should == ['<virtual/>']
121
+ end
122
+
123
+ it "should match virtual hosts with patterns" do
124
+ url = 'http://virtual0.host.com/test/virtual_host_patterns.xml'
125
+
126
+ @server.route(url).body.should == ['<virtual-patterns/>']
127
+ end
128
+
129
+ it "should provide access to servers via their host-names" do
130
+ virtual_host = @server.virtual_host('virtual.host.com')
131
+ url = 'http://virtual.host.com/test/virtual_host.xml'
132
+
133
+ virtual_host.route(url).body.should == ['<virtual/>']
134
+ end
135
+
136
+ it "should provide access to servers via their host-names that match virtual host patterns" do
137
+ virtual_host = @server.virtual_host('virtual1.host.com')
138
+ url = 'http://virtual0.host.com/test/virtual_host_patterns.xml'
139
+
140
+ virtual_host.route(url).body.should == ['<virtual-patterns/>']
141
+ end
142
+ end
data/tasks/spec.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'spec/rake/spectask'
2
+
3
+ desc "Run all specifications"
4
+ Spec::Rake::SpecTask.new(:spec) do |t|
5
+ t.libs += ['lib', 'spec']
6
+ t.spec_opts = ['--colour', '--format', 'specdoc']
7
+ end
8
+
9
+ task :default => :spec
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ronin-web
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Postmodern
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-01-22 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: nokogiri
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.1.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: mechanize
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: spidr
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.1.3
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: rack
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.9.1
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: ronin
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.1.4
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: hoe
67
+ type: :development
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 1.8.3
74
+ version:
75
+ description: Ronin Web is a Ruby library for Ronin that provides support for web scraping and spidering functionality. Ronin is a Ruby platform designed for information security and data exploration tasks. Ronin allows for the rapid development and distribution of code over many of the common Source-Code-Management (SCM) systems.
76
+ email:
77
+ - postmodern.mod3@gmail.com
78
+ executables: []
79
+
80
+ extensions: []
81
+
82
+ extra_rdoc_files:
83
+ - History.txt
84
+ - Manifest.txt
85
+ - README.txt
86
+ - spec/web/helpers/root/test.txt
87
+ files:
88
+ - History.txt
89
+ - Manifest.txt
90
+ - README.txt
91
+ - Rakefile
92
+ - lib/ronin/sessions/web.rb
93
+ - lib/ronin/web.rb
94
+ - lib/ronin/web/extensions.rb
95
+ - lib/ronin/web/extensions/nokogiri.rb
96
+ - lib/ronin/web/extensions/nokogiri/xml.rb
97
+ - lib/ronin/web/extensions/nokogiri/xml/node.rb
98
+ - lib/ronin/web/extensions/nokogiri/xml/text.rb
99
+ - lib/ronin/web/extensions/nokogiri/xml/attr.rb
100
+ - lib/ronin/web/extensions/nokogiri/xml/element.rb
101
+ - lib/ronin/web/extensions/nokogiri/xml/document.rb
102
+ - lib/ronin/web/server.rb
103
+ - lib/ronin/web/spider.rb
104
+ - lib/ronin/web/web.rb
105
+ - lib/ronin/web/version.rb
106
+ - tasks/spec.rb
107
+ - spec/spec_helper.rb
108
+ - spec/web/helpers/server.rb
109
+ - spec/web/helpers/root/index.html
110
+ - spec/web/helpers/root/test.txt
111
+ - spec/web/extensions/nokogiri_spec.rb
112
+ - spec/web/server_spec.rb
113
+ has_rdoc: true
114
+ homepage: http://ronin.rubyforge.org/web/
115
+ post_install_message:
116
+ rdoc_options:
117
+ - --main
118
+ - README.txt
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: "0"
126
+ version:
127
+ required_rubygems_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: "0"
132
+ version:
133
+ requirements: []
134
+
135
+ rubyforge_project: ronin
136
+ rubygems_version: 1.3.1
137
+ signing_key:
138
+ specification_version: 2
139
+ summary: Ronin Web is a Ruby library for Ronin that provides support for web scraping and spidering functionality
140
+ test_files: []
141
+