inspec 0.14.8 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -2
  3. data/bin/inspec +3 -4
  4. data/examples/inheritance/README.md +19 -0
  5. data/examples/inheritance/controls/example.rb +11 -0
  6. data/examples/inheritance/inspec.yml +10 -0
  7. data/lib/bundles/inspec-compliance/cli.rb +1 -4
  8. data/lib/bundles/inspec-supermarket/cli.rb +1 -4
  9. data/lib/inspec/dsl.rb +48 -55
  10. data/lib/inspec/profile.rb +6 -2
  11. data/lib/inspec/profile_context.rb +21 -8
  12. data/lib/inspec/runner.rb +17 -12
  13. data/lib/inspec/runner_rspec.rb +1 -0
  14. data/lib/inspec/version.rb +1 -1
  15. data/lib/resources/apache.rb +20 -18
  16. data/lib/resources/apache_conf.rb +92 -90
  17. data/lib/resources/apt.rb +92 -90
  18. data/lib/resources/audit_policy.rb +35 -33
  19. data/lib/resources/auditd_conf.rb +41 -39
  20. data/lib/resources/auditd_rules.rb +155 -153
  21. data/lib/resources/bond.rb +1 -1
  22. data/lib/resources/bridge.rb +97 -95
  23. data/lib/resources/command.rb +47 -45
  24. data/lib/resources/csv.rb +23 -21
  25. data/lib/resources/directory.rb +1 -1
  26. data/lib/resources/etc_group.rb +116 -114
  27. data/lib/resources/file.rb +1 -1
  28. data/lib/resources/gem.rb +39 -37
  29. data/lib/resources/group.rb +100 -98
  30. data/lib/resources/host.rb +103 -101
  31. data/lib/resources/inetd_conf.rb +42 -40
  32. data/lib/resources/ini.rb +15 -13
  33. data/lib/resources/interface.rb +106 -104
  34. data/lib/resources/iptables.rb +36 -34
  35. data/lib/resources/json.rb +64 -62
  36. data/lib/resources/kernel_module.rb +30 -28
  37. data/lib/resources/kernel_parameter.rb +44 -42
  38. data/lib/resources/limits_conf.rb +41 -39
  39. data/lib/resources/login_def.rb +38 -36
  40. data/lib/resources/mount.rb +43 -41
  41. data/lib/resources/mysql.rb +67 -65
  42. data/lib/resources/mysql_conf.rb +89 -87
  43. data/lib/resources/mysql_session.rb +46 -44
  44. data/lib/resources/npm.rb +35 -33
  45. data/lib/resources/ntp_conf.rb +44 -42
  46. data/lib/resources/oneget.rb +46 -44
  47. data/lib/resources/os.rb +22 -20
  48. data/lib/resources/os_env.rb +47 -45
  49. data/lib/resources/package.rb +213 -211
  50. data/lib/resources/parse_config.rb +59 -57
  51. data/lib/resources/passwd.rb +89 -87
  52. data/lib/resources/pip.rb +60 -58
  53. data/lib/resources/port.rb +352 -350
  54. data/lib/resources/postgres.rb +26 -24
  55. data/lib/resources/postgres_conf.rb +66 -64
  56. data/lib/resources/postgres_session.rb +47 -45
  57. data/lib/resources/processes.rb +56 -54
  58. data/lib/resources/registry_key.rb +150 -148
  59. data/lib/resources/script.rb +30 -28
  60. data/lib/resources/security_policy.rb +56 -54
  61. data/lib/resources/service.rb +638 -636
  62. data/lib/resources/shadow.rb +98 -96
  63. data/lib/resources/ssh_conf.rb +58 -56
  64. data/lib/resources/user.rb +363 -361
  65. data/lib/resources/windows_feature.rb +46 -44
  66. data/lib/resources/xinetd.rb +111 -109
  67. data/lib/resources/yaml.rb +16 -14
  68. data/lib/resources/yum.rb +107 -105
  69. data/lib/utils/base_cli.rb +18 -0
  70. data/test/helper.rb +2 -2
  71. data/test/unit/profile_context_test.rb +1 -1
  72. data/test/unit/resources/file_test.rb +1 -1
  73. data/test/unit/resources/mount_test.rb +1 -1
  74. metadata +5 -2
