inspec 0.34.1 → 1.0.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 +4 -4
- data/CHANGELOG.md +323 -6
- data/Gemfile +10 -0
- data/README.md +1 -1
- data/Rakefile +4 -55
- data/docs/.gitignore +2 -0
- data/docs/README.md +40 -0
- data/docs/cli.md +342 -0
- data/docs/dsl_inspec.md +245 -0
- data/docs/dsl_resource.md +93 -0
- data/docs/inspec_and_friends.md +105 -0
- data/docs/matchers.md +136 -0
- data/docs/plugin_kitchen_inspec.html.md +55 -0
- data/docs/profiles.md +285 -0
- data/docs/resources/apache_conf.md.erb +75 -0
- data/docs/resources/apt.md.erb +84 -0
- data/docs/resources/audit_policy.md.erb +61 -0
- data/docs/resources/auditd_conf.md.erb +79 -0
- data/docs/resources/auditd_rules.md.erb +132 -0
- data/docs/resources/bash.md.erb +84 -0
- data/docs/resources/bond.md.erb +97 -0
- data/docs/resources/bridge.md.erb +67 -0
- data/docs/resources/bsd_service.md.erb +76 -0
- data/docs/resources/command.md.erb +151 -0
- data/docs/resources/csv.md.erb +62 -0
- data/docs/resources/directory.md.erb +43 -0
- data/docs/resources/etc_group.md.erb +116 -0
- data/docs/resources/etc_passwd.md.erb +155 -0
- data/docs/resources/etc_shadow.md.erb +149 -0
- data/docs/resources/file.md.erb +460 -0
- data/docs/resources/gem.md.erb +73 -0
- data/docs/resources/group.md.erb +74 -0
- data/docs/resources/grub_conf.md.erb +115 -0
- data/docs/resources/host.md.erb +85 -0
- data/docs/resources/iis_site.md.erb +142 -0
- data/docs/resources/inetd_conf.md.erb +99 -0
- data/docs/resources/ini.md.erb +69 -0
- data/docs/resources/interface.md.erb +66 -0
- data/docs/resources/iptables.md.erb +70 -0
- data/docs/resources/json.md.erb +76 -0
- data/docs/resources/kernel_module.md.erb +60 -0
- data/docs/resources/kernel_parameter.md.erb +72 -0
- data/docs/resources/launchd_service.md.erb +76 -0
- data/docs/resources/limits_conf.md.erb +80 -0
- data/docs/resources/login_def.md.erb +77 -0
- data/docs/resources/mount.md.erb +83 -0
- data/docs/resources/mysql_conf.md.erb +102 -0
- data/docs/resources/mysql_session.md.erb +63 -0
- data/docs/resources/npm.md.erb +75 -0
- data/docs/resources/ntp_conf.md.erb +76 -0
- data/docs/resources/oneget.md.erb +67 -0
- data/docs/resources/os.md.erb +154 -0
- data/docs/resources/os_env.md.erb +98 -0
- data/docs/resources/package.md.erb +115 -0
- data/docs/resources/parse_config.md.erb +122 -0
- data/docs/resources/parse_config_file.md.erb +143 -0
- data/docs/resources/pip.md.erb +74 -0
- data/docs/resources/port.md.erb +150 -0
- data/docs/resources/postgres_conf.md.erb +90 -0
- data/docs/resources/postgres_session.md.erb +75 -0
- data/docs/resources/powershell.md.erb +116 -0
- data/docs/resources/process.md.erb +73 -0
- data/docs/resources/registry_key.md.erb +149 -0
- data/docs/resources/runit_service.md.erb +76 -0
- data/docs/resources/security_policy.md.erb +61 -0
- data/docs/resources/service.md.erb +135 -0
- data/docs/resources/ssh_config.md.erb +94 -0
- data/docs/resources/sshd_config.md.erb +97 -0
- data/docs/resources/ssl.md.erb +133 -0
- data/docs/resources/sys_info.md.erb +55 -0
- data/docs/resources/systemd_service.md.erb +76 -0
- data/docs/resources/sysv_service.md.erb +76 -0
- data/docs/resources/upstart_service.md.erb +76 -0
- data/docs/resources/user.md.erb +154 -0
- data/docs/resources/users.md.erb +140 -0
- data/docs/resources/vbscript.md.erb +69 -0
- data/docs/resources/windows_feature.md.erb +61 -0
- data/docs/resources/wmi.md.erb +95 -0
- data/docs/resources/xinetd_conf.md.erb +170 -0
- data/docs/resources/yaml.md.erb +69 -0
- data/docs/resources/yum.md.erb +103 -0
- data/docs/ruby_usage.md +154 -0
- data/docs/shared/matcher_be.md.erb +1 -0
- data/docs/shared/matcher_cmp.md.erb +45 -0
- data/docs/shared/matcher_eq.md.erb +3 -0
- data/docs/shared/matcher_include.md.erb +1 -0
- data/docs/shared/matcher_match.md.erb +1 -0
- data/docs/shell.md +150 -0
- data/examples/meta-profile/inspec.yml +2 -4
- data/inspec.gemspec +1 -1
- data/lib/bundles/inspec-compliance/api.rb +28 -18
- data/lib/bundles/inspec-compliance/cli.rb +19 -27
- data/lib/bundles/inspec-compliance/target.rb +15 -2
- data/lib/fetchers/git.rb +4 -0
- data/lib/fetchers/local.rb +16 -1
- data/lib/fetchers/mock.rb +4 -0
- data/lib/fetchers/url.rb +46 -20
- data/lib/inspec/base_cli.rb +14 -0
- data/lib/inspec/cached_fetcher.rb +67 -0
- data/lib/inspec/cli.rb +22 -8
- data/lib/inspec/control_eval_context.rb +12 -2
- data/lib/inspec/dependencies/{vendor_index.rb → cache.rb} +5 -4
- data/lib/inspec/dependencies/dependency_set.rb +8 -14
- data/lib/inspec/dependencies/lockfile.rb +5 -13
- data/lib/inspec/dependencies/requirement.rb +31 -41
- data/lib/inspec/dependencies/resolver.rb +3 -3
- data/lib/inspec/dsl.rb +16 -4
- data/lib/inspec/errors.rb +1 -0
- data/lib/inspec/fetcher.rb +25 -1
- data/lib/inspec/objects/each_loop.rb +5 -2
- data/lib/inspec/objects/test.rb +8 -2
- data/lib/inspec/plugins/fetcher.rb +13 -12
- data/lib/inspec/plugins/resource.rb +3 -0
- data/lib/inspec/profile.rb +62 -26
- data/lib/inspec/profile_context.rb +44 -8
- data/lib/inspec/resource.rb +18 -3
- data/lib/inspec/rspec_json_formatter.rb +91 -40
- data/lib/inspec/rule.rb +45 -2
- data/lib/inspec/runner.rb +51 -39
- data/lib/inspec/shell.rb +5 -16
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/apache_conf.rb +1 -1
- data/lib/resources/gem.rb +1 -0
- data/lib/resources/groups.rb +190 -0
- data/lib/resources/oneget.rb +1 -0
- data/lib/resources/os.rb +1 -1
- data/lib/resources/package.rb +3 -1
- data/lib/resources/parse_config.rb +18 -1
- data/lib/resources/pip.rb +1 -1
- data/lib/resources/security_policy.rb +54 -0
- data/lib/resources/ssl.rb +9 -11
- data/lib/resources/sys_info.rb +1 -1
- data/lib/resources/users.rb +81 -64
- metadata +91 -17
- data/docs/cli.rst +0 -442
- data/docs/dsl_inspec.rst +0 -259
- data/docs/dsl_resource.rst +0 -90
- data/docs/inspec_and_friends.rst +0 -85
- data/docs/matchers.rst +0 -137
- data/docs/profiles.rst +0 -169
- data/docs/readme.rst +0 -105
- data/docs/resources.rst +0 -4836
- data/docs/ruby_usage.rst +0 -145
- data/docs/shell.rst +0 -130
- data/docs/template.rst +0 -51
- data/lib/resources/group.rb +0 -137
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Christoph Hartmann
|
|
3
|
+
# author: Dominik Richter
|
|
4
|
+
|
|
5
|
+
require 'utils/filter'
|
|
6
|
+
|
|
7
|
+
module Inspec::Resources
|
|
8
|
+
# This file contains two resources, the `group` and `groups` resource.
|
|
9
|
+
# The `group` resource is optimized for requests that verify specific groups
|
|
10
|
+
# that you know upfront for testing. If you need to query all groups or search
|
|
11
|
+
# specific groups with certain properties, use the `groups` resource.
|
|
12
|
+
module GroupManagementSelector
|
|
13
|
+
# select group provider based on the operating system
|
|
14
|
+
# returns nil, if no group manager was found for the operating system
|
|
15
|
+
def select_group_manager(os)
|
|
16
|
+
if os.unix?
|
|
17
|
+
@group_provider = UnixGroup.new(inspec)
|
|
18
|
+
elsif os.windows?
|
|
19
|
+
@group_provider = WindowsGroup.new(inspec)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Groups < Inspec.resource(1)
|
|
25
|
+
include GroupManagementSelector
|
|
26
|
+
|
|
27
|
+
name 'groups'
|
|
28
|
+
desc 'Use the group InSpec audit resource to test groups on the system. Groups can be filtered.'
|
|
29
|
+
example "
|
|
30
|
+
describe groups.where { name == 'root'} do
|
|
31
|
+
its('names') { should eq ['root'] }
|
|
32
|
+
its('gids') { should eq [0] }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
describe groups.where { name == 'Administrators'} do
|
|
36
|
+
its('names') { should eq ['Administrators'] }
|
|
37
|
+
its('gids') { should eq ['S-1-5-32-544'] }
|
|
38
|
+
end
|
|
39
|
+
"
|
|
40
|
+
|
|
41
|
+
def initialize
|
|
42
|
+
# select group manager
|
|
43
|
+
@group_provider = select_group_manager(inspec.os)
|
|
44
|
+
return skip_resource 'The `groups` resource is not supported on your OS yet.' if @group_provider.nil?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
filter = FilterTable.create
|
|
48
|
+
filter.add_accessor(:where)
|
|
49
|
+
.add_accessor(:entries)
|
|
50
|
+
.add(:names, field: 'name')
|
|
51
|
+
.add(:gids, field: 'gid')
|
|
52
|
+
.add(:domains, field: 'domain')
|
|
53
|
+
.add(:exists?) { |x| !x.entries.empty? }
|
|
54
|
+
filter.connect(self, :collect_group_details)
|
|
55
|
+
|
|
56
|
+
def to_s
|
|
57
|
+
'Groups'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# collects information about every group
|
|
63
|
+
def collect_group_details
|
|
64
|
+
return @groups_cache ||= @group_provider.groups unless @group_provider.nil?
|
|
65
|
+
[]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Usage:
|
|
70
|
+
# describe group('root') do
|
|
71
|
+
# it { should exist }
|
|
72
|
+
# its('gid') { should eq 0 }
|
|
73
|
+
# end
|
|
74
|
+
#
|
|
75
|
+
# deprecated has matcher
|
|
76
|
+
# describe group('root') do
|
|
77
|
+
# it { should have_gid 0 }
|
|
78
|
+
# end
|
|
79
|
+
class Group < Inspec.resource(1)
|
|
80
|
+
include GroupManagementSelector
|
|
81
|
+
|
|
82
|
+
name 'group'
|
|
83
|
+
desc 'Use the group InSpec audit resource to test groups on the system.'
|
|
84
|
+
example "
|
|
85
|
+
describe group('root') do
|
|
86
|
+
it { should exist }
|
|
87
|
+
its('gid') { should eq 0 }
|
|
88
|
+
end
|
|
89
|
+
"
|
|
90
|
+
|
|
91
|
+
def initialize(groupname)
|
|
92
|
+
@group = groupname
|
|
93
|
+
@group = @group.downcase unless inspec.os.windows?
|
|
94
|
+
|
|
95
|
+
# select group manager
|
|
96
|
+
@group_provider = select_group_manager(inspec.os)
|
|
97
|
+
return skip_resource 'The `group` resource is not supported on your OS yet.' if @group_provider.nil?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# verifies if a group exists
|
|
101
|
+
def exists?
|
|
102
|
+
group_info.entries.size > 0
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def gid
|
|
106
|
+
gids = group_info.gids
|
|
107
|
+
if gids.size == 0
|
|
108
|
+
nil
|
|
109
|
+
# the default case should be one group
|
|
110
|
+
elsif gids.size == 1
|
|
111
|
+
gids.entries[0]
|
|
112
|
+
else
|
|
113
|
+
fail 'found more than one group with the same name, please use `groups` resource'
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# implements rspec has matcher, to be compatible with serverspec
|
|
118
|
+
def has_gid?(compare_gid)
|
|
119
|
+
gid == compare_gid
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def local
|
|
123
|
+
# at this point the implementation only returns local groups
|
|
124
|
+
true
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def to_s
|
|
128
|
+
"Group #{@group}"
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def group_info
|
|
134
|
+
# we need a local copy for the block
|
|
135
|
+
group = @group.dup
|
|
136
|
+
@groups_cache ||= inspec.groups.where { name == group }
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
class GroupInfo
|
|
141
|
+
attr_reader :inspec
|
|
142
|
+
def initialize(inspec)
|
|
143
|
+
@inspec = inspec
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def groups
|
|
147
|
+
fail 'group provider must implement the `groups` method'
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# implements generic unix groups via /etc/group
|
|
152
|
+
class UnixGroup < GroupInfo
|
|
153
|
+
def groups
|
|
154
|
+
inspec.etc_group.entries
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
class WindowsGroup < GroupInfo
|
|
159
|
+
# returns all local groups
|
|
160
|
+
def groups
|
|
161
|
+
script = <<-EOH
|
|
162
|
+
Function ConvertTo-SID { Param([byte[]]$BinarySID)
|
|
163
|
+
(New-Object System.Security.Principal.SecurityIdentifier($BinarySID,0)).Value
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
$Computername = $Env:Computername
|
|
167
|
+
$adsi = [ADSI]"WinNT://$Computername"
|
|
168
|
+
$groups = $adsi.Children | where {$_.SchemaClassName -eq 'group'} | ForEach {
|
|
169
|
+
$name = $_.Name[0]
|
|
170
|
+
$sid = ConvertTo-SID -BinarySID $_.ObjectSID[0]
|
|
171
|
+
$group =[ADSI]$_.Path
|
|
172
|
+
new-object psobject -property @{name = $group.Name[0]; gid = $sid; domain=$Computername}
|
|
173
|
+
}
|
|
174
|
+
$groups | ConvertTo-Json -Depth 3
|
|
175
|
+
EOH
|
|
176
|
+
cmd = inspec.powershell(script)
|
|
177
|
+
# cannot rely on exit code for now, successful command returns exit code 1
|
|
178
|
+
# return nil if cmd.exit_status != 0, try to parse json
|
|
179
|
+
begin
|
|
180
|
+
groups = JSON.parse(cmd.stdout)
|
|
181
|
+
rescue JSON::ParserError => _e
|
|
182
|
+
return []
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# ensure we have an array of groups
|
|
186
|
+
groups = [groups] if !groups.is_a?(Array)
|
|
187
|
+
groups
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
data/lib/resources/oneget.rb
CHANGED
data/lib/resources/os.rb
CHANGED
data/lib/resources/package.rb
CHANGED
|
@@ -91,9 +91,11 @@ module Inspec::Resources
|
|
|
91
91
|
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
|
92
92
|
multiple_values: false,
|
|
93
93
|
).params
|
|
94
|
+
# If the package is removed and not purged, Status is "deinstall ok config-files" with exit_status 0
|
|
95
|
+
# If the package is purged cmd fails with non-zero exit status
|
|
94
96
|
{
|
|
95
97
|
name: params['Package'],
|
|
96
|
-
installed:
|
|
98
|
+
installed: params['Status'].split(' ').first == 'install',
|
|
97
99
|
version: params['Version'],
|
|
98
100
|
type: 'deb',
|
|
99
101
|
}
|
|
@@ -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
|
data/lib/resources/pip.rb
CHANGED
|
@@ -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/ssl.rb
CHANGED
|
@@ -44,18 +44,16 @@ class SSL < Inspec.resource(1)
|
|
|
44
44
|
attr_reader :host, :port
|
|
45
45
|
|
|
46
46
|
def initialize(opts = {})
|
|
47
|
-
@host = opts[:host]
|
|
48
|
-
inspec.backend.instance_variable_get(:@hostname)
|
|
49
|
-
# FIXME: This can be removed when/if @hostname is available as a property for 'Train::Transports::WinRM::Connection'
|
|
50
|
-
# Train enhancement request for this here: https://github.com/chef/train/issues/128
|
|
51
|
-
if @host.nil? && inspec.backend.class.to_s == 'Train::Transports::WinRM::Connection'
|
|
52
|
-
@host = URI.parse(inspec.backend.instance_variable_get(:@options)[:endpoint]).hostname
|
|
53
|
-
end
|
|
54
|
-
if @host.nil? && inspec.backend.class.to_s == 'Train::Transports::Local::Connection'
|
|
55
|
-
@host = 'localhost'
|
|
56
|
-
end
|
|
47
|
+
@host = opts[:host]
|
|
57
48
|
if @host.nil?
|
|
58
|
-
|
|
49
|
+
# Transports like SSH and WinRM will provide a hostname
|
|
50
|
+
if inspec.backend.respond_to?('hostname')
|
|
51
|
+
@host = inspec.backend.hostname
|
|
52
|
+
elsif inspec.backend.class.to_s == 'Train::Transports::Local::Connection'
|
|
53
|
+
@host = 'localhost'
|
|
54
|
+
else
|
|
55
|
+
fail 'Cannot determine host for SSL test. Please specify it or use a different target.'
|
|
56
|
+
end
|
|
59
57
|
end
|
|
60
58
|
@port = opts[:port] || 443
|
|
61
59
|
@timeout = opts[:timeout]
|
data/lib/resources/sys_info.rb
CHANGED
data/lib/resources/users.rb
CHANGED
|
@@ -4,11 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
require 'utils/parser'
|
|
6
6
|
require 'utils/convert'
|
|
7
|
+
require 'utils/filter'
|
|
7
8
|
|
|
8
9
|
module Inspec::Resources
|
|
9
10
|
# This file contains two resources, the `user` and `users` resource.
|
|
10
11
|
# The `user` resource is optimized for requests that verify specific users
|
|
11
|
-
# that you know upfront for testing. If you need to query all users or
|
|
12
|
+
# that you know upfront for testing. If you need to query all users or search
|
|
12
13
|
# specific users with certain properties, use the `users` resource.
|
|
13
14
|
module UserManagementSelector
|
|
14
15
|
# select user provider based on the operating system
|
|
@@ -65,7 +66,7 @@ module Inspec::Resources
|
|
|
65
66
|
def initialize
|
|
66
67
|
# select user provider
|
|
67
68
|
@user_provider = select_user_manager(inspec.os)
|
|
68
|
-
return skip_resource 'The `
|
|
69
|
+
return skip_resource 'The `users` resource is not supported on your OS yet.' if @user_provider.nil?
|
|
69
70
|
end
|
|
70
71
|
|
|
71
72
|
filter = FilterTable.create
|
|
@@ -546,21 +547,12 @@ module Inspec::Resources
|
|
|
546
547
|
end
|
|
547
548
|
end
|
|
548
549
|
|
|
549
|
-
#
|
|
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
|
|
550
553
|
# @see https://msdn.microsoft.com/en-us/library/aa394507(v=vs.85).aspx
|
|
551
554
|
# @see https://msdn.microsoft.com/en-us/library/aa394153(v=vs.85).aspx
|
|
552
|
-
#
|
|
553
|
-
# using Get-AdUser would be the best command for domain machines, but it will not be installed
|
|
554
|
-
# on client machines by default
|
|
555
|
-
# @see https://technet.microsoft.com/en-us/library/ee617241.aspx
|
|
556
|
-
# @see https://technet.microsoft.com/en-us/library/hh509016(v=WS.10).aspx
|
|
557
|
-
# @see http://woshub.com/get-aduser-getting-active-directory-users-data-via-powershell/
|
|
558
|
-
# @see http://stackoverflow.com/questions/17548523/the-term-get-aduser-is-not-recognized-as-the-name-of-a-cmdlet
|
|
559
|
-
#
|
|
560
|
-
# Just for reference, we could also use ADSI (Active Directory Service Interfaces)
|
|
561
|
-
# @see https://mcpmag.com/articles/2015/04/15/reporting-on-local-accounts.aspx
|
|
562
555
|
class WindowsUser < UserInfo
|
|
563
|
-
# parse windows account name
|
|
564
556
|
def parse_windows_account(username)
|
|
565
557
|
account = username.split('\\')
|
|
566
558
|
name = account.pop
|
|
@@ -569,67 +561,92 @@ module Inspec::Resources
|
|
|
569
561
|
end
|
|
570
562
|
|
|
571
563
|
def identity(username)
|
|
572
|
-
#
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
if
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
|
570
|
+
|
|
571
|
+
def list_users
|
|
572
|
+
collect_user_details.map { |user| user[:username] }
|
|
573
|
+
end
|
|
581
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)
|
|
582
578
|
script = <<-EOH
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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
|
|
594
636
|
EOH
|
|
595
|
-
|
|
596
637
|
cmd = inspec.powershell(script)
|
|
597
|
-
|
|
598
638
|
# cannot rely on exit code for now, successful command returns exit code 1
|
|
599
639
|
# return nil if cmd.exit_status != 0, try to parse json
|
|
600
640
|
begin
|
|
601
|
-
|
|
641
|
+
users = JSON.parse(cmd.stdout)
|
|
602
642
|
rescue JSON::ParserError => _e
|
|
603
643
|
return nil
|
|
604
644
|
end
|
|
605
645
|
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
#
|
|
609
|
-
|
|
610
|
-
group_names = group_hashes.map { |grp| grp['Caption'] }
|
|
611
|
-
{
|
|
612
|
-
uid: user_hash['SID'],
|
|
613
|
-
username: user_hash['Caption'],
|
|
614
|
-
gid: nil,
|
|
615
|
-
group: nil,
|
|
616
|
-
groups: group_names,
|
|
617
|
-
disabled: user_hash['Disabled'],
|
|
618
|
-
}
|
|
619
|
-
end
|
|
620
|
-
|
|
621
|
-
# not implemented yet
|
|
622
|
-
def meta_info(_username)
|
|
623
|
-
{
|
|
624
|
-
home: nil,
|
|
625
|
-
shell: nil,
|
|
626
|
-
}
|
|
627
|
-
end
|
|
628
|
-
|
|
629
|
-
def list_users
|
|
630
|
-
script = 'Get-WmiObject Win32_UserAccount | Select-Object -ExpandProperty Caption'
|
|
631
|
-
cmd = inspec.powershell(script)
|
|
632
|
-
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 } }
|
|
633
650
|
end
|
|
634
651
|
end
|
|
635
652
|
end
|