ssh_scan_api 0.0.1.pre

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3c72ff89102b44b67171f9770f217b45b2df1d94
4
+ data.tar.gz: 2c114be00642cc13fba1af1522c30e0581c1b244
5
+ SHA512:
6
+ metadata.gz: 934ae8b24626af9c471702f7ff7156b1d019c3f95264e3421bd9d165e84549a1a8f6d4eae7c2191cc0cb9559bfedde4739c4b484ca7913d5449cb61cbdeda954
7
+ data.tar.gz: 25084d857389c656fb392a9efe62600431e85650f2b9f36b03c9e826e56a9af4ffc3bac39555582952f550026f634ca904852caea30c2e7139a8dcffd2dcc25d
@@ -0,0 +1,24 @@
1
+ *.gem
2
+ *.db
3
+ /coverage/
4
+
5
+ ## Documentation cache and generated files:
6
+ /.yardoc/
7
+ /_yardoc/
8
+ /doc/
9
+ /rdoc/
10
+
11
+ ## Environment normalization:
12
+ /.bundle/
13
+ /vendor/bundle
14
+ /lib/bundler/man/
15
+
16
+ # for a library or gem, you might want to ignore these files since the code is
17
+ # intended to run in multiple environments; otherwise, check them in:
18
+ Gemfile.lock
19
+ # .ruby-version
20
+ # .ruby-gemset
21
+
22
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
23
+ .rvmrc
24
+ gh-pages/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format documentation
@@ -0,0 +1,30 @@
1
+ language: ruby
2
+ matrix:
3
+ include:
4
+ - rvm: ruby-head
5
+ env:
6
+ - LABEL=unit_tests
7
+ services:
8
+ - mongodb
9
+ after_success:
10
+ - coveralls
11
+ - rvm: 2.3.0
12
+ env:
13
+ - LABEL=unit_tests
14
+ services:
15
+ - mongodb
16
+ - rvm: 2.2.0
17
+ env:
18
+ - LABEL=unit_tests
19
+ services:
20
+ - mongodb
21
+ - rvm: 2.1.3
22
+ env:
23
+ - LABEL=unit_tests
24
+ services:
25
+ - mongodb
26
+ - rvm: 2.0.0
27
+ env:
28
+ - LABEL=unit_tests
29
+ services:
30
+ - mongodb
@@ -0,0 +1,47 @@
1
+ # Contributing to ssh_scan_api
2
+
3
+ Thanks for your interest in contributing to ssh_scan_api.
4
+
5
+ If you could follow the following guidelines, you will make it much easier for
6
+ us to give feedback, help you find whatever problem you have and fix it.
7
+
8
+ ## Issues
9
+
10
+ If you have questions of any kind, or are unsure of how something works, please
11
+ [create an issue](https://github.com/claudijd/ssh_scan_api/issues/new).
12
+
13
+ Please try to answer the following questions in your issue:
14
+
15
+ - What did you do?
16
+ - What did you expect to happen?
17
+ - What happened instead?
18
+
19
+ If you have identified a bug, it would be very helpful if you could include a
20
+ way to replicate the bug. Ideally a failing test would be perfect, but even a
21
+ simple script demonstrating the error would suffice.
22
+
23
+ Feature requests are great and if submitted they will be considered for
24
+ inclusion, but sending a pull request is much more awesome.
25
+
26
+ ## Pull Requests
27
+
28
+ If you want your pull requests to be accepted, please follow the following guidelines:
29
+
30
+ - [**Add tests!**](http://rspec.info/) Your patch won't be accepted (or will be delayed) if it doesn't have tests.
31
+
32
+ - [**Document any change in behaviour**](http://yardoc.org/) Make sure the README and any other
33
+ relevant documentation are kept up-to-date.
34
+
35
+ - [**Create topic branches**](https://github.com/dchelimsky/rspec/wiki/Topic-Branches) Don't ask us to pull from your master branch.
36
+
37
+ - [**One pull request per feature**](https://help.github.com/articles/using-pull-requests) If you want to do more than one thing, send
38
+ multiple pull requests.
39
+
40
+ - [**Send coherent history**](http://stackoverflow.com/questions/6934752/git-combining-multiple-commits-before-pushing) Make sure each individual commit in your pull
41
+ request is meaningful. If you had to make multiple intermediate commits while
42
+ developing, please squash them before sending them to us.
43
+
44
+ - [**Follow coding conventions**](https://github.com/styleguide/ruby) The standard Ruby stuff, two spaces indent,
45
+ don't omit parens unless you have a good reason.
46
+
47
+ Thank you so much for contributing!
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'coveralls', require: false
4
+ gemspec
@@ -0,0 +1,59 @@
1
+ # ssh_scan_api
2
+
3
+ A web api to scale ssh_scan operations
4
+
5
+ ## Setup
6
+
7
+ To install and run as a gem, type:
8
+
9
+ ```bash
10
+ gem install ssh_scan_api
11
+ ssh_scan_api
12
+ ```
13
+
14
+ To install and run from source, type:
15
+
16
+ ```bash
17
+ # clone repo
18
+ git clone https://github.com/mozilla/ssh_scan_api.git
19
+ cd ssh_scan_api
20
+
21
+ # install rvm,
22
+ # you might have to provide root to install missing packages
23
+ gpg2 --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
24
+ curl -sSL https://get.rvm.io | bash -s stable
25
+
26
+ # install Ruby 2.3.1 with rvm,
27
+ # again, you might have to install missing devel packages
28
+ rvm install 2.3.1
29
+ rvm use 2.3.1
30
+
31
+ # resolve dependencies
32
+ gem install bundler
33
+ bundle install
34
+
35
+ ./bin/ssh_scan_api
36
+ ```
37
+
38
+ ## Rubies Supported
39
+
40
+ This project is integrated with [travis-ci](http://about.travis-ci.org/) and is regularly tested to work with the following rubies:
41
+
42
+ * [ruby-head](https://github.com/ruby/ruby)
43
+ * [2.3.0](https://github.com/ruby/ruby/tree/ruby_2_1)
44
+ * [2.2.0](https://github.com/ruby/ruby/tree/ruby_2_1)
45
+ * [2.1.3](https://github.com/ruby/ruby/tree/ruby_2_1)
46
+ * [2.1.0](https://github.com/ruby/ruby/tree/ruby_2_1)
47
+ * [2.0.0](https://github.com/ruby/ruby/tree/ruby_2_0_0)
48
+
49
+ To checkout the current build status for these rubies, click [here](https://travis-ci.org/#!/mozilla/ssh_scan).
50
+
51
+ ## Contributing
52
+
53
+ If you are interested in contributing to this project, please see [CONTRIBUTING.md](https://github.com/mozilla/ssh_scan/blob/master/CONTRIBUTING.md).
54
+
55
+ ## Credits
56
+
57
+ **Sources of Inspiration for ssh_scan**
58
+
59
+ - [**Mozilla OpenSSH Security Guide**](https://wiki.mozilla.org/Security/Guidelines/OpenSSH) - For providing a sane baseline policy recommendation for SSH configuration parameters (eg. Ciphers, MACs, and KexAlgos).
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rubygems/package_task'
4
+ require 'rspec'
5
+ require 'rspec/core'
6
+ require 'rspec/core/rake_task'
7
+ require 'bundler/setup'
8
+ require 'ssh_scan_api/version'
9
+
10
+ $:.unshift File.join(File.dirname(__FILE__), "lib")
11
+
12
+ require 'ssh_scan'
13
+
14
+ task :default => :spec
15
+
16
+ desc "Run all specs in spec directory"
17
+ RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), "../lib")
4
+
5
+ require 'ssh_scan_api'
6
+
7
+ # Get the api config from command-line or via an example location
8
+ config_file = ARGV[0] ||
9
+ File.join(
10
+ File.dirname(__FILE__),
11
+ "../config/api/config.yml"
12
+ )
13
+ opts = YAML.load_file(config_file)
14
+ opts["config_file"] = config_file
15
+
16
+ SSHScan::API.run!(opts)
@@ -0,0 +1,12 @@
1
+ #External Deps
2
+ require 'timeout'
3
+ require 'resolv'
4
+ require 'ssh_scan'
5
+
6
+ #Internal Deps
7
+ require 'ssh_scan_api/api'
8
+ require 'ssh_scan_api/version'
9
+ require 'ssh_scan_api/job_queue'
10
+ require 'ssh_scan_api/database'
11
+ require 'ssh_scan_api/stats'
12
+ require 'ssh_scan_api/authenticator'
@@ -0,0 +1,231 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/namespace'
3
+ require 'json'
4
+ require 'haml'
5
+ require 'secure_headers'
6
+ require 'thin'
7
+ require 'securerandom'
8
+ require 'ssh_scan'
9
+ require 'ssh_scan_api/job_queue'
10
+ require 'ssh_scan_api/database'
11
+
12
+ module SSHScan
13
+ class API < Sinatra::Base
14
+ if ENV['RACK_ENV'] == 'test'
15
+ configure do
16
+ set :job_queue, SSHScan::JobQueue.new()
17
+ set :authentication, false
18
+ config_file = File.join(Dir.pwd, "./config/api/config.yml")
19
+ opts = YAML.load_file(config_file)
20
+ opts["config_file"] = config_file
21
+ set :db, SSHScan::Database.from_hash(opts)
22
+ end
23
+ end
24
+
25
+ # Configure all the secure headers we want to use
26
+ use SecureHeaders::Middleware
27
+ SecureHeaders::Configuration.default do |config|
28
+ config.cookies = {
29
+ secure: true, # mark all cookies as "Secure"
30
+ httponly: true, # mark all cookies as "HttpOnly"
31
+ }
32
+ config.hsts = "max-age=31536000; includeSubdomains; preload"
33
+ config.x_frame_options = "DENY"
34
+ config.x_content_type_options = "nosniff"
35
+ config.x_xss_protection = "1; mode=block"
36
+ config.x_download_options = "noopen"
37
+ config.x_permitted_cross_domain_policies = "none"
38
+ config.referrer_policy = "no-referrer"
39
+ config.csp = {
40
+ default_src: ["'none'"],
41
+ frame_ancestors: ["'none'"],
42
+ upgrade_insecure_requests: true, # see https://www.w3.org/TR/upgrade-insecure-requests/
43
+ }
44
+ end
45
+
46
+ register Sinatra::Namespace
47
+
48
+ before do
49
+ headers "Server" => "ssh_scan_api"
50
+ headers "Cache-control" => "no-store"
51
+ headers "Pragma" => "no-cache"
52
+ end
53
+
54
+ helpers do
55
+ def cache_valid?(start_time)
56
+ (Time.now - Time.parse(start_time.to_s)) / (60 * 60 * 24) < 1
57
+ end
58
+ end
59
+
60
+ # Custom 404 handling
61
+ not_found do
62
+ content_type "text/plain"
63
+ "Invalid request, see API documentation here: \
64
+ https://github.com/mozilla/ssh_scan_api/wiki/ssh_scan-Web-API\n"
65
+ end
66
+
67
+ get '/' do
68
+ content_type "text/plain"
69
+ "See API documentation here: \
70
+ https://github.com/mozilla/ssh_scan_api/wiki/ssh_scan-Web-API\n"
71
+ end
72
+
73
+ get '/robots.txt' do
74
+ content_type "text/plain"
75
+ "User-agent: *\nDisallow: /\n"
76
+ end
77
+
78
+ get '/contribute.json' do
79
+ content_type :json
80
+ SSHScan::Constants::CONTRIBUTE_JSON.to_json
81
+ end
82
+
83
+ get '/__version__' do
84
+ {
85
+ :ssh_scan_version => SSHScan::VERSION,
86
+ :api_version => SSHScan::API_VERSION,
87
+ }.to_json
88
+ end
89
+
90
+ namespace "/api/v1" do
91
+ before do
92
+ content_type :json
93
+ if settings.authentication == true
94
+ token = request.env['HTTP_SSH_SCAN_AUTH_TOKEN']
95
+
96
+ # If a token is not provided, only localhost can proceed
97
+ if token.nil? && request.ip != "127.0.0.1"
98
+ halt '{"error" : "authentication failure"}'
99
+ end
100
+
101
+ # If a token is provided, it must be valid to proceed
102
+ if token && settings.authenticator.valid_token?(token) == false
103
+ halt '{"error" : "authentication failure"}'
104
+ end
105
+ end
106
+ end
107
+
108
+ post '/scan' do
109
+ options = {
110
+ :sockets => [],
111
+ :policy => File.join(Dir.pwd,
112
+ '/config/policies/mozilla_modern.yml'),
113
+ :timeout => 2,
114
+ :verbosity => nil,
115
+ #:fingerprint_database => "fingerprints.db",
116
+ }
117
+ options[:sockets] <<
118
+ "#{params[:target]}:#{params[:port] ? params[:port] : "22"}"
119
+ options[:policy_file] = options[:policy]
120
+ options[:force] = params[:force] ? params[:force] : false
121
+
122
+ unless options[:force] == 'true'
123
+ available_result = settings.db.fetch_cached_result(params)
124
+ unless available_result.nil?
125
+ if cache_valid?(available_result[:start_time])
126
+ return {
127
+ uuid: available_result[:uuid]
128
+ }.to_json
129
+ end
130
+ end
131
+ end
132
+ options[:uuid] = SecureRandom.uuid
133
+ settings.job_queue.add(options)
134
+ {
135
+ uuid: options[:uuid]
136
+ }.to_json
137
+ end
138
+
139
+ get '/scan/results' do
140
+ uuid = params[:uuid]
141
+ return {"scan" => "not found"}.to_json if uuid.nil? || uuid.empty?
142
+ result = settings.db.find_scan_result(uuid)
143
+ return {"scan" => "not found"}.to_json if result.nil?
144
+ return result.to_json
145
+ end
146
+
147
+ post '/scan/results/delete' do
148
+ uuid = params[:uuid]
149
+
150
+ if uuid.nil? || uuid.empty?
151
+ return {"deleted" => "false"}.to_json
152
+ else
153
+ scan = settings.db.find_scan_result(uuid)
154
+ if scan.empty?
155
+ return {"deleted" => "false"}.to_json
156
+ else
157
+ settings.db.delete_scan(uuid)
158
+ return {"deleted" => "true"}.to_json
159
+ end
160
+ end
161
+ end
162
+
163
+ get '/scan/results/delete/all' do
164
+ settings.db.delete_all
165
+ end
166
+
167
+ get '/work' do
168
+ worker_id = params[:worker_id]
169
+ logger.warn("Worker #{worker_id} polls for Job")
170
+ job = settings.job_queue.next
171
+ if job.nil?
172
+ logger.warn("Worker #{worker_id} didn't get any work")
173
+ {"work" => false}.to_json
174
+ else
175
+ logger.warn("Worker #{worker_id} got job #{job[:uuid]}")
176
+ {"work" => job}.to_json
177
+ end
178
+ end
179
+
180
+ post '/work/results/:worker_id/:uuid' do
181
+ worker_id = params['worker_id']
182
+ uuid = params['uuid']
183
+ result = JSON.parse(request.body.first).first
184
+ socket = {}
185
+ socket[:target] = result['ip']
186
+ socket[:port] = result['port']
187
+
188
+ if worker_id.empty? || uuid.empty?
189
+ return {"accepted" => "false"}.to_json
190
+ end
191
+ settings.stats.new_scan_request
192
+ settings.db.add_scan(worker_id, uuid, result, socket)
193
+ end
194
+
195
+ get '/stats' do
196
+ settings.stats.get_stats(settings.job_queue.size)
197
+ end
198
+
199
+ get '/__lbheartbeat__' do
200
+ {
201
+ :status => "OK",
202
+ :message => "Keep sending requests. I am still alive."
203
+ }.to_json
204
+ end
205
+ end
206
+
207
+ def self.run!(options = {}, &block)
208
+ set options
209
+
210
+ configure do
211
+ enable :logging
212
+ set :bind, options["bind"] || '127.0.0.1'
213
+ set :server, "thin"
214
+ set :logger, Logger.new(STDOUT)
215
+ set :job_queue, JobQueue.new()
216
+ set :db, SSHScan::Database.from_hash(options)
217
+ set :results, {}
218
+ set :stats, SSHScan::Stats.new
219
+ set :authentication, options["authentication"]
220
+ set :authenticator, SSHScan::Authenticator.from_config_file(
221
+ options["config_file"]
222
+ )
223
+ end
224
+
225
+ super do |server|
226
+ # No SSL on app, SSL termination happens in nginx for a prod deployment
227
+ server.ssl = false
228
+ end
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,30 @@
1
+ module SSHScan
2
+ class Authenticator
3
+ attr_reader :config
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def self.from_config_file(config_file)
10
+ opts = YAML.load_file(config_file)
11
+ SSHScan::Authenticator.new(opts)
12
+ end
13
+
14
+ def valid_token?(token)
15
+ if @config["users"]
16
+ @config["users"].each do |user|
17
+ return true if user["token"] == token
18
+ end
19
+ end
20
+
21
+ if @config["workers"]
22
+ @config["workers"].each do |worker|
23
+ return true if worker["token"] == token
24
+ end
25
+ end
26
+
27
+ return false
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,61 @@
1
+ require 'ssh_scan_api/database/mongo'
2
+ require 'ssh_scan_api/database/sqlite'
3
+
4
+ module SSHScan
5
+ class Database
6
+ attr_reader :database
7
+
8
+ # @param [SSHScan::Database::MongoDb, SSHScan::Database::SQLite] database
9
+ def initialize(database)
10
+ @database = database
11
+ end
12
+
13
+ # @param [Hash] opts
14
+ # @return [SSHScan::Database]
15
+ def self.from_hash(opts)
16
+ database_options = opts["database"]
17
+
18
+ # Figure out what database object to load
19
+ case database_options["type"]
20
+ when "sqlite"
21
+ database = SSHScan::DB::SQLite.from_hash(database_options)
22
+ when "mongodb"
23
+ database = SSHScan::DB::MongoDb.from_hash(database_options)
24
+ else
25
+ raise "Database type of #{database_options[:type].class} not supported"
26
+ end
27
+
28
+ SSHScan::Database.new(database)
29
+ end
30
+
31
+ # @param [String] worker_id
32
+ # @param [String] uuid
33
+ # @param [Hash] result
34
+ # @return [Nil]
35
+ def add_scan(worker_id, uuid, result, socket)
36
+ @database.add_scan(worker_id, uuid, result, socket)
37
+ return nil
38
+ end
39
+
40
+ # @param [String] uuid
41
+ # @return [Nil]
42
+ def delete_scan(uuid)
43
+ @database.delete_scan(uuid)
44
+ end
45
+
46
+ # @return [Nil]
47
+ def delete_all
48
+ @database.delete_all
49
+ end
50
+
51
+ # @return [Hash] result
52
+ def find_scan_result(uuid)
53
+ @database.find_scan_result(uuid)
54
+ end
55
+
56
+ # @return [Hash] result
57
+ def fetch_cached_result(socket)
58
+ @database.fetch_cached_result(socket)
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,67 @@
1
+ require 'mongo'
2
+
3
+ Mongo::Logger.logger.level = ::Logger::FATAL
4
+
5
+ module SSHScan
6
+ module DB
7
+ class MongoDb
8
+ attr_reader :collection
9
+
10
+ def initialize(client)
11
+ @client = client
12
+ @collection = @client[:ssh_scan]
13
+ end
14
+
15
+ # Helps us create a SSHScan::DB::MongoDB object with a hash
16
+ def self.from_hash(opts)
17
+ name = opts["name"]
18
+ server = opts["server"]
19
+ port = opts["port"]
20
+ socket = server + ":" + port.to_s
21
+
22
+ client = Mongo::Client.new([socket], :database => name)
23
+ return SSHScan::DB::MongoDb.new(client)
24
+ end
25
+
26
+ # @param [String] worker_id
27
+ # @param [String] uuid
28
+ # @param [Hash] result
29
+ def add_scan(worker_id, uuid, result, socket)
30
+ @collection.insert_one("uuid" => uuid,
31
+ "target" => socket[:target],
32
+ "port" => socket[:port],
33
+ "scan" => result,
34
+ "worker_id" => worker_id)
35
+ end
36
+
37
+ def delete_scan(uuid)
38
+ @collection.delete_one(:uuid => uuid)
39
+ end
40
+
41
+ def delete_all
42
+ @collection.delete_many({})
43
+ end
44
+
45
+ # LEFT OFF HERE: the results of this method should be the exact same format as with SQLite
46
+ def find_scan_result(uuid)
47
+ @collection.find(:uuid => uuid).each do |doc|
48
+ return doc[:scan].to_hash
49
+ end
50
+
51
+ return nil
52
+ end
53
+
54
+ def fetch_cached_result(socket)
55
+ results = @collection.find(:target => socket[:target], :port => socket[:port])
56
+ results = results.skip(results.count() - 1)
57
+ return nil if results.count.zero?
58
+ result = {}
59
+ results.each do |result|
60
+ result[:uuid] = result[:uuid]
61
+ result[:start_time] = result[:scan][:start_time]
62
+ return result
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,91 @@
1
+ require 'sqlite3'
2
+ require 'json'
3
+
4
+ module SSHScan
5
+ module DB
6
+ class SQLite
7
+ attr_reader :database
8
+
9
+ def initialize(database)
10
+ @database = database # the SQLite database object
11
+ end
12
+
13
+ # Helps us create a SSHScan::DB::SQLite object with a hash
14
+ def self.from_hash(opts)
15
+ file_name = opts["file"]
16
+
17
+ if File.exist?(file_name)
18
+ db = ::SQLite3::Database.open(file_name)
19
+ else
20
+ db = ::SQLite3::Database.new(file_name)
21
+ end
22
+
23
+ #Check to see if the schema is setup or not
24
+ result = db.execute <<-SQL
25
+ SELECT count(*) FROM sqlite_master WHERE type = 'table' AND name = 'ssh_scan';
26
+ SQL
27
+
28
+ # If not, create it
29
+ if result == [[0]]
30
+ # Create the schema for the database
31
+ db.execute <<-SQL
32
+ create table ssh_scan (
33
+ uuid varchar(100),
34
+ target varchar(100),
35
+ port varchar(100),
36
+ result json,
37
+ worker_id varchar(100)
38
+ );
39
+ SQL
40
+ end
41
+
42
+ return SSHScan::DB::SQLite.new(db)
43
+ end
44
+
45
+ def size
46
+ count = @database.execute("select count() from ssh_scan")
47
+ return count
48
+ end
49
+
50
+ def add_scan(worker_id, uuid, result, socket)
51
+ @database.execute "insert into ssh_scan values ( ? , ? , ? , ? , ? )",
52
+ [uuid, socket[:target], socket[:port],
53
+ result.to_json, worker_id]
54
+ end
55
+
56
+ def delete_scan(uuid)
57
+ @database.execute(
58
+ "delete from ssh_scan where uuid = ?",
59
+ uuid
60
+ )
61
+ end
62
+
63
+ def delete_all
64
+ @database.execute("delete from ssh_scan")
65
+ end
66
+
67
+ def find_scan_result(uuid)
68
+ @database.execute(
69
+ "select * from ssh_scan where uuid like ( ? )",
70
+ uuid
71
+ ) do |row|
72
+ return JSON.parse(row[3])
73
+ end
74
+ return nil
75
+ end
76
+
77
+ def fetch_cached_result(socket)
78
+ result = {}
79
+ results = @database.execute(
80
+ "select uuid, result from ssh_scan
81
+ where target like ( ? ) and port like ( ? )",
82
+ [socket[:target], socket[:port]]
83
+ )
84
+ return nil if results == []
85
+ result[:uuid] = results[result.length()-1][0]
86
+ result[:start_time] = JSON.parse(results[result.length()-1][1])["start_time"]
87
+ return result
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,24 @@
1
+ module SSHScan
2
+ class JobQueue
3
+ def initialize
4
+ @queue = Queue.new
5
+ end
6
+
7
+ # @param [String] a socket we want to scan (Example: "192.168.1.1:22")
8
+ # @return [nil]
9
+ def add(socket)
10
+ @queue.push(socket)
11
+ end
12
+
13
+ # @return [String] a socket we want to scan (Example: "192.168.1.1:22")
14
+ def next
15
+ return nil if @queue.empty?
16
+ @queue.pop
17
+ end
18
+
19
+ # @return [FixNum] the number of jobs in the JobQueue
20
+ def size
21
+ @queue.size
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ module SSHScan
2
+ class Stats
3
+ def initialize
4
+ @requests = []
5
+ end
6
+
7
+ def new_scan_request
8
+ @requests << Time.now
9
+ # Purges the request queue of old requests
10
+ purge_old_requests
11
+ end
12
+
13
+ def get_stats(queue_size)
14
+ {
15
+ :items_queued => queue_size,
16
+ :avg_requests_per_min => requests_avg_per,
17
+ :requests_per_min => requests_per
18
+ }.to_json
19
+ end
20
+
21
+ def size
22
+ @requests.size
23
+ end
24
+
25
+ # Purges the request queue of old requests, so we don't run the API out of memory
26
+ # @param [Fixnum] seconds
27
+ def purge_old_requests(seconds = 60)
28
+ @requests.delete_if {|request_time| request_time < Time.now - seconds}
29
+ end
30
+
31
+ # Determines the number of requests in a second-based
32
+ # time period (up to 60 seconds)
33
+ # @param [Fixnum] seconds
34
+ # @return [Fixnum] request per time period
35
+ def requests_per(seconds = 60)
36
+ requests_per = 0
37
+ past_time = Time.now - seconds
38
+
39
+ @requests.each do |request_time|
40
+ requests_per += 1 if request_time >= past_time
41
+ end
42
+
43
+ return requests_per
44
+ end
45
+
46
+ # Determines average requests per time period
47
+ def requests_avg_per(seconds = 60)
48
+ requests_per = requests_per(seconds)
49
+ requests_per / seconds.to_f
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,3 @@
1
+ module SSHScan
2
+ API_VERSION = '0.0.1.pre'
3
+ end
@@ -0,0 +1,41 @@
1
+ $: << "lib"
2
+ require 'ssh_scan_api/version'
3
+ require 'date'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ssh_scan_api'
7
+ s.version = SSHScan::API_VERSION
8
+ s.authors = ["Harsh Vardhan", "Rishabh Saxena", "Ashish Gaurav", "Jonathan Claudius" ]
9
+ s.date = Date.today.to_s
10
+ s.email = 'jclaudius@mozilla.com'
11
+ s.platform = Gem::Platform::RUBY
12
+ s.files = Dir.glob("lib/**/*") +
13
+ Dir.glob("bin/**/*") +
14
+ [".gitignore",
15
+ ".rspec",
16
+ ".travis.yml",
17
+ "CONTRIBUTING.md",
18
+ "Gemfile",
19
+ "Rakefile",
20
+ "README.md",
21
+ "ssh_scan_api.gemspec"]
22
+ s.license = "ruby"
23
+ s.require_paths = ["lib"]
24
+ s.executables = s.files.grep(%r{^bin/[^\/]+$}) { |f| File.basename(f) }
25
+ s.summary = 'ssh_scan API'
26
+ s.description = 'An API for performing SSH scans'
27
+ s.homepage = 'http://rubygems.org/gems/ssh_scan_api'
28
+
29
+ s.add_dependency('ssh_scan', '0.0.17.pre')
30
+ s.add_dependency('mongo')
31
+ s.add_dependency('sqlite3')
32
+ s.add_dependency('sinatra')
33
+ s.add_dependency('sinatra-contrib')
34
+ s.add_dependency('thin')
35
+ s.add_development_dependency('rack-test')
36
+ s.add_development_dependency('pry')
37
+ s.add_development_dependency('rspec', '~> 3.0')
38
+ s.add_development_dependency('rspec-its', '~> 1.2')
39
+ s.add_development_dependency('rake', '~> 10.3')
40
+ s.add_development_dependency('rubocop')
41
+ end
metadata ADDED
@@ -0,0 +1,233 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ssh_scan_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.pre
5
+ platform: ruby
6
+ authors:
7
+ - Harsh Vardhan
8
+ - Rishabh Saxena
9
+ - Ashish Gaurav
10
+ - Jonathan Claudius
11
+ autorequire:
12
+ bindir: bin
13
+ cert_chain: []
14
+ date: 2017-03-02 00:00:00.000000000 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: ssh_scan
18
+ requirement: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - '='
21
+ - !ruby/object:Gem::Version
22
+ version: 0.0.17.pre
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - '='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.0.17.pre
30
+ - !ruby/object:Gem::Dependency
31
+ name: mongo
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: sqlite3
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ - !ruby/object:Gem::Dependency
59
+ name: sinatra
60
+ requirement: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ - !ruby/object:Gem::Dependency
73
+ name: sinatra-contrib
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ - !ruby/object:Gem::Dependency
87
+ name: thin
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ type: :runtime
94
+ prerelease: false
95
+ version_requirements: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ - !ruby/object:Gem::Dependency
101
+ name: rack-test
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ type: :development
108
+ prerelease: false
109
+ version_requirements: !ruby/object:Gem::Requirement
110
+ requirements:
111
+ - - ">="
112
+ - !ruby/object:Gem::Version
113
+ version: '0'
114
+ - !ruby/object:Gem::Dependency
115
+ name: pry
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ type: :development
122
+ prerelease: false
123
+ version_requirements: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ - !ruby/object:Gem::Dependency
129
+ name: rspec
130
+ requirement: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - "~>"
133
+ - !ruby/object:Gem::Version
134
+ version: '3.0'
135
+ type: :development
136
+ prerelease: false
137
+ version_requirements: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: '3.0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rspec-its
144
+ requirement: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - "~>"
147
+ - !ruby/object:Gem::Version
148
+ version: '1.2'
149
+ type: :development
150
+ prerelease: false
151
+ version_requirements: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: '1.2'
156
+ - !ruby/object:Gem::Dependency
157
+ name: rake
158
+ requirement: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - "~>"
161
+ - !ruby/object:Gem::Version
162
+ version: '10.3'
163
+ type: :development
164
+ prerelease: false
165
+ version_requirements: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - "~>"
168
+ - !ruby/object:Gem::Version
169
+ version: '10.3'
170
+ - !ruby/object:Gem::Dependency
171
+ name: rubocop
172
+ requirement: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: '0'
177
+ type: :development
178
+ prerelease: false
179
+ version_requirements: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ description: An API for performing SSH scans
185
+ email: jclaudius@mozilla.com
186
+ executables:
187
+ - ssh_scan_api
188
+ extensions: []
189
+ extra_rdoc_files: []
190
+ files:
191
+ - ".gitignore"
192
+ - ".rspec"
193
+ - ".travis.yml"
194
+ - CONTRIBUTING.md
195
+ - Gemfile
196
+ - README.md
197
+ - Rakefile
198
+ - bin/ssh_scan_api
199
+ - lib/ssh_scan_api.rb
200
+ - lib/ssh_scan_api/api.rb
201
+ - lib/ssh_scan_api/authenticator.rb
202
+ - lib/ssh_scan_api/database.rb
203
+ - lib/ssh_scan_api/database/mongo.rb
204
+ - lib/ssh_scan_api/database/sqlite.rb
205
+ - lib/ssh_scan_api/job_queue.rb
206
+ - lib/ssh_scan_api/stats.rb
207
+ - lib/ssh_scan_api/version.rb
208
+ - ssh_scan_api.gemspec
209
+ homepage: http://rubygems.org/gems/ssh_scan_api
210
+ licenses:
211
+ - ruby
212
+ metadata: {}
213
+ post_install_message:
214
+ rdoc_options: []
215
+ require_paths:
216
+ - lib
217
+ required_ruby_version: !ruby/object:Gem::Requirement
218
+ requirements:
219
+ - - ">="
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ required_rubygems_version: !ruby/object:Gem::Requirement
223
+ requirements:
224
+ - - ">"
225
+ - !ruby/object:Gem::Version
226
+ version: 1.3.1
227
+ requirements: []
228
+ rubyforge_project:
229
+ rubygems_version: 2.6.2
230
+ signing_key:
231
+ specification_version: 4
232
+ summary: ssh_scan API
233
+ test_files: []