@@ -24,40 +24,42 @@
24
24
  #
25
25
  # Further information is available at: https://msdn.microsoft.com/en-us/library/dd973859.aspx
26
26
 
27
- class AuditPolicy < Inspec.resource(1)
28
- name 'audit_policy'
29
- desc 'Use the audit_policy InSpec audit resource to test auditing policies on the Microsoft Windows platform. An auditing policy is a category of security-related events to be audited. Auditing is disabled by default and may be enabled for categories like account management, logon events, policy changes, process tracking, privilege use, system events, or object access. For each auditing category property that is enabled, the auditing level may be set to No Auditing, Not Specified, Success, Success and Failure, or Failure.'
30
- example "
31
- describe audit_policy do
32
- its('parameter') { should eq 'value' }
33
- end
34
- "
35
-
36
- def method_missing(method)
37
- key = method.to_s
38
-
39
- # expected result:
40
- # Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting
41
- # WIN-MB8NINQ388J,System,Kerberos Authentication Service,{0CCE9242-69AE-11D9-BED3-505054503030},No Auditing,
42
- result ||= inspec.command("Auditpol /get /subcategory:'#{key}' /r").stdout
43
-
44
- # find line
45
- target = nil
46
- result.each_line {|s|
47
- target = s.strip if s =~ /\b.*#{key}.*\b/
48
- }
49
-
50
- # extract value
51
- values = nil
52
- unless target.nil?
53
- # split csv values and return value
54
- values = target.split(',')[4]
55
- end
27
+ module Inspec::Resources
28
+ class AuditPolicy < Inspec.resource(1)
29
+ name 'audit_policy'
30
+ desc 'Use the audit_policy InSpec audit resource to test auditing policies on the Microsoft Windows platform. An auditing policy is a category of security-related events to be audited. Auditing is disabled by default and may be enabled for categories like account management, logon events, policy changes, process tracking, privilege use, system events, or object access. For each auditing category property that is enabled, the auditing level may be set to No Auditing, Not Specified, Success, Success and Failure, or Failure.'
31
+ example "
32
+ describe audit_policy do
33
+ its('parameter') { should eq 'value' }
34
+ end
35
+ "
56
36
 
57
- values
58
- end
37
+ def method_missing(method)
38
+ key = method.to_s
39
+
40
+ # expected result:
41
+ # Machine Name,Policy Target,Subcategory,Subcategory GUID,Inclusion Setting,Exclusion Setting
42
+ # WIN-MB8NINQ388J,System,Kerberos Authentication Service,{0CCE9242-69AE-11D9-BED3-505054503030},No Auditing,
43
+ result ||= inspec.command("Auditpol /get /subcategory:'#{key}' /r").stdout
44
+
45
+ # find line
46
+ target = nil
47
+ result.each_line {|s|
48
+ target = s.strip if s =~ /\b.*#{key}.*\b/
49
+ }
59
50
 
60
- def to_s
61
- 'Audit Policy'
51
+ # extract value
52
+ values = nil
53
+ unless target.nil?
54
+ # split csv values and return value
55
+ values = target.split(',')[4]
56
+ end
57
+
58
+ values
59
+ end
60
+
61
+ def to_s
62
+ 'Audit Policy'
63
+ end
62
64
  end
63
65
  end
@@ -6,50 +6,52 @@
6
6
 
7
7
  require 'utils/simpleconfig'
8
8
 
