inspec 1.31.0 → 1.45.9
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/.rubocop.yml +3 -0
- data/CHANGELOG.md +2599 -1
- data/Gemfile +0 -1
- data/README.md +24 -34
- data/Rakefile +0 -31
- data/docs/dsl_inspec.md +10 -0
- data/docs/matchers.md +29 -0
- data/docs/plugin_kitchen_inspec.md +18 -24
- data/docs/profiles.md +101 -81
- data/docs/resources/aide_conf.md.erb +71 -0
- data/docs/resources/apache_conf.md.erb +19 -33
- data/docs/resources/apt.md.erb +22 -36
- data/docs/resources/audit_policy.md.erb +9 -24
- data/docs/resources/auditd.md.erb +78 -0
- data/docs/resources/auditd_conf.md.erb +20 -34
- data/docs/resources/auditd_rules.md.erb +8 -24
- data/docs/resources/bash.md.erb +4 -26
- data/docs/resources/bond.md.erb +25 -40
- data/docs/resources/bridge.md.erb +5 -25
- data/docs/resources/bsd_service.md.erb +5 -25
- data/docs/resources/command.md.erb +35 -50
- data/docs/resources/cpan.md.erb +62 -0
- data/docs/resources/cran.md.erb +54 -0
- data/docs/resources/crontab.md.erb +9 -23
- data/docs/resources/csv.md.erb +12 -27
- data/docs/resources/dh_params.md +1 -0
- data/docs/resources/directory.md.erb +5 -25
- data/docs/resources/docker.md.erb +61 -58
- data/docs/resources/docker_container.md.erb +23 -19
- data/docs/resources/docker_image.md.erb +20 -16
- data/docs/resources/elasticsearch.md.erb +245 -0
- data/docs/resources/etc_fstab.md.erb +120 -0
- data/docs/resources/etc_group.md.erb +29 -45
- data/docs/resources/etc_hosts.md.erb +68 -0
- data/docs/resources/etc_hosts_allow.md.erb +68 -0
- data/docs/resources/etc_hosts_deny.md.erb +68 -0
- data/docs/resources/file.md.erb +227 -216
- data/docs/resources/firewalld.md.erb +104 -0
- data/docs/resources/gem.md.erb +21 -35
- data/docs/resources/group.md.erb +16 -30
- data/docs/resources/grub_conf.md.erb +9 -24
- data/docs/resources/host.md.erb +32 -49
- data/docs/resources/http.md.erb +38 -44
- data/docs/resources/iis_app.md.erb +25 -35
- data/docs/resources/iis_site.md.erb +26 -40
- data/docs/resources/inetd_conf.md.erb +27 -42
- data/docs/resources/ini.md.erb +9 -23
- data/docs/resources/interface.md.erb +5 -25
- data/docs/resources/iptables.md.erb +15 -29
- data/docs/resources/json.md.erb +12 -27
- data/docs/resources/kernel_module.md.erb +47 -61
- data/docs/resources/kernel_parameter.md.erb +15 -29
- data/docs/resources/key_rsa.md.erb +3 -0
- data/docs/resources/launchd_service.md.erb +5 -25
- data/docs/resources/limits_conf.md.erb +15 -29
- data/docs/resources/login_def.md.erb +15 -30
- data/docs/resources/mount.md.erb +18 -33
- data/docs/resources/mssql_session.md.erb +9 -12
- data/docs/resources/mysql_conf.md.erb +28 -32
- data/docs/resources/mysql_session.md.erb +15 -29
- data/docs/resources/nginx.md.erb +78 -0
- data/docs/resources/nginx_conf.md.erb +127 -0
- data/docs/resources/npm.md.erb +19 -35
- data/docs/resources/ntp_conf.md.erb +20 -37
- data/docs/resources/oneget.md.erb +15 -30
- data/docs/resources/oracledb_session.md.erb +9 -11
- data/docs/resources/os.md.erb +29 -43
- data/docs/resources/os_env.md.erb +23 -44
- data/docs/resources/package.md.erb +34 -36
- data/docs/resources/parse_config.md.erb +5 -25
- data/docs/resources/parse_config_file.md.erb +31 -43
- data/docs/resources/{etc_passwd.md.erb → passwd.md.erb} +24 -39
- data/docs/resources/pip.md.erb +20 -35
- data/docs/resources/port.md.erb +43 -57
- data/docs/resources/postgres_conf.md.erb +17 -31
- data/docs/resources/postgres_hba_conf.md.erb +26 -38
- data/docs/resources/postgres_ident_conf.md.erb +25 -37
- data/docs/resources/postgres_session.md.erb +15 -29
- data/docs/resources/powershell.md.erb +27 -42
- data/docs/resources/processes.md.erb +17 -33
- data/docs/resources/rabbitmq_config.md.erb +9 -24
- data/docs/resources/registry_key.md.erb +27 -42
- data/docs/resources/runit_service.md.erb +5 -25
- data/docs/resources/security_policy.md.erb +12 -27
- data/docs/resources/service.md.erb +27 -42
- data/docs/resources/{etc_shadow.md.erb → shadow.md.erb} +41 -47
- data/docs/resources/ssh_config.md.erb +19 -34
- data/docs/resources/sshd_config.md.erb +19 -34
- data/docs/resources/ssl.md.erb +39 -54
- data/docs/resources/sys_info.md.erb +12 -26
- data/docs/resources/systemd_service.md.erb +5 -25
- data/docs/resources/sysv_service.md.erb +5 -25
- data/docs/resources/upstart_service.md.erb +5 -25
- data/docs/resources/user.md.erb +29 -44
- data/docs/resources/users.md.erb +12 -26
- data/docs/resources/vbscript.md.erb +9 -24
- data/docs/resources/virtualization.md.erb +8 -23
- data/docs/resources/windows_feature.md.erb +15 -30
- data/docs/resources/windows_hotfix.md.erb +50 -0
- data/docs/resources/windows_task.md.erb +12 -26
- data/docs/resources/wmi.md.erb +9 -24
- data/docs/resources/x509_certificate.md.erb +4 -0
- data/docs/resources/xinetd_conf.md.erb +65 -80
- data/docs/resources/xml.md.erb +61 -0
- data/docs/resources/yaml.md.erb +12 -27
- data/docs/resources/yum.md.erb +37 -51
- data/docs/resources/zfs_dataset.md.erb +15 -26
- data/docs/resources/zfs_pool.md.erb +9 -20
- data/docs/ruby_usage.md +0 -1
- data/examples/meta-profile/README.md +1 -1
- data/examples/profile-sensitive/README.md +29 -0
- data/examples/profile-sensitive/controls/sensitive-failures.rb +9 -0
- data/examples/profile-sensitive/controls/sensitive.rb +9 -0
- data/examples/profile-sensitive/inspec.yml +8 -0
- data/inspec.gemspec +3 -3
- data/lib/bundles/inspec-artifact/cli.rb +5 -5
- data/lib/bundles/inspec-compliance/README.md +24 -22
- data/lib/bundles/inspec-compliance/api/login.rb +152 -0
- data/lib/bundles/inspec-compliance/api.rb +46 -16
- data/lib/bundles/inspec-compliance/cli.rb +58 -165
- data/lib/bundles/inspec-compliance/target.rb +2 -3
- data/lib/bundles/inspec-habitat/cli.rb +7 -2
- data/lib/bundles/inspec-habitat/profile.rb +44 -20
- data/lib/bundles/inspec-init/templates/profile/README.md +1 -1
- data/lib/bundles/inspec-init/templates/profile/controls/example.rb +1 -1
- data/lib/bundles/inspec-supermarket/api.rb +1 -1
- data/lib/inspec/backend.rb +8 -0
- data/lib/inspec/cli.rb +1 -1
- data/lib/inspec/control_eval_context.rb +16 -6
- data/lib/inspec/dependencies/requirement.rb +22 -8
- data/lib/inspec/dependencies/resolver.rb +1 -1
- data/lib/inspec/exceptions.rb +4 -0
- data/lib/inspec/file_provider.rb +38 -9
- data/lib/inspec/metadata.rb +17 -3
- data/lib/inspec/objects/attribute.rb +16 -7
- data/lib/inspec/objects/control.rb +10 -2
- data/lib/inspec/plugins/resource.rb +34 -5
- data/lib/inspec/profile.rb +10 -1
- data/lib/inspec/profile_context.rb +3 -1
- data/lib/inspec/resource.rb +17 -5
- data/lib/inspec/rspec_json_formatter.rb +48 -14
- data/lib/inspec/rule.rb +4 -0
- data/lib/inspec/runner.rb +72 -30
- data/lib/inspec/shell.rb +24 -19
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +40 -0
- data/lib/resources/aide_conf.rb +162 -0
- data/lib/resources/auditd.rb +231 -0
- data/lib/resources/auditd_rules.rb +3 -1
- data/lib/resources/command.rb +1 -1
- data/lib/resources/cpan.rb +60 -0
- data/lib/resources/cran.rb +66 -0
- data/lib/resources/docker.rb +8 -1
- data/lib/resources/docker_container.rb +19 -2
- data/lib/resources/elasticsearch.rb +172 -0
- data/lib/resources/etc_fstab.rb +102 -0
- data/lib/resources/etc_group.rb +1 -1
- data/lib/resources/etc_hosts.rb +81 -0
- data/lib/resources/etc_hosts_allow_deny.rb +122 -0
- data/lib/resources/file.rb +87 -10
- data/lib/resources/firewalld.rb +144 -0
- data/lib/resources/groups.rb +0 -1
- data/lib/resources/host.rb +30 -15
- data/lib/resources/http.rb +178 -22
- data/lib/resources/mssql_session.rb +3 -3
- data/lib/resources/mysql_conf.rb +10 -0
- data/lib/resources/mysql_session.rb +1 -1
- data/lib/resources/nginx.rb +97 -0
- data/lib/resources/nginx_conf.rb +134 -1
- data/lib/resources/oracledb_session.rb +9 -8
- data/lib/resources/os_env.rb +7 -4
- data/lib/resources/package.rb +14 -2
- data/lib/resources/packages.rb +1 -1
- data/lib/resources/pip.rb +28 -20
- data/lib/resources/port.rb +251 -3
- data/lib/resources/postgres.rb +1 -1
- data/lib/resources/postgres_session.rb +1 -1
- data/lib/resources/processes.rb +106 -20
- data/lib/resources/registry_key.rb +11 -2
- data/lib/resources/service.rb +5 -24
- data/lib/resources/ssh_conf.rb +1 -1
- data/lib/resources/ssl.rb +4 -3
- data/lib/resources/toml.rb +2 -2
- data/lib/resources/windows_hotfix.rb +35 -0
- data/lib/resources/xinetd.rb +4 -6
- data/lib/resources/xml.rb +27 -0
- data/lib/source_readers/inspec.rb +3 -3
- data/lib/utils/find_files.rb +2 -1
- data/lib/utils/nginx_parser.rb +10 -1
- data/lib/utils/object_traversal.rb +35 -10
- data/lib/utils/parser.rb +21 -3
- metadata +48 -18
- data/docs/cli.md +0 -392
|
@@ -18,100 +18,90 @@ module Compliance
|
|
|
18
18
|
namespace
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
desc "login SERVER --insecure --user='USER' --token='TOKEN'", 'Log in to a Chef Compliance SERVER'
|
|
21
|
+
desc "login https://SERVER --insecure --user='USER' --ent='ENTERPRISE' --token='TOKEN'", 'Log in to a Chef Compliance/Chef Automate SERVER'
|
|
22
|
+
long_desc <<-LONGDESC
|
|
23
|
+
`login` allows you to use InSpec with Chef Automate or a Chef Compliance Server
|
|
24
|
+
|
|
25
|
+
You need to a token for communication. More information about token retrieval
|
|
26
|
+
is available at:
|
|
27
|
+
https://docs.chef.io/api_automate.html#authentication-methods
|
|
28
|
+
https://docs.chef.io/api_compliance.html#obtaining-an-api-token
|
|
29
|
+
LONGDESC
|
|
22
30
|
option :insecure, aliases: :k, type: :boolean,
|
|
23
31
|
desc: 'Explicitly allows InSpec to perform "insecure" SSL connections and transfers'
|
|
24
32
|
option :user, type: :string, required: false,
|
|
25
|
-
desc: '
|
|
33
|
+
desc: 'Username'
|
|
26
34
|
option :password, type: :string, required: false,
|
|
27
|
-
desc: 'Chef Compliance
|
|
28
|
-
option :apipath, type: :string, default: '/api',
|
|
29
|
-
desc: 'Set the path to the API, defaults to /api'
|
|
35
|
+
desc: 'Password (Chef Compliance Only)'
|
|
30
36
|
option :token, type: :string, required: false,
|
|
31
|
-
desc: '
|
|
37
|
+
desc: 'Access token'
|
|
32
38
|
option :refresh_token, type: :string, required: false,
|
|
33
|
-
desc: 'Chef Compliance refresh token'
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
desc: 'Chef Compliance refresh token (Chef Compliance Only)'
|
|
40
|
+
option :dctoken, type: :string, required: false,
|
|
41
|
+
desc: 'Data Collector token (Chef Automate Only)'
|
|
42
|
+
option :ent, type: :string, required: false,
|
|
43
|
+
desc: 'Enterprise for Chef Automate reporting (Chef Automate Only)'
|
|
44
|
+
def login(server)
|
|
37
45
|
options['server'] = server
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
if !options['user'].nil? && !options['password'].nil?
|
|
41
|
-
# username / password
|
|
42
|
-
_success, msg = login_username_password(url, options['user'], options['password'], options['insecure'])
|
|
43
|
-
elsif !options['user'].nil? && !options['token'].nil?
|
|
44
|
-
# access token
|
|
45
|
-
_success, msg = store_access_token(url, options['user'], options['token'], options['insecure'])
|
|
46
|
-
elsif !options['refresh_token'].nil? && !options['user'].nil?
|
|
47
|
-
# refresh token
|
|
48
|
-
_success, msg = store_refresh_token(url, options['refresh_token'], true, options['user'], options['insecure'])
|
|
49
|
-
# TODO: we should login with the refreshtoken here
|
|
50
|
-
elsif !options['refresh_token'].nil?
|
|
51
|
-
_success, msg = login_refreshtoken(url, options)
|
|
52
|
-
else
|
|
53
|
-
puts 'Please run `inspec compliance login SERVER` with options --token or --refresh_token, --user, and --insecure or --not-insecure'
|
|
54
|
-
exit 1
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
puts '', msg
|
|
46
|
+
Compliance::API.login(options)
|
|
58
47
|
end
|
|
59
48
|
|
|
60
|
-
desc "login_automate SERVER --insecure --user='USER' --ent='
|
|
49
|
+
desc "login_automate https://SERVER --insecure --user='USER' --ent='ENTERPRISE' --usertoken='TOKEN'", 'Log in to a Chef Automate SERVER (DEPRECATED: Please use `login`)'
|
|
61
50
|
long_desc <<-LONGDESC
|
|
62
|
-
|
|
51
|
+
This commmand is deprecated and will be removed, please use `--login`.
|
|
52
|
+
|
|
53
|
+
`login_automate` allows you to use InSpec with Chef Automate.
|
|
63
54
|
|
|
64
|
-
You need to a
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
You need to a token for communication. More information about token retrieval
|
|
56
|
+
is available at:
|
|
57
|
+
https://docs.chef.io/api_automate.html#authentication-methods
|
|
58
|
+
https://docs.chef.io/api_compliance.html#obtaining-an-api-token
|
|
67
59
|
LONGDESC
|
|
68
|
-
option :dctoken, type: :string,
|
|
69
|
-
desc: 'Data Collector token'
|
|
70
|
-
option :usertoken, type: :string,
|
|
71
|
-
desc: 'Automate user token'
|
|
72
|
-
option :user, type: :string,
|
|
73
|
-
desc: 'Automate username'
|
|
74
|
-
option :ent, type: :string,
|
|
75
|
-
desc: 'Enterprise for Chef Automate reporting'
|
|
76
60
|
option :insecure, aliases: :k, type: :boolean,
|
|
77
61
|
desc: 'Explicitly allows InSpec to perform "insecure" SSL connections and transfers'
|
|
78
|
-
|
|
62
|
+
option :user, type: :string, required: true,
|
|
63
|
+
desc: 'Username'
|
|
64
|
+
option :usertoken, type: :string, required: false,
|
|
65
|
+
desc: 'Access token (DEPRECATED: Please use `--token`)'
|
|
66
|
+
option :token, type: :string, required: false,
|
|
67
|
+
desc: 'Access token'
|
|
68
|
+
option :dctoken, type: :string, required: false,
|
|
69
|
+
desc: 'Data Collector token'
|
|
70
|
+
option :ent, type: :string, required: true,
|
|
71
|
+
desc: 'Enterprise for Chef Automate reporting'
|
|
72
|
+
def login_automate(server)
|
|
73
|
+
warn '[DEPRECATION] `inspec compliance login_automate` is deprecated. Please use `inspec compliance login`'
|
|
79
74
|
options['server'] = server
|
|
80
|
-
url = options['server'] + '/compliance'
|
|
81
75
|
|
|
82
|
-
|
|
83
|
-
msg = login_automate_config(url, options['user'], options['dctoken'], options['usertoken'], options['ent'], options['insecure'])
|
|
84
|
-
puts '', msg
|
|
85
|
-
exit 0
|
|
86
|
-
end
|
|
76
|
+
options['token'] = options['usertoken'] if options['usertoken']
|
|
87
77
|
|
|
88
|
-
|
|
89
|
-
puts 'Please specify an user using `--user \'USER\'`' if options['user'].nil?
|
|
90
|
-
puts 'Please specify an enterprise using `--ent \'cd\'`' if options['ent'].nil?
|
|
91
|
-
puts 'Please specify a token using `--usertoken=\'AUTOMATE_TOKEN\'`' if options['usertoken'].nil? && options['dctoken'].nil?
|
|
92
|
-
exit 1
|
|
78
|
+
Compliance::API.login(options)
|
|
93
79
|
end
|
|
94
80
|
|
|
95
81
|
desc 'profiles', 'list all available profiles in Chef Compliance'
|
|
96
|
-
|
|
82
|
+
option :owner, type: :string, required: false,
|
|
83
|
+
desc: 'owner whose profiles to list'
|
|
97
84
|
def profiles
|
|
98
85
|
config = Compliance::Configuration.new
|
|
99
86
|
return if !loggedin(config)
|
|
100
87
|
|
|
88
|
+
# set owner to config
|
|
89
|
+
config['owner'] = options['owner'] || config['user']
|
|
90
|
+
|
|
101
91
|
msg, profiles = Compliance::API.profiles(config)
|
|
102
92
|
profiles.sort_by! { |hsh| hsh['title'] }
|
|
103
93
|
if !profiles.empty?
|
|
104
94
|
# iterate over profiles
|
|
105
95
|
headline('Available profiles:')
|
|
106
96
|
profiles.each { |profile|
|
|
107
|
-
li("#{profile['title']} #{mark_text(profile['owner_id'] + '/' + profile['name'])}")
|
|
97
|
+
li("#{profile['title']} v#{profile['version']} (#{mark_text(profile['owner_id'] + '/' + profile['name'])})")
|
|
108
98
|
}
|
|
109
99
|
else
|
|
110
100
|
puts msg, 'Could not find any profiles'
|
|
111
101
|
exit 1
|
|
112
102
|
end
|
|
113
103
|
rescue Compliance::ServerConfigurationMissing
|
|
114
|
-
puts "\nServer configuration information is missing. Please login using `inspec compliance login`"
|
|
104
|
+
STDERR.puts "\nServer configuration information is missing. Please login using `inspec compliance login`"
|
|
115
105
|
exit 1
|
|
116
106
|
end
|
|
117
107
|
|
|
@@ -159,11 +149,16 @@ module Compliance
|
|
|
159
149
|
|
|
160
150
|
desc 'upload PATH', 'uploads a local profile to Chef Compliance'
|
|
161
151
|
option :overwrite, type: :boolean, default: false,
|
|
162
|
-
desc: 'Overwrite existing profile on
|
|
152
|
+
desc: 'Overwrite existing profile on Server.'
|
|
153
|
+
option :owner, type: :string, required: false,
|
|
154
|
+
desc: 'Owner that should own the profile'
|
|
163
155
|
def upload(path) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, PerceivedComplexity, Metrics/CyclomaticComplexity
|
|
164
156
|
config = Compliance::Configuration.new
|
|
165
157
|
return if !loggedin(config)
|
|
166
158
|
|
|
159
|
+
# set owner to config
|
|
160
|
+
config['owner'] = options['owner'] || config['user']
|
|
161
|
+
|
|
167
162
|
unless File.exist?(path)
|
|
168
163
|
puts "Directory #{path} does not exist."
|
|
169
164
|
exit 1
|
|
@@ -195,14 +190,12 @@ module Compliance
|
|
|
195
190
|
error.call('Please login via `inspec compliance login`')
|
|
196
191
|
end
|
|
197
192
|
|
|
198
|
-
# owner
|
|
199
|
-
owner = config['user']
|
|
200
193
|
# read profile name from inspec.yml
|
|
201
194
|
profile_name = profile.params[:name]
|
|
202
195
|
|
|
203
196
|
# check that the profile is not uploaded already,
|
|
204
197
|
# confirm upload to the user (overwrite with --force)
|
|
205
|
-
if Compliance::API.exist?(config, "#{owner}/#{profile_name}") && !options['overwrite']
|
|
198
|
+
if Compliance::API.exist?(config, "#{config['owner']}/#{profile_name}") && !options['overwrite']
|
|
206
199
|
error.call('Profile exists on the server, use --overwrite')
|
|
207
200
|
end
|
|
208
201
|
|
|
@@ -221,12 +214,12 @@ module Compliance
|
|
|
221
214
|
archive_path = path
|
|
222
215
|
end
|
|
223
216
|
|
|
224
|
-
puts "Start upload to #{owner}/#{profile_name}"
|
|
217
|
+
puts "Start upload to #{config['owner']}/#{profile_name}"
|
|
225
218
|
pname = ERB::Util.url_encode(profile_name)
|
|
226
219
|
|
|
227
220
|
Compliance::API.is_automate_server?(config) ? upload_msg = 'Uploading to Chef Automate' : upload_msg = 'Uploading to Chef Compliance'
|
|
228
221
|
puts upload_msg
|
|
229
|
-
success, msg = Compliance::API.upload(config, owner, pname, archive_path)
|
|
222
|
+
success, msg = Compliance::API.upload(config, config['owner'], pname, archive_path)
|
|
230
223
|
|
|
231
224
|
if success
|
|
232
225
|
puts 'Successfully uploaded profile'
|
|
@@ -272,109 +265,9 @@ module Compliance
|
|
|
272
265
|
|
|
273
266
|
private
|
|
274
267
|
|
|
275
|
-
def login_automate_config(url, user, dctoken, usertoken, ent, insecure) # rubocop:disable Metrics/ParameterLists
|
|
276
|
-
config = Compliance::Configuration.new
|
|
277
|
-
config.clean
|
|
278
|
-
config['user'] = user
|
|
279
|
-
config['server'] = url
|
|
280
|
-
config['automate'] = {}
|
|
281
|
-
config['automate']['ent'] = ent
|
|
282
|
-
config['server_type'] = 'automate'
|
|
283
|
-
config['insecure'] = insecure
|
|
284
|
-
|
|
285
|
-
# determine token method being used
|
|
286
|
-
if !dctoken.nil?
|
|
287
|
-
config['token'] = dctoken
|
|
288
|
-
token_type = 'dctoken'
|
|
289
|
-
token_msg = 'data collector token'
|
|
290
|
-
else
|
|
291
|
-
config['token'] = usertoken
|
|
292
|
-
token_type = 'usertoken'
|
|
293
|
-
token_msg = 'automate user token'
|
|
294
|
-
end
|
|
295
|
-
config['automate']['token_type'] = token_type
|
|
296
|
-
config['version'] = Compliance::API.version(config)
|
|
297
|
-
config.store
|
|
298
|
-
msg = "Stored configuration for Chef Automate: '#{url}' with user: '#{user}', ent: '#{ent}' and your #{token_msg}"
|
|
299
|
-
msg
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
def login_refreshtoken(url, options)
|
|
303
|
-
success, msg, _access_token = Compliance::API.get_token_via_refresh_token(url, options['refresh_token'], options['insecure'])
|
|
304
|
-
if success
|
|
305
|
-
config = Compliance::Configuration.new
|
|
306
|
-
config.clean
|
|
307
|
-
config['server'] = url
|
|
308
|
-
config['insecure'] = options['insecure']
|
|
309
|
-
config['server_type'] = 'compliance'
|
|
310
|
-
config['version'] = Compliance::API.version(config)
|
|
311
|
-
config.store
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
[success, msg]
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
def login_username_password(url, username, password, insecure)
|
|
318
|
-
config = Compliance::Configuration.new
|
|
319
|
-
config.clean
|
|
320
|
-
success, msg, api_token = Compliance::API.get_token_via_password(url, username, password, insecure)
|
|
321
|
-
if success
|
|
322
|
-
config['server'] = url
|
|
323
|
-
config['user'] = username
|
|
324
|
-
config['token'] = api_token
|
|
325
|
-
config['insecure'] = insecure
|
|
326
|
-
config['server_type'] = 'compliance'
|
|
327
|
-
config['version'] = Compliance::API.version(config)
|
|
328
|
-
config.store
|
|
329
|
-
success = true
|
|
330
|
-
end
|
|
331
|
-
[success, msg]
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
# saves a user access token (limited time)
|
|
335
|
-
def store_access_token(url, user, token, insecure)
|
|
336
|
-
config = Compliance::Configuration.new
|
|
337
|
-
config.clean
|
|
338
|
-
config['server'] = url
|
|
339
|
-
config['insecure'] = insecure
|
|
340
|
-
config['user'] = user
|
|
341
|
-
config['token'] = token
|
|
342
|
-
config['server_type'] = 'compliance'
|
|
343
|
-
config['version'] = Compliance::API.version(config)
|
|
344
|
-
config.store
|
|
345
|
-
|
|
346
|
-
[true, 'API access token stored']
|
|
347
|
-
end
|
|
348
|
-
|
|
349
|
-
# saves a refresh token supplied by the user
|
|
350
|
-
def store_refresh_token(url, refresh_token, verify, user, insecure)
|
|
351
|
-
config = Compliance::Configuration.new
|
|
352
|
-
config.clean
|
|
353
|
-
config['server'] = url
|
|
354
|
-
config['refresh_token'] = refresh_token
|
|
355
|
-
config['user'] = user
|
|
356
|
-
config['insecure'] = insecure
|
|
357
|
-
config['server_type'] = 'compliance'
|
|
358
|
-
config['version'] = Compliance::API.version(config)
|
|
359
|
-
|
|
360
|
-
if !verify
|
|
361
|
-
config.store
|
|
362
|
-
success = true
|
|
363
|
-
msg = 'API refresh token stored'
|
|
364
|
-
else
|
|
365
|
-
success, msg, _access_token= Compliance::API.get_token_via_refresh_token(url, refresh_token, insecure)
|
|
366
|
-
if success
|
|
367
|
-
config.store
|
|
368
|
-
msg = 'API access token verified'
|
|
369
|
-
end
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
[success, msg]
|
|
373
|
-
end
|
|
374
|
-
|
|
375
268
|
def loggedin(config)
|
|
376
269
|
serverknown = !config['server'].nil?
|
|
377
|
-
puts 'You need to login first with `inspec compliance login`
|
|
270
|
+
puts 'You need to login first with `inspec compliance login`' if !serverknown
|
|
378
271
|
serverknown
|
|
379
272
|
end
|
|
380
273
|
end
|
|
@@ -32,7 +32,7 @@ module Compliance
|
|
|
32
32
|
if config['token'].nil? && config['refresh_token'].nil?
|
|
33
33
|
if config['server_type'] == 'automate'
|
|
34
34
|
server = 'automate'
|
|
35
|
-
msg = 'inspec compliance
|
|
35
|
+
msg = 'inspec compliance login https://your_automate_server --user USER --ent ENT --dctoken DCTOKEN or --token USERTOKEN'
|
|
36
36
|
else
|
|
37
37
|
server = 'compliance'
|
|
38
38
|
msg = "inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE' "
|
|
@@ -90,8 +90,7 @@ EOF
|
|
|
90
90
|
|
|
91
91
|
raise 'Unable to determine compliance profile name. This can be caused by ' \
|
|
92
92
|
'an incorrect server in your configuration. Try to login to compliance ' \
|
|
93
|
-
'via the `inspec compliance login`
|
|
94
|
-
'commands.' if m.nil?
|
|
93
|
+
'via the `inspec compliance login` command.' if m.nil?
|
|
95
94
|
|
|
96
95
|
"#{m[:owner]}/#{m[:id]}"
|
|
97
96
|
end
|
|
@@ -7,14 +7,19 @@ module Habitat
|
|
|
7
7
|
class HabitatProfileCLI < Thor
|
|
8
8
|
namespace 'habitat profile'
|
|
9
9
|
|
|
10
|
-
desc 'create PATH', 'Create a Habitat artifact for the profile found at PATH'
|
|
10
|
+
desc 'create PATH', 'Create a one-time Habitat artifact for the profile found at PATH'
|
|
11
11
|
option :output_dir, type: :string, required: false,
|
|
12
12
|
desc: 'Directory in which to save the generated Habitat artifact. Default: current directory'
|
|
13
13
|
def create(path)
|
|
14
14
|
Habitat::Profile.create(path, options)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
desc '
|
|
17
|
+
desc 'setup PATH', 'Configure the profile at PATH for Habitat, including a plan and hooks'
|
|
18
|
+
def setup(path)
|
|
19
|
+
Habitat::Profile.setup(path)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc 'upload PATH', 'Create a one-time Habitat artifact for the profile found at PATH, and upload it to a Habitat Depot'
|
|
18
23
|
def upload(path)
|
|
19
24
|
Habitat::Profile.upload(path, options)
|
|
20
25
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
require 'inspec/profile_vendor'
|
|
5
5
|
require 'mixlib/shellout'
|
|
6
|
-
require '
|
|
6
|
+
require 'tomlrb'
|
|
7
7
|
|
|
8
8
|
module Habitat
|
|
9
9
|
class Profile # rubocop:disable Metrics/ClassLength
|
|
@@ -17,6 +17,10 @@ module Habitat
|
|
|
17
17
|
creator.delete_work_dir
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def self.setup(path)
|
|
21
|
+
new(path).setup
|
|
22
|
+
end
|
|
23
|
+
|
|
20
24
|
def self.upload(path, options = {})
|
|
21
25
|
uploader = new(path, options)
|
|
22
26
|
uploader.upload
|
|
@@ -41,10 +45,11 @@ module Habitat
|
|
|
41
45
|
verify_profile
|
|
42
46
|
vendor_profile_dependencies
|
|
43
47
|
copy_profile_to_work_dir
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
create_habitat_directories(work_dir)
|
|
49
|
+
create_plan(work_dir)
|
|
50
|
+
create_run_hook(work_dir)
|
|
51
|
+
create_settings_file(work_dir)
|
|
52
|
+
create_default_config(work_dir)
|
|
48
53
|
|
|
49
54
|
# returns the path to the .hart file in the work directory
|
|
50
55
|
build_hart
|
|
@@ -80,6 +85,18 @@ module Habitat
|
|
|
80
85
|
FileUtils.rm_rf(work_dir) if Dir.exist?(work_dir)
|
|
81
86
|
end
|
|
82
87
|
|
|
88
|
+
def setup
|
|
89
|
+
Habitat::Log.info("Setting up profile at #{path} for Habitat...")
|
|
90
|
+
create_profile_object
|
|
91
|
+
verify_profile
|
|
92
|
+
vendor_profile_dependencies
|
|
93
|
+
create_habitat_directories(path)
|
|
94
|
+
create_plan(path)
|
|
95
|
+
create_run_hook(path)
|
|
96
|
+
create_settings_file(path)
|
|
97
|
+
create_default_config(path)
|
|
98
|
+
end
|
|
99
|
+
|
|
83
100
|
private
|
|
84
101
|
|
|
85
102
|
def create_profile_object
|
|
@@ -151,20 +168,26 @@ module Habitat
|
|
|
151
168
|
return @work_dir if @work_dir
|
|
152
169
|
|
|
153
170
|
@work_dir ||= Dir.mktmpdir('inspec-habitat-exporter')
|
|
154
|
-
Dir.mkdir(File.join(@work_dir, 'src'))
|
|
155
|
-
Dir.mkdir(File.join(@work_dir, 'habitat'))
|
|
156
|
-
Dir.mkdir(File.join(@work_dir, 'habitat', 'config'))
|
|
157
|
-
Dir.mkdir(File.join(@work_dir, 'habitat', 'hooks'))
|
|
158
171
|
Habitat::Log.debug("Generated work directory #{@work_dir}")
|
|
159
172
|
|
|
160
173
|
@work_dir
|
|
161
174
|
end
|
|
162
175
|
|
|
176
|
+
def create_habitat_directories(parent_directory)
|
|
177
|
+
[
|
|
178
|
+
File.join(parent_directory, 'habitat'),
|
|
179
|
+
File.join(parent_directory, 'habitat', 'config'),
|
|
180
|
+
File.join(parent_directory, 'habitat', 'hooks'),
|
|
181
|
+
].each do |dir|
|
|
182
|
+
Dir.mkdir(dir) unless Dir.exist?(dir)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
163
186
|
def copy_profile_to_work_dir
|
|
164
187
|
Habitat::Log.info('Copying profile contents to the work directory...')
|
|
165
188
|
profile.files.each do |f|
|
|
166
189
|
src = File.join(profile.root_path, f)
|
|
167
|
-
dst = File.join(work_dir,
|
|
190
|
+
dst = File.join(work_dir, f)
|
|
168
191
|
if File.directory?(f)
|
|
169
192
|
Habitat::Log.debug("Creating directory #{dst}")
|
|
170
193
|
FileUtils.mkdir_p(dst)
|
|
@@ -175,26 +198,26 @@ module Habitat
|
|
|
175
198
|
end
|
|
176
199
|
end
|
|
177
200
|
|
|
178
|
-
def create_plan
|
|
179
|
-
plan_file = File.join(
|
|
201
|
+
def create_plan(directory)
|
|
202
|
+
plan_file = File.join(directory, 'habitat', 'plan.sh')
|
|
180
203
|
Habitat::Log.info("Generating Habitat plan at #{plan_file}...")
|
|
181
204
|
File.write(plan_file, plan_contents)
|
|
182
205
|
end
|
|
183
206
|
|
|
184
|
-
def create_run_hook
|
|
185
|
-
run_hook_file = File.join(
|
|
207
|
+
def create_run_hook(directory)
|
|
208
|
+
run_hook_file = File.join(directory, 'habitat', 'hooks', 'run')
|
|
186
209
|
Habitat::Log.info("Generating a Habitat run hook at #{run_hook_file}...")
|
|
187
210
|
File.write(run_hook_file, run_hook_contents)
|
|
188
211
|
end
|
|
189
212
|
|
|
190
|
-
def create_settings_file
|
|
191
|
-
settings_file = File.join(
|
|
213
|
+
def create_settings_file(directory)
|
|
214
|
+
settings_file = File.join(directory, 'habitat', 'config', 'settings.sh')
|
|
192
215
|
Habitat::Log.info("Generating a settings file at #{settings_file}...")
|
|
193
216
|
File.write(settings_file, "SLEEP_TIME={{cfg.sleep_time}}\n")
|
|
194
217
|
end
|
|
195
218
|
|
|
196
|
-
def create_default_config
|
|
197
|
-
default_toml = File.join(
|
|
219
|
+
def create_default_config(directory)
|
|
220
|
+
default_toml = File.join(directory, 'habitat', 'default.toml')
|
|
198
221
|
Habitat::Log.info("Generating Habitat's default.toml configuration...")
|
|
199
222
|
File.write(default_toml, 'sleep_time = 300')
|
|
200
223
|
end
|
|
@@ -275,7 +298,7 @@ module Habitat
|
|
|
275
298
|
config_file = File.join(ENV['HOME'], '.hab', 'etc', 'cli.toml')
|
|
276
299
|
return {} unless File.exist?(config_file)
|
|
277
300
|
|
|
278
|
-
@cli_config =
|
|
301
|
+
@cli_config = Tomlrb.load_file(config_file)
|
|
279
302
|
end
|
|
280
303
|
|
|
281
304
|
def output_dir
|
|
@@ -308,7 +331,7 @@ EOL
|
|
|
308
331
|
plan += <<-EOL
|
|
309
332
|
|
|
310
333
|
do_build() {
|
|
311
|
-
cp -vr $PLAN_CONTEXT
|
|
334
|
+
cp -vr $PLAN_CONTEXT/../* $HAB_CACHE_SRC_PATH/$pkg_dirname
|
|
312
335
|
}
|
|
313
336
|
|
|
314
337
|
do_install() {
|
|
@@ -321,6 +344,7 @@ do_install() {
|
|
|
321
344
|
profile_contents=(${profile_contents[@]/$item/})
|
|
322
345
|
done
|
|
323
346
|
|
|
347
|
+
mkdir ${pkg_prefix}/dist
|
|
324
348
|
cp -r ${profile_contents[@]} ${pkg_prefix}/dist/
|
|
325
349
|
}
|
|
326
350
|
EOL
|
data/lib/inspec/backend.rb
CHANGED
|
@@ -17,6 +17,14 @@ module Inspec
|
|
|
17
17
|
Inspec::VERSION
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
# Determine whether the connection/transport is a local connection
|
|
21
|
+
# Useful for resources to modify behavior as necessary, such as using
|
|
22
|
+
# the Ruby stdlib for a better experience.
|
|
23
|
+
def local_transport?
|
|
24
|
+
return false unless defined?(Train::Transports::Local)
|
|
25
|
+
backend.is_a?(Train::Transports::Local::Connection)
|
|
26
|
+
end
|
|
27
|
+
|
|
20
28
|
# Ruby internal for printing a nice name for this class
|
|
21
29
|
def to_s
|
|
22
30
|
'Inspec::Backend::Class'
|
data/lib/inspec/cli.rb
CHANGED
|
@@ -186,7 +186,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
|
186
186
|
option :command, aliases: :c,
|
|
187
187
|
desc: 'A single command string to run instead of launching the shell'
|
|
188
188
|
option :format, type: :string, default: nil, hide: true,
|
|
189
|
-
desc: 'Which formatter to use: cli,
|
|
189
|
+
desc: 'Which formatter to use: cli, documentation, html, json, json-min, junit, progress'
|
|
190
190
|
def shell_func
|
|
191
191
|
diagnose
|
|
192
192
|
o = opts.dup
|
|
@@ -35,7 +35,7 @@ module Inspec
|
|
|
35
35
|
# @param profile_context [Inspec::ProfileContext]
|
|
36
36
|
# @param outer_dsl [OuterDSLClass]
|
|
37
37
|
# @return [ProfileContextClass]
|
|
38
|
-
def self.create(profile_context, resources_dsl) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
38
|
+
def self.create(profile_context, resources_dsl) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
39
39
|
rule_class = rule_context(resources_dsl)
|
|
40
40
|
profile_context_owner = profile_context
|
|
41
41
|
profile_id = profile_context.profile_id
|
|
@@ -45,12 +45,14 @@ module Inspec
|
|
|
45
45
|
include Inspec::DSL::RequireOverride
|
|
46
46
|
include resources_dsl
|
|
47
47
|
|
|
48
|
+
attr_accessor :skip_file
|
|
49
|
+
|
|
48
50
|
def initialize(backend, conf, dependencies, require_loader)
|
|
49
51
|
@backend = backend
|
|
50
52
|
@conf = conf
|
|
51
53
|
@dependencies = dependencies
|
|
52
54
|
@require_loader = require_loader
|
|
53
|
-
@
|
|
55
|
+
@skip_file = false
|
|
54
56
|
end
|
|
55
57
|
|
|
56
58
|
define_method :title do |arg|
|
|
@@ -113,7 +115,7 @@ module Inspec
|
|
|
113
115
|
end
|
|
114
116
|
|
|
115
117
|
define_method :register_control do |control, &block|
|
|
116
|
-
if @
|
|
118
|
+
if @skip_file || !profile_context_owner.profile_supports_os?
|
|
117
119
|
::Inspec::Rule.set_skip_rule(control, true)
|
|
118
120
|
end
|
|
119
121
|
|
|
@@ -129,9 +131,17 @@ module Inspec
|
|
|
129
131
|
profile_context_owner.unregister_rule(id)
|
|
130
132
|
end
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
return unless
|
|
134
|
-
@
|
|
134
|
+
define_method :only_if do |&block|
|
|
135
|
+
return unless block
|
|
136
|
+
return if @skip_file == true || block.yield == true
|
|
137
|
+
|
|
138
|
+
# Apply `set_skip_rule` for other rules in the same file
|
|
139
|
+
profile_context_owner.rules.values.each do |r|
|
|
140
|
+
sources_match = r.source_file == block.source_location[0]
|
|
141
|
+
Inspec::Rule.set_skip_rule(r, true) if sources_match
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
@skip_file = true
|
|
135
145
|
end
|
|
136
146
|
|
|
137
147
|
alias_method :rule, :control
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
require 'inspec/cached_fetcher'
|
|
3
3
|
require 'inspec/dependencies/dependency_set'
|
|
4
|
+
require 'semverse'
|
|
4
5
|
|
|
5
6
|
module Inspec
|
|
6
7
|
#
|
|
@@ -10,7 +11,18 @@ module Inspec
|
|
|
10
11
|
class Requirement
|
|
11
12
|
def self.from_metadata(dep, cache, opts)
|
|
12
13
|
raise 'Cannot load empty dependency.' if dep.nil? || dep.empty?
|
|
13
|
-
|
|
14
|
+
|
|
15
|
+
req_path = opts[:cwd]
|
|
16
|
+
|
|
17
|
+
if dep[:path]
|
|
18
|
+
req_path = File.expand_path(dep[:path], req_path)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
new(dep[:name],
|
|
22
|
+
dep[:version],
|
|
23
|
+
cache,
|
|
24
|
+
req_path,
|
|
25
|
+
opts.merge(dep))
|
|
14
26
|
end
|
|
15
27
|
|
|
16
28
|
def self.from_lock_entry(entry, cwd, cache, backend, opts = {})
|
|
@@ -28,10 +40,10 @@ module Inspec
|
|
|
28
40
|
req
|
|
29
41
|
end
|
|
30
42
|
|
|
31
|
-
attr_reader :cwd, :opts, :
|
|
43
|
+
attr_reader :cwd, :opts, :version_constraints
|
|
32
44
|
def initialize(name, version_constraints, cache, cwd, opts)
|
|
33
45
|
@name = name
|
|
34
|
-
@
|
|
46
|
+
@version_constraints = Array(version_constraints)
|
|
35
47
|
@cache = cache
|
|
36
48
|
@backend = opts[:backend]
|
|
37
49
|
@opts = opts
|
|
@@ -51,11 +63,13 @@ module Inspec
|
|
|
51
63
|
end
|
|
52
64
|
|
|
53
65
|
def source_satisfies_spec?
|
|
54
|
-
|
|
55
|
-
end
|
|
66
|
+
return true if version_constraints.empty?
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
|
|
68
|
+
# Semverse::Constraint.satisfy_all returns a list of versions that match all of the
|
|
69
|
+
# supplied constraints. Since we're only matching against a single version, the return
|
|
70
|
+
# of satisfy_all will be non-empty if the profile version we have satisfies the constraints.
|
|
71
|
+
constraints = @version_constraints.map { |x| Semverse::Constraint.new(x) }
|
|
72
|
+
!Semverse::Constraint.satisfy_all(constraints, Semverse::Version.new(profile.version)).empty?
|
|
59
73
|
end
|
|
60
74
|
|
|
61
75
|
def resolved_source
|
|
@@ -66,7 +80,7 @@ module Inspec
|
|
|
66
80
|
h = {
|
|
67
81
|
'name' => name,
|
|
68
82
|
'resolved_source' => resolved_source,
|
|
69
|
-
'version_constraints' =>
|
|
83
|
+
'version_constraints' => version_constraints.to_s,
|
|
70
84
|
}
|
|
71
85
|
|
|
72
86
|
if !dependencies.empty?
|