shodan-ruby 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.
- data/COPYING.txt +339 -0
- data/History.txt +9 -0
- data/Manifest.txt +25 -0
- data/README.txt +129 -0
- data/Rakefile +26 -0
- data/lib/shodan/extensions/uri/http.rb +31 -0
- data/lib/shodan/extensions/uri/query_params.rb +105 -0
- data/lib/shodan/extensions/uri.rb +22 -0
- data/lib/shodan/extensions.rb +21 -0
- data/lib/shodan/has_pages.rb +231 -0
- data/lib/shodan/host.rb +146 -0
- data/lib/shodan/page.rb +308 -0
- data/lib/shodan/query.rb +250 -0
- data/lib/shodan/shodan.rb +216 -0
- data/lib/shodan/version.rb +24 -0
- data/lib/shodan.rb +23 -0
- data/spec/has_pages_examples.rb +17 -0
- data/spec/host_spec.rb +115 -0
- data/spec/page_has_hosts_examples.rb +31 -0
- data/spec/page_spec.rb +91 -0
- data/spec/query_spec.rb +95 -0
- data/spec/shodan_spec.rb +34 -0
- data/spec/spec_helper.rb +7 -0
- data/tasks/spec.rb +10 -0
- data/tasks/yard.rb +13 -0
- data.tar.gz.sig +0 -0
- metadata +144 -0
- metadata.gz.sig +1 -0
data/lib/shodan/query.rb
ADDED
@@ -0,0 +1,250 @@
|
|
1
|
+
#
|
2
|
+
# shodan-ruby - A Ruby interface to SHODAN, a computer search engine.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2009 Hal Brodigan (postmodern.mod3 at gmail.com)
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation; either version 2 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'shodan/extensions/uri'
|
22
|
+
require 'shodan/countries'
|
23
|
+
require 'shodan/has_pages'
|
24
|
+
require 'shodan/page'
|
25
|
+
require 'shodan/shodan'
|
26
|
+
|
27
|
+
require 'net/http'
|
28
|
+
require 'nokogiri'
|
29
|
+
|
30
|
+
module Shodan
|
31
|
+
class Query
|
32
|
+
|
33
|
+
include HasPages
|
34
|
+
|
35
|
+
# Search URL
|
36
|
+
SEARCH_URL = 'http://shodan.surtri.com/'
|
37
|
+
|
38
|
+
# Results per page
|
39
|
+
RESULTS_PER_PAGE = 20
|
40
|
+
|
41
|
+
# Search query
|
42
|
+
attr_accessor :query
|
43
|
+
|
44
|
+
# Countries to search within
|
45
|
+
attr_accessor :countries
|
46
|
+
|
47
|
+
# Hostnames to search for
|
48
|
+
attr_accessor :hostnames
|
49
|
+
|
50
|
+
# CIDR Network blocks to search within
|
51
|
+
attr_accessor :networks
|
52
|
+
|
53
|
+
# Ports to search for
|
54
|
+
attr_accessor :ports
|
55
|
+
|
56
|
+
#
|
57
|
+
# Creates a new Query object.
|
58
|
+
#
|
59
|
+
# @param [Hash] options
|
60
|
+
# Additional options.
|
61
|
+
#
|
62
|
+
# @option options [String] :query
|
63
|
+
# The query expression.
|
64
|
+
#
|
65
|
+
# @option options [Array<String>] :countries
|
66
|
+
# The Country Codes to search within.
|
67
|
+
#
|
68
|
+
# @option options [String] :country
|
69
|
+
# A Country Code to search within.
|
70
|
+
#
|
71
|
+
# @option options [Array<String>] :hostnames
|
72
|
+
# The host names to search for.
|
73
|
+
#
|
74
|
+
# @option options [String] :hostname
|
75
|
+
# A host name to search for.
|
76
|
+
#
|
77
|
+
# @option options [Array<String>] :networks
|
78
|
+
# The CIDR network blocks to search within.
|
79
|
+
#
|
80
|
+
# @option options [String] :network
|
81
|
+
# A CIDR network blocks to search within.
|
82
|
+
#
|
83
|
+
# @option options [Array<Integer>] :ports
|
84
|
+
# The ports to search for.
|
85
|
+
#
|
86
|
+
# @option options [Integer] :port
|
87
|
+
# A port to search for.
|
88
|
+
#
|
89
|
+
# @yield [query]
|
90
|
+
# If a block is given, it will be passed the newly created Query
|
91
|
+
# object.
|
92
|
+
#
|
93
|
+
# @yieldparam [Query] query
|
94
|
+
# The newly created Query object.
|
95
|
+
#
|
96
|
+
def initialize(options={},&block)
|
97
|
+
@agent = Shodan.web_agent
|
98
|
+
@query = options[:query]
|
99
|
+
|
100
|
+
@countries = []
|
101
|
+
|
102
|
+
if options[:countries]
|
103
|
+
@countries += options[:countries]
|
104
|
+
elsif options[:country]
|
105
|
+
@countries << option[:country]
|
106
|
+
end
|
107
|
+
|
108
|
+
@hostnames = []
|
109
|
+
|
110
|
+
if options[:hostnames]
|
111
|
+
@hostnames += options[:hostnames]
|
112
|
+
elsif options[:hostname]
|
113
|
+
@hostnames << options[:hostname]
|
114
|
+
end
|
115
|
+
|
116
|
+
@networks = []
|
117
|
+
|
118
|
+
if options[:networks]
|
119
|
+
@networks += options[:networks]
|
120
|
+
elsif options[:network]
|
121
|
+
@networks << options[:network]
|
122
|
+
end
|
123
|
+
|
124
|
+
@ports = []
|
125
|
+
|
126
|
+
if options[:ports]
|
127
|
+
@ports += options[:ports]
|
128
|
+
elsif options[:port]
|
129
|
+
@ports << options[:port]
|
130
|
+
end
|
131
|
+
|
132
|
+
block.call(self) if block
|
133
|
+
end
|
134
|
+
|
135
|
+
#
|
136
|
+
# Converts a given URL into a Query object.
|
137
|
+
#
|
138
|
+
# @param [URI::HTTP, String] url
|
139
|
+
# The query URL.
|
140
|
+
#
|
141
|
+
# @return [Query]
|
142
|
+
# The Query object created from the URL.
|
143
|
+
#
|
144
|
+
def self.from_url(url)
|
145
|
+
url = URI(url.to_s)
|
146
|
+
|
147
|
+
return self.new(
|
148
|
+
:query => url.query_params['q'].gsub('+',' ')
|
149
|
+
)
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# The results per page.
|
154
|
+
#
|
155
|
+
# @return [Integer]
|
156
|
+
# The resutls per page.
|
157
|
+
#
|
158
|
+
# @see RESULTS_PER_PAGE
|
159
|
+
#
|
160
|
+
def results_per_page
|
161
|
+
RESULTS_PER_PAGE
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
# The query expression from the query.
|
166
|
+
#
|
167
|
+
# @return [String]
|
168
|
+
# The query expression.
|
169
|
+
#
|
170
|
+
def expression
|
171
|
+
expr = []
|
172
|
+
|
173
|
+
expr << @query if @query
|
174
|
+
|
175
|
+
expr += @countries.map { |code| "country:#{code}" }
|
176
|
+
expr += @hostnames.map { |host| "hostname:#{host}" }
|
177
|
+
expr += @networks.map { |net| "net:#{net}" }
|
178
|
+
expr += @ports.map { |port| "port:#{port}" }
|
179
|
+
|
180
|
+
return expr.join(' ')
|
181
|
+
end
|
182
|
+
|
183
|
+
#
|
184
|
+
# The search URL for the query.
|
185
|
+
#
|
186
|
+
# @return [URI::HTTP]
|
187
|
+
# The search URL.
|
188
|
+
#
|
189
|
+
def search_url
|
190
|
+
url = URI(SEARCH_URL)
|
191
|
+
|
192
|
+
url.query_params['q'] = expression
|
193
|
+
return url
|
194
|
+
end
|
195
|
+
|
196
|
+
#
|
197
|
+
# The search URL for the query and given page index.
|
198
|
+
#
|
199
|
+
# @param [Integer] index
|
200
|
+
# The page index to lookup.
|
201
|
+
#
|
202
|
+
# @retrun [URI::HTTP]
|
203
|
+
# The search URL for the query and the specified page index.
|
204
|
+
#
|
205
|
+
def page_url(index)
|
206
|
+
url = search_url
|
207
|
+
|
208
|
+
unless index == 1
|
209
|
+
url.query_params['page'] = index
|
210
|
+
end
|
211
|
+
|
212
|
+
return url
|
213
|
+
end
|
214
|
+
|
215
|
+
#
|
216
|
+
# Requests a page at the given index.
|
217
|
+
#
|
218
|
+
# @param [Integer] index
|
219
|
+
# The index of the page.
|
220
|
+
#
|
221
|
+
# @return [Page]
|
222
|
+
# The page at the specified index.
|
223
|
+
#
|
224
|
+
def page(index)
|
225
|
+
Page.new do |new_page|
|
226
|
+
doc = @agent.get(page_url(index))
|
227
|
+
|
228
|
+
doc.search('#search/div.result').each do |result|
|
229
|
+
div = result.at('div')
|
230
|
+
|
231
|
+
ip = if (a = div.at('a'))
|
232
|
+
a.inner_text
|
233
|
+
else
|
234
|
+
div.children.first.inner_text
|
235
|
+
end
|
236
|
+
|
237
|
+
hostname = if (host_node = result.at('div/a:last'))
|
238
|
+
host_node.inner_text
|
239
|
+
end
|
240
|
+
|
241
|
+
date = result.at('div/span').inner_text.scan(/\d+\.\d+\.\d/).first
|
242
|
+
response = result.at('p').inner_text.strip
|
243
|
+
|
244
|
+
new_page << Host.new(ip,date,response,hostname)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
#
|
2
|
+
# shodan-ruby - A Ruby interface to SHODAN, a computer search engine.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2009 Hal Brodigan (postmodern.mod3 at gmail.com)
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation; either version 2 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'shodan/query'
|
22
|
+
|
23
|
+
require 'mechanize'
|
24
|
+
|
25
|
+
module Shodan
|
26
|
+
# Common proxy port.
|
27
|
+
COMMON_PROXY_PORT = 8080
|
28
|
+
|
29
|
+
#
|
30
|
+
# The proxy information used by {Query}.
|
31
|
+
#
|
32
|
+
# @return [Hash]
|
33
|
+
# The proxy information.
|
34
|
+
#
|
35
|
+
def Shodan.proxy
|
36
|
+
@@shodan_proxy ||= {:host => nil, :port => COMMON_PROXY_PORT, :user => nil, :password => nil}
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Creates a HTTP URI based from the given proxy information.
|
41
|
+
#
|
42
|
+
# @param [Hash] proxy_info (Shodan.proxy)
|
43
|
+
# The proxy information.
|
44
|
+
#
|
45
|
+
# @option proxy_info [String] :host
|
46
|
+
# The host of the proxy.
|
47
|
+
#
|
48
|
+
# @option proxy_info [Integer] :port (COMMON_PROXY_PORT)
|
49
|
+
# The port of the proxy.
|
50
|
+
#
|
51
|
+
# @option proxy_info [String] :user
|
52
|
+
# The user name to authenticate as.
|
53
|
+
#
|
54
|
+
# @option proxy_info [String] :password
|
55
|
+
# The password to authenticate with.
|
56
|
+
#
|
57
|
+
def Shodan.proxy_uri(proxy_info=Shodan.proxy)
|
58
|
+
if Shodan.proxy[:host]
|
59
|
+
return URI::HTTP.build(
|
60
|
+
:host => Shodan.proxy[:host],
|
61
|
+
:port => Shodan.proxy[:port],
|
62
|
+
:userinfo => "#{Shodan.proxy[:user]}:#{Shodan.proxy[:password]}",
|
63
|
+
:path => '/'
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# The supported Shodan User-Agent Aliases.
|
70
|
+
#
|
71
|
+
# @return [Hash{String => String}]
|
72
|
+
# The User-Agent aliases and strings.
|
73
|
+
#
|
74
|
+
def Shodan.user_agent_aliases
|
75
|
+
WWW::Mechanize::AGENT_ALIASES
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# The default Shodan User-Agent
|
80
|
+
#
|
81
|
+
# @return [String]
|
82
|
+
# The default User-Agent string used by Shodan.
|
83
|
+
#
|
84
|
+
def Shodan.user_agent
|
85
|
+
@@shodan_user_agent ||= Shodan.user_agent_aliases['Windows IE 6']
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Sets the default Shodan User-Agent.
|
90
|
+
#
|
91
|
+
# @param [String] agent
|
92
|
+
# The User-Agent string to use.
|
93
|
+
#
|
94
|
+
# @return [String]
|
95
|
+
# The new User-Agent string.
|
96
|
+
#
|
97
|
+
def Shodan.user_agent=(agent)
|
98
|
+
@@shodan_user_agent = agent
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Sets the default Shodan User-Agent alias.
|
103
|
+
#
|
104
|
+
# @param [String] name
|
105
|
+
# The alias of the User-Agent string to use.
|
106
|
+
#
|
107
|
+
# @return [String]
|
108
|
+
# The new User-Agent string.
|
109
|
+
#
|
110
|
+
def Shodan.user_agent_alias=(name)
|
111
|
+
@@shodan_user_agent = Shodan.user_agent_aliases[name.to_s]
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Creates a new WWW::Mechanize agent.
|
116
|
+
#
|
117
|
+
# @param [Hash] options
|
118
|
+
# Additional options.
|
119
|
+
#
|
120
|
+
# @option options [String] :user_agent_alias
|
121
|
+
# The User-Agent alias to use.
|
122
|
+
#
|
123
|
+
# @option options [String] :user_agent (Shodan.user_agent)
|
124
|
+
# The User-Agent string to use.
|
125
|
+
#
|
126
|
+
# @option options [Hash] :proxy (Shodan.proxy)
|
127
|
+
# The proxy information to use.
|
128
|
+
#
|
129
|
+
# @option :proxy [String] :host
|
130
|
+
# The host of the proxy.
|
131
|
+
#
|
132
|
+
# @option :proxy [Integer] :port
|
133
|
+
# The port of the proxy.
|
134
|
+
#
|
135
|
+
# @option :proxy [String] :user
|
136
|
+
# The user to authenticate as.
|
137
|
+
#
|
138
|
+
# @option :proxy [String] :password
|
139
|
+
# The password to authenticate with.
|
140
|
+
#
|
141
|
+
# @example Creating a new Mechanize agent.
|
142
|
+
# Shodan.web_agent
|
143
|
+
#
|
144
|
+
# @example Creating a new Mechanize agent with a User-Agent alias.
|
145
|
+
# Shodan.web_agent(:user_agent_alias => 'Linux Mozilla')
|
146
|
+
#
|
147
|
+
# @example Creating a new Mechanize agent with a User-Agent string.
|
148
|
+
# Shodan.web_agent(:user_agent => 'Google Bot')
|
149
|
+
#
|
150
|
+
def Shodan.web_agent(options={},&block)
|
151
|
+
agent = WWW::Mechanize.new
|
152
|
+
|
153
|
+
if options[:user_agent_alias]
|
154
|
+
agent.user_agent_alias = options[:user_agent_alias]
|
155
|
+
elsif options[:user_agent]
|
156
|
+
agent.user_agent = options[:user_agent]
|
157
|
+
elsif Shodan.user_agent
|
158
|
+
agent.user_agent = Shodan.user_agent
|
159
|
+
end
|
160
|
+
|
161
|
+
proxy = (options[:proxy] || Shodan.proxy)
|
162
|
+
if proxy[:host]
|
163
|
+
agent.set_proxy(proxy[:host],proxy[:port],proxy[:user],proxy[:password])
|
164
|
+
end
|
165
|
+
|
166
|
+
block.call(agent) if block
|
167
|
+
return agent
|
168
|
+
end
|
169
|
+
|
170
|
+
#
|
171
|
+
# Creates a new Query object.
|
172
|
+
#
|
173
|
+
# @param [Hash] options
|
174
|
+
# Additional options.
|
175
|
+
#
|
176
|
+
# @option options [String] :query
|
177
|
+
# The query expression.
|
178
|
+
#
|
179
|
+
# @option options [Array<String>] :countries
|
180
|
+
# The Country Codes to search within.
|
181
|
+
#
|
182
|
+
# @option options [String] :country
|
183
|
+
# A Country Code to search within.
|
184
|
+
#
|
185
|
+
# @option options [Array<String>] :hostnames
|
186
|
+
# The host names to search for.
|
187
|
+
#
|
188
|
+
# @option options [String] :hostname
|
189
|
+
# A host name to search for.
|
190
|
+
#
|
191
|
+
# @option options [Array<String>] :networks
|
192
|
+
# The CIDR network blocks to search within.
|
193
|
+
#
|
194
|
+
# @option options [String] :network
|
195
|
+
# A CIDR network blocks to search within.
|
196
|
+
#
|
197
|
+
# @option options [Array<Integer>] :ports
|
198
|
+
# The ports to search for.
|
199
|
+
#
|
200
|
+
# @option options [Integer] :port
|
201
|
+
# A port to search for.
|
202
|
+
#
|
203
|
+
# @yield [query]
|
204
|
+
# If a block is given, it will be passed the newly created Query
|
205
|
+
# object.
|
206
|
+
#
|
207
|
+
# @yieldparam [Query] query
|
208
|
+
# The newly created Query object.
|
209
|
+
#
|
210
|
+
# @return [Query]
|
211
|
+
# The new Query object.
|
212
|
+
#
|
213
|
+
def Shodan.query(options={},&block)
|
214
|
+
Query.new(options,&block)
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#
|
2
|
+
# shodan-ruby - A Ruby interface to SHODAN, a computer search engine.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2009 Hal Brodigan (postmodern.mod3 at gmail.com)
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation; either version 2 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
+
#
|
20
|
+
|
21
|
+
module Shodan
|
22
|
+
# shodanrb version
|
23
|
+
VERSION = '0.1.0'
|
24
|
+
end
|
data/lib/shodan.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# shodan-ruby - A Ruby interface to SHODAN, a computer search engine.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2009 Hal Brodigan (postmodern.mod3 at gmail.com)
|
5
|
+
#
|
6
|
+
# This program is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation; either version 2 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
15
|
+
#
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program; if not, write to the Free Software
|
18
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
+
#
|
20
|
+
|
21
|
+
require 'shodan/query'
|
22
|
+
require 'shodan/shodan'
|
23
|
+
require 'shodan/version'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for "has Pages" do
|
4
|
+
it "should have a first page" do
|
5
|
+
@query.first_page.should_not be_nil
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should allow indexed access" do
|
9
|
+
@query[1].should_not be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should allow accessing multiple pages" do
|
13
|
+
pages = @query.pages(1..2)
|
14
|
+
pages.should_not be_nil
|
15
|
+
pages.length.should == 2
|
16
|
+
end
|
17
|
+
end
|
data/spec/host_spec.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'shodan/host'
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Host do
|
6
|
+
describe "non-HTTP host" do
|
7
|
+
before(:all) do
|
8
|
+
@host = Host.new('127.0.0.1','16.11.2009','SSH-2.0-OpenSSH_4.2')
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should parse the date" do
|
12
|
+
@host.date.class.should == Date
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should not have a HTTP version" do
|
16
|
+
@host.http_version.should be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should not have a HTTP status code" do
|
20
|
+
@host.http_code.should be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should not have a HTTP status message" do
|
24
|
+
@host.http_status.should be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should not have HTTP response headers" do
|
28
|
+
@host.http_headers.should == {}
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should not have a HTTP response body" do
|
32
|
+
@host.http_body.should be_nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "HTTP host" do
|
37
|
+
before(:all) do
|
38
|
+
@host = Host.new(
|
39
|
+
'127.0.0.1',
|
40
|
+
'16.11.2009',
|
41
|
+
%{
|
42
|
+
HTTP/1.0 200 OK
|
43
|
+
X-powered-by: PHP/5.3.0
|
44
|
+
Transfer-encoding: chunked
|
45
|
+
X-gpg-key: http://chrislea.com/gpgkey.txt http://chrislea.com/gpgkey-virb.txt
|
46
|
+
Vary: Cookie
|
47
|
+
X-ssh-key: http://chrislea.com/sshkey.txt
|
48
|
+
Server: nginx/0.8.24
|
49
|
+
Connection: keep-alive
|
50
|
+
Date: Fri, 20 Nov 2009 00:52:24 GMT
|
51
|
+
X-the-question: Quis custodiet ipsos custodes?
|
52
|
+
Content-type: text/html; charset=UTF-8
|
53
|
+
X-pingback: http://chrislea.com/xmlrpc.php
|
54
|
+
|
55
|
+
<HTML>
|
56
|
+
</HTML>
|
57
|
+
}.strip
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should parse the date" do
|
62
|
+
@host.date.class.should == Date
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not have a HTTP version" do
|
66
|
+
@host.http_version.should == '1.0'
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should not have a HTTP status code" do
|
70
|
+
@host.http_code.should == 200
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not have a HTTP status message" do
|
74
|
+
@host.http_status.should == 'OK'
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should not have HTTP response headers" do
|
78
|
+
@host.http_headers.should == {
|
79
|
+
'X-powered-by' => 'PHP/5.3.0',
|
80
|
+
'Transfer-encoding' => 'chunked',
|
81
|
+
'X-gpg-key' => 'http://chrislea.com/gpgkey.txt http://chrislea.com/gpgkey-virb.txt',
|
82
|
+
'Vary' => 'Cookie',
|
83
|
+
'X-ssh-key' => 'http://chrislea.com/sshkey.txt',
|
84
|
+
'Server' => 'nginx/0.8.24',
|
85
|
+
'Connection' => 'keep-alive',
|
86
|
+
'Date' => 'Fri, 20 Nov 2009 00:52:24 GMT',
|
87
|
+
'X-the-question' => 'Quis custodiet ipsos custodes?',
|
88
|
+
'Content-type' => 'text/html; charset=UTF-8',
|
89
|
+
'X-pingback' => 'http://chrislea.com/xmlrpc.php'
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should provide a server name" do
|
94
|
+
@host.server_name.should == 'nginx'
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should provide a server version" do
|
98
|
+
@host.server_version.should == '0.8.24'
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should provide transparent access to the HTTP response headers" do
|
102
|
+
@host.x_the_question.should == 'Quis custodiet ipsos custodes?'
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should raise a NoMethodError when accessing missing headers" do
|
106
|
+
lambda {
|
107
|
+
@host.lol
|
108
|
+
}.should raise_error(NoMethodError)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should not have a HTTP response body" do
|
112
|
+
@host.http_body.should == "<HTML>\n</HTML>"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'shodan/page'
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
shared_examples_for "Page has Hosts" do
|
6
|
+
it "should have hosts" do
|
7
|
+
@page.length.should_not == 0
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should have the maximum amount of hosts per page" do
|
11
|
+
@page.length.should == @query.results_per_page
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have IP addresses" do
|
15
|
+
@page.ips.should_not be_empty
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have valid IP addresses" do
|
19
|
+
@page.each_ip do |ip|
|
20
|
+
ip =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should have dates" do
|
25
|
+
@page.dates.should_not be_empty
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should have responses" do
|
29
|
+
@page.responses.should_not be_empty
|
30
|
+
end
|
31
|
+
end
|