9
- class AuditDaemonConf < Inspec.resource(1)
10
- name 'auditd_conf'
11
- desc "Use the auditd_conf InSpec audit resource to test the configuration settings for the audit daemon. This file is typically located under /etc/audit/auditd.conf' on UNIX and Linux platforms."
12
- example "
13
- describe auditd_conf do
14
- its('space_left_action') { should eq 'email' }
9
+ module Inspec::Resources
10
+ class AuditDaemonConf < Inspec.resource(1)
11
+ name 'auditd_conf'
12
+ desc "Use the auditd_conf InSpec audit resource to test the configuration settings for the audit daemon. This file is typically located under /etc/audit/auditd.conf' on UNIX and Linux platforms."
13
+ example "
14
+ describe auditd_conf do
15
+ its('space_left_action') { should eq 'email' }
16
+ end
17
+ "
18
+
19
+ def initialize(path = nil)
20
+ @conf_path = path || '/etc/audit/auditd.conf'
15
21
  end
16
- "
17
22
 
18
- def initialize(path = nil)
19
- @conf_path = path || '/etc/audit/auditd.conf'
20
- end
21
-
22
- def method_missing(name)
23
- read_params[name.to_s]
24
- end
25
-
26
- def to_s
27
- 'Audit Daemon Config'
28
- end
29
-
30
- private
31
-
32
- def read_params
33
- return @params if defined?(@params)
34
-
35
- # read the file
36
- file = inspec.file(@conf_path)
37
- if !file.file?
38
- skip_resource "Can't find file '#{@conf_path}'"
39
- return @params = {}
23
+ def method_missing(name)
24
+ read_params[name.to_s]
40
25
  end
41
26
 
42
- content = file.content
43
- if content.empty? && file.size > 0
44
- skip_resource "Can't read file '#{@conf_path}'"
45
- return @params = {}
27
+ def to_s
28
+ 'Audit Daemon Config'
46
29
  end
47
30
 
48
- # parse the file
49
- conf = SimpleConfig.new(
50
- content,
51
- multiple_values: false,
52
- )
53
- @params = conf.params
31
+ private
32
+
33
+ def read_params
34
+ return @params if defined?(@params)
35
+
36
+ # read the file
37
+ file = inspec.file(@conf_path)
38
+ if !file.file?
39
+ skip_resource "Can't find file '#{@conf_path}'"
40
+ return @params = {}
41
+ end
42
+
43
+ content = file.content
44
+ if content.empty? && file.size > 0
45
+ skip_resource "Can't read file '#{@conf_path}'"
46
+ return @params = {}
47
+ end
48
+
49
+ # parse the file
50
+ conf = SimpleConfig.new(
51
+ content,
52
+ multiple_values: false,
53
+ )
54
+ @params = conf.params
55
+ end
54
56
  end
55
57
  end
@@ -7,197 +7,199 @@
7
7
  require 'forwardable'
8
8
  require 'utils/filter_array'
9
9
 
10
- class AuditdRulesLegacy
11
- def initialize(content)
12
- @content = content
13
- @opts = {
14
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
15
- multiple_values: true,
16
- }
17
- end
10
+ module Inspec::Resources
11
+ class AuditdRulesLegacy
12
+ def initialize(content)
13
+ @content = content
14
+ @opts = {
15
+ assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
16
+ multiple_values: true,
17
+ }
18
+ end
18
19
 
19
- def params
20
- @params ||= SimpleConfig.new(@content, @opts).params
21
- end
20
+ def params
21
+ @params ||= SimpleConfig.new(@content, @opts).params
22
+ end
22
23
 
23
- def method_missing(name)
24
- params[name.to_s]
25
- end
24
+ def method_missing(name)
25
+ params[name.to_s]
26
+ end
26
27
 
27
- def status(name)
28
- @status_opts = {
29
- assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
30
- multiple_values: false,
31
- }
32
- @status_content ||= inspec.command('/sbin/auditctl -s').stdout.chomp
33
- @status_params = SimpleConfig.new(@status_content, @status_opts).params
28
+ def status(name)
29
+ @status_opts = {
30
+ assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
31
+ multiple_values: false,
32
+ }
33
+ @status_content ||= inspec.command('/sbin/auditctl -s').stdout.chomp
34
+ @status_params = SimpleConfig.new(@status_content, @status_opts).params
34
35
 
