inspec-core 4.10.4 → 4.12.0

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