inspec-core 4.10.4 → 4.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 431744ec266e2ed0cbde565cc232ee4bb944ff76076d8c10d381c73346796337
4
- data.tar.gz: a8a844283829729c5a886a9e324e7fc22b31a986eb25e36c0a01fe7a26fbda78
3
+ metadata.gz: 873c4c83c46030d3a3adb7093f085650f8400c6bbfc7c87984ebc77305aa660b
4
+ data.tar.gz: '091ccf8b261dfeafac84a771ec549e46f015dcc9e5da9836982788df40c456c1'
5
5
  SHA512:
6
- metadata.gz: 3678f2a31c972d7e6e08c3352249ce9bc72b82b7989a1758b70a3bdc41912cc0eb048bd70cc4da0794b929f53aacdfe6227e5defc13aeb3465cc5cc744393a8c
7
- data.tar.gz: 5ded1f1c7315194f7ae8a791aea4e21221b524debdc4136fe2e60911b9c050c9e948af0529aca1eff1dcfd1e68a8f3ab17be448b65aab202bf16c789d0e7b4a1
6
+ metadata.gz: ed9c35b0c96d17f96eea08658b251812ac3f03c4912a1663b9d5de600a5d6637460bf223ca90e9ca84625298b5dce64f999ac8d019c2b615b621247c48e1693a
7
+ data.tar.gz: fca132a2f7221c41c78bf7379ccf74e4c07cc412d36da10817d99adc15a9c2e5a049f6311a8c6bf323a8c7deffed18462d8fb660b0c9669d5182b477cf3ca585
data/README.md CHANGED
@@ -327,13 +327,13 @@ Remote Targets
327
327
 
328
328
  In addition, runtime support is provided for:
329
329
 
330
- | Platform | Versions |
331
- | -------- | -------- |
332
- | Debian | 8, 9 |
333
- | RHEL | 6, 7 |
334
- | Ubuntu | 12.04+ |
335
- | Windows | 7+ |
336
- | Windows | 2012+ |
330
+ | Platform | Versions | Arch |
331
+ | -------- | -------- | ------ |
332
+ | Debian | 8, 9 | x86_64 |
333
+ | RHEL | 6, 7 | x86_64 |
334
+ | Ubuntu | 12.04+ | x86_64 |
335
+ | Windows | 7+ | x86_64 |
336
+ | Windows | 2012+ | x86_64 |
337
337
 
338
338
  ## Documentation
339
339
 
@@ -1,6 +1,8 @@
1
1
  # copyright: 2015, Vulcano Security GmbH
2
2
 
3
3
  require "inspec/resource"
4
+ require "inspec/resources/platform"
5
+ require "inspec/resources/os"
4
6
 
5
7
  module Inspec::Resources
6
8
  class Cmd < Inspec.resource(1)
@@ -1,81 +1,83 @@
1
1
  require "openssl"
2
2
  require "inspec/utils/file_reader"
3
3
 
4
- class DhParams < Inspec.resource(1)
5
- name "dh_params"
6
- supports platform: "unix"
7
- desc '
8
- Use the `dh_params` InSpec audit resource to test Diffie-Hellman (DH)
9
- parameters.
10
- '
11
-
12
- example <<~EXAMPLE
13
- describe dh_params('/path/to/file.dh_pem') do
14
- it { should be_dh_params }
15
- it { should be_valid }
16
- its('generator') { should eq 2 }
17
- its('modulus') { should eq '00:91:a0:15:89:e5:bc:38:93:12:02:fc:...' }
18
- its('prime_length') { should eq 2048 }
19
- its('pem') { should eq '-----BEGIN DH PARAMETERS...' }
20
- its('text') { should eq 'PKCS#3 DH Parameters: (2048 bit)...' }
4
+ module Inspec::Resources
5
+ class DhParams < Inspec.resource(1)
6
+ name "dh_params"
7
+ supports platform: "unix"
8
+ desc '
9
+ Use the `dh_params` InSpec audit resource to test Diffie-Hellman (DH)
10
+ parameters.
11
+ '
12
+
13
+ example <<~EXAMPLE
14
+ describe dh_params('/path/to/file.dh_pem') do
15
+ it { should be_dh_params }
16
+ it { should be_valid }
17
+ its('generator') { should eq 2 }
18
+ its('modulus') { should eq '00:91:a0:15:89:e5:bc:38:93:12:02:fc:...' }
19
+ its('prime_length') { should eq 2048 }
20
+ its('pem') { should eq '-----BEGIN DH PARAMETERS...' }
21
+ its('text') { should eq 'PKCS#3 DH Parameters: (2048 bit)...' }
22
+ end
23
+ EXAMPLE
24
+
25
+ include FileReader
26
+
27
+ def initialize(filename)
28
+ @dh_params_path = filename
29
+ @dh_params = OpenSSL::PKey::DH.new read_file_content(@dh_params_path)
21
30
  end
22
- EXAMPLE
23
31
 
24
- include FileReader
25
-
26
- def initialize(filename)
27
- @dh_params_path = filename
28
- @dh_params = OpenSSL::PKey::DH.new read_file_content(@dh_params_path)
29
- end
30
-
31
- # it { should be_dh_params }
32
- def dh_params?
33
- !@dh_params.nil?
34
- end
32
+ # it { should be_dh_params }
33
+ def dh_params?
34
+ !@dh_params.nil?
35
+ end
35
36
 
36
- # its('generator') { should eq 2 }
37
- def generator
38
- return if @dh_params.nil?
37
+ # its('generator') { should eq 2 }
38
+ def generator
39
+ return if @dh_params.nil?
39
40
 
40
- @dh_params.g.to_i
41
- end
41
+ @dh_params.g.to_i
42
+ end
42
43
 
43
- # its('modulus') { should eq '00:91:a0:15:89:e5:bc:38:93:12:02:fc:...' }
44
- def modulus
45
- return if @dh_params.nil?
44
+ # its('modulus') { should eq '00:91:a0:15:89:e5:bc:38:93:12:02:fc:...' }
45
+ def modulus
46
+ return if @dh_params.nil?
46
47
 
47
- "00:" + @dh_params.p.to_s(16).downcase.scan(/.{2}/).join(":")
48
- end
48
+ "00:" + @dh_params.p.to_s(16).downcase.scan(/.{2}/).join(":")
49
+ end
49
50
 