35
- status = @status_params['AUDIT_STATUS']
36
- return nil if status.nil?
36
+ status = @status_params['AUDIT_STATUS']
37
+ return nil if status.nil?
37
38
 
38
- items = Hash[status.scan(/([^=]+)=(\w*)\s*/)]
39
- items[name]
40
- end
39
+ items = Hash[status.scan(/([^=]+)=(\w*)\s*/)]
40
+ items[name]
41
+ end
41
42
 
42
- def to_s
43
- 'Audit Daemon Rules (for auditd version < 2.3)'
43
+ def to_s
44
+ 'Audit Daemon Rules (for auditd version < 2.3)'
45
+ end
44
46
  end
45
- end
46
-
47
- # rubocop:disable Metrics/ClassLength
48
- class AuditDaemonRules < Inspec.resource(1)
49
- extend Forwardable
50
- attr_accessor :rules, :lines
51
47
 
52
- name 'auditd_rules'
53
- desc 'Use the auditd_rules InSpec audit resource to test the rules for logging that exist on the system. The audit.rules file is typically located under /etc/audit/ and contains the list of rules that define what is captured in log files.'
54
- example "
55
- # syntax for auditd < 2.3
56
- describe auditd_rules do
57
- its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=adjtimex,settimeofday/) }
58
- its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=stime,settimeofday,adjtimex/) }
59
- its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=clock_settime/)}
60
- its('LIST_RULES') {should contain_match(/^exit,always watch=\/etc\/localtime perm=wa key=time-change/)}
61
- end
48
+ # rubocop:disable Metrics/ClassLength
49
+ class AuditDaemonRules < Inspec.resource(1)
50
+ extend Forwardable
51
+ attr_accessor :rules, :lines
52
+
53
+ name 'auditd_rules'
54
+ desc 'Use the auditd_rules InSpec audit resource to test the rules for logging that exist on the system. The audit.rules file is typically located under /etc/audit/ and contains the list of rules that define what is captured in log files.'
55
+ example "
56
+ # syntax for auditd < 2.3
57
+ describe auditd_rules do
58
+ its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=adjtimex,settimeofday/) }
59
+ its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=stime,settimeofday,adjtimex/) }
60
+ its('LIST_RULES') {should contain_match(/^exit,always arch=.* key=time-change syscall=clock_settime/)}
61
+ its('LIST_RULES') {should contain_match(/^exit,always watch=\/etc\/localtime perm=wa key=time-change/)}
62
+ end
62
63
 
63
- # syntax for auditd >= 2.3
64
- describe auditd_rules.syscall('open').action do
65
- it { should eq(['always']) }
66
- end
64
+ # syntax for auditd >= 2.3
65
+ describe auditd_rules.syscall('open').action do
66
+ it { should eq(['always']) }
67
+ end
67
68
 
68
- describe auditd_rules.key('sshd_config') do
69
- its(:permissions) { should contain_match(/x/) }
70
- end
69
+ describe auditd_rules.key('sshd_config') do
70
+ its(:permissions) { should contain_match(/x/) }
71
+ end
71
72
 
72
- describe auditd_rules do
73
- its(:lines) { should contain_match(%r{-w /etc/ssh/sshd_config/}) }
74
- end
75
- "
73
+ describe auditd_rules do
74
+ its(:lines) { should contain_match(%r{-w /etc/ssh/sshd_config/}) }
75
+ end
76
+ "
76
77
 
77
- def initialize
78
- @content = inspec.command('/sbin/auditctl -l').stdout.chomp
78
+ def initialize
79
+ @content = inspec.command('/sbin/auditctl -l').stdout.chomp
79
80
 
