ronin-web 0.1.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.
@@ -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
+