inspec 1.29.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -0
- data/bin/inspec +1 -1
- data/docs/profiles.md +14 -5
- data/docs/resources/iptables.md.erb +12 -5
- data/docs/resources/mssql_session.md.erb +11 -28
- data/docs/resources/mysql_session.md.erb +12 -0
- data/docs/resources/oracledb_session.md.erb +10 -28
- data/docs/resources/package.md.erb +6 -0
- data/docs/resources/postgres_conf.md.erb +2 -0
- data/examples/inheritance/controls/example.rb +0 -1
- data/examples/meta-profile/controls/example.rb +0 -1
- data/examples/profile/controls/example.rb +0 -1
- data/examples/profile/controls/gordon.rb +0 -1
- data/inspec.gemspec +1 -0
- data/lib/bundles/inspec-compliance/api.rb +12 -10
- data/lib/bundles/inspec-init/templates/profile/controls/example.rb +0 -1
- data/lib/inspec.rb +0 -1
- data/lib/inspec/backend.rb +0 -1
- data/lib/inspec/cli.rb +1 -1
- data/lib/inspec/metadata.rb +1 -1
- data/lib/inspec/polyfill.rb +0 -1
- data/lib/inspec/profile.rb +1 -1
- data/lib/inspec/resource.rb +1 -1
- data/lib/inspec/rule.rb +0 -1
- data/lib/inspec/runner.rb +0 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +0 -1
- data/lib/resources/apache.rb +0 -1
- data/lib/resources/apache_conf.rb +0 -1
- data/lib/resources/audit_policy.rb +0 -1
- data/lib/resources/auditd_conf.rb +0 -1
- data/lib/resources/auditd_rules.rb +0 -1
- data/lib/resources/command.rb +0 -1
- data/lib/resources/directory.rb +7 -3
- data/lib/resources/docker.rb +30 -3
- data/lib/resources/etc_group.rb +0 -1
- data/lib/resources/file.rb +0 -1
- data/lib/resources/grub_conf.rb +0 -1
- data/lib/resources/inetd_conf.rb +0 -1
- data/lib/resources/kernel_module.rb +0 -1
- data/lib/resources/kernel_parameter.rb +0 -1
- data/lib/resources/limits_conf.rb +0 -1
- data/lib/resources/login_def.rb +0 -1
- data/lib/resources/mssql_session.rb +62 -14
- data/lib/resources/mysql.rb +0 -1
- data/lib/resources/mysql_conf.rb +0 -1
- data/lib/resources/mysql_session.rb +15 -6
- data/lib/resources/nginx_conf.rb +95 -0
- data/lib/resources/ntp_conf.rb +0 -1
- data/lib/resources/oracledb_session.rb +109 -12
- data/lib/resources/os_env.rb +0 -1
- data/lib/resources/package.rb +47 -3
- data/lib/resources/packages.rb +0 -1
- data/lib/resources/parse_config.rb +0 -1
- data/lib/resources/passwd.rb +0 -1
- data/lib/resources/postgres.rb +9 -5
- data/lib/resources/postgres_conf.rb +12 -3
- data/lib/resources/postgres_session.rb +0 -1
- data/lib/resources/powershell.rb +0 -1
- data/lib/resources/processes.rb +0 -1
- data/lib/resources/registry_key.rb +0 -1
- data/lib/resources/service.rb +1 -1
- data/lib/resources/ssh_conf.rb +0 -1
- data/lib/resources/ssl.rb +0 -1
- data/lib/utils/database_helpers.rb +77 -0
- data/lib/utils/filter_array.rb +0 -1
- data/lib/utils/find_files.rb +0 -1
- data/lib/utils/nginx_parser.rb +4 -2
- data/lib/utils/simpleconfig.rb +0 -1
- metadata +18 -2
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Dominik Richter
|
3
|
+
# author: Christoph Hartmann
|
4
|
+
|
5
|
+
require 'utils/nginx_parser'
|
6
|
+
|
7
|
+
# STABILITY: Experimental
|
8
|
+
# This resouce needs a proper interace to the underlying data, which is currently missing.
|
9
|
+
# Until it is added, we will keep it experimental.
|
10
|
+
#
|
11
|
+
# TODO: Support it on Windows. To do so, we need to recognize the base os and how
|
12
|
+
# it combines the file path. Calling `File.join` or similar methods may lead to errors
|
13
|
+
# when running remotely.
|
14
|
+
module Inspec::Resources
|
15
|
+
class NginxConf < Inspec.resource(1)
|
16
|
+
name 'nginx_conf'
|
17
|
+
desc 'Use the nginx_conf InSpec resource to test configuration data '\
|
18
|
+
'for the NginX web server located in /etc/nginx/nginx.conf on '\
|
19
|
+
'Linux and UNIX platforms.'
|
20
|
+
example "
|
21
|
+
describe nginx_conf.params ...
|
22
|
+
describe nginx_conf('/path/to/my/nginx.conf').params ...
|
23
|
+
"
|
24
|
+
|
25
|
+
attr_reader :contents
|
26
|
+
|
27
|
+
def initialize(conf_path = nil)
|
28
|
+
@conf_path = conf_path || '/etc/nginx/nginx.conf'
|
29
|
+
@contents = {}
|
30
|
+
return skip_resource 'The `nginx_conf` resource is currently not supported on Windows.' if inspec.os.windows?
|
31
|
+
end
|
32
|
+
|
33
|
+
def params
|
34
|
+
@params ||= parse_nginx(@conf_path)
|
35
|
+
rescue StandardError => e
|
36
|
+
skip_resource e.message
|
37
|
+
@params = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
"nginx_conf #{@conf_path}"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def read_content(path)
|
47
|
+
return @contents[path] if @contents.key?(path)
|
48
|
+
file = inspec.file(path)
|
49
|
+
if !file.file?
|
50
|
+
return skip_resource "Can't find file \"#{path}\""
|
51
|
+
end
|
52
|
+
@contents[path] = file.content
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse_nginx(path)
|
56
|
+
return nil if inspec.os.windows?
|
57
|
+
content = read_content(path)
|
58
|
+
data = NginxConfig.parse(content)
|
59
|
+
resolve_references(data, File.dirname(path))
|
60
|
+
rescue StandardError => _
|
61
|
+
raise "Cannot parse NginX config in #{path}."
|
62
|
+
end
|
63
|
+
|
64
|
+
# Cycle through the complete parsed data structure and try to find any
|
65
|
+
# calls to `include`. In NginX, this is used to embed data from other
|
66
|
+
# files into the current data structure.
|
67
|
+
#
|
68
|
+
# The method steps through the object structure that is passed in to
|
69
|
+
# find any calls to 'include' and returns the object structure with the
|
70
|
+
# included data merged in.
|
71
|
+
#
|
72
|
+
# @param data [Hash] data structure from NginxConfig.parse
|
73
|
+
# @param rel_path [String] the relative path from which this config is read
|
74
|
+
# @return [Hash] data structure with references included
|
75
|
+
def resolve_references(data, rel_path)
|
76
|
+
# Walk through all array entries to find more references
|
77
|
+
return data.map { |x| resolve_references(x, rel_path) } if data.is_a?(Array)
|
78
|
+
|
79
|
+
# Return any data that we cannot step into to find more `include` calls
|
80
|
+
return data unless data.is_a?(Hash)
|
81
|
+
|
82
|
+
# Any call to `include` gets its data read, parsed, and merged back
|
83
|
+
# into the current data structure
|
84
|
+
if data.key?('include')
|
85
|
+
data.delete('include').flatten
|
86
|
+
.map { |x| File.expand_path(x, rel_path) }
|
87
|
+
.map { |path| parse_nginx(path) }
|
88
|
+
.map { |e| data.merge!(e) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Walk through the remaining hash fields to find more references
|
92
|
+
Hash[data.map { |k, v| [k, resolve_references(v, rel_path)] }]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/resources/ntp_conf.rb
CHANGED
@@ -1,42 +1,139 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# author: Nolan Davidson
|
3
|
-
#
|
3
|
+
# author: Christoph Hartmann
|
4
|
+
# author: Dominik Richter
|
5
|
+
|
6
|
+
require 'hashie/mash'
|
7
|
+
require 'utils/database_helpers'
|
8
|
+
require 'htmlentities'
|
9
|
+
require 'rexml/document'
|
10
|
+
require 'csv'
|
4
11
|
|
5
12
|
module Inspec::Resources
|
13
|
+
# STABILITY: Experimental
|
14
|
+
# This resource needs further testing and refinement
|
15
|
+
#
|
6
16
|
class OracledbSession < Inspec.resource(1)
|
7
17
|
name 'oracledb_session'
|
8
18
|
desc 'Use the oracledb_session InSpec resource to test commands against an Oracle database'
|
9
19
|
example "
|
10
20
|
sql = oracledb_session(user: 'my_user', pass: 'password')
|
11
|
-
describe sql.query(
|
12
|
-
its('
|
21
|
+
describe sql.query(\"SELECT UPPER(VALUE) AS VALUE FROM V$PARAMETER WHERE UPPER(NAME)='AUDIT_SYS_OPERATIONS'\").row(0).column('value') do
|
22
|
+
its('value') { should eq 'TRUE' }
|
13
23
|
end
|
14
24
|
"
|
15
25
|
|
16
|
-
attr_reader :user, :
|
17
|
-
|
26
|
+
attr_reader :user, :password, :host, :service
|
18
27
|
def initialize(opts = {})
|
19
28
|
@user = opts[:user]
|
20
|
-
@
|
29
|
+
@password = opts[:password] || opts[:pass]
|
30
|
+
if opts[:pass]
|
31
|
+
warn '[DEPRECATED] use `password` option to supply password instead of `pass`'
|
32
|
+
end
|
33
|
+
|
21
34
|
@host = opts[:host] || 'localhost'
|
22
|
-
@
|
35
|
+
@port = opts[:port] || '1521'
|
36
|
+
@service = opts[:service]
|
37
|
+
|
38
|
+
# we prefer sqlci although it is way slower than sqlplus, but it understands csv properly
|
39
|
+
@sqlcl_bin = 'sql'
|
23
40
|
@sqlplus_bin = opts[:sqlplus_bin] || 'sqlplus'
|
24
|
-
|
41
|
+
|
42
|
+
return skip_resource "Can't run Oracle checks without authentication" if @user.nil? || @password.nil?
|
43
|
+
return skip_resource 'You must provide a service name for the session' if @service.nil?
|
25
44
|
end
|
26
45
|
|
27
46
|
def query(q)
|
28
47
|
escaped_query = q.gsub(/\\/, '\\\\').gsub(/"/, '\\"')
|
29
|
-
|
48
|
+
# escape tables with $
|
49
|
+
escaped_query = escaped_query.gsub('$', '\\$')
|
50
|
+
|
51
|
+
p = nil
|
52
|
+
# check if sqlplus is available and prefer that
|
53
|
+
if inspec.command(@sqlplus_bin).exist?
|
54
|
+
bin = @sqlplus_bin
|
55
|
+
opts = "SET MARKUP HTML ON\nSET FEEDBACK OFF"
|
56
|
+
p = :parse_html_result
|
57
|
+
elsif inspec.command(@sqlcl_bin).exist?
|
58
|
+
bin = @sqlcl_bin
|
59
|
+
opts = "set sqlformat csv\nSET FEEDBACK OFF"
|
60
|
+
p = :parse_csv_result
|
61
|
+
end
|
62
|
+
|
63
|
+
return skip_resource("Can't find suitable Oracle CLI") if p.nil?
|
64
|
+
command = "echo \"#{opts}\n#{verify_query(escaped_query)}\nEXIT\" | #{bin} -s #{@user}/#{@password}@//#{@host}:#{@port}/#{@service}"
|
65
|
+
cmd = inspec.command(command)
|
66
|
+
|
30
67
|
out = cmd.stdout + "\n" + cmd.stderr
|
31
68
|
if out.downcase =~ /^error/
|
32
|
-
|
69
|
+
# TODO: we need to throw an exception here
|
70
|
+
# change once https://github.com/chef/inspec/issues/1205 is in
|
71
|
+
warn "Could not execute the sql query #{out}"
|
72
|
+
DatabaseHelper::SQLQueryResult.new(cmd, Hashie::Mash.new({}))
|
33
73
|
end
|
34
|
-
|
35
|
-
cmd
|
74
|
+
DatabaseHelper::SQLQueryResult.new(cmd, send(p, cmd.stdout))
|
36
75
|
end
|
37
76
|
|
38
77
|
def to_s
|
39
78
|
'Oracle Session'
|
40
79
|
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def verify_query(query)
|
84
|
+
# ensure we have a ; at the end
|
85
|
+
query + ';' if !query.strip.end_with?(';')
|
86
|
+
query
|
87
|
+
end
|
88
|
+
|
89
|
+
def parse_csv_result(stdout)
|
90
|
+
output = stdout.delete(/\r/)
|
91
|
+
table = CSV.parse(output, { headers: true })
|
92
|
+
|
93
|
+
# convert to hash
|
94
|
+
headers = table.headers
|
95
|
+
|
96
|
+
results = table.map { |row|
|
97
|
+
res = {}
|
98
|
+
headers.each { |header|
|
99
|
+
res[header.downcase] = row[header]
|
100
|
+
}
|
101
|
+
Hashie::Mash.new(res)
|
102
|
+
}
|
103
|
+
results
|
104
|
+
end
|
105
|
+
|
106
|
+
def parse_html_result(stdout) # rubocop:disable Metrics/AbcSize
|
107
|
+
result = stdout
|
108
|
+
# make oracle html valid html by removing the p tag, it does not include a closing tag
|
109
|
+
result = result.gsub('<p>', '').gsub('</p>', '').gsub('<br>', '')
|
110
|
+
doc = REXML::Document.new result
|
111
|
+
table = doc.elements['table']
|
112
|
+
hash = []
|
113
|
+
if !table.nil?
|
114
|
+
rows = table.elements.to_a
|
115
|
+
headers = rows[0].elements.to_a('th').map { |entry| entry.text.strip }
|
116
|
+
rows.delete_at(0)
|
117
|
+
|
118
|
+
# iterate over each row, first row is header
|
119
|
+
hash = []
|
120
|
+
if !rows.nil? && !rows.empty?
|
121
|
+
hash = rows.map { |row|
|
122
|
+
res = {}
|
123
|
+
entries = row.elements.to_a('td')
|
124
|
+
# ignore if we have empty entries, oracle is adding th rows in between
|
125
|
+
return nil if entries.empty?
|
126
|
+
headers.each_with_index { |header, index|
|
127
|
+
# we need htmlentities since we do not have nokogiri
|
128
|
+
coder = HTMLEntities.new
|
129
|
+
val = coder.decode(entries[index].text).strip
|
130
|
+
res[header.downcase] = val
|
131
|
+
}
|
132
|
+
Hashie::Mash.new(res)
|
133
|
+
}.compact
|
134
|
+
end
|
135
|
+
end
|
136
|
+
hash
|
137
|
+
end
|
41
138
|
end
|
42
139
|
end
|
data/lib/resources/os_env.rb
CHANGED
data/lib/resources/package.rb
CHANGED
@@ -19,7 +19,7 @@ module Inspec::Resources
|
|
19
19
|
end
|
20
20
|
"
|
21
21
|
|
22
|
-
def initialize(package_name = nil) # rubocop:disable Metrics/AbcSize
|
22
|
+
def initialize(package_name = nil, opts = {}) # rubocop:disable Metrics/AbcSize
|
23
23
|
@package_name = package_name
|
24
24
|
@name = @package_name
|
25
25
|
@cache = nil
|
@@ -30,7 +30,7 @@ module Inspec::Resources
|
|
30
30
|
if os.debian?
|
31
31
|
@pkgman = Deb.new(inspec)
|
32
32
|
elsif os.redhat? || %w{suse amazon fedora}.include?(os[:family])
|
33
|
-
@pkgman = Rpm.new(inspec)
|
33
|
+
@pkgman = Rpm.new(inspec, opts)
|
34
34
|
elsif ['arch'].include?(os[:family])
|
35
35
|
@pkgman = Pacman.new(inspec)
|
36
36
|
elsif ['darwin'].include?(os[:family])
|
@@ -46,6 +46,8 @@ module Inspec::Resources
|
|
46
46
|
else
|
47
47
|
return skip_resource 'The `package` resource is not supported on your OS yet.'
|
48
48
|
end
|
49
|
+
|
50
|
+
evaluate_missing_requirements
|
49
51
|
end
|
50
52
|
|
51
53
|
# returns true if the package is installed
|
@@ -71,6 +73,14 @@ module Inspec::Resources
|
|
71
73
|
def to_s
|
72
74
|
"System Package #{@package_name}"
|
73
75
|
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def evaluate_missing_requirements
|
80
|
+
missing_requirements_string = @pkgman.missing_requirements.uniq.join(', ')
|
81
|
+
return if missing_requirements_string.empty?
|
82
|
+
skip_resource "The following requirements are not met for this resource: #{missing_requirements_string}"
|
83
|
+
end
|
74
84
|
end
|
75
85
|
|
76
86
|
class PkgManagement
|
@@ -78,6 +88,12 @@ module Inspec::Resources
|
|
78
88
|
def initialize(inspec)
|
79
89
|
@inspec = inspec
|
80
90
|
end
|
91
|
+
|
92
|
+
def missing_requirements
|
93
|
+
# Each provider can provide an Array of missing requirements that will be
|
94
|
+
# combined into a `skip_resource` message
|
95
|
+
[]
|
96
|
+
end
|
81
97
|
end
|
82
98
|
|
83
99
|
# Debian / Ubuntu
|
@@ -104,8 +120,25 @@ module Inspec::Resources
|
|
104
120
|
|
105
121
|
# RHEL family
|
106
122
|
class Rpm < PkgManagement
|
123
|
+
def initialize(inspec, opts)
|
124
|
+
super(inspec)
|
125
|
+
|
126
|
+
@dbpath = opts.fetch(:rpm_dbpath, nil)
|
127
|
+
end
|
128
|
+
|
129
|
+
def missing_requirements
|
130
|
+
missing_requirements = []
|
131
|
+
|
132
|
+
unless @dbpath.nil? || inspec.directory(@dbpath).directory?
|
133
|
+
missing_requirements << "RPMDB #{@dbpath} does not exist"
|
134
|
+
end
|
135
|
+
|
136
|
+
missing_requirements
|
137
|
+
end
|
138
|
+
|
107
139
|
def info(package_name)
|
108
|
-
|
140
|
+
rpm_cmd = rpm_command(package_name)
|
141
|
+
cmd = inspec.command(rpm_cmd)
|
109
142
|
# CentOS does not return an error code if the package is not installed,
|
110
143
|
# therefore we need to check for emptyness
|
111
144
|
return nil if cmd.exit_status.to_i != 0 || cmd.stdout.chomp.empty?
|
@@ -133,6 +166,17 @@ module Inspec::Resources
|
|
133
166
|
type: 'rpm',
|
134
167
|
}
|
135
168
|
end
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
def rpm_command(package_name)
|
173
|
+
cmd = ''
|
174
|
+
cmd += 'rpm -qia'
|
175
|
+
cmd += " --dbpath #{@dbpath}" if @dbpath
|
176
|
+
cmd += ' ' + package_name
|
177
|
+
|
178
|
+
cmd
|
179
|
+
end
|
136
180
|
end
|
137
181
|
|
138
182
|
# MacOS / Darwin implementation
|
data/lib/resources/packages.rb
CHANGED
data/lib/resources/passwd.rb
CHANGED
data/lib/resources/postgres.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# author: Dominik Richter
|
4
4
|
# author: Christoph Hartmann
|
5
5
|
# author: Aaron Lippold
|
6
|
-
# license: All rights reserved
|
7
6
|
|
8
7
|
module Inspec::Resources
|
9
8
|
class Postgres < Inspec.resource(1)
|
@@ -11,8 +10,7 @@ module Inspec::Resources
|
|
11
10
|
|
12
11
|
attr_reader :service, :data_dir, :conf_dir, :conf_path, :version, :cluster
|
13
12
|
def initialize
|
14
|
-
|
15
|
-
if os.debian?
|
13
|
+
if inspec.os.debian?
|
16
14
|
#
|
17
15
|
# https://wiki.debian.org/PostgreSql
|
18
16
|
#
|
@@ -31,7 +29,7 @@ module Inspec::Resources
|
|
31
29
|
a version number and unversioned data directories were found.'
|
32
30
|
nil
|
33
31
|
else
|
34
|
-
@version = version_from_dir('/var/lib/pgsql
|
32
|
+
@version = version_from_dir('/var/lib/pgsql')
|
35
33
|
end
|
36
34
|
end
|
37
35
|
@data_dir = locate_data_dir_location_by_version(@version)
|
@@ -40,8 +38,14 @@ module Inspec::Resources
|
|
40
38
|
@service = 'postgresql'
|
41
39
|
@service += "-#{@version}" if @version.to_f >= 9.4
|
42
40
|
@conf_dir ||= @data_dir
|
41
|
+
|
43
42
|
verify_dirs
|
44
|
-
|
43
|
+
if !@version.nil? && !@conf_dir.empty?
|
44
|
+
@conf_path = File.join @conf_dir, 'postgresql.conf'
|
45
|
+
else
|
46
|
+
@conf_path = nil
|
47
|
+
return skip_resource 'Seems like PostgreSQL is not installed on your system'
|
48
|
+
end
|
45
49
|
end
|
46
50
|
|
47
51
|
def to_s
|
@@ -2,7 +2,7 @@
|
|
2
2
|
# copyright: 2015, Vulcano Security GmbH
|
3
3
|
# author: Dominik Richter
|
4
4
|
# author: Christoph Hartmann
|
5
|
-
#
|
5
|
+
# author: Aaron Lippold
|
6
6
|
|
7
7
|
require 'utils/simpleconfig'
|
8
8
|
require 'utils/find_files'
|
@@ -19,9 +19,13 @@ module Inspec::Resources
|
|
19
19
|
"
|
20
20
|
|
21
21
|
include FindFiles
|
22
|
+
include ObjectTraverser
|
22
23
|
|
23
24
|
def initialize(conf_path = nil)
|
24
25
|
@conf_path = conf_path || inspec.postgres.conf_path
|
26
|
+
if @conf_path.nil?
|
27
|
+
return skip_resource 'PostgreSQL conf path is not set'
|
28
|
+
end
|
25
29
|
@conf_dir = File.expand_path(File.dirname(@conf_path))
|
26
30
|
@files_contents = {}
|
27
31
|
@content = nil
|
@@ -42,8 +46,13 @@ module Inspec::Resources
|
|
42
46
|
res
|
43
47
|
end
|
44
48
|
|
45
|
-
def
|
46
|
-
|
49
|
+
def value(key)
|
50
|
+
extract_value(key, @params)
|
51
|
+
end
|
52
|
+
|
53
|
+
def method_missing(*keys)
|
54
|
+
keys.shift if keys.is_a?(Array) && keys[0] == :[]
|
55
|
+
param = value(keys)
|
47
56
|
return nil if param.nil?
|
48
57
|
# extract first value if we have only one value in array
|
49
58
|
return param[0] if param.length == 1
|