inspec 0.16.3 → 0.16.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -2
- data/docs/dsl_inspec.rst +43 -0
- data/examples/inheritance/controls/example.rb +1 -1
- data/examples/inheritance/inspec.yml +1 -1
- data/examples/profile/README.md +26 -8
- data/examples/profile/controls/gordon.rb +4 -2
- data/examples/profile/controls/meta.rb +34 -0
- data/examples/profile/inspec.yml +1 -1
- data/examples/profile/libraries/gordon_config.rb +28 -2
- data/lib/bundles/inspec-compliance/cli.rb +3 -1
- data/lib/inspec/backend.rb +5 -0
- data/lib/inspec/cli.rb +2 -0
- data/lib/inspec/profile_context.rb +2 -0
- data/lib/inspec/runner.rb +1 -1
- data/lib/inspec/shell.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/test/functional/helper.rb +36 -0
- data/test/functional/inheritance_test.rb +49 -0
- data/test/functional/inspec_archive_test.rb +80 -0
- data/test/functional/inspec_exec_test.rb +141 -0
- data/test/functional/inspec_json_test.rb +104 -0
- data/test/functional/inspec_test.rb +54 -0
- data/test/unit/profile_context_test.rb +3 -3
- metadata +15 -7
- data/examples/resource/controls/tiny.rb +0 -3
- data/examples/resource/inspec.yml +0 -10
- data/examples/resource/libraries/tiny.rb +0 -3
- data/test/functional/command_test.rb +0 -390
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Dominik Richter
|
3
|
+
# author: Christoph Hartmann
|
4
|
+
|
5
|
+
require 'functional/helper'
|
6
|
+
|
7
|
+
describe 'inspec archive' do
|
8
|
+
include FunctionalHelper
|
9
|
+
|
10
|
+
it 'archive is successful' do
|
11
|
+
out = inspec('archive ' + example_profile + ' --overwrite')
|
12
|
+
out.exit_status.must_equal 0
|
13
|
+
out.stdout.must_match /Generate archive [^ ]*profile.tar.gz/
|
14
|
+
out.stdout.must_include 'Finished archive generation.'
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'archives to output file' do
|
18
|
+
out = inspec('archive ' + example_profile + ' --output ' + dst.path)
|
19
|
+
out.stderr.must_equal ''
|
20
|
+
out.stdout.must_include 'Generate archive '+dst.path
|
21
|
+
out.stdout.must_include 'Finished archive generation.'
|
22
|
+
out.exit_status.must_equal 0
|
23
|
+
File.exist?(dst.path).must_equal true
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'auto-archives when no --output is given' do
|
27
|
+
auto_dst = File.join(repo_path, 'profile.tar.gz')
|
28
|
+
out = inspec('archive ' + example_profile + ' --overwrite')
|
29
|
+
out.stderr.must_equal ''
|
30
|
+
out.stdout.must_include 'Generate archive '+auto_dst
|
31
|
+
out.stdout.must_include 'Finished archive generation.'
|
32
|
+
out.exit_status.must_equal 0
|
33
|
+
File.exist?(auto_dst).must_equal true
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'archive on invalid archive' do
|
37
|
+
out = inspec('archive /proc --output ' + dst.path)
|
38
|
+
# out.stdout.must_equal '' => we have partial stdout output right now
|
39
|
+
out.stderr.must_include "Don't understand inspec profile in \"/proc\""
|
40
|
+
out.exit_status.must_equal 1
|
41
|
+
File.exist?(dst.path).must_equal false
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'archive wont overwrite existing files' do
|
45
|
+
x = rand.to_s
|
46
|
+
File.write(dst.path, x)
|
47
|
+
out = inspec('archive ' + example_profile + ' --output ' + dst.path)
|
48
|
+
out.stderr.must_equal '' # uh...
|
49
|
+
out.stdout.must_include "Archive #{dst.path} exists already. Use --overwrite."
|
50
|
+
out.exit_status.must_equal 1
|
51
|
+
File.read(dst.path).must_equal x
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'archive will overwrite files if necessary' do
|
55
|
+
x = rand.to_s
|
56
|
+
File.write(dst.path, x)
|
57
|
+
out = inspec('archive ' + example_profile + ' --output ' + dst.path + ' --overwrite')
|
58
|
+
out.stderr.must_equal ''
|
59
|
+
out.stdout.must_include 'Generate archive '+dst.path
|
60
|
+
out.exit_status.must_equal 0
|
61
|
+
File.read(dst.path).wont_equal x
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'creates valid tar.gz archives' do
|
65
|
+
out = inspec('archive ' + example_profile + ' --output ' + dst.path + ' --tar')
|
66
|
+
out.stderr.must_equal ''
|
67
|
+
out.stdout.must_include 'Generate archive '+dst.path
|
68
|
+
out.exit_status.must_equal 0
|
69
|
+
t = Zlib::GzipReader.open(dst.path)
|
70
|
+
Gem::Package::TarReader.new(t).entries.map(&:header).map(&:name).must_include 'inspec.yml'
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'creates valid zip archives' do
|
74
|
+
out = inspec('archive ' + example_profile + ' --output ' + dst.path + ' --zip')
|
75
|
+
out.stderr.must_equal ''
|
76
|
+
out.stdout.must_include 'Generate archive '+dst.path
|
77
|
+
out.exit_status.must_equal 0
|
78
|
+
Zip::File.new(dst.path).entries.map(&:name).must_include 'inspec.yml'
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Dominik Richter
|
3
|
+
# author: Christoph Hartmann
|
4
|
+
|
5
|
+
require 'functional/helper'
|
6
|
+
|
7
|
+
describe 'inspec exec' do
|
8
|
+
include FunctionalHelper
|
9
|
+
|
10
|
+
it 'can execute the profile' do
|
11
|
+
out = inspec('exec ' + example_profile)
|
12
|
+
out.stderr.must_equal ''
|
13
|
+
out.exit_status.must_equal 0
|
14
|
+
out.stdout.must_match /^Pending: /
|
15
|
+
out.stdout.must_include '4 examples, 0 failures, 1 pending'
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'executes only specified controls' do
|
19
|
+
out = inspec('exec ' + example_profile + ' --controls tmp-1.0')
|
20
|
+
out.stderr.must_equal ''
|
21
|
+
out.exit_status.must_equal 0
|
22
|
+
out.stdout.must_include '1 example, 0 failures'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can execute the profile with the json formatter' do
|
26
|
+
out = inspec('exec ' + example_profile + ' --format json')
|
27
|
+
out.stderr.must_equal ''
|
28
|
+
out.exit_status.must_equal 0
|
29
|
+
JSON.load(out.stdout).must_be_kind_of Hash
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'execute a profile with json formatting' do
|
33
|
+
let(:json) { JSON.load(inspec('exec ' + example_profile + ' --format json').stdout) }
|
34
|
+
let(:examples) { json['examples'] }
|
35
|
+
let(:ex1) { examples.find{|x| x['id'] == 'tmp-1.0'} }
|
36
|
+
let(:ex2) { examples.find{|x| x['id'] =~ /generated/} }
|
37
|
+
let(:ex3) { examples.find{|x| x['id'] == 'gordon-1.0'} }
|
38
|
+
|
39
|
+
it 'must have 4 examples' do
|
40
|
+
json['examples'].length.must_equal 4
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'id in json' do
|
44
|
+
examples.find { |ex| !ex.key? 'id' }.must_be :nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'impact in json' do
|
48
|
+
ex1['impact'].must_equal 0.7
|
49
|
+
ex2['impact'].must_be :nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'status in json' do
|
53
|
+
ex1['status'].must_equal 'passed'
|
54
|
+
ex3['status'].must_equal 'pending'
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'pending message in json' do
|
58
|
+
ex1['pending_message'].must_be :nil?
|
59
|
+
ex3['pending_message'].must_equal 'Not yet implemented'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe 'execute a profile with fulljson formatting' do
|
64
|
+
let(:json) { JSON.load(inspec('exec ' + example_profile + ' --format fulljson').stdout) }
|
65
|
+
let(:examples) { json['examples'] }
|
66
|
+
let(:metadata) { json['profiles'][0] }
|
67
|
+
let(:ex1) { examples.find{|x| x['id'] == 'tmp-1.0'} }
|
68
|
+
let(:ex2) { examples.find{|x| x['id'] =~ /generated/} }
|
69
|
+
let(:ex3) { examples.find{|x| x['id'] == 'gordon-1.0'} }
|
70
|
+
|
71
|
+
it 'has all the metadata' do
|
72
|
+
metadata.must_equal({
|
73
|
+
"name" => "profile",
|
74
|
+
"title" => "InSpec Example Profile",
|
75
|
+
"maintainer" => "Chef Software, Inc.",
|
76
|
+
"copyright" => "Chef Software, Inc.",
|
77
|
+
"copyright_email" => "support@chef.io",
|
78
|
+
"license" => "Apache 2 license",
|
79
|
+
"summary" => "Demonstrates the use of InSpec Compliance Profile",
|
80
|
+
"version" => "1.0.0",
|
81
|
+
"supports" => [{"os-family" => "unix"}]
|
82
|
+
})
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'must have 4 examples' do
|
86
|
+
json['examples'].length.must_equal 4
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'id in json' do
|
90
|
+
examples.find { |ex| !ex.key? 'id' }.must_be :nil?
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'title in json' do
|
94
|
+
ex3['title'].must_equal 'Verify the version number of Gordon'
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'desc in json' do
|
98
|
+
ex3['desc'].must_equal 'An optional description...'
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'code in json' do
|
102
|
+
ex3['code'].wont_be :nil?
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'code_desc in json' do
|
106
|
+
ex3['code_desc'].wont_be :nil?
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'impact in json' do
|
110
|
+
ex1['impact'].must_equal 0.7
|
111
|
+
ex2['impact'].must_be :nil?
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'status in json' do
|
115
|
+
ex1['status'].must_equal 'passed'
|
116
|
+
ex3['status'].must_equal 'pending'
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'ref in json' do
|
120
|
+
ex1['ref'].must_match %r{examples/profile/controls/example.rb$}
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'ref_line in json' do
|
124
|
+
ex1['ref_line'].must_equal 14
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'run_time in json' do
|
128
|
+
ex1['run_time'].wont_be :nil?
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'start_time in json' do
|
132
|
+
ex1['start_time'].wont_be :nil?
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'pending message in json' do
|
136
|
+
ex1['pending'].must_be :nil?
|
137
|
+
ex3['pending'].must_equal "Can't find file \"/tmp/gordon/config.yaml\""
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Dominik Richter
|
3
|
+
# author: Christoph Hartmann
|
4
|
+
|
5
|
+
require 'functional/helper'
|
6
|
+
|
7
|
+
describe 'inspec json' do
|
8
|
+
include FunctionalHelper
|
9
|
+
|
10
|
+
it 'read the profile json' do
|
11
|
+
out = inspec('json ' + example_profile)
|
12
|
+
out.stderr.must_equal ''
|
13
|
+
out.exit_status.must_equal 0
|
14
|
+
s = out.stdout
|
15
|
+
JSON.load(s).must_be_kind_of Hash
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'json profile data' do
|
19
|
+
let(:json) { JSON.load(inspec('json ' + example_profile).stdout) }
|
20
|
+
|
21
|
+
it 'has a name' do
|
22
|
+
json['name'].must_equal 'profile'
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'has a title' do
|
26
|
+
json['title'].must_equal 'InSpec Example Profile'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'has a summary' do
|
30
|
+
json['summary'].must_equal 'Demonstrates the use of InSpec Compliance Profile'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'has a version' do
|
34
|
+
json['version'].must_equal '1.0.0'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'has a maintainer' do
|
38
|
+
json['maintainer'].must_equal 'Chef Software, Inc.'
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'has a copyright' do
|
42
|
+
json['copyright'].must_equal 'Chef Software, Inc.'
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'has rules' do
|
46
|
+
json['rules'].length.must_equal 3 # TODO: flatten out or search deeper!
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'a rule' do
|
50
|
+
let(:rule) { json['rules']['controls/example.rb']['rules']['tmp-1.0'] }
|
51
|
+
|
52
|
+
it 'has a title' do
|
53
|
+
rule['title'].must_equal 'Create /tmp directory'
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'has a description' do
|
57
|
+
rule['desc'].must_equal 'An optional description...'
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'has an impact' do
|
61
|
+
rule['impact'].must_equal 0.7
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'has a ref' do
|
65
|
+
rule['refs'].must_equal([{'ref' => 'Document A-12', 'url' => 'http://...'}])
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'has a source location' do
|
69
|
+
loc = File.join(example_profile, '/controls/example.rb')
|
70
|
+
rule['source_location'].must_equal [loc, 8]
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'has a the source code' do
|
74
|
+
rule['code'].must_match /\Acontrol \"tmp-1.0\" do.*end\n\Z/m
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'filter with --controls' do
|
80
|
+
let(:out) { inspec('json ' + example_profile + ' --controls tmp-1.0') }
|
81
|
+
|
82
|
+
it 'still succeeds' do
|
83
|
+
out.stderr.must_equal ''
|
84
|
+
out.exit_status.must_equal 0
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'only has one control included' do
|
88
|
+
json = JSON.load(out.stdout)
|
89
|
+
grps = json['rules']
|
90
|
+
grps.keys.must_equal ['controls/example.rb']
|
91
|
+
rules = grps.values[0]['rules']
|
92
|
+
rules.keys.must_equal ['tmp-1.0']
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'writes json to file' do
|
97
|
+
out = inspec('json ' + example_profile + ' --output ' + dst.path)
|
98
|
+
out.stderr.must_equal ''
|
99
|
+
out.exit_status.must_equal 0
|
100
|
+
hm = JSON.load(File.read(dst.path))
|
101
|
+
hm['name'].must_equal 'profile'
|
102
|
+
hm['rules'].length.must_equal 3 # TODO: flatten out or search deeper!
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Dominik Richter
|
3
|
+
# author: Christoph Hartmann
|
4
|
+
|
5
|
+
require 'functional/helper'
|
6
|
+
|
7
|
+
describe 'command tests' do
|
8
|
+
include FunctionalHelper
|
9
|
+
|
10
|
+
describe 'detect' do
|
11
|
+
it 'runs well on all nodes' do
|
12
|
+
out = inspec('detect')
|
13
|
+
out.stderr.must_equal ''
|
14
|
+
out.exit_status.must_equal 0
|
15
|
+
j = JSON.load(out.stdout)
|
16
|
+
j.keys.must_include 'name'
|
17
|
+
j.keys.must_include 'family'
|
18
|
+
j.keys.must_include 'arch'
|
19
|
+
j.keys.must_include 'release'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'version' do
|
24
|
+
it 'provides the version number on stdout' do
|
25
|
+
out = inspec('version')
|
26
|
+
out.stderr.must_equal ''
|
27
|
+
out.exit_status.must_equal 0
|
28
|
+
out.stdout.must_equal Inspec::VERSION+"\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'shell' do
|
33
|
+
it 'provides a help command' do
|
34
|
+
out = CMD.run_command("echo \"help\nexit\" | #{exec_inspec} shell")
|
35
|
+
out.exit_status.must_equal 0
|
36
|
+
out.stdout.must_include 'Available commands:'
|
37
|
+
out.stdout.must_include 'You are currently running on:'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'exposes all resources' do
|
41
|
+
out = CMD.run_command("echo \"os\nexit\" | #{exec_inspec} shell")
|
42
|
+
out.exit_status.must_equal 0
|
43
|
+
out.stdout.must_match /^=> .*Operating.* .*System.* .*Detection.*$/
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'check' do
|
48
|
+
it 'verifies that a profile is ok' do
|
49
|
+
out = inspec('check ' + example_profile)
|
50
|
+
out.stdout.must_match /Valid.*true/
|
51
|
+
out.exit_status.must_equal 0
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -91,7 +91,7 @@ describe Inspec::ProfileContext do
|
|
91
91
|
it 'supports empty describe calls' do
|
92
92
|
load('describe').must_output ''
|
93
93
|
profile.rules.keys.length.must_equal 1
|
94
|
-
profile.rules.keys[0].must_match /^\(generated from
|
94
|
+
profile.rules.keys[0].must_match /^\(generated from \(eval\):1 [0-9a-f]+\)$/
|
95
95
|
profile.rules.values[0].must_be_kind_of Inspec::Rule
|
96
96
|
end
|
97
97
|
|
@@ -99,7 +99,7 @@ describe Inspec::ProfileContext do
|
|
99
99
|
load('describe true do; it { should_eq true }; end')
|
100
100
|
.must_output ''
|
101
101
|
profile.rules.keys.length.must_equal 1
|
102
|
-
profile.rules.keys[0].must_match /^\(generated from
|
102
|
+
profile.rules.keys[0].must_match /^\(generated from \(eval\):1 [0-9a-f]+\)$/
|
103
103
|
profile.rules.values[0].must_be_kind_of Inspec::Rule
|
104
104
|
end
|
105
105
|
|
@@ -108,7 +108,7 @@ describe Inspec::ProfileContext do
|
|
108
108
|
.must_output ''
|
109
109
|
profile.rules.keys.length.must_equal 3
|
110
110
|
[0, 1, 2].each do |i|
|
111
|
-
profile.rules.keys[i].must_match /^\(generated from
|
111
|
+
profile.rules.keys[i].must_match /^\(generated from \(eval\):2 [0-9a-f]+\)$/
|
112
112
|
profile.rules.values[i].must_be_kind_of Inspec::Rule
|
113
113
|
end
|
114
114
|
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: 0.16.
|
4
|
+
version: 0.16.4
|
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-03-
|
11
|
+
date: 2016-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: r-train
|
@@ -208,11 +208,9 @@ files:
|
|
208
208
|
- examples/profile/README.md
|
209
209
|
- examples/profile/controls/example.rb
|
210
210
|
- examples/profile/controls/gordon.rb
|
211
|
+
- examples/profile/controls/meta.rb
|
211
212
|
- examples/profile/inspec.yml
|
212
213
|
- examples/profile/libraries/gordon_config.rb
|
213
|
-
- examples/resource/controls/tiny.rb
|
214
|
-
- examples/resource/inspec.yml
|
215
|
-
- examples/resource/libraries/tiny.rb
|
216
214
|
- inspec.gemspec
|
217
215
|
- lib/bundles/README.md
|
218
216
|
- lib/bundles/inspec-compliance.rb
|
@@ -360,7 +358,12 @@ files:
|
|
360
358
|
- test/cookbooks/os_prepare/templates/default/sv-default-svlog-run.erb
|
361
359
|
- test/docker_run.rb
|
362
360
|
- test/docker_test.rb
|
363
|
-
- test/functional/
|
361
|
+
- test/functional/helper.rb
|
362
|
+
- test/functional/inheritance_test.rb
|
363
|
+
- test/functional/inspec_archive_test.rb
|
364
|
+
- test/functional/inspec_exec_test.rb
|
365
|
+
- test/functional/inspec_json_test.rb
|
366
|
+
- test/functional/inspec_test.rb
|
364
367
|
- test/helper.rb
|
365
368
|
- test/integration/default/_debug_spec.rb
|
366
369
|
- test/integration/default/apache_conf_spec.rb
|
@@ -617,7 +620,12 @@ test_files:
|
|
617
620
|
- test/cookbooks/os_prepare/templates/default/sv-default-svlog-run.erb
|
618
621
|
- test/docker_run.rb
|
619
622
|
- test/docker_test.rb
|
620
|
-
- test/functional/
|
623
|
+
- test/functional/helper.rb
|
624
|
+
- test/functional/inheritance_test.rb
|
625
|
+
- test/functional/inspec_archive_test.rb
|
626
|
+
- test/functional/inspec_exec_test.rb
|
627
|
+
- test/functional/inspec_json_test.rb
|
628
|
+
- test/functional/inspec_test.rb
|
621
629
|
- test/helper.rb
|
622
630
|
- test/integration/default/_debug_spec.rb
|
623
631
|
- test/integration/default/apache_conf_spec.rb
|