80
- if @content =~ /^LIST_RULES:/
81
- # do not warn on centos 5
82
- unless inspec.os[:family] == 'centos' && inspec.os[:release].to_i == 5
83
- warn '[WARN] this version of auditd is outdated. Updating it allows for using more precise matchers.'
81
+ if @content =~ /^LIST_RULES:/
82
+ # do not warn on centos 5
83
+ unless inspec.os[:family] == 'centos' && inspec.os[:release].to_i == 5
84
+ warn '[WARN] this version of auditd is outdated. Updating it allows for using more precise matchers.'
85
+ end
86
+ @legacy = AuditdRulesLegacy.new(@content)
87
+ else
88
+ parse_content
84
89
  end
85
- @legacy = AuditdRulesLegacy.new(@content)
86
- else
87
- parse_content
88
90
  end
89
- end
90
91
 
91
- # non-legacy instances are not asked for `its('LIST_RULES')`
92
- # rubocop:disable Style/MethodName
93
- def LIST_RULES
94
- return @legacy.LIST_RULES if @legacy
95
- fail 'Using legacy auditd_rules LIST_RULES interface with non-legacy audit package. Please use the new syntax.'
96
- end
92
+ # non-legacy instances are not asked for `its('LIST_RULES')`
93
+ # rubocop:disable Style/MethodName
94
+ def LIST_RULES
95
+ return @legacy.LIST_RULES if @legacy
96
+ fail 'Using legacy auditd_rules LIST_RULES interface with non-legacy audit package. Please use the new syntax.'
97
+ end
97
98
 
98
- def status(name = nil)
99
- return @legacy.status(name) if @legacy
99
+ def status(name = nil)
100
+ return @legacy.status(name) if @legacy
100
101
 
101
- @status_content ||= inspec.command('/sbin/auditctl -s').stdout.chomp
102
- @status_params ||= Hash[@status_content.scan(/^([^ ]+) (.*)$/)]
102
+ @status_content ||= inspec.command('/sbin/auditctl -s').stdout.chomp
103
+ @status_params ||= Hash[@status_content.scan(/^([^ ]+) (.*)$/)]
103
104
 
104
- return @status_params[name] if name
105
- @status_params
106
- end
105
+ return @status_params[name] if name
106
+ @status_params
107
+ end
107
108
 
108
- def parse_content
109
- @rules = {
110
- syscalls: [],
111
- files: [],
112
- }
113
- @lines = @content.lines.map(&:chomp)
114
-
115
- lines.each do |line|
116
- if is_syscall?(line)
117
- syscalls = get_syscalls line
118
- action, list = get_action_list line
119
- fields, opts = get_fields line
120
-
121
- # create a 'flatter' structure because sanity
122
- syscalls.each do |s|
123
- @rules[:syscalls] << { syscall: s, list: list, action: action, fields: fields }.merge(opts)
109
+ def parse_content
110
+ @rules = {
111
+ syscalls: [],
112
+ files: [],
113
+ }
114
+ @lines = @content.lines.map(&:chomp)
115
+
116
+ lines.each do |line|
117
+ if is_syscall?(line)
118
+ syscalls = get_syscalls line
119
+ action, list = get_action_list line
120
+ fields, opts = get_fields line
121
+
122
+ # create a 'flatter' structure because sanity
123
+ syscalls.each do |s|
124
+ @rules[:syscalls] << { syscall: s, list: list, action: action, fields: fields }.merge(opts)
125
+ end
126
+ elsif is_file?(line)
127
+ file = get_file line
128
+ perms = get_permissions line
129
+ key = get_key line
130
+
131
+ @rules[:files] << { file: file, key: key, permissions: perms }
124
132
  end
125
- elsif is_file?(line)
126
- file = get_file line
127
- perms = get_permissions line
128
- key = get_key line
129
-
130
- @rules[:files] << { file: file, key: key, permissions: perms }
131
133
  end
132
134
  end
133
- end
134
135
 
135
- def syscall(name)
136
- select_name(:syscall, name)
137
- end
136
+ def syscall(name)
137
+ select_name(:syscall, name)
138
+ end
138
139
 
139
- def file(name)
140
- select_name(:file, name)
141
- end
140
+ def file(name)
141
+ select_name(:file, name)
142
+ end
142
143
 