50
- # its('pem') { should eq '-----BEGIN DH PARAMETERS...' }
51
- def pem
52
- return if @dh_params.nil?
51
+ # its('pem') { should eq '-----BEGIN DH PARAMETERS...' }
52
+ def pem
53
+ return if @dh_params.nil?
53
54
 
54
- @dh_params.to_pem
55
- end
55
+ @dh_params.to_pem
56
+ end
56
57
 
57
- # its('prime_length') { should be 2048 }
58
- def prime_length
59
- return if @dh_params.nil?
58
+ # its('prime_length') { should be 2048 }
59
+ def prime_length
60
+ return if @dh_params.nil?
60
61
 
61
- @dh_params.p.num_bits
62
- end
62
+ @dh_params.p.num_bits
63
+ end
63
64
 
64
- # its('text') { should eq 'human-readable-text' }
65
- def text
66
- return if @dh_params.nil?
65
+ # its('text') { should eq 'human-readable-text' }
66
+ def text
67
+ return if @dh_params.nil?
67
68
 
68
- @dh_params.to_text
69
- end
69
+ @dh_params.to_text
70
+ end
70
71
 
71
- # it { should be_valid }
72
- def valid?
73
- return if @dh_params.nil?
72
+ # it { should be_valid }
73
+ def valid?
74
+ return if @dh_params.nil?
74
75
 
75
- @dh_params.params_ok?
76
- end
76
+ @dh_params.params_ok?
77
+ end
77
78
 
78
- def to_s
79
- "dh_params #{@dh_params_path}"
79
+ def to_s
80
+ "dh_params #{@dh_params_path}"
81
+ end
80
82
  end
81
83
  end
@@ -1,62 +1,64 @@
1
1
  require "inspec/utils/parser"
2
2
  require "inspec/utils/file_reader"
3
3
 
4
- class EtcHosts < Inspec.resource(1)
5
- name "etc_hosts"
6
- supports platform: "linux"
7
- supports platform: "bsd"
8
- supports platform: "windows"
9
- desc 'Use the etc_hosts InSpec audit resource to find an
10
- ip_address and its associated hosts'
11
- example <<~EXAMPLE
12
- describe etc_hosts.where { ip_address == '127.0.0.1' } do
13
- its('ip_address') { should cmp '127.0.0.1' }
14
- its('primary_name') { should cmp 'localhost' }
15
- its('all_host_names') { should eq [['localhost', 'localhost.localdomain', 'localhost4', 'localhost4.localdomain4']] }
16
- end
17
- EXAMPLE
4
+ module Inspec::Resources
5
+ class EtcHosts < Inspec.resource(1)
6
+ name "etc_hosts"
7
+ supports platform: "linux"
8
+ supports platform: "bsd"
9
+ supports platform: "windows"
10
+ desc 'Use the etc_hosts InSpec audit resource to find an
11
+ ip_address and its associated hosts'
12
+ example <<~EXAMPLE
13
+ describe etc_hosts.where { ip_address == '127.0.0.1' } do
14
+ its('ip_address') { should cmp '127.0.0.1' }
15
+ its('primary_name') { should cmp 'localhost' }
16
+ its('all_host_names') { should eq [['localhost', 'localhost.localdomain', 'localhost4', 'localhost4.localdomain4']] }
17
+ end
18
+ EXAMPLE
18
19
 
19
- attr_reader :params
20
+ attr_reader :params
20
21
 
21
- include CommentParser
22
- include FileReader
22
+ include CommentParser
23
+ include FileReader
23
24
 
24
- DEFAULT_UNIX_PATH = "/etc/hosts".freeze
25
- DEFAULT_WINDOWS_PATH = 'C:\windows\system32\drivers\etc\hosts'.freeze
25
+ DEFAULT_UNIX_PATH = "/etc/hosts".freeze
26
+ DEFAULT_WINDOWS_PATH = 'C:\windows\system32\drivers\etc\hosts'.freeze
26
27
 
27
- def initialize(hosts_path = nil)
28
- content = read_file_content(hosts_path || default_hosts_file_path)
28
+ def initialize(hosts_path = nil)
29
+ content = read_file_content(hosts_path || default_hosts_file_path)
29
30
 
30
- @params = parse_conf(content.lines)
31
- end
31
+ @params = parse_conf(content.lines)
32
+ end
32
33
 
33
- FilterTable.create
34
- .register_column(:ip_address, field: "ip_address")
35
- .register_column(:primary_name, field: "primary_name")
36
- .register_column(:all_host_names, field: "all_host_names")
37
- .install_filter_methods_on_resource(self, :params)
34
+ FilterTable.create
35
+ .register_column(:ip_address, field: "ip_address")
36
+ .register_column(:primary_name, field: "primary_name")
37
+ .register_column(:all_host_names, field: "all_host_names")
38
+ .install_filter_methods_on_resource(self, :params)
38
39
 
39
- private
40
+ private
40
41
 
41
- def default_hosts_file_path
42
- inspec.os.windows? ? DEFAULT_WINDOWS_PATH : DEFAULT_UNIX_PATH
43
- end
42
+ def default_hosts_file_path
43
+ inspec.os.windows? ? DEFAULT_WINDOWS_PATH : DEFAULT_UNIX_PATH
44
+ end
44
45
 
45
- def parse_conf(lines)
46
- lines.reject(&:empty?).reject(&comment?).map(&parse_data).map(&format_data)
47
- end
46
+ def parse_conf(lines)
47
+ lines.reject(&:empty?).reject(&comment?).map(&parse_data).map(&format_data)
48
+ end
48
49
 
49
- def comment?
50
- parse_options = { comment_char: "#", standalone_comments: false }
50
+ def comment?
51
+ parse_options = { comment_char: "#", standalone_comments: false }
51
52
 
52
- ->(data) { parse_comment_line(data, parse_options).first.empty? }
53
- end
53
+ ->(data) { parse_comment_line(data, parse_options).first.empty? }
54
+ end
54
55
 
55
- def parse_data
56
- ->(data) { [data.split[0], data.split[1], data.split[1..-1]] }
57
- end
56
+ def parse_data
57
+ ->(data) { [data.split[0], data.split[1], data.split[1..-1]] }
58
+ end
58
59
 
