inspec 1.29.0 → 1.30.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|