inspec 1.0.0.beta3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -2
- data/README.md +1 -1
- data/docs/inspec_and_friends.md +4 -1
- data/docs/profiles.md +14 -0
- data/lib/bundles/inspec-compliance/target.rb +15 -2
- data/lib/inspec/base_cli.rb +10 -0
- data/lib/inspec/cli.rb +16 -0
- data/lib/inspec/dependencies/lockfile.rb +5 -13
- data/lib/inspec/dsl.rb +7 -4
- data/lib/inspec/errors.rb +1 -0
- data/lib/inspec/profile.rb +1 -0
- data/lib/inspec/profile_context.rb +7 -0
- data/lib/inspec/runner.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/parse_config.rb +18 -1
- data/lib/resources/security_policy.rb +54 -0
- data/lib/resources/sys_info.rb +1 -1
- data/lib/resources/users.rb +78 -62
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c1825c5508e4853cf4bc1446a5b2bd679d9d1be
|
4
|
+
data.tar.gz: 6ffbe626a987b38ea03561d8bd18681803d89aaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bb3e9f9c6295dcb29e500b134e5b264ac239f8270cb695dd6c28aeefa7fc9b65604e93b9c144f2754536988a8c1220dc31df12add23729fcaea9eabecbd6b11
|
7
|
+
data.tar.gz: 5916f874af8a593d5b08e7456bacf42b5ed7c6dadf73b243c4e0d6d675af470f7aa2928abb603da4a32d97c76ded9e1354e6ab05eac61ffd85d4d0030e7fe60f
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,39 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
-
## [1.0.0
|
4
|
-
[Full Changelog](https://github.com/chef/inspec/compare/v1.0.0.
|
3
|
+
## [1.0.0](https://github.com/chef/inspec/tree/1.0.0) (2016-09-26)
|
4
|
+
[Full Changelog](https://github.com/chef/inspec/compare/v1.0.0.beta3...1.0.0)
|
5
|
+
|
6
|
+
**Implemented enhancements:**
|
7
|
+
|
8
|
+
- InSpec OS package [\#646](https://github.com/chef/inspec/issues/646)
|
9
|
+
- replace wmi win32\_useraccount with adsi users [\#1149](https://github.com/chef/inspec/pull/1149) ([chris-rock](https://github.com/chris-rock))
|
10
|
+
|
11
|
+
**Fixed bugs:**
|
12
|
+
|
13
|
+
- README.md has broken link to non-existent file [\#1136](https://github.com/chef/inspec/issues/1136)
|
14
|
+
|
15
|
+
**Merged pull requests:**
|
16
|
+
|
17
|
+
- update omnibus images [\#1164](https://github.com/chef/inspec/pull/1164) ([chris-rock](https://github.com/chris-rock))
|
18
|
+
- website / tutorial interaction [\#1163](https://github.com/chef/inspec/pull/1163) ([chris-rock](https://github.com/chris-rock))
|
19
|
+
- fix buttons on community page [\#1162](https://github.com/chef/inspec/pull/1162) ([arlimus](https://github.com/arlimus))
|
20
|
+
- fix alignment of community buttons [\#1161](https://github.com/chef/inspec/pull/1161) ([arlimus](https://github.com/arlimus))
|
21
|
+
- Fix require\_controls DSL method [\#1160](https://github.com/chef/inspec/pull/1160) ([stevendanna](https://github.com/stevendanna))
|
22
|
+
- Document the require\_resource function [\#1158](https://github.com/chef/inspec/pull/1158) ([stevendanna](https://github.com/stevendanna))
|
23
|
+
- fix css in docs search [\#1157](https://github.com/chef/inspec/pull/1157) ([arlimus](https://github.com/arlimus))
|
24
|
+
- update www readme for releasing the site [\#1156](https://github.com/chef/inspec/pull/1156) ([arlimus](https://github.com/arlimus))
|
25
|
+
- Fix minor typo in sys\_info documentation [\#1155](https://github.com/chef/inspec/pull/1155) ([stevendanna](https://github.com/stevendanna))
|
26
|
+
- fix outdated link in readme [\#1154](https://github.com/chef/inspec/pull/1154) ([arlimus](https://github.com/arlimus))
|
27
|
+
- fix minor website bugs [\#1153](https://github.com/chef/inspec/pull/1153) ([arlimus](https://github.com/arlimus))
|
28
|
+
- clean www before releasing [\#1152](https://github.com/chef/inspec/pull/1152) ([arlimus](https://github.com/arlimus))
|
29
|
+
- add docs to the website [\#1151](https://github.com/chef/inspec/pull/1151) ([arlimus](https://github.com/arlimus))
|
30
|
+
- return empty array for known privileges [\#1150](https://github.com/chef/inspec/pull/1150) ([chris-rock](https://github.com/chris-rock))
|
31
|
+
- Extend example for parse\_config.rb [\#1148](https://github.com/chef/inspec/pull/1148) ([nvtkaszpir](https://github.com/nvtkaszpir))
|
32
|
+
- Bump lockfile version to 1.0 [\#1141](https://github.com/chef/inspec/pull/1141) ([stevendanna](https://github.com/stevendanna))
|
33
|
+
- Improve error messages from compliance fetcher [\#1126](https://github.com/chef/inspec/pull/1126) ([stevendanna](https://github.com/stevendanna))
|
34
|
+
|
35
|
+
## [v1.0.0.beta3](https://github.com/chef/inspec/tree/v1.0.0.beta3) (2016-09-25)
|
36
|
+
[Full Changelog](https://github.com/chef/inspec/compare/v1.0.0.beta2...v1.0.0.beta3)
|
5
37
|
|
6
38
|
**Implemented enhancements:**
|
7
39
|
|
data/README.md
CHANGED
@@ -18,7 +18,7 @@ describe inetd_conf do
|
|
18
18
|
end
|
19
19
|
```
|
20
20
|
|
21
|
-
InSpec makes it easy to run your tests wherever you need. More options
|
21
|
+
InSpec makes it easy to run your tests wherever you need. More options are found in our [CLI docs](http://inspec.io/docs/reference/cli/).
|
22
22
|
|
23
23
|
```bash
|
24
24
|
# run test locally
|
data/docs/inspec_and_friends.md
CHANGED
@@ -4,6 +4,9 @@ title: InSpec and friends
|
|
4
4
|
|
5
5
|
# InSpec and friends
|
6
6
|
|
7
|
+
This page looks at projects that are similar to InSpec to explain how they
|
8
|
+
relate to each other.
|
9
|
+
|
7
10
|
## RSpec
|
8
11
|
|
9
12
|
RSpec is an awesome framework that is widely used to test Ruby code. It
|
@@ -37,7 +40,7 @@ control "sshd-11" do
|
|
37
40
|
end
|
38
41
|
```
|
39
42
|
|
40
|
-
|
43
|
+
## Serverspec
|
41
44
|
|
42
45
|
Serverspec can be credited as the first extension of RSpec that enabled
|
43
46
|
users to run RSpec tests on servers to verify deployed artifacts. It was
|
data/docs/profiles.md
CHANGED
@@ -242,6 +242,20 @@ For example, to require that controls `cis-fs-2.1` and `cis-fs-2.2` be loaded fr
|
|
242
242
|
|
243
243
|
end
|
244
244
|
|
245
|
+
|
246
|
+
## require_resource
|
247
|
+
|
248
|
+
By default, all of the resources from a listed dependency are available
|
249
|
+
for use in your profile. If two of your dependencies provide a resource with
|
250
|
+
the same name, you can use the `require_resource` DSL function to
|
251
|
+
disambiguate the two:
|
252
|
+
|
253
|
+
require_resource(profile: 'my_dep', resource: 'my_res',
|
254
|
+
as: 'my_res2')
|
255
|
+
|
256
|
+
This will allow you to reference the resource `my_res` from the
|
257
|
+
profile `my_dep` using the name `my_res2`.
|
258
|
+
|
245
259
|
# Profile Attributes
|
246
260
|
|
247
261
|
Attributes may be used in profiles to define secrets, such as user names and passwords, that should not otherwise be stored in plain-text in a cookbook. First specify a variable in the control for each secret, then add the secret to a Yaml file located on the local machine, and then run `inspec exec` and specify the path to that Yaml file using the `--attrs` attribute.
|
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
require 'uri'
|
6
6
|
require 'inspec/fetcher'
|
7
|
+
require 'inspec/errors'
|
7
8
|
|
8
9
|
# InSpec Target Helper for Chef Compliance
|
9
10
|
# reuses UrlHelper, but it knows the target server and the access token already
|
@@ -24,11 +25,23 @@ module Compliance
|
|
24
25
|
|
25
26
|
# check if we have a compliance token
|
26
27
|
config = Compliance::Configuration.new
|
27
|
-
|
28
|
+
if config['token'].nil?
|
29
|
+
fail Inspec::FetcherFailure, <<EOF
|
30
|
+
|
31
|
+
Cannot fetch #{uri} because your compliance token has not been
|
32
|
+
configured.
|
33
|
+
|
34
|
+
Please login using
|
35
|
+
|
36
|
+
inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE'
|
37
|
+
EOF
|
38
|
+
end
|
28
39
|
|
29
40
|
# verifies that the target e.g base/ssh exists
|
30
41
|
profile = uri.host + uri.path
|
31
|
-
Compliance::API.exist?(config, profile)
|
42
|
+
if !Compliance::API.exist?(config, profile)
|
43
|
+
fail Inpsec::FetcherFailure, "The compliance profile #{profile} was not found on the configured compliance server"
|
44
|
+
end
|
32
45
|
new(target_url(profile, config), config)
|
33
46
|
rescue URI::Error => _e
|
34
47
|
nil
|
data/lib/inspec/base_cli.rb
CHANGED
@@ -132,6 +132,16 @@ module Inspec
|
|
132
132
|
Logger.const_get(l.upcase)
|
133
133
|
end
|
134
134
|
|
135
|
+
def pretty_handle_exception(exception)
|
136
|
+
case exception
|
137
|
+
when Inspec::Error
|
138
|
+
$stderr.puts exception.message
|
139
|
+
exit(1)
|
140
|
+
else
|
141
|
+
raise exception # rubocop:disable Style/SignalException
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
135
145
|
def configure_logger(o)
|
136
146
|
#
|
137
147
|
# TODO(ssd): This is a big gross, but this configures the
|
data/lib/inspec/cli.rb
CHANGED
@@ -48,6 +48,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
48
48
|
fdst = File.expand_path(dst)
|
49
49
|
File.write(fdst, JSON.dump(profile.info))
|
50
50
|
end
|
51
|
+
rescue StandardError => e
|
52
|
+
pretty_handle_exception(e)
|
51
53
|
end
|
52
54
|
|
53
55
|
desc 'check PATH', 'verify all tests at the specified PATH'
|
@@ -97,6 +99,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
97
99
|
end
|
98
100
|
end
|
99
101
|
exit 1 unless result[:summary][:valid]
|
102
|
+
rescue StandardError => e
|
103
|
+
pretty_handle_exception(e)
|
100
104
|
end
|
101
105
|
|
102
106
|
desc 'vendor', 'Download all dependencies and generate a lockfile'
|
@@ -105,6 +109,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
105
109
|
profile = Inspec::Profile.for_target('./', opts.merge(cache: Inspec::Cache.new(path)))
|
106
110
|
lockfile = profile.generate_lockfile
|
107
111
|
File.write('inspec.lock', lockfile.to_yaml)
|
112
|
+
rescue StandardError => e
|
113
|
+
pretty_handle_exception(e)
|
108
114
|
end
|
109
115
|
|
110
116
|
desc 'archive PATH', 'archive a profile to tar.gz (default) or zip'
|
@@ -136,6 +142,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
136
142
|
|
137
143
|
# generate archive
|
138
144
|
exit 1 unless profile.archive(opts)
|
145
|
+
rescue StandardError => e
|
146
|
+
pretty_handle_exception(e)
|
139
147
|
end
|
140
148
|
|
141
149
|
desc 'exec PATHS', 'run all test files at the specified PATH.'
|
@@ -147,6 +155,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
147
155
|
|
148
156
|
# run tests
|
149
157
|
run_tests(targets, o)
|
158
|
+
rescue StandardError => e
|
159
|
+
pretty_handle_exception(e)
|
150
160
|
end
|
151
161
|
|
152
162
|
desc 'detect', 'detect the target OS'
|
@@ -165,6 +175,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
165
175
|
mark_text(res[item.to_sym]))
|
166
176
|
}
|
167
177
|
end
|
178
|
+
rescue StandardError => e
|
179
|
+
pretty_handle_exception(e)
|
168
180
|
end
|
169
181
|
|
170
182
|
desc 'shell', 'open an interactive debugging shell'
|
@@ -196,12 +208,16 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
196
208
|
exit 0
|
197
209
|
rescue RuntimeError, Train::UserError => e
|
198
210
|
$stderr.puts e.message
|
211
|
+
rescue StandardError => e
|
212
|
+
pretty_handle_exception(e)
|
199
213
|
end
|
200
214
|
|
201
215
|
desc 'env', 'Output shell-appropriate completion configuration'
|
202
216
|
def env(shell = nil)
|
203
217
|
p = Inspec::EnvPrinter.new(self.class, shell)
|
204
218
|
p.print_and_exit!
|
219
|
+
rescue StandardError => e
|
220
|
+
pretty_handle_exception(e)
|
205
221
|
end
|
206
222
|
|
207
223
|
desc 'version', 'prints the version of this tool'
|
@@ -4,8 +4,8 @@ require 'yaml'
|
|
4
4
|
module Inspec
|
5
5
|
class Lockfile
|
6
6
|
# When we finalize this feature, we should set these to 1
|
7
|
-
MINIMUM_SUPPORTED_VERSION =
|
8
|
-
CURRENT_LOCKFILE_VERSION =
|
7
|
+
MINIMUM_SUPPORTED_VERSION = 1
|
8
|
+
CURRENT_LOCKFILE_VERSION = 1
|
9
9
|
|
10
10
|
def self.from_dependency_set(dep_set)
|
11
11
|
lockfile_content = {
|
@@ -32,14 +32,6 @@ lower than the minimum supported version #{MINIMUM_SUPPORTED_VERSION}.
|
|
32
32
|
Please create a new lockfile for this project by running:
|
33
33
|
|
34
34
|
inspec vendor
|
35
|
-
EOF
|
36
|
-
elsif version == 0
|
37
|
-
# Remove this case once this feature stablizes
|
38
|
-
$stderr.puts <<EOF
|
39
|
-
WARNING: This is a version 0 lockfile. Thank you for trying the
|
40
|
-
experimental dependency management feature. Please be aware you may
|
41
|
-
need to regenerate this lockfile in future versions as the feature is
|
42
|
-
currently in development.
|
43
35
|
EOF
|
44
36
|
elsif version > CURRENT_LOCKFILE_VERSION
|
45
37
|
fail <<EOF
|
@@ -78,8 +70,8 @@ EOF
|
|
78
70
|
# different entry points of the API.
|
79
71
|
def parse_content_hash(lockfile_content_hash)
|
80
72
|
case version
|
81
|
-
when
|
82
|
-
|
73
|
+
when 1
|
74
|
+
parse_content_hash_1(lockfile_content_hash)
|
83
75
|
else
|
84
76
|
# If we've gotten here, there is likely a mistake in the
|
85
77
|
# lockfile version validation in the constructor.
|
@@ -87,7 +79,7 @@ EOF
|
|
87
79
|
end
|
88
80
|
end
|
89
81
|
|
90
|
-
def
|
82
|
+
def parse_content_hash_1(lockfile_content_hash)
|
91
83
|
@deps = if lockfile_content_hash['depends']
|
92
84
|
lockfile_content_hash['depends'].map { |i| symbolize_keys(i) }
|
93
85
|
end
|
data/lib/inspec/dsl.rb
CHANGED
@@ -53,12 +53,15 @@ EOF
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def self.filter_included_controls(context, profile, &block)
|
56
|
-
|
56
|
+
mock = Inspec::Backend.create({ backend: 'mock' })
|
57
|
+
include_ctx = Inspec::ProfileContext.for_profile(profile, mock, {})
|
57
58
|
include_ctx.load(block) if block_given?
|
58
59
|
# remove all rules that were not registered
|
59
|
-
context.
|
60
|
-
|
61
|
-
|
60
|
+
context.all_rules.each do |r|
|
61
|
+
id = Inspec::Rule.rule_id(r)
|
62
|
+
fid = Inspec::Rule.profile_id(r) + '/' + id
|
63
|
+
unless include_ctx.rules[id] || include_ctx.rules[fid]
|
64
|
+
context.remove_rule(fid)
|
62
65
|
end
|
63
66
|
end
|
64
67
|
end
|
data/lib/inspec/errors.rb
CHANGED
data/lib/inspec/profile.rb
CHANGED
@@ -67,6 +67,13 @@ module Inspec
|
|
67
67
|
@conf['profile'].supports_os?
|
68
68
|
end
|
69
69
|
|
70
|
+
def remove_rule(id)
|
71
|
+
@rules[id] = nil if @rules.key?(id)
|
72
|
+
@control_subcontexts.each do |c|
|
73
|
+
c.remove_rule(id)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
70
77
|
def all_controls
|
71
78
|
ret = @rules.values
|
72
79
|
ret += @control_subcontexts.map(&:all_rules).flatten
|
data/lib/inspec/runner.rb
CHANGED
data/lib/inspec/version.rb
CHANGED
@@ -19,10 +19,27 @@ module Inspec::Resources
|
|
19
19
|
desc 'Use the parse_config InSpec audit resource to test arbitrary configuration files.'
|
20
20
|
example "
|
21
21
|
output = command('some-command').stdout
|
22
|
-
|
23
22
|
describe parse_config(output, { data_config_option: value } ) do
|
24
23
|
its('setting') { should eq 1 }
|
25
24
|
end
|
25
|
+
|
26
|
+
output2 = command('curl http://127.0.0.1/php_status').stdout
|
27
|
+
# php status is in format 'key : value', and we do not allow for multiple values
|
28
|
+
options2 = {
|
29
|
+
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
30
|
+
multiple_values: false
|
31
|
+
}
|
32
|
+
|
33
|
+
describe parse_config(output2, options2) do
|
34
|
+
its('pool') { should eq 'www'}
|
35
|
+
its('process manager') { should eq process_manager }
|
36
|
+
end
|
37
|
+
|
38
|
+
# getting specific key from the output above, convert it to integer and then compare
|
39
|
+
# make sure 'listen queue' is below 100
|
40
|
+
describe parse_config(output2, options2 ).params['listen queue'].to_i do
|
41
|
+
it { should be < 100 }
|
42
|
+
end
|
26
43
|
"
|
27
44
|
|
28
45
|
attr_reader :content
|
@@ -16,6 +16,57 @@
|
|
16
16
|
require 'hashie'
|
17
17
|
|
18
18
|
module Inspec::Resources
|
19
|
+
# known and supported MS privilege rights
|
20
|
+
# @see https://technet.microsoft.com/en-us/library/dd277311.aspx
|
21
|
+
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/bb530716(v=vs.85).aspx
|
22
|
+
MS_PRIVILEGES_RIGHTS = [
|
23
|
+
'SeNetworkLogonRight',
|
24
|
+
'SeBackupPrivilege',
|
25
|
+
'SeChangeNotifyPrivilege',
|
26
|
+
'SeSystemtimePrivilege',
|
27
|
+
'SeCreatePagefilePrivilege',
|
28
|
+
'SeDebugPrivilege',
|
29
|
+
'SeRemoteShutdownPrivilege',
|
30
|
+
'SeAuditPrivilege',
|
31
|
+
'SeIncreaseQuotaPrivilege',
|
32
|
+
'SeIncreaseBasePriorityPrivilege',
|
33
|
+
'SeLoadDriverPrivilege',
|
34
|
+
'SeBatchLogonRight',
|
35
|
+
'SeServiceLogonRight',
|
36
|
+
'SeInteractiveLogonRight',
|
37
|
+
'SeSecurityPrivilege',
|
38
|
+
'SeSystemEnvironmentPrivilege',
|
39
|
+
'SeProfileSingleProcessPrivilege',
|
40
|
+
'SeSystemProfilePrivilege',
|
41
|
+
'SeAssignPrimaryTokenPrivilege',
|
42
|
+
'SeRestorePrivilege',
|
43
|
+
'SeShutdownPrivilege',
|
44
|
+
'SeTakeOwnershipPrivilege',
|
45
|
+
'SeUndockPrivilege',
|
46
|
+
'SeManageVolumePrivilege',
|
47
|
+
'SeRemoteInteractiveLogonRight',
|
48
|
+
'SeImpersonatePrivilege',
|
49
|
+
'SeCreateGlobalPrivilege',
|
50
|
+
'SeIncreaseWorking',
|
51
|
+
'SeTimeZonePrivilege',
|
52
|
+
'SeCreateSymbolicLinkPrivilege',
|
53
|
+
'SeDenyNetworkLogonRight', # Deny access to this computer from the network
|
54
|
+
'SeDenyInteractiveLogonRight', # Deny logon locally
|
55
|
+
'SeDenyBatchLogonRight', # Deny logon as a batch job
|
56
|
+
'SeDenyServiceLogonRight', # Deny logon as a service
|
57
|
+
'SeTcbPrivilege',
|
58
|
+
'SeMachineAccountPrivilege',
|
59
|
+
'SeCreateTokenPrivilege',
|
60
|
+
'SeCreatePermanentPrivilege',
|
61
|
+
'SeEnableDelegationPrivilege',
|
62
|
+
'SeLockMemoryPrivilege',
|
63
|
+
'SeSyncAgentPrivilege',
|
64
|
+
'SeUnsolicitedInputPrivilege',
|
65
|
+
'SeTrustedCredManAccessPrivilege',
|
66
|
+
'SeRelabelPrivilege', # the privilege to change a Windows integrity label (new to Windows Vista)
|
67
|
+
'SeDenyRemoteInteractiveLogonRight', # Deny logon through Terminal Services
|
68
|
+
].freeze
|
69
|
+
|
19
70
|
class SecurityPolicy < Inspec.resource(1)
|
20
71
|
name 'security_policy'
|
21
72
|
desc 'Use the security_policy InSpec audit resource to test security policies on the Microsoft Windows platform.'
|
@@ -42,6 +93,9 @@ module Inspec::Resources
|
|
42
93
|
# deep search for hash key
|
43
94
|
params.extend Hashie::Extensions::DeepFind
|
44
95
|
res = params.deep_find(name.to_s)
|
96
|
+
|
97
|
+
# return an empty array if configuration does not include rights configuration
|
98
|
+
return [] if res.nil? && MS_PRIVILEGES_RIGHTS.include?(name.to_s)
|
45
99
|
res
|
46
100
|
end
|
47
101
|
|
data/lib/resources/sys_info.rb
CHANGED
data/lib/resources/users.rb
CHANGED
@@ -547,21 +547,12 @@ module Inspec::Resources
|
|
547
547
|
end
|
548
548
|
end
|
549
549
|
|
550
|
-
#
|
550
|
+
# This optimization was inspired by
|
551
|
+
# @see https://mcpmag.com/articles/2015/04/15/reporting-on-local-accounts.aspx
|
552
|
+
# Alternative solutions are WMI Win32_UserAccount
|
551
553
|
# @see https://msdn.microsoft.com/en-us/library/aa394507(v=vs.85).aspx
|
552
554
|
# @see https://msdn.microsoft.com/en-us/library/aa394153(v=vs.85).aspx
|
553
|
-
#
|
554
|
-
# using Get-AdUser would be the best command for domain machines, but it will not be installed
|
555
|
-
# on client machines by default
|
556
|
-
# @see https://technet.microsoft.com/en-us/library/ee617241.aspx
|
557
|
-
# @see https://technet.microsoft.com/en-us/library/hh509016(v=WS.10).aspx
|
558
|
-
# @see http://woshub.com/get-aduser-getting-active-directory-users-data-via-powershell/
|
559
|
-
# @see http://stackoverflow.com/questions/17548523/the-term-get-aduser-is-not-recognized-as-the-name-of-a-cmdlet
|
560
|
-
#
|
561
|
-
# Just for reference, we could also use ADSI (Active Directory Service Interfaces)
|
562
|
-
# @see https://mcpmag.com/articles/2015/04/15/reporting-on-local-accounts.aspx
|
563
555
|
class WindowsUser < UserInfo
|
564
|
-
# parse windows account name
|
565
556
|
def parse_windows_account(username)
|
566
557
|
account = username.split('\\')
|
567
558
|
name = account.pop
|
@@ -570,67 +561,92 @@ module Inspec::Resources
|
|
570
561
|
end
|
571
562
|
|
572
563
|
def identity(username)
|
573
|
-
#
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
if
|
578
|
-
|
579
|
-
else
|
580
|
-
filter = "Name = '#{account}' and LocalAccount = true"
|
581
|
-
end
|
564
|
+
# TODO: we look for local users only at this point
|
565
|
+
name, _domain = parse_windows_account(username)
|
566
|
+
return if collect_user_details.nil?
|
567
|
+
res = collect_user_details.select { |user| user[:username] == name }
|
568
|
+
res[0] if res.length > 0
|
569
|
+
end
|
582
570
|
|
571
|
+
def list_users
|
572
|
+
collect_user_details.map { |user| user[:username] }
|
573
|
+
end
|
574
|
+
|
575
|
+
# https://msdn.microsoft.com/en-us/library/aa746340(v=vs.85).aspx
|
576
|
+
def collect_user_details # rubocop:disable Metrics/MethodLength
|
577
|
+
return @users_cache if defined?(@users_cache)
|
583
578
|
script = <<-EOH
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
579
|
+
Function ConvertTo-SID { Param([byte[]]$BinarySID)
|
580
|
+
(New-Object System.Security.Principal.SecurityIdentifier($BinarySID,0)).Value
|
581
|
+
}
|
582
|
+
|
583
|
+
Function Convert-UserFlag { Param ($UserFlag)
|
584
|
+
$List = @()
|
585
|
+
Switch ($UserFlag) {
|
586
|
+
($UserFlag -BOR 0x0001) { $List += 'SCRIPT' }
|
587
|
+
($UserFlag -BOR 0x0002) { $List += 'ACCOUNTDISABLE' }
|
588
|
+
($UserFlag -BOR 0x0008) { $List += 'HOMEDIR_REQUIRED' }
|
589
|
+
($UserFlag -BOR 0x0010) { $List += 'LOCKOUT' }
|
590
|
+
($UserFlag -BOR 0x0020) { $List += 'PASSWD_NOTREQD' }
|
591
|
+
($UserFlag -BOR 0x0040) { $List += 'PASSWD_CANT_CHANGE' }
|
592
|
+
($UserFlag -BOR 0x0080) { $List += 'ENCRYPTED_TEXT_PWD_ALLOWED' }
|
593
|
+
($UserFlag -BOR 0x0100) { $List += 'TEMP_DUPLICATE_ACCOUNT' }
|
594
|
+
($UserFlag -BOR 0x0200) { $List += 'NORMAL_ACCOUNT' }
|
595
|
+
($UserFlag -BOR 0x0800) { $List += 'INTERDOMAIN_TRUST_ACCOUNT' }
|
596
|
+
($UserFlag -BOR 0x1000) { $List += 'WORKSTATION_TRUST_ACCOUNT' }
|
597
|
+
($UserFlag -BOR 0x2000) { $List += 'SERVER_TRUST_ACCOUNT' }
|
598
|
+
($UserFlag -BOR 0x10000) { $List += 'DONT_EXPIRE_PASSWORD' }
|
599
|
+
($UserFlag -BOR 0x20000) { $List += 'MNS_LOGON_ACCOUNT' }
|
600
|
+
($UserFlag -BOR 0x40000) { $List += 'SMARTCARD_REQUIRED' }
|
601
|
+
($UserFlag -BOR 0x80000) { $List += 'TRUSTED_FOR_DELEGATION' }
|
602
|
+
($UserFlag -BOR 0x100000) { $List += 'NOT_DELEGATED' }
|
603
|
+
($UserFlag -BOR 0x200000) { $List += 'USE_DES_KEY_ONLY' }
|
604
|
+
($UserFlag -BOR 0x400000) { $List += 'DONT_REQ_PREAUTH' }
|
605
|
+
($UserFlag -BOR 0x800000) { $List += 'PASSWORD_EXPIRED' }
|
606
|
+
($UserFlag -BOR 0x1000000) { $List += 'TRUSTED_TO_AUTH_FOR_DELEGATION' }
|
607
|
+
($UserFlag -BOR 0x04000000) { $List += 'PARTIAL_SECRETS_ACCOUNT' }
|
608
|
+
}
|
609
|
+
$List
|
610
|
+
}
|
611
|
+
|
612
|
+
$Computername = $Env:Computername
|
613
|
+
$adsi = [ADSI]"WinNT://$Computername"
|
614
|
+
$adsi.Children | where {$_.SchemaClassName -eq 'user'} | ForEach {
|
615
|
+
New-Object PSObject -property @{
|
616
|
+
uid = ConvertTo-SID -BinarySID $_.ObjectSID[0]
|
617
|
+
username = $_.Name[0]
|
618
|
+
description = $_.Description[0]
|
619
|
+
disabled = $_.AccountDisabled[0]
|
620
|
+
userflags = Convert-UserFlag -UserFlag $_.UserFlags[0]
|
621
|
+
passwordage = [math]::Round($_.PasswordAge[0]/86400)
|
622
|
+
minpasswordlength = $_.MinPasswordLength[0]
|
623
|
+
mindays = [math]::Round($_.MinPasswordAge[0]/86400)
|
624
|
+
maxdays = [math]::Round($_.MaxPasswordAge[0]/86400)
|
625
|
+
warndays = $null
|
626
|
+
badpasswordattempts = $_.BadPasswordAttempts[0]
|
627
|
+
maxbadpasswords = $_.MaxBadPasswordsAllowed[0]
|
628
|
+
gid = $null
|
629
|
+
group = $null
|
630
|
+
groups = $null
|
631
|
+
home = $_.HomeDirectory[0]
|
632
|
+
shell = $null
|
633
|
+
domain = $Computername
|
634
|
+
}
|
635
|
+
} | ConvertTo-Json
|
595
636
|
EOH
|
596
|
-
|
597
637
|
cmd = inspec.powershell(script)
|
598
|
-
|
599
638
|
# cannot rely on exit code for now, successful command returns exit code 1
|
600
639
|
# return nil if cmd.exit_status != 0, try to parse json
|
601
640
|
begin
|
602
|
-
|
641
|
+
users = JSON.parse(cmd.stdout)
|
603
642
|
rescue JSON::ParserError => _e
|
604
643
|
return nil
|
605
644
|
end
|
606
645
|
|
607
|
-
|
608
|
-
|
609
|
-
#
|
610
|
-
|
611
|
-
group_names = group_hashes.map { |grp| grp['Caption'] }
|
612
|
-
{
|
613
|
-
uid: user_hash['SID'],
|
614
|
-
username: user_hash['Caption'],
|
615
|
-
gid: nil,
|
616
|
-
group: nil,
|
617
|
-
groups: group_names,
|
618
|
-
disabled: user_hash['Disabled'],
|
619
|
-
}
|
620
|
-
end
|
621
|
-
|
622
|
-
# not implemented yet
|
623
|
-
def meta_info(_username)
|
624
|
-
{
|
625
|
-
home: nil,
|
626
|
-
shell: nil,
|
627
|
-
}
|
628
|
-
end
|
629
|
-
|
630
|
-
def list_users
|
631
|
-
script = 'Get-WmiObject Win32_UserAccount | Select-Object -ExpandProperty Caption'
|
632
|
-
cmd = inspec.powershell(script)
|
633
|
-
cmd.stdout.chomp.lines
|
646
|
+
# ensure we have an array of groups
|
647
|
+
users = [users] if !users.is_a?(Array)
|
648
|
+
# convert keys to symbols
|
649
|
+
@users_cache = users.map { |user| user.each_with_object({}) { |(k, v), h| h[k.to_sym] = v } }
|
634
650
|
end
|
635
651
|
end
|
636
652
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.0
|
4
|
+
version: 1.0.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: 2016-09-
|
11
|
+
date: 2016-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: train
|
@@ -525,9 +525,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
525
525
|
version: '0'
|
526
526
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
527
527
|
requirements:
|
528
|
-
- - "
|
528
|
+
- - ">="
|
529
529
|
- !ruby/object:Gem::Version
|
530
|
-
version:
|
530
|
+
version: '0'
|
531
531
|
requirements: []
|
532
532
|
rubyforge_project:
|
533
533
|
rubygems_version: 2.4.6
|
@@ -535,3 +535,4 @@ signing_key:
|
|
535
535
|
specification_version: 4
|
536
536
|
summary: Infrastructure and compliance testing.
|
537
537
|
test_files: []
|
538
|
+
has_rdoc:
|