59
- def format_data
60
- ->(data) { %w{ip_address primary_name all_host_names}.zip(data).to_h }
60
+ def format_data
61
+ ->(data) { %w{ip_address primary_name all_host_names}.zip(data).to_h }
62
+ end
61
63
  end
62
64
  end
@@ -164,22 +164,40 @@ module Inspec::Resources
164
164
  # OSX uses opendirectory for groups, so `/etc/group` may not be fully accurate
165
165
  # This uses `dscacheutil` to get the group info instead of `etc_group`
166
166
  class DarwinGroup < GroupInfo
167
+ def runmap(cmd, &blk)
168
+ hashmap(inspec.command(cmd).stdout.lines, &blk)
169
+ end
170
+
171
+ def hashmap(enum, &blk)
172
+ enum.map(&blk).to_h
173
+ end
174
+
167
175
  def groups
168
- group_info = inspec.command("dscacheutil -q group").stdout.split("\n\n")
176
+ group_by_id = runmap("dscl . -list /Groups PrimaryGroupID") { |l| name, id = l.split; [id.to_i, name] }
177
+ userss = runmap("dscl . -list /Users PrimaryGroupID") { |l| name, id = l.split; [name, id.to_i] }
178
+ membership = runmap("dscl . -list /Groups GroupMembership") { |l| key, *vs = l.split; [key, vs] }
179
+ membership.default_proc = ->(h, k) { h[k] = [] }
180
+
181
+ users_by_group = hashmap(userss.keys.group_by { |k| userss[k] }) { |k, vs| [group_by_id[k], vs] }
182
+ users_by_group.each do |name, users|
183
+ membership[name].concat users
184
+ end
185
+
186
+ group_info = inspec.command("dscacheutil -q group").stdout.split("\n\n").uniq
169
187
 
170
- groups = []
171
188
  regex = /^([^:]*?)\s*:\s(.*?)\s*$/
172
- group_info.each do |data|
173
- groups << inspec.parse_config(data, assignment_regex: regex).params
189
+ groups = group_info.map do |data|
190
+ inspec.parse_config(data, assignment_regex: regex).params
174
191
  end
175
192
 
176
193
  # Convert the `dscacheutil` groups to match `inspec.etc_group.entries`
177
194
  groups.each { |g| g["gid"] = g["gid"].to_i }
178
195
  groups.each do |g|
179
- next if g["users"].nil?
180
-
181
- g["members"] = g.delete("users")
182
- g["members"].tr!(" ", ",")
196
+ users = g.delete("users") || ""
197
+ users = users.split
198
+ users += Array(users_by_group[g["name"]])
199
+ g["members"] = users
200
+ g["members"].sort.join ","
183
201
  end
184
202
  end
185
203
  end
@@ -1,228 +1,230 @@
1
1
  require "inspec/utils/simpleconfig"
2
2
  require "inspec/utils/file_reader"
3
3
 
4
- class GrubConfig < Inspec.resource(1)
5
- name "grub_conf"
6
- supports platform: "unix"
7
- desc "Use the grub_conf InSpec audit resource to test the boot config of Linux systems that use Grub."
8
- example <<~EXAMPLE
9
- describe grub_conf('/etc/grub.conf', 'default') do
10
- its('kernel') { should include '/vmlinuz-2.6.32-573.7.1.el6.x86_64' }
11
- its('initrd') { should include '/initramfs-2.6.32-573.el6.x86_64.img=1' }
12
- its('default') { should_not eq '1' }
13
- its('timeout') { should eq '5' }
14
- end
4
+ module Inspec::Resources
5
+ class GrubConfig < Inspec.resource(1)
6
+ name "grub_conf"
7
+ supports platform: "unix"
8
+ desc "Use the grub_conf InSpec audit resource to test the boot config of Linux systems that use Grub."
9
+ example <<~EXAMPLE
10
+ describe grub_conf('/etc/grub.conf', 'default') do
11
+ its('kernel') { should include '/vmlinuz-2.6.32-573.7.1.el6.x86_64' }
12
+ its('initrd') { should include '/initramfs-2.6.32-573.el6.x86_64.img=1' }
13
+ its('default') { should_not eq '1' }
14
+ its('timeout') { should eq '5' }
15
+ end
15
16
 
16
- also check specific kernels
17
- describe grub_conf('/etc/grub.conf', 'CentOS (2.6.32-573.12.1.el6.x86_64)') do
18
- its('kernel') { should include 'audit=1' }
19
- end
20
- EXAMPLE
17
+ also check specific kernels
18
+ describe grub_conf('/etc/grub.conf', 'CentOS (2.6.32-573.12.1.el6.x86_64)') do
19
+ its('kernel') { should include 'audit=1' }
20
+ end
21
+ EXAMPLE
21
22
 
22
- include FileReader
23
+ include FileReader
23
24
 
24
- class UnknownGrubConfig < StandardError; end
25
+ class UnknownGrubConfig < StandardError; end
25
26
 
26
- def initialize(path = nil, kernel = nil)
27
- config_for_platform(path)
28
- @content = read_file(@conf_path)
29
- @kernel = kernel || "default"
30
- rescue UnknownGrubConfig
31
- skip_resource "The `grub_config` resource is not supported on your OS yet."
32
- end
27
+ def initialize(path = nil, kernel = nil)
28
+ config_for_platform(path)
29
+ @content = read_file(@conf_path)
30
+ @kernel = kernel || "default"
31
+ rescue UnknownGrubConfig
32
+ skip_resource "The `grub_config` resource is not supported on your OS yet."
33
+ end
33
34
 
34
- def config_for_platform(path)
35
- os = inspec.os
36
- if os.redhat? || os[:name] == "fedora"
37
- config_for_redhatish(path)
38
- elsif os.debian?
39
- @conf_path = path || "/boot/grub/grub.cfg"
40
- @defaults_path = "/etc/default/grub"
41
- @grubenv_path = "/boot/grub2/grubenv"
42
- @version = "grub2"
43
- elsif os[:name] == "amazon"
44
- @conf_path = path || "/etc/grub.conf"
45
- @version = "legacy"
46
- else
47
- raise UnknownGrubConfig
35
+ def config_for_platform(path)
36
+ os = inspec.os
37
+ if os.redhat? || os[:name] == "fedora"
38
+ config_for_redhatish(path)
39
+ elsif os.debian?
40
+ @conf_path = path || "/boot/grub/grub.cfg"
41
+ @defaults_path = "/etc/default/grub"
42
+ @grubenv_path = "/boot/grub2/grubenv"
43
+ @version = "grub2"
44
+ elsif os[:name] == "amazon"
45
+ @conf_path = path || "/etc/grub.conf"
46
+ @version = "legacy"
47
+ else
48
+ raise UnknownGrubConfig
49
+ end
48
50
  end
