ssh_scan_api 0.0.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []