nexpose_sccm 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +14 -0
- data/MIT-LICENSE +21 -0
- data/README.md +212 -0
- data/bin/encrypt_settings.rb +135 -0
- data/bin/nexpose_sccm +69 -0
- data/bin/vuln_ids.csv +7 -0
- data/conf/logging.yml +8 -0
- data/conf/nexpose.yml +8 -0
- data/conf/postgres.yml +8 -0
- data/conf/queries.yml +87 -0
- data/conf/sccm.yml +30 -0
- data/conf/secret.yml +4 -0
- data/lib/nexpose_sccm.rb +294 -0
- data/lib/nexpose_sccm/collection.rb +42 -0
- data/lib/nexpose_sccm/connection.rb +165 -0
- data/lib/nexpose_sccm/data_source.rb +67 -0
- data/lib/nexpose_sccm/deployment_package.rb +37 -0
- data/lib/nexpose_sccm/device.rb +11 -0
- data/lib/nexpose_sccm/helpers/nexpose_helper.rb +65 -0
- data/lib/nexpose_sccm/helpers/pg_helper.rb +26 -0
- data/lib/nexpose_sccm/powershell.rb +111 -0
- data/lib/nexpose_sccm/remediation_item.rb +5 -0
- data/lib/nexpose_sccm/software_update_group.rb +54 -0
- data/lib/nexpose_sccm/utilities/nx_logger.rb +171 -0
- data/lib/nexpose_sccm/utilities/utility_config.rb +59 -0
- data/lib/nexpose_sccm/version.rb +5 -0
- data/lib/nexpose_sccm/wql.rb +35 -0
- data/nexpose_sccm.gemspec +35 -0
- metadata +128 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'winrm'
|
2
|
+
require_relative 'powershell'
|
3
|
+
|
4
|
+
module NexposeSCCM
|
5
|
+
# Configuration settings for creating a Software Update Group
|
6
|
+
#
|
7
|
+
# * *Args* :
|
8
|
+
# - +name+ - A String identifying the display name of the Software Update Group
|
9
|
+
# - +description+ - A String identifying the description of the Software Update Group
|
10
|
+
# - +ci_ids+ - An Array identifying the list of ci_ids to add to a Software Update Group
|
11
|
+
#
|
12
|
+
class SoftwareUpdateGroup
|
13
|
+
attr_accessor :name, :description, :ci_ids
|
14
|
+
|
15
|
+
def initialize(name, description, ci_ids, ci_id=nil)
|
16
|
+
@name = name
|
17
|
+
@description = description
|
18
|
+
@ci_ids = ci_ids
|
19
|
+
@ci_id = ci_id
|
20
|
+
end
|
21
|
+
|
22
|
+
def save(conn)
|
23
|
+
if @ci_id.nil?
|
24
|
+
if @ci_ids.length > 0
|
25
|
+
Powershell.run(conn.conn,
|
26
|
+
:create_software_update_group,
|
27
|
+
conn.namespace,
|
28
|
+
conn.host,
|
29
|
+
@name,
|
30
|
+
@description,
|
31
|
+
@ci_ids.join(','))
|
32
|
+
else
|
33
|
+
NexposeSCCM.logger.debug('No CI_IDs to add to Software Update Group, going to next...')
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
else
|
37
|
+
if @ci_ids.length > 0
|
38
|
+
Powershell.run(conn.conn,
|
39
|
+
:update_software_update_group,
|
40
|
+
conn.namespace,
|
41
|
+
conn.host,
|
42
|
+
@ci_id,
|
43
|
+
@ci_ids.join(','))
|
44
|
+
else
|
45
|
+
Powershell.run(conn.conn,
|
46
|
+
:update_software_update_group_empty,
|
47
|
+
conn.namespace,
|
48
|
+
conn.host,
|
49
|
+
@ci_id)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
require 'net/http'
|
4
|
+
require 'singleton'
|
5
|
+
|
6
|
+
module NexposeSCCM
|
7
|
+
class NxLogger
|
8
|
+
include Singleton
|
9
|
+
LOG_PATH = "../logs/rapid7_%s.log"
|
10
|
+
KEY_FORMAT = "external.integration.%s"
|
11
|
+
PRODUCT_FORMAT = "%s_%s"
|
12
|
+
|
13
|
+
DEFAULT_LOG = 'integration'
|
14
|
+
PRODUCT_RANGE = 4..30
|
15
|
+
KEY_RANGE = 3..15
|
16
|
+
|
17
|
+
ENDPOINT = '/data/external/statistic/'
|
18
|
+
|
19
|
+
def initialize()
|
20
|
+
create_calls
|
21
|
+
@logger_file = get_log_path @product
|
22
|
+
setup_logging(true, 'info')
|
23
|
+
end
|
24
|
+
|
25
|
+
def setup_statistics_collection(vendor, product_name, gem_version)
|
26
|
+
begin
|
27
|
+
@statistic_key = get_statistic_key vendor
|
28
|
+
@product = get_product product_name, gem_version
|
29
|
+
rescue => e
|
30
|
+
#Continue
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def setup_logging(enabled,
|
35
|
+
log_level = 'info',
|
36
|
+
stdout=false,
|
37
|
+
rotation='weekly',
|
38
|
+
log_size=nil)
|
39
|
+
@stdout = stdout
|
40
|
+
|
41
|
+
log_message('Logging disabled.') unless enabled || @log.nil?
|
42
|
+
@enabled = enabled
|
43
|
+
return unless @enabled
|
44
|
+
|
45
|
+
@logger_file = get_log_path @product
|
46
|
+
|
47
|
+
require 'logger'
|
48
|
+
directory = File.dirname(@logger_file)
|
49
|
+
FileUtils.mkdir_p(directory) unless File.directory?(directory)
|
50
|
+
io = IO.for_fd(IO.sysopen(@logger_file, 'a'), 'a')
|
51
|
+
io.autoclose = false
|
52
|
+
io.sync = true
|
53
|
+
@log = Logger.new(io, rotation, log_size)
|
54
|
+
@log.level = if log_level.to_s.casecmp('info') == 0
|
55
|
+
Logger::INFO
|
56
|
+
else
|
57
|
+
Logger::DEBUG
|
58
|
+
end
|
59
|
+
log_message("Logging enabled at level <#{log_level}>")
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_calls
|
63
|
+
levels = [:info, :debug, :error, :warn]
|
64
|
+
levels.each do |level|
|
65
|
+
method_name = "log_#{level.to_s}_message"
|
66
|
+
define_singleton_method(method_name) do |message|
|
67
|
+
puts message if @stdout
|
68
|
+
@log.send(level, message) unless !@enabled || @log.nil?
|
69
|
+
end
|
70
|
+
singleton_class.send(:alias_method, level, method_name.to_sym)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def log_message(message)
|
75
|
+
log_info_message message
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_stat_message(message)
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_log_path(product)
|
82
|
+
product.downcase! unless product.nil?
|
83
|
+
File.join(File.dirname(__FILE__), LOG_PATH % (product || DEFAULT_LOG))
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_statistic_key(vendor)
|
87
|
+
if vendor.nil? || vendor.length < KEY_RANGE.min
|
88
|
+
log_stat_message("Vendor length is below minimum of <#{KEY_RANGE}>")
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
|
92
|
+
vendor.gsub!('-', '_')
|
93
|
+
vendor.slice! vendor.rindex('_') until vendor.count('_') <= 1
|
94
|
+
|
95
|
+
vendor.delete! "^A-Za-z0-9\_"
|
96
|
+
|
97
|
+
KEY_FORMAT % vendor[0...KEY_RANGE.max].downcase
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_product(product, version)
|
101
|
+
return nil if ((product.nil? || product.empty?) ||
|
102
|
+
(version.nil? || version.empty?))
|
103
|
+
|
104
|
+
product.gsub!('-', '_')
|
105
|
+
product.slice! product.rindex('_') until product.count('_') <= 1
|
106
|
+
|
107
|
+
product.delete! "^A-Za-z0-9\_"
|
108
|
+
version.delete! "^A-Za-z0-9\.\-"
|
109
|
+
|
110
|
+
product = (PRODUCT_FORMAT % [product, version])[0...PRODUCT_RANGE.max]
|
111
|
+
|
112
|
+
product.slice! product.rindex(/[A-Z0-9]/i)+1..-1
|
113
|
+
|
114
|
+
if product.length < PRODUCT_RANGE.min
|
115
|
+
log_stat_message("Product length below minimum <#{PRODUCT_RANGE.min}>.")
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
product.downcase
|
119
|
+
end
|
120
|
+
|
121
|
+
def generate_payload(statistic_value='')
|
122
|
+
product_name, separator, version = @product.to_s.rpartition('_')
|
123
|
+
payload_value = {'version' => version}.to_json
|
124
|
+
|
125
|
+
payload = {'statistic-key' => @statistic_key.to_s,
|
126
|
+
'statistic-value' => payload_value,
|
127
|
+
'product' => product_name}
|
128
|
+
JSON.generate(payload)
|
129
|
+
end
|
130
|
+
|
131
|
+
def send(nexpose_address, nexpose_port, session_id, payload)
|
132
|
+
header = {'Content-Type' => 'application/json',
|
133
|
+
'nexposeCCSessionID' => session_id,
|
134
|
+
'Cookie' => "nexposeCCSessionID=#{session_id}"}
|
135
|
+
req = Net::HTTP::Put.new(ENDPOINT, header)
|
136
|
+
req.body = payload
|
137
|
+
http_instance = Net::HTTP.new(nexpose_address, nexpose_port)
|
138
|
+
http_instance.use_ssl = true
|
139
|
+
http_instance.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
140
|
+
response = http_instance.start { |http| http.request(req) }
|
141
|
+
log_stat_message "Received code #{response.code} from Nexpose console."
|
142
|
+
log_stat_message "Received message #{response.msg} from Nexpose console."
|
143
|
+
log_stat_message 'Finished sending statistics data to Nexpose.'
|
144
|
+
|
145
|
+
response.code
|
146
|
+
end
|
147
|
+
|
148
|
+
def on_connect(nexpose_address, nexpose_port, session_id, value)
|
149
|
+
log_stat_message 'Sending statistics data to Nexpose'
|
150
|
+
|
151
|
+
if @product.nil? || @statistic_key.nil?
|
152
|
+
log_stat_message('Invalid product name and/or statistics key.')
|
153
|
+
log_stat_message('Statistics collection not enabled.')
|
154
|
+
return
|
155
|
+
end
|
156
|
+
|
157
|
+
begin
|
158
|
+
payload = generate_payload value
|
159
|
+
send(nexpose_address, nexpose_port, session_id, payload)
|
160
|
+
rescue => e
|
161
|
+
#Let the program continue
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
#Used by net library for debugging
|
166
|
+
def <<(value)
|
167
|
+
log_debug_message(value)
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module UtilityConfig
|
2
|
+
def self.load_config(settings_locations, settings_dir=nil)
|
3
|
+
# Load settings.yml from -s option or parse known locations for configuration file
|
4
|
+
begin
|
5
|
+
settings_dir = settings_dir.nil? ? settings_locations : [settings_dir]
|
6
|
+
settings = nil
|
7
|
+
|
8
|
+
settings_dir.each do |dir|
|
9
|
+
Dir.glob(File.join(dir, '*.yml')) do |yml_file|
|
10
|
+
tmp_settings = YAML::load_file(yml_file)
|
11
|
+
next if tmp_settings.nil?
|
12
|
+
settings = settings.nil? ? tmp_settings.dup : settings.merge(tmp_settings.to_h)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
rescue Exception => e
|
16
|
+
abort("Could not parse or load settings file: #{e}")
|
17
|
+
end
|
18
|
+
settings
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.load_encrypt(settings)
|
22
|
+
# Load encrypted settings if defined
|
23
|
+
begin
|
24
|
+
if settings[:secret]
|
25
|
+
priv_key = OpenSSL::PKey::RSA.new(File.read(File.join(settings[:secret][:encrypt_key_dir], 'rsa_key')))
|
26
|
+
dec_buf = priv_key.private_decrypt(Base64.decode64(File.read(settings[:secret][:encrypt_cred_file])))
|
27
|
+
YAML::load(dec_buf).each do |key,value|
|
28
|
+
value.each do |k,v|
|
29
|
+
if v.class == String
|
30
|
+
settings[key][k] = v
|
31
|
+
elsif v.class == Hash
|
32
|
+
v.each do |k2,v2|
|
33
|
+
settings[key][k][k2] = v2
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
return settings
|
40
|
+
rescue Exception => e
|
41
|
+
abort("Could not parse or load encrypted file #{settings[:secret][:encrypt_cred_file]} : #{e}")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.save_encrypt(settings_locations, settings_dir=nil)
|
46
|
+
begin
|
47
|
+
settings_dir = settings_dir.nil? ? settings_locations : [settings_dir]
|
48
|
+
settings_dir.each do |dir|
|
49
|
+
Dir.glob(File.join(dir, '*.yml')) do |yml_file|
|
50
|
+
text = File.read(yml_file)
|
51
|
+
new_contents = text.gsub(/:\s+[\'\"]\(enc\).*[\'\"][\r\n]*/, ": 'secret'\n")
|
52
|
+
File.open(yml_file, "w") {|file| file.puts new_contents}
|
53
|
+
end
|
54
|
+
end
|
55
|
+
rescue Exception => e
|
56
|
+
abort("Could not parse or save settings file: #{e}")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'winrm'
|
2
|
+
|
3
|
+
module NexposeSCCM
|
4
|
+
module Wql
|
5
|
+
|
6
|
+
@queries = {
|
7
|
+
:get_ci_ids => "SELECT ci_id FROM SMS_SoftwareUpdate WHERE CI_UniqueID=\"%s\"",
|
8
|
+
:get_sups => "SELECT ci_id, localizeddisplayname, localizeddescription FROM SMS_AuthorizationList WHERE LocalizedDisplayName like \"Rapid7%%\"",
|
9
|
+
:get_collections => "SELECT * FROM SMS_Collection WHERE Name LIKE \"Rapid7%%\"",
|
10
|
+
:get_collection_by_name => "SELECT * FROM SMS_Collection WHERE Name = \"%s\"",
|
11
|
+
:get_collection_members => "SELECT * FROM SMS_FullCollectionMembership WHERE CollectionId=\"%s\"",
|
12
|
+
:get_devices => "SELECT * FROM SMS_R_System WHERE client IS NOT NULL",
|
13
|
+
:get_device_by_id => "SELECT * FROM SMS_R_System WHERE ResourceID = \"%s\"",
|
14
|
+
:get_asset_mac => "SELECT * FROM SMS_R_System WHERE MACAddresses = \"%s\"",
|
15
|
+
:get_asset_ip => "SELECT * FROM SMS_R_System WHERE IPAddresses = \"%s\"",
|
16
|
+
:get_asset_hostname => "SELECT * FROM SMS_R_System WHERE Name = \"%s\"",
|
17
|
+
:get_update_url => "SELECT FileName,SourceURL from SMS_CIContentfiles WHERE ContentID=\"%s\"",
|
18
|
+
:ci_to_contentid => "SELECT contentid FROM SMS_CIToContent WHERE ci_id = \"%s\"",
|
19
|
+
:get_deployment_package => "SELECT * FROM SMS_SoftwareUpdatesPackage WHERE Name = \"%s\""
|
20
|
+
}
|
21
|
+
|
22
|
+
def self.run(conn, namespace, query, *args)
|
23
|
+
unless @queries.key?(query)
|
24
|
+
NexposeSCCM.logger.error("Invalid query supplied: #{query}")
|
25
|
+
end
|
26
|
+
wql = @queries[query] % [*args]
|
27
|
+
results = []
|
28
|
+
conn.run_wql(wql, "#{namespace.gsub('\\','/')}/*") do |_, item|
|
29
|
+
item = item.inject({}){|i,(k,v)| i[k.to_sym] = v; i}
|
30
|
+
results.push(item)
|
31
|
+
end
|
32
|
+
results
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require_relative './lib/nexpose_sccm/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'nexpose_sccm'
|
7
|
+
spec.version = NexposeSCCM::VERSION
|
8
|
+
spec.authors = ['Ben Glass', 'Zac Youtz', 'Adam Robinson']
|
9
|
+
spec.email = ['support@rapid7.com']
|
10
|
+
spec.summary = %q{Ruby Gem for Nexpose SCCM Integration}
|
11
|
+
spec.description = %q{This gem pulls data from InsightVM/Nexpose and creates content in SCCM for remediation}
|
12
|
+
spec.homepage = 'http://www.rapid7.com/'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.files = Dir.glob("{bin,conf,lib}/**/*")
|
16
|
+
spec.files += Dir['README.md', 'nexpose_sccm.gemspec', 'MIT-LICENSE', 'Gemfile']
|
17
|
+
spec.files.reject! { |f| f.match(%r{(logs)/}) }
|
18
|
+
spec.require_paths = ['lib']
|
19
|
+
spec.executables = ['nexpose_sccm']
|
20
|
+
spec.bindir = 'bin'
|
21
|
+
|
22
|
+
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
|
23
|
+
# delete this section to allow pushing this gem to any host.
|
24
|
+
if spec.respond_to?(:metadata)
|
25
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
26
|
+
else
|
27
|
+
raise 'RubyGems 2.0 or newer is required to protect against public gem pushes.'
|
28
|
+
end
|
29
|
+
|
30
|
+
spec.add_runtime_dependency 'nexpose', '~> 7.2'
|
31
|
+
spec.add_runtime_dependency 'pg', '~> 0.21.0'
|
32
|
+
spec.add_runtime_dependency 'winrm', '~> 2.2', '>= 2.2.3'
|
33
|
+
|
34
|
+
spec.required_ruby_version = ['>= 2.3.1', '< 2.5.0']
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nexpose_sccm
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ben Glass
|
8
|
+
- Zac Youtz
|
9
|
+
- Adam Robinson
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2018-04-13 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: nexpose
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - "~>"
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '7.2'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - "~>"
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: '7.2'
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: pg
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - "~>"
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 0.21.0
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - "~>"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.21.0
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: winrm
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - "~>"
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '2.2'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 2.2.3
|
53
|
+
type: :runtime
|
54
|
+
prerelease: false
|
55
|
+
version_requirements: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '2.2'
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.2.3
|
63
|
+
description: This gem pulls data from InsightVM/Nexpose and creates content in SCCM
|
64
|
+
for remediation
|
65
|
+
email:
|
66
|
+
- support@rapid7.com
|
67
|
+
executables:
|
68
|
+
- nexpose_sccm
|
69
|
+
extensions: []
|
70
|
+
extra_rdoc_files: []
|
71
|
+
files:
|
72
|
+
- Gemfile
|
73
|
+
- MIT-LICENSE
|
74
|
+
- README.md
|
75
|
+
- bin/encrypt_settings.rb
|
76
|
+
- bin/nexpose_sccm
|
77
|
+
- bin/vuln_ids.csv
|
78
|
+
- conf/logging.yml
|
79
|
+
- conf/nexpose.yml
|
80
|
+
- conf/postgres.yml
|
81
|
+
- conf/queries.yml
|
82
|
+
- conf/sccm.yml
|
83
|
+
- conf/secret.yml
|
84
|
+
- lib/nexpose_sccm.rb
|
85
|
+
- lib/nexpose_sccm/collection.rb
|
86
|
+
- lib/nexpose_sccm/connection.rb
|
87
|
+
- lib/nexpose_sccm/data_source.rb
|
88
|
+
- lib/nexpose_sccm/deployment_package.rb
|
89
|
+
- lib/nexpose_sccm/device.rb
|
90
|
+
- lib/nexpose_sccm/helpers/nexpose_helper.rb
|
91
|
+
- lib/nexpose_sccm/helpers/pg_helper.rb
|
92
|
+
- lib/nexpose_sccm/powershell.rb
|
93
|
+
- lib/nexpose_sccm/remediation_item.rb
|
94
|
+
- lib/nexpose_sccm/software_update_group.rb
|
95
|
+
- lib/nexpose_sccm/utilities/nx_logger.rb
|
96
|
+
- lib/nexpose_sccm/utilities/utility_config.rb
|
97
|
+
- lib/nexpose_sccm/version.rb
|
98
|
+
- lib/nexpose_sccm/wql.rb
|
99
|
+
- nexpose_sccm.gemspec
|
100
|
+
homepage: http://www.rapid7.com/
|
101
|
+
licenses:
|
102
|
+
- MIT
|
103
|
+
metadata:
|
104
|
+
allowed_push_host: https://rubygems.org
|
105
|
+
post_install_message:
|
106
|
+
rdoc_options: []
|
107
|
+
require_paths:
|
108
|
+
- lib
|
109
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: 2.3.1
|
114
|
+
- - "<"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 2.5.0
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubyforge_project:
|
124
|
+
rubygems_version: 2.6.11
|
125
|
+
signing_key:
|
126
|
+
specification_version: 4
|
127
|
+
summary: Ruby Gem for Nexpose SCCM Integration
|
128
|
+
test_files: []
|