49
- end
50
51
 
51
- def config_for_redhatish(path)
52
- if inspec.os[:release].to_f < 7
53
- @conf_path = path || "/etc/grub.conf"
54
- @version = "legacy"
55
- else
56
- @conf_path = path || "/boot/grub2/grub.cfg"
57
- @defaults_path = "/etc/default/grub"
58
- @grubenv_path = "/boot/grub2/grubenv"
59
- @version = "grub2"
52
+ def config_for_redhatish(path)
53
+ if inspec.os[:release].to_f < 7
54
+ @conf_path = path || "/etc/grub.conf"
55
+ @version = "legacy"
56
+ else
57
+ @conf_path = path || "/boot/grub2/grub.cfg"
58
+ @defaults_path = "/etc/default/grub"
59
+ @grubenv_path = "/boot/grub2/grubenv"
60
+ @version = "grub2"
61
+ end
60
62
  end
61
- end
62
63
 
63
- def method_missing(name)
64
- read_params[name.to_s]
65
- end
64
+ def method_missing(name)
65
+ read_params[name.to_s]
66
+ end
66
67
 
67
- def to_s
68
- "Grub Config"
69
- end
68
+ def to_s
69
+ "Grub Config"
70
+ end
70
71
 
71
- private
72
+ private
72
73
 
73
- ######################################################################
74
- # Grub2 This is used by all supported versions of Ubuntu and Rhel 7+ #
75
- ######################################################################
74
+ ######################################################################
75
+ # Grub2 This is used by all supported versions of Ubuntu and Rhel 7+ #
76
+ ######################################################################
76
77
 
77
- def grub2_parse_kernel_lines(content, conf)
78
- menu_entries = extract_menu_entries(content)
78
+ def grub2_parse_kernel_lines(content, conf)
79
+ menu_entries = extract_menu_entries(content)
79
80
 
80
- if @kernel == "default"
81
- default_menu_entry(menu_entries, conf["GRUB_DEFAULT"])
82
- else
83
- menu_entries.find { |entry| entry["name"] == @kernel }
81
+ if @kernel == "default"
82
+ default_menu_entry(menu_entries, conf["GRUB_DEFAULT"])
83
+ else
84
+ menu_entries.find { |entry| entry["name"] == @kernel }
85
+ end
84
86
  end
85
- end
86
87
 
87
- def extract_menu_entries(content)
88
- menu_entries = []
88
+ def extract_menu_entries(content)
89
+ menu_entries = []
89
90
 
90
- lines = content.split("\n")
91
- lines.each_with_index do |line, index|
92
- next unless line =~ /^menuentry\s+.*/
91
+ lines = content.split("\n")
92
+ lines.each_with_index do |line, index|
93
+ next unless line =~ /^menuentry\s+.*/
93
94
 
94
- entry = {}
95
- entry["insmod"] = []
95
+ entry = {}
96
+ entry["insmod"] = []
96
97
 