143
- # both files and syscalls have `key` identifiers
144
- def key(name)
145
- res = rules.values.flatten.find_all { |rule| rule[:key] == name }
146
- FilterArray.new(res)
147
- end
144
+ # both files and syscalls have `key` identifiers
145
+ def key(name)
146
+ res = rules.values.flatten.find_all { |rule| rule[:key] == name }
147
+ FilterArray.new(res)
148
+ end
148
149
 
149
- def to_s
150
- 'Audit Daemon Rules'
151
- end
150
+ def to_s
151
+ 'Audit Daemon Rules'
152
+ end
152
153
 
153
- private
154
+ private
154
155
 
155
- def select_name(key, name)
156
- plural = "#{key}s".to_sym
157
- res = rules[plural].find_all { |rule| rule[key] == name }
158
- FilterArray.new(res)
159
- end
156
+ def select_name(key, name)
157
+ plural = "#{key}s".to_sym
158
+ res = rules[plural].find_all { |rule| rule[key] == name }
159
+ FilterArray.new(res)
160
+ end
160
161
 
161
- def is_syscall?(line)
162
- line.match(/\ -S /)
163
- end
162
+ def is_syscall?(line)
163
+ line.match(/\ -S /)
164
+ end
164
165
 
165
- def is_file?(line)
166
- line.match(/-w /)
167
- end
166
+ def is_file?(line)
167
+ line.match(/-w /)
168
+ end
168
169
 
169
- def get_syscalls(line)
170
- line.scan(/-S ([^ ]+) /).flatten.first.split(',')
171
- end
170
+ def get_syscalls(line)
171
+ line.scan(/-S ([^ ]+) /).flatten.first.split(',')
172
+ end
172
173
 
173
- def get_action_list(line)
174
- line.scan(/-a ([^,]+),([^ ]+)/).flatten
175
- end
174
+ def get_action_list(line)
175
+ line.scan(/-a ([^,]+),([^ ]+)/).flatten
176
+ end
176
177
 
177
- # NB only in file lines
178
- def get_key(line)
179
- line.match(/-k ([^ ]+)/)[1]
180
- end
178
+ # NB only in file lines
179
+ def get_key(line)
180
+ line.match(/-k ([^ ]+)/)[1]
181
+ end
181
182
 
182
- # NOTE there are NO precautions wrt. filenames containing spaces in auditctl
183
- # `auditctl -w /foo\ bar` gives the following line: `-w /foo bar -p rwxa`
184
- def get_file(line)
185
- line.match(/-w (.+) -p/)[1]
186
- end
183
+ # NOTE there are NO precautions wrt. filenames containing spaces in auditctl
184
+ # `auditctl -w /foo\ bar` gives the following line: `-w /foo bar -p rwxa`
185
+ def get_file(line)
186
+ line.match(/-w (.+) -p/)[1]
187
+ end
187
188
 
188
- def get_permissions(line)
189
- line.match(/-p ([^ ]+)/)[1]
190
- end
189
+ def get_permissions(line)
190
+ line.match(/-p ([^ ]+)/)[1]
191
+ end
191
192
 
192
- def get_fields(line)
193
- fields = line.gsub(/-[aS] [^ ]+ /, '').split('-F ').map { |l| l.split(' ') }.flatten
193
+ def get_fields(line)
194
+ fields = line.gsub(/-[aS] [^ ]+ /, '').split('-F ').map { |l| l.split(' ') }.flatten
194
195
 
195
- opts = {}
196
- fields.find_all { |x| x.match(/[a-z]+=.*/) }.each do |kv|
197
- k, v = kv.split('=')
198
- opts[k.to_sym] = v
199
- end
196
+ opts = {}
197
+ fields.find_all { |x| x.match(/[a-z]+=.*/) }.each do |kv|
198
+ k, v = kv.split('=')
199
+ opts[k.to_sym] = v
200
+ end
200
201
 
201
- [fields, opts]
202
+ [fields, opts]
203
+ end
202
204
  end
203
205
  end