inspec 1.42.3 → 1.43.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +36 -22
- data/docs/profiles.md +1 -1
- data/docs/resources/cpan.md.erb +62 -0
- data/docs/resources/cran.md.erb +54 -0
- data/docs/resources/elasticsearch.md.erb +245 -0
- data/docs/resources/shadow.md.erb +20 -6
- data/lib/bundles/inspec-compliance/README.md +13 -22
- data/lib/bundles/inspec-compliance/api.rb +13 -2
- data/lib/bundles/inspec-compliance/api/login.rb +150 -0
- data/lib/bundles/inspec-compliance/cli.rb +43 -157
- data/lib/bundles/inspec-compliance/target.rb +2 -3
- data/lib/inspec/objects/control.rb +10 -2
- data/lib/inspec/profile.rb +2 -1
- data/lib/inspec/resource.rb +3 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/cpan.rb +60 -0
- data/lib/resources/cran.rb +66 -0
- data/lib/resources/elasticsearch.rb +172 -0
- metadata +9 -2
@@ -32,7 +32,7 @@ module Compliance
|
|
32
32
|
if config['token'].nil? && config['refresh_token'].nil?
|
33
33
|
if config['server_type'] == 'automate'
|
34
34
|
server = 'automate'
|
35
|
-
msg = 'inspec compliance
|
35
|
+
msg = 'inspec compliance login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN'
|
36
36
|
else
|
37
37
|
server = 'compliance'
|
38
38
|
msg = "inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' "
|
@@ -90,8 +90,7 @@ EOF
|
|
90
90
|
|
91
91
|
raise 'Unable to determine compliance profile name. This can be caused by ' \
|
92
92
|
'an incorrect server in your configuration. Try to login to compliance ' \
|
93
|
-
'via the `inspec compliance login`
|
94
|
-
'commands.' if m.nil?
|
93
|
+
'via the `inspec compliance login` command.' if m.nil?
|
95
94
|
|
96
95
|
"#{m[:owner]}/#{m[:id]}"
|
97
96
|
end
|
@@ -2,10 +2,11 @@
|
|
2
2
|
|
3
3
|
module Inspec
|
4
4
|
class Control
|
5
|
-
attr_accessor :id, :title, :desc, :impact, :tests, :tags
|
5
|
+
attr_accessor :id, :title, :desc, :impact, :tests, :tags, :refs
|
6
6
|
def initialize
|
7
7
|
@tests = []
|
8
8
|
@tags = []
|
9
|
+
@refs = []
|
9
10
|
end
|
10
11
|
|
11
12
|
def add_test(t)
|
@@ -20,12 +21,13 @@ module Inspec
|
|
20
21
|
{ id: id, title: title, desc: desc, impact: impact, tests: tests.map(&:to_hash), tags: tags.map(&:to_hash) }
|
21
22
|
end
|
22
23
|
|
23
|
-
def to_ruby
|
24
|
+
def to_ruby # rubocop:disable Metrics/AbcSize
|
24
25
|
res = ["control #{id.inspect} do"]
|
25
26
|
res.push " title #{title.inspect}" unless title.to_s.empty?
|
26
27
|
res.push " desc #{prettyprint_text(desc, 2)}" unless desc.to_s.empty?
|
27
28
|
res.push " impact #{impact}" unless impact.nil?
|
28
29
|
tags.each { |t| res.push(indent(t.to_ruby, 2)) }
|
30
|
+
refs.each { |t| res.push(" ref #{print_ref(t)}") }
|
29
31
|
tests.each { |t| res.push(indent(t.to_ruby, 2)) }
|
30
32
|
res.push 'end'
|
31
33
|
res.join("\n")
|
@@ -33,6 +35,12 @@ module Inspec
|
|
33
35
|
|
34
36
|
private
|
35
37
|
|
38
|
+
def print_ref(x)
|
39
|
+
return x.inspect if x.is_a?(String)
|
40
|
+
raise "Cannot process the ref: #{x}" unless x.is_a?(Hash)
|
41
|
+
'('+x.inspect+')'
|
42
|
+
end
|
43
|
+
|
36
44
|
# Pretty-print a text block of InSpec code
|
37
45
|
#
|
38
46
|
# @param s [String] should not be empty
|
data/lib/inspec/profile.rb
CHANGED
@@ -106,7 +106,8 @@ module Inspec
|
|
106
106
|
# we share the backend between profiles.
|
107
107
|
#
|
108
108
|
# This will cause issues if a profile attempts to load a file via `inspec.profile.file`
|
109
|
-
|
109
|
+
train_options = options.select { |k, _| k != 'target' } # See https://github.com/chef/inspec/pull/1646
|
110
|
+
@backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
|
110
111
|
@runtime_profile = RuntimeProfile.new(self)
|
111
112
|
@backend.profile = @runtime_profile
|
112
113
|
|
data/lib/inspec/resource.rb
CHANGED
@@ -84,12 +84,15 @@ require 'resources/bash'
|
|
84
84
|
require 'resources/bond'
|
85
85
|
require 'resources/bridge'
|
86
86
|
require 'resources/command'
|
87
|
+
require 'resources/cran'
|
88
|
+
require 'resources/cpan'
|
87
89
|
require 'resources/crontab'
|
88
90
|
require 'resources/dh_params'
|
89
91
|
require 'resources/directory'
|
90
92
|
require 'resources/docker'
|
91
93
|
require 'resources/docker_container'
|
92
94
|
require 'resources/docker_image'
|
95
|
+
require 'resources/elasticsearch'
|
93
96
|
require 'resources/etc_fstab'
|
94
97
|
require 'resources/etc_group'
|
95
98
|
require 'resources/etc_hosts_allow_deny'
|
data/lib/inspec/version.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Christoph Hartmann
|
3
|
+
# author: Dominik Richter
|
4
|
+
# author: Markus Grobelin
|
5
|
+
|
6
|
+
# Usage:
|
7
|
+
# describe cpan('DBD::Pg') do
|
8
|
+
# it { should be_installed }
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
|
12
|
+
module Inspec::Resources
|
13
|
+
class CpanPackage < Inspec.resource(1)
|
14
|
+
name 'cpan'
|
15
|
+
desc 'Use the `cpan` InSpec audit resource to test Perl modules that are installed by system packages or the CPAN installer.'
|
16
|
+
example "
|
17
|
+
describe cpan('DBD::Pg') do
|
18
|
+
it { should be_installed }
|
19
|
+
end
|
20
|
+
"
|
21
|
+
|
22
|
+
def initialize(package_name, perl_lib_path = nil)
|
23
|
+
@package_name = package_name
|
24
|
+
@perl_lib_path = perl_lib_path
|
25
|
+
@perl_cmd = 'perl'
|
26
|
+
|
27
|
+
# this resource is not supported on Windows
|
28
|
+
return skip_resource 'The `cpan` resource is not supported on your OS yet.' if inspec.os.windows?
|
29
|
+
return skip_resource 'perl not found' unless inspec.command(@perl_cmd).exist?
|
30
|
+
end
|
31
|
+
|
32
|
+
def info
|
33
|
+
return @info if defined?(@info)
|
34
|
+
|
35
|
+
@info = {}
|
36
|
+
@info[:type] = 'cpan'
|
37
|
+
@info[:name] = @package_name
|
38
|
+
# set PERL5LIB environment variable if a custom lib path is given
|
39
|
+
lib_path = @perl_lib_path.nil? ? '' : "PERL5LIB=#{@perl_lib_path} "
|
40
|
+
cmd = inspec.command("#{lib_path+@perl_cmd} -le 'eval \"require $ARGV[0]\" and print $ARGV[0]->VERSION or exit 1' #{@package_name}")
|
41
|
+
@info[:installed] = cmd.exit_status.zero?
|
42
|
+
return @info unless cmd.exit_status.zero?
|
43
|
+
|
44
|
+
@info[:version] = cmd.stdout.strip
|
45
|
+
@info
|
46
|
+
end
|
47
|
+
|
48
|
+
def installed?
|
49
|
+
info[:installed] == true
|
50
|
+
end
|
51
|
+
|
52
|
+
def version
|
53
|
+
info[:version]
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
"Perl Module #{@package_name}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Christoph Hartmann
|
3
|
+
# author: Dominik Richter
|
4
|
+
# author: Markus Grobelin
|
5
|
+
|
6
|
+
# Usage:
|
7
|
+
# describe cran('DBI') do
|
8
|
+
# it { should be_installed }
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
|
12
|
+
module Inspec::Resources
|
13
|
+
class CranPackage < Inspec.resource(1)
|
14
|
+
name 'cran'
|
15
|
+
desc 'Use the `cran` InSpec audit resource to test R modules that are installed from CRAN package repository.'
|
16
|
+
example "
|
17
|
+
describe cran('DBI') do
|
18
|
+
it { should be_installed }
|
19
|
+
end
|
20
|
+
"
|
21
|
+
|
22
|
+
def initialize(package_name)
|
23
|
+
@package_name = package_name
|
24
|
+
@r_cmd = 'Rscript'
|
25
|
+
|
26
|
+
# this resource is not supported on Windows
|
27
|
+
return skip_resource 'The `cran` resource is not supported on your OS yet.' if inspec.os.windows?
|
28
|
+
return skip_resource 'Rscript not found' unless inspec.command(@r_cmd).exist?
|
29
|
+
end
|
30
|
+
|
31
|
+
def info
|
32
|
+
return @info if defined?(@info)
|
33
|
+
|
34
|
+
@info = {}
|
35
|
+
@info[:type] = 'cran'
|
36
|
+
@info[:name] = @package_name
|
37
|
+
cmd = inspec.command("#{@r_cmd} -e 'packageVersion(\"#{@package_name}\")'")
|
38
|
+
return @info unless cmd.exit_status.zero?
|
39
|
+
|
40
|
+
# Extract package version from Rscript output
|
41
|
+
# Output includes unicode punctuation (backticks) characters like so:
|
42
|
+
# [1] '0.5.1'
|
43
|
+
#
|
44
|
+
# So make sure command output is converted to unicode, as it returns ASCII-8BIT by default
|
45
|
+
utf8_stdout = cmd.stdout.chomp.force_encoding(Encoding::UTF_8)
|
46
|
+
params = /^\[\d+\]\s+(?:\p{Initial_Punctuation})(.+)(?:\p{Final_Punctuation})$/.match(utf8_stdout)
|
47
|
+
@info[:installed] = !params.nil?
|
48
|
+
return @info unless @info[:installed]
|
49
|
+
|
50
|
+
@info[:version] = params[1]
|
51
|
+
@info
|
52
|
+
end
|
53
|
+
|
54
|
+
def installed?
|
55
|
+
info[:installed] == true
|
56
|
+
end
|
57
|
+
|
58
|
+
def version
|
59
|
+
info[:version]
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
"R Module #{@package_name}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'utils/filter'
|
4
|
+
require 'hashie/mash'
|
5
|
+
require 'resources/package'
|
6
|
+
|
7
|
+
module Inspec::Resources
|
8
|
+
class Elasticsearch < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
|
9
|
+
name 'elasticsearch'
|
10
|
+
desc "Use the Elasticsearch InSpec audit resource to test the status of nodes in
|
11
|
+
an Elasticsearch cluster."
|
12
|
+
|
13
|
+
example "
|
14
|
+
describe elasticsearch('http://eshost.mycompany.biz:9200/', username: 'elastic', password: 'changeme', ssl_verify: false) do
|
15
|
+
its('node_count') { should >= 3 }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe elasticsearch do
|
19
|
+
its('node_name') { should include 'node1' }
|
20
|
+
its('os') { should_not include 'MacOS' }
|
21
|
+
its('version') { should cmp > 1.2.0 }
|
22
|
+
end
|
23
|
+
"
|
24
|
+
|
25
|
+
filter = FilterTable.create
|
26
|
+
filter.add_accessor(:where)
|
27
|
+
.add_accessor(:entries)
|
28
|
+
.add(:cluster_name, field: 'cluster_name')
|
29
|
+
.add(:node_name, field: 'name')
|
30
|
+
.add(:transport_address, field: 'transport_address')
|
31
|
+
.add(:host, field: 'host')
|
32
|
+
.add(:ip, field: 'ip')
|
33
|
+
.add(:version, field: 'version')
|
34
|
+
.add(:build_hash, field: 'build_hash')
|
35
|
+
.add(:total_indexing_buffer, field: 'total_indexing_buffer')
|
36
|
+
.add(:roles, field: 'roles')
|
37
|
+
.add(:settings, field: 'settings')
|
38
|
+
.add(:os, field: 'os')
|
39
|
+
.add(:process, field: 'process')
|
40
|
+
.add(:jvm, field: 'jvm')
|
41
|
+
.add(:transport, field: 'transport')
|
42
|
+
.add(:http, field: 'http')
|
43
|
+
.add(:plugins, field: 'plugins')
|
44
|
+
.add(:plugin_list, field: 'plugin_list')
|
45
|
+
.add(:modules, field: 'modules')
|
46
|
+
.add(:module_list, field: 'module_list')
|
47
|
+
.add(:node_id, field: 'node_id')
|
48
|
+
.add(:ingest, field: 'ingest')
|
49
|
+
.add(:exists?) { |x| !x.entries.empty? }
|
50
|
+
.add(:node_count) { |t, _|
|
51
|
+
t.entries.length
|
52
|
+
}
|
53
|
+
|
54
|
+
filter.connect(self, :nodes)
|
55
|
+
|
56
|
+
attr_reader :nodes, :url
|
57
|
+
|
58
|
+
def initialize(opts = {})
|
59
|
+
return skip_resource 'Package `curl` not avaiable on the host' unless inspec.command('curl').exist?
|
60
|
+
|
61
|
+
@url = opts.fetch(:url, 'http://localhost:9200')
|
62
|
+
|
63
|
+
username = opts.fetch(:username, nil)
|
64
|
+
password = opts.fetch(:password, nil)
|
65
|
+
ssl_verify = opts.fetch(:ssl_verify, true)
|
66
|
+
|
67
|
+
cmd = inspec.command(curl_command_string(username, password, ssl_verify))
|
68
|
+
|
69
|
+
# after implementation of PR #2235, this begin..rescue won't be necessary.
|
70
|
+
# The checks in verify_curl_success! can raise their own skip message exception.
|
71
|
+
begin
|
72
|
+
verify_curl_success!(cmd)
|
73
|
+
rescue => e
|
74
|
+
return skip_resource e.message
|
75
|
+
end
|
76
|
+
|
77
|
+
begin
|
78
|
+
content = JSON.parse(cmd.stdout)
|
79
|
+
# after implementation of PR #2235, this can be broken out of the begin..rescue
|
80
|
+
# clause. The checks in verify_json_payload! can raise their own skip message exception.
|
81
|
+
verify_json_payload!(content)
|
82
|
+
rescue JSON::ParserError => e
|
83
|
+
return skip_resource "Couldn't parse the Elasticsearch response: #{e.message}"
|
84
|
+
rescue => e
|
85
|
+
return skip_resource e.message
|
86
|
+
end
|
87
|
+
|
88
|
+
@nodes = parse_cluster(content)
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_s
|
92
|
+
"Elasticsearch Cluster #{url}"
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def parse_cluster(content)
|
98
|
+
return [] unless content['nodes']
|
99
|
+
|
100
|
+
nodes = []
|
101
|
+
|
102
|
+
content['nodes'].each do |node_id, node_data|
|
103
|
+
node_data = fix_mash_key_collision(node_data)
|
104
|
+
|
105
|
+
node = Hashie::Mash.new(node_data)
|
106
|
+
node.node_id = node_id
|
107
|
+
node.plugin_list = node.plugins.map(&:name)
|
108
|
+
node.module_list = node.modules.map(&:name)
|
109
|
+
node.cluster_name = node.settings.cluster.name
|
110
|
+
nodes << node
|
111
|
+
end
|
112
|
+
|
113
|
+
nodes
|
114
|
+
end
|
115
|
+
|
116
|
+
#
|
117
|
+
# Hashie::Mash will throw warnings if the Mash contains a key that is the same as a built-in
|
118
|
+
# method on a Hashie::Mash instance. This is a crude way of avoiding those warnings without
|
119
|
+
# hard-coding a bunch of key renames.
|
120
|
+
#
|
121
|
+
# Any key that is in conflict will be renamed "es_ORIGINALKEY"
|
122
|
+
#
|
123
|
+
def fix_mash_key_collision(data)
|
124
|
+
test_mash = Hashie::Mash.new
|
125
|
+
|
126
|
+
new_data = {}
|
127
|
+
data.each do |key, value|
|
128
|
+
new_key = test_mash.respond_to?(key.to_sym) ? "es_#{key}" : key
|
129
|
+
new_value = value.is_a?(Hash) ? fix_mash_key_collision(value) : value
|
130
|
+
|
131
|
+
new_data[new_key] = new_value
|
132
|
+
end
|
133
|
+
|
134
|
+
new_data
|
135
|
+
end
|
136
|
+
|
137
|
+
def curl_command_string(username, password, ssl_verify)
|
138
|
+
cmd_string = ['curl']
|
139
|
+
cmd_string << '-k' unless ssl_verify
|
140
|
+
cmd_string << "-H 'Content-Type: application/json'"
|
141
|
+
cmd_string << " -u #{username}:#{password}" unless username.nil? || password.nil?
|
142
|
+
cmd_string << URI.join(url, '_nodes')
|
143
|
+
|
144
|
+
cmd_string.join(' ')
|
145
|
+
end
|
146
|
+
|
147
|
+
def verify_curl_success!(cmd)
|
148
|
+
# the following lines captures known possible curl command errors and provides compact skip resource messeges
|
149
|
+
if cmd.stderr =~ /Failed to connect/
|
150
|
+
raise "Connection refused - please check the URL #{url} for accuracy"
|
151
|
+
end
|
152
|
+
|
153
|
+
if cmd.stderr =~ /Peer's Certificate issuer is not recognized/
|
154
|
+
raise 'Connection refused - peer certificate issuer is not recognized'
|
155
|
+
end
|
156
|
+
|
157
|
+
if !cmd.exit_status.zero?
|
158
|
+
raise "Error fetching Elastcsearch data from curl #{url}: #{cmd.stderr}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def verify_json_payload!(content)
|
163
|
+
unless content['error'].nil?
|
164
|
+
raise "#{content['error']['type']}: #{content['error']['reason']}"
|
165
|
+
end
|
166
|
+
|
167
|
+
if content['_nodes']['successful'].zero?
|
168
|
+
raise 'No successful nodes available in cluster'
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.43.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Richter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-10-
|
11
|
+
date: 2017-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: train
|
@@ -324,6 +324,8 @@ files:
|
|
324
324
|
- docs/resources/bridge.md.erb
|
325
325
|
- docs/resources/bsd_service.md.erb
|
326
326
|
- docs/resources/command.md.erb
|
327
|
+
- docs/resources/cpan.md.erb
|
328
|
+
- docs/resources/cran.md.erb
|
327
329
|
- docs/resources/crontab.md.erb
|
328
330
|
- docs/resources/csv.md.erb
|
329
331
|
- docs/resources/dh_params.md
|
@@ -331,6 +333,7 @@ files:
|
|
331
333
|
- docs/resources/docker.md.erb
|
332
334
|
- docs/resources/docker_container.md.erb
|
333
335
|
- docs/resources/docker_image.md.erb
|
336
|
+
- docs/resources/elasticsearch.md.erb
|
334
337
|
- docs/resources/etc_fstab.md.erb
|
335
338
|
- docs/resources/etc_group.md.erb
|
336
339
|
- docs/resources/etc_hosts.md.erb
|
@@ -467,6 +470,7 @@ files:
|
|
467
470
|
- lib/bundles/inspec-compliance/.kitchen.yml
|
468
471
|
- lib/bundles/inspec-compliance/README.md
|
469
472
|
- lib/bundles/inspec-compliance/api.rb
|
473
|
+
- lib/bundles/inspec-compliance/api/login.rb
|
470
474
|
- lib/bundles/inspec-compliance/bootstrap.sh
|
471
475
|
- lib/bundles/inspec-compliance/cli.rb
|
472
476
|
- lib/bundles/inspec-compliance/configuration.rb
|
@@ -572,6 +576,8 @@ files:
|
|
572
576
|
- lib/resources/bond.rb
|
573
577
|
- lib/resources/bridge.rb
|
574
578
|
- lib/resources/command.rb
|
579
|
+
- lib/resources/cpan.rb
|
580
|
+
- lib/resources/cran.rb
|
575
581
|
- lib/resources/crontab.rb
|
576
582
|
- lib/resources/csv.rb
|
577
583
|
- lib/resources/dh_params.rb
|
@@ -579,6 +585,7 @@ files:
|
|
579
585
|
- lib/resources/docker.rb
|
580
586
|
- lib/resources/docker_container.rb
|
581
587
|
- lib/resources/docker_image.rb
|
588
|
+
- lib/resources/elasticsearch.rb
|
582
589
|
- lib/resources/etc_fstab.rb
|
583
590
|
- lib/resources/etc_group.rb
|
584
591
|
- lib/resources/etc_hosts.rb
|