97
- # Extract name from menuentry line
98
- capture_data = line.match(/(?:^|\s+).*menuentry\s*['|"](.*)['|"]\s*--/)
99
- if capture_data.nil? || capture_data.captures[0].nil?
100
- raise Inspec::Exceptions::ResourceFailed "Failed to extract menuentry name from #{line}"
101
- end
98
+ # Extract name from menuentry line
99
+ capture_data = line.match(/(?:^|\s+).*menuentry\s*['|"](.*)['|"]\s*--/)
100
+ if capture_data.nil? || capture_data.captures[0].nil?
101
+ raise Inspec::Exceptions::ResourceFailed "Failed to extract menuentry name from #{line}"
102
+ end
102
103
 
103
- entry["name"] = capture_data.captures[0]
104
-
105
- # Begin processing from index forward until a `}` line is met
106
- lines.drop(index + 1).each do |mline|
107
- break if mline =~ /^\s*}\s*$/
108
-
109
- case mline
110
- when /(?:^|\s*)initrd.*/
111
- entry["initrd"] = mline.split(" ")[1]
112
- when /(?:^|\s*)linux.*/
113
- entry["kernel"] = mline.split
114
- when /(?:^|\s*)set root=.*/
115
- entry["root"] = mline.split("=")[1].tr("'", "")
116
- when /(?:^|\s*)insmod.*/
117
- entry["insmod"] << mline.split(" ")[1]
104
+ entry["name"] = capture_data.captures[0]
105
+
106
+ # Begin processing from index forward until a `}` line is met
107
+ lines.drop(index + 1).each do |mline|
108
+ break if mline =~ /^\s*}\s*$/
109
+
110
+ case mline
111
+ when /(?:^|\s*)initrd.*/
112
+ entry["initrd"] = mline.split(" ")[1]
113
+ when /(?:^|\s*)linux.*/
114
+ entry["kernel"] = mline.split
115
+ when /(?:^|\s*)set root=.*/
116
+ entry["root"] = mline.split("=")[1].tr("'", "")
117
+ when /(?:^|\s*)insmod.*/
118
+ entry["insmod"] << mline.split(" ")[1]
119
+ end
118
120
  end
121
+
122
+ menu_entries << entry
119
123
  end
120
124
 
121
- menu_entries << entry
125
+ menu_entries
122
126
  end
123
127
 
124
- menu_entries
125
- end
126
-
127
- def default_menu_entry(menu_entries, default)
128
- # If the default entry isn't `saved` then a number is used as an index.
129
- # By default this is `0`, which would be the first item in the list.
130
- return menu_entries[default.to_i] unless default == "saved"
128
+ def default_menu_entry(menu_entries, default)
129
+ # If the default entry isn't `saved` then a number is used as an index.
130
+ # By default this is `0`, which would be the first item in the list.
131
+ return menu_entries[default.to_i] unless default == "saved"
131
132
 
132
- grubenv_contents = inspec.file(@grubenv_path).content
133
+ grubenv_contents = inspec.file(@grubenv_path).content
133
134
 
134
- # The location of the grubenv file is not guaranteed. In the case that
135
- # the file does not exist this will return the 0th entry. This will also
136
- # return the 0th entry if InSpec lacks permission to read the file. Both
137
- # of these reflect the default Grub2 behavior.
138
- return menu_entries[0] if grubenv_contents.nil?
135
+ # The location of the grubenv file is not guaranteed. In the case that
136
+ # the file does not exist this will return the 0th entry. This will also
137
+ # return the 0th entry if InSpec lacks permission to read the file. Both
138
+ # of these reflect the default Grub2 behavior.
139
+ return menu_entries[0] if grubenv_contents.nil?
139
140
 
140
- default_name = SimpleConfig.new(grubenv_contents).params["saved_entry"]
141
- default_entry = menu_entries.select { |k| k["name"] == default_name }[0]
142
- return default_entry unless default_entry.nil?
141
+ default_name = SimpleConfig.new(grubenv_contents).params["saved_entry"]
142
+ default_entry = menu_entries.select { |k| k["name"] == default_name }[0]
143
+ return default_entry unless default_entry.nil?
143
144
 
144
- # It is possible for the saved entry to not be valid . For example, grubenv
145
- # not being up to date. If so, the 0th entry is the default.
146
- menu_entries[0]
147
- end
145
+ # It is possible for the saved entry to not be valid . For example, grubenv
146
+ # not being up to date. If so, the 0th entry is the default.
147
+ menu_entries[0]
148
+ end
148
149
 
149
- ###################################################################
150
- # Grub1 aka legacy-grub config. Primarily used by Centos/Rhel 6.x #
151
- ###################################################################
152
-
153
- def parse_kernel_lines(content, conf)
154
- # Find all "title" lines and then parse them into arrays
155
- menu_entry = 0
156
- lines = content.split("\n")
157
- kernel_opts = {}
158
- lines.each_with_index do |file_line, index|
159
- next unless file_line =~ /^title.*/
160
-
161
- current_kernel = file_line.split(" ", 2)[1]
162
- lines.drop(index + 1).each do |kernel_line|
163
- if kernel_line =~ /^\s.*/
164
- option_type = kernel_line.split(" ")[0]
165
- line_options = kernel_line.split(" ").drop(1)
166
- if (menu_entry == conf["default"].to_i && @kernel == "default") || current_kernel == @kernel
167
- if option_type == "kernel"
168
- kernel_opts["kernel"] = line_options
169
- else
170
- kernel_opts[option_type] = line_options[0]
150
+ ###################################################################
151
+ # Grub1 aka legacy-grub config. Primarily used by Centos/Rhel 6.x #
152
+ ###################################################################
153
+
154
+ def parse_kernel_lines(content, conf)
155
+ # Find all "title" lines and then parse them into arrays
156
+ menu_entry = 0
157
+ lines = content.split("\n")
158
+ kernel_opts = {}
159
+ lines.each_with_index do |file_line, index|
160
+ next unless file_line =~ /^title.*/
161
+
162
+ current_kernel = file_line.split(" ", 2)[1]
163
+ lines.drop(index + 1).each do |kernel_line|
164
+ if kernel_line =~ /^\s.*/
165
+ option_type = kernel_line.split(" ")[0]
166
+ line_options = kernel_line.split(" ").drop(1)
167
+ if (menu_entry == conf["default"].to_i && @kernel == "default") || current_kernel == @kernel
168
+ if option_type == "kernel"
169
+ kernel_opts["kernel"] = line_options
170
+ else
171
+ kernel_opts[option_type] = line_options[0]
172
+ end
171
173
  end
174
+ else
175
+ menu_entry += 1
176
+ break
172
177
  end
173
- else
174
- menu_entry += 1
175
- break
176
178
  end
177
179
  end
180
+ kernel_opts
178
181
  end
179
- kernel_opts
180
- end
181
182
 
182
- def read_file(config_file)
183
- read_file_content(config_file)
184
- end
183
+ def read_file(config_file)
184
+ read_file_content(config_file)
185
+ end
185
186
 
186
- def read_params
187
- return @params if defined?(@params)
188
-
189
- content = read_file(@conf_path)
190
-
191
- if @version == "legacy"
192
- # parse the file
193
- conf = SimpleConfig.new(
194
- content,
195
- multiple_values: true
196
- ).params
197
- # convert single entry arrays into strings
198
- conf.each do |key, value|
199
- if value.size == 1
200
- conf[key] = conf[key][0].to_s
187
+ def read_params
188
+ return @params if defined?(@params)
189
+
190
+ content = read_file(@conf_path)
191
+
192
+ if @version == "legacy"
193
+ # parse the file
194
+ conf = SimpleConfig.new(
195
+ content,
196
+ multiple_values: true
197
+ ).params
198
+ # convert single entry arrays into strings
199
+ conf.each do |key, value|
200
+ if value.size == 1
201
+ conf[key] = conf[key][0].to_s
202
+ end
201
203
  end
204
+ kernel_opts = parse_kernel_lines(content, conf)
205
+ @params = conf.merge(kernel_opts)
202
206
  end
203
- kernel_opts = parse_kernel_lines(content, conf)
204
- @params = conf.merge(kernel_opts)
205
- end
206
207
 
207
- if @version == "grub2"
208
- # read defaults
209
- defaults = read_file(@defaults_path)
208
+ if @version == "grub2"
209
+ # read defaults
210
+ defaults = read_file(@defaults_path)
210
211
 
211
- conf = SimpleConfig.new(
212
- defaults,
213
- multiple_values: true
214
- ).params
212
+ conf = SimpleConfig.new(
213
+ defaults,
214
+ multiple_values: true
215
+ ).params
215
216
 
216
- # convert single entry arrays into strings
217
- conf.each do |key, value|
218
- if value.size == 1
219
- conf[key] = conf[key][0].to_s
217
+ # convert single entry arrays into strings
218
+ conf.each do |key, value|
219
+ if value.size == 1
220
+ conf[key] = conf[key][0].to_s
221
+ end
220
222
  end
221
- end
222
223
 
223
- kernel_opts = grub2_parse_kernel_lines(content, conf)
224
- @params = conf.merge(kernel_opts)
224
+ kernel_opts = grub2_parse_kernel_lines(content, conf)
225
+ @params = conf.merge(kernel_opts)
226
+ end
227
+ @params
225
228
  end
226
- @params
227
229
  end
228
230
  end
@@ -5,125 +5,127 @@ require "inspec/resources/powershell"
5
5
  # check for web applications in IIS
6
6
  # Note: this is only supported in windows 2012 and later
7
7
 
8
- class IisAppPool < Inspec.resource(1)
9
- name "iis_app_pool"
10
- desc "Tests IIS application pool configuration on windows."
11
- supports platform: "windows"
12
- example <<~EXAMPLE
13
- describe iis_app_pool('DefaultAppPool') do
14
- it { should exist }
15
- its('enable32bit') { should cmp 'True' }
16
- its('runtime_version') { should eq 'v4.0' }
17
- its('pipeline_mode') { should eq 'Integrated' }
18
- end
19
- EXAMPLE
20
-
21
- def initialize(pool_name)
22
- @pool_name = pool_name
23
- @pool_path = "IIS:\\AppPools\\#{@pool_name}"
24
- @cache = nil
25
-
26
- # verify that this resource is only supported on Windows
27
- return skip_resource "The `iis_app_pool` resource is not supported on your OS." unless inspec.os.windows?
28
- end
29
-
30
- def pool_name
31
- iis_app_pool[:pool_name]
32
- end
33
-
34
- def runtime_version
35
- iis_app_pool[:version]
36
- end
8
+ module Inspec::Resources
9
+ class IisAppPool < Inspec.resource(1)
10
+ name "iis_app_pool"
11
+ desc "Tests IIS application pool configuration on windows."
12
+ supports platform: "windows"
13
+ example <<~EXAMPLE
14
+ describe iis_app_pool('DefaultAppPool') do
15
+ it { should exist }
16
+ its('enable32bit') { should cmp 'True' }
17
+ its('runtime_version') { should eq 'v4.0' }
18
+ its('pipeline_mode') { should eq 'Integrated' }
19
+ end
20
+ EXAMPLE
21
+
22
+ def initialize(pool_name)
23
+ @pool_name = pool_name
24
+ @pool_path = "IIS:\\AppPools\\#{@pool_name}"
25
+ @cache = nil
26
+
27
+ # verify that this resource is only supported on Windows
28
+ return skip_resource "The `iis_app_pool` resource is not supported on your OS." unless inspec.os.windows?
29
+ end
37
30
 
38
- def enable32bit
39
- iis_app_pool[:e32b]
40
- end
31
+ def pool_name
32
+ iis_app_pool[:pool_name]
33
+ end
41
34
 
42
- def pipeline_mode
43
- iis_app_pool[:mode]
44
- end
35
+ def runtime_version
36
+ iis_app_pool[:version]
37
+ end
45
38
 
46
- def max_processes
47
- iis_app_pool[:processes]
48
- end
39
+ def enable32bit
40
+ iis_app_pool[:e32b]
41
+ end
49
42
 
50
- def timeout
51
- iis_app_pool[:timeout]
52
- end
43
+ def pipeline_mode
44
+ iis_app_pool[:mode]
45
+ end
53
46
 
54
- def timeout_days
55
- iis_app_pool[:timeout_days]
56
- end
47
+ def max_processes
48
+ iis_app_pool[:processes]
49
+ end
57
50
 
58
- def timeout_hours
59
- iis_app_pool[:timeout_hours]
60
- end
51
+ def timeout
52
+ iis_app_pool[:timeout]
53
+ end
61
54
 
62
- def timeout_minutes
63
- iis_app_pool[:timeout_minutes]
64
- end
55
+ def timeout_days
56
+ iis_app_pool[:timeout_days]
57
+ end
65
58
 
66
- def timeout_seconds
67
- iis_app_pool[:timeout_seconds]
68
- end
59
+ def timeout_hours
60
+ iis_app_pool[:timeout_hours]
61
+ end
69
62
 
70
- def user_identity_type
71
- iis_app_pool[:user_identity_type]
72
- end
63
+ def timeout_minutes
64
+ iis_app_pool[:timeout_minutes]
65
+ end
73
66
 
74
- def username
75
- iis_app_pool[:username]
76
- end
67
+ def timeout_seconds
68
+ iis_app_pool[:timeout_seconds]
69
+ end
77
70
 
78
- def exists?
79
- !iis_app_pool[:pool_name].empty?
80
- end
71
+ def user_identity_type
72
+ iis_app_pool[:user_identity_type]
73
+ end
81
74
 
82
- def to_s
83
- "IIS App Pool '#{@pool_name}'"
84
- end
75
+ def username
76
+ iis_app_pool[:username]
77
+ end
85
78
 
86
- private
79
+ def exists?
80
+ !iis_app_pool[:pool_name].empty?
81
+ end
87
82
 
88
- def iis_app_pool
89
- return @cache unless @cache.nil?
83
+ def to_s
84
+ "IIS App Pool '#{@pool_name}'"
85
+ end
90
86
 
91
- # We use `-Compress` here to avoid a bug in PowerShell
92
- # It does not affect validity of the output, only the representation
93
- # See: https://github.com/inspec/inspec/pull/3842
94
- script = <<~EOH
95
- Import-Module WebAdministration
96
- If (Test-Path '#{@pool_path}') {
97
- Get-Item '#{@pool_path}' | Select-Object * | ConvertTo-Json -Compress
98
- } Else {
99
- Write-Host '{}'
87
+ private
88
+
89
+ def iis_app_pool
90
+ return @cache unless @cache.nil?
91
+
92
+ # We use `-Compress` here to avoid a bug in PowerShell
93
+ # It does not affect validity of the output, only the representation
94
+ # See: https://github.com/inspec/inspec/pull/3842
95
+ script = <<~EOH
96
+ Import-Module WebAdministration
97
+ If (Test-Path '#{@pool_path}') {
98
+ Get-Item '#{@pool_path}' | Select-Object * | ConvertTo-Json -Compress
99
+ } Else {
100
+ Write-Host '{}'
101
+ }
102
+ EOH
103
+ cmd = inspec.powershell(script)
104
+
105
+ begin
106
+ pool = JSON.parse(cmd.stdout)
107
+ rescue JSON::ParserError => _e
108
+ raise Inspec::Exceptions::ResourceFailed, "Unable to parse app pool JSON"
109
+ end
110
+
111
+ process_model = pool.fetch("processModel", {})
112
+ idle_timeout = process_model.fetch("idleTimeout", {})
113
+
114
+ # map our values to a hash table
115
+ @cache = {
116
+ pool_name: pool["name"],
117
+ version: pool["managedRuntimeVersion"],
118
+ e32b: pool["enable32BitAppOnWin64"],
119
+ mode: pool["managedPipelineMode"],
120
+ processes: process_model["maxProcesses"],
121
+ timeout: "#{idle_timeout["Hours"]}:#{idle_timeout["Minutes"]}:#{idle_timeout["Seconds"]}",
122
+ timeout_days: idle_timeout["Days"],
123
+ timeout_hours: idle_timeout["Hours"],
124
+ timeout_minutes: idle_timeout["Minutes"],
125
+ timeout_seconds: idle_timeout["Seconds"],
126
+ user_identity_type: process_model["identityType"],
127
+ username: process_model["userName"],
100
128
  }
101
- EOH
102
- cmd = inspec.powershell(script)
103
-
104
- begin
105
- pool = JSON.parse(cmd.stdout)
106
- rescue JSON::ParserError => _e
107
- raise Inspec::Exceptions::ResourceFailed, "Unable to parse app pool JSON"
108
- end
109
-
110
- process_model = pool.fetch("processModel", {})
111
- idle_timeout = process_model.fetch("idleTimeout", {})
112
-
113
- # map our values to a hash table
114
- @cache = {
115
- pool_name: pool["name"],
116
- version: pool["managedRuntimeVersion"],
117
- e32b: pool["enable32BitAppOnWin64"],
118
- mode: pool["managedPipelineMode"],
119
- processes: process_model["maxProcesses"],
120
- timeout: "#{idle_timeout["Hours"]}:#{idle_timeout["Minutes"]}:#{idle_timeout["Seconds"]}",
121
- timeout_days: idle_timeout["Days"],
122
- timeout_hours: idle_timeout["Hours"],
123
- timeout_minutes: idle_timeout["Minutes"],
124
- timeout_seconds: idle_timeout["Seconds"],
125
- user_identity_type: process_model["identityType"],
126
- username: process_model["userName"],
127
- }
129
+ end
128
130
  end
129
131
  end
@@ -243,6 +243,13 @@ module Inspec::Resources
243
243
  info[:startmode]
244
244
  end
245
245
 
246
+ # returns the service's user from info
247
+ def startname
248
+ return nil if info.nil?
249
+
250
+ info[:startname]
251
+ end
252
+
246
253
  def to_s
247
254
  "Service #{@service_name}"
248
255
  end
@@ -575,12 +582,13 @@ module Inspec::Resources
575
582
  # Also see: https://msdn.microsoft.com/en-us/library/aa384896(v=vs.85).aspx
576
583
  # Use the following powershell to determine the start mode
577
584
  # PS: Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq $name -or $_.DisplayName -eq $name} | Select-Object -Prop
578
- # erty Name, StartMode, State, Status | ConvertTo-Json
585
+ # erty Name, StartMode, State, Status, StartName | ConvertTo-Json
579
586
  # {
580
587
  # "Name": "Dhcp",
581
588
  # "StartMode": "Auto",
582
589
  # "State": "Running",
583
- # "Status": "OK"
590
+ # "Status": "OK",
591
+ # "StartName": "LocalSystem"
584
592
  # }
585
593
  #
586
594
  # Windows Services have the following status code:
@@ -593,7 +601,7 @@ module Inspec::Resources
593
601
  # - 6: Pause Pending
594
602
  # - 7: Paused
595
603
  def info(service_name)
596
- cmd = inspec.command("New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name '#{service_name}'| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq '#{service_name}' -or $_.DisplayName -eq '#{service_name}'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json")
604
+ cmd = inspec.command("New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name '#{service_name}'| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq '#{service_name}' -or $_.DisplayName -eq '#{service_name}'} | Select-Object -Property StartMode, StartName) -PassThru | ConvertTo-Json")
597
605
 
598
606
  # cannot rely on exit code for now, successful command returns exit code 1
599
607
  # return nil if cmd.exit_status != 0
@@ -614,6 +622,7 @@ module Inspec::Resources
614
622
  running: service_running?(service),
615
623
  enabled: service_enabled?(service),
616
624
  startmode: service["WMI"]["StartMode"],
625
+ startname: service["WMI"]["StartName"],
617
626
  type: "windows",
618
627
  }
619
628
  end
@@ -6,92 +6,94 @@ require "uri"
6
6
  require "parallel"
7
7
 
8
8
  # Custom resource based on the InSpec resource DSL
9
- class SSL < Inspec.resource(1)
10
- name "ssl"
11
- supports platform: "unix"
12
- supports platform: "windows"
9
+ module Inspec::Resources
10
+ class SSL < Inspec.resource(1)
11
+ name "ssl"
12
+ supports platform: "unix"
13
+ supports platform: "windows"
13
14
 
14
- desc "
15
- SSL test resource
16
- "
15
+ desc "
16
+ SSL test resource
17
+ "
17
18
 
18
- example <<~EXAMPLE
19
- describe ssl(port: 443) do
20
- it { should be_enabled }
21
- end
19
+ example <<~EXAMPLE
20
+ describe ssl(port: 443) do
21
+ it { should be_enabled }
22
+ end
22
23
 
23
- # protocols: ssl2, ssl3, tls1.0, tls1.1, tls1.2
24
- describe ssl(port: 443).protocols('ssl2') do
25
- it { should_not be_enabled }
26
- end
24
+ # protocols: ssl2, ssl3, tls1.0, tls1.1, tls1.2
25
+ describe ssl(port: 443).protocols('ssl2') do
26
+ it { should_not be_enabled }
27
+ end
27
28
 
28
- # any ciphers, filter by name or regex
29
- describe ssl(port: 443).ciphers(/rc4/i) do
30
- it { should_not be_enabled }
31
- end
32
- EXAMPLE
29
+ # any ciphers, filter by name or regex
30
+ describe ssl(port: 443).ciphers(/rc4/i) do
31
+ it { should_not be_enabled }
32
+ end
33
+ EXAMPLE
33
34
 
34
- VERSIONS = [
35
- "ssl2",
36
- "ssl3",
37
- "tls1.0",
38
- "tls1.1",
39
- "tls1.2",
40
- ].freeze
35
+ VERSIONS = [
36
+ "ssl2",
37
+ "ssl3",
38
+ "tls1.0",
39
+ "tls1.1",
40
+ "tls1.2",
41
+ ].freeze
41
42
 
42
- attr_reader :host, :port, :timeout, :retries
43
+ attr_reader :host, :port, :timeout, :retries
43
44
 
44
- def initialize(opts = {})
45
- @host = opts[:host]
46
- if @host.nil?
47
- # Transports like SSH and WinRM will provide a hostname
48
- if inspec.backend.respond_to?("hostname")
49
- @host = inspec.backend.hostname
50
- elsif inspec.backend.class.to_s == "Train::Transports::Local::Connection"
51
- @host = "localhost"
45
+ def initialize(opts = {})
46
+ @host = opts[:host]
47
+ if @host.nil?
48
+ # Transports like SSH and WinRM will provide a hostname
49
+ if inspec.backend.respond_to?("hostname")
50
+ @host = inspec.backend.hostname
51
+ elsif inspec.backend.class.to_s == "Train::Transports::Local::Connection"
52
+ @host = "localhost"
53
+ end
52
54
  end
55
+ @port = opts[:port] || 443
56
+ @timeout = opts[:timeout]
57
+ @retries = opts[:retries]
53
58
  end
54
- @port = opts[:port] || 443
55
- @timeout = opts[:timeout]
56
- @retries = opts[:retries]
57
- end
58
59
 
59
- filter = FilterTable.create
60
- filter.register_custom_matcher(:enabled?) do |x|
61
- raise "Cannot determine host for SSL test. Please specify it or use a different target." if x.resource.host.nil?
60
+ filter = FilterTable.create
61
+ filter.register_custom_matcher(:enabled?) do |x|
62
+ raise "Cannot determine host for SSL test. Please specify it or use a different target." if x.resource.host.nil?
62
63
 
63
- x.handshake.values.any? { |i| i["success"] }
64
- end
65
- filter.register_column(:ciphers, field: "cipher")
66
- .register_column(:protocols, field: "protocol")
67
- .register_custom_property(:handshake) do |x|
68
- groups = x.entries.group_by(&:protocol)
69
- res = Parallel.map(groups, in_threads: 8) do |proto, e|
70
- [proto, SSLShake.hello(x.resource.host, port: x.resource.port,
71
- protocol: proto, ciphers: e.map(&:cipher),
72
- timeout: x.resource.timeout, retries: x.resource.retries, servername: x.resource.host)]
73
- end
74
- Hash[res]
64
+ x.handshake.values.any? { |i| i["success"] }
75
65
  end
76
- .install_filter_methods_on_resource(self, :scan_config)
66
+ filter.register_column(:ciphers, field: "cipher")
67
+ .register_column(:protocols, field: "protocol")
68
+ .register_custom_property(:handshake) do |x|
69
+ groups = x.entries.group_by(&:protocol)
70
+ res = Parallel.map(groups, in_threads: 8) do |proto, e|
71
+ [proto, SSLShake.hello(x.resource.host, port: x.resource.port,
72
+ protocol: proto, ciphers: e.map(&:cipher),
73
+ timeout: x.resource.timeout, retries: x.resource.retries, servername: x.resource.host)]
74
+ end
75
+ Hash[res]
76
+ end
77
+ .install_filter_methods_on_resource(self, :scan_config)
77
78
 
78
- def to_s
79
- "SSL/TLS on #{@host}:#{@port}"
80
- end
79
+ def to_s
80
+ "SSL/TLS on #{@host}:#{@port}"
81
+ end
81
82
 
82
- private
83
+ private
83
84
 
84
- def scan_config
85
- [
86
- { "protocol" => "ssl2", "ciphers" => SSLShake::SSLv2::CIPHERS.keys },
87
- { "protocol" => "ssl3", "ciphers" => SSLShake::TLS::SSL3_CIPHERS.keys },
88
- { "protocol" => "tls1.0", "ciphers" => SSLShake::TLS::TLS10_CIPHERS.keys },
89
- { "protocol" => "tls1.1", "ciphers" => SSLShake::TLS::TLS10_CIPHERS.keys },
90
- { "protocol" => "tls1.2", "ciphers" => SSLShake::TLS::TLS_CIPHERS.keys },
91
- ].map do |line|
92
- line["ciphers"].map do |cipher|
93
- { "protocol" => line["protocol"], "cipher" => cipher }
94
- end
95
- end.flatten
85
+ def scan_config
86
+ [
87
+ { "protocol" => "ssl2", "ciphers" => SSLShake::SSLv2::CIPHERS.keys },
88
+ { "protocol" => "ssl3", "ciphers" => SSLShake::TLS::SSL3_CIPHERS.keys },
89
+ { "protocol" => "tls1.0", "ciphers" => SSLShake::TLS::TLS10_CIPHERS.keys },
90
+ { "protocol" => "tls1.1", "ciphers" => SSLShake::TLS::TLS10_CIPHERS.keys },
91
+ { "protocol" => "tls1.2", "ciphers" => SSLShake::TLS::TLS_CIPHERS.keys },
92
+ ].map do |line|
93
+ line["ciphers"].map do |cipher|
94
+ { "protocol" => line["protocol"], "cipher" => cipher }
95
+ end
96
+ end.flatten
97
+ end
96
98
  end
97
99
  end
@@ -33,6 +33,7 @@ module Inspec
33
33
  extend Forwardable
34
34
 
35
35
  attr_reader :backend, :rules
36
+ attr_accessor :target_profiles
36
37
 
37
38
  def attributes
38
39
  Inspec.deprecate(:rename_attributes_to_inputs, "Don't call runner.attributes, call runner.inputs")
@@ -1,3 +1,3 @@
1
1
  module Inspec
2
- VERSION = "4.10.4".freeze
2
+ VERSION = "4.12.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.10.4
4
+ version: 4.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-01 00:00:00.000000000 Z
11
+ date: 2019-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.0'
19
+ version: '3.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.0'
26
+ version: '3.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: license-acceptance
29
29
  requirement: !ruby/object:Gem::Requirement