inspec 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f48211d9143cfdb1bdf736389d72e2048bc26b3
4
- data.tar.gz: 697546cf50666e244c114390238fbe1f0de18a91
3
+ metadata.gz: bc80c218f1945dc66bf6d693530077e66212968a
4
+ data.tar.gz: b54754846a031670396d5069a43f7434113e7fde
5
5
  SHA512:
6
- metadata.gz: c8cf98323a40791eecd63b25cc6af48f49b2d139069f4f0e02d92e8daa03862048a01eec1c10f5d1b0008083eb43d6478b1721da7e3af8e30c87740cf61a54af
7
- data.tar.gz: 5942695dbda9915900163f637ad847e0e75a7072ea0f5e415b74776a1c6158e0c4ccd0d80cfafb4c42c018b1f2c5a1dda76b88861c7a11ee92e41d89a4c05366
6
+ metadata.gz: 60442e056460a6c14ed1c7587b499f75f40f942f4064f43275ac11943ddb881968edcae4d5c49f49a37b551330d72aa89289438418e6cc24c5d992024365f102
7
+ data.tar.gz: fa8866e5fc94fc087908b6bd313d50295077b436757e70b49e1c4e7c1caed27c344908c548183f0c73096bb1c574b91bebcbd9d67d11b81cf235060ba7a29b66
data/CHANGELOG.md CHANGED
@@ -1,7 +1,52 @@
1
1
  # Change Log
2
2
 
3
- ## [1.2.1](https://github.com/chef/inspec/tree/1.2.1) (2016-10-15)
4
- [Full Changelog](https://github.com/chef/inspec/compare/v1.2.0...1.2.1)
3
+ ## [1.3.0](https://github.com/chef/inspec/tree/1.3.0) (2016-10-28)
4
+ [Full Changelog](https://github.com/chef/inspec/compare/v1.2.1...1.3.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - extend the attributes object with helper methods [\#1220](https://github.com/chef/inspec/pull/1220) ([chris-rock](https://github.com/chris-rock))
9
+
10
+ **Fixed bugs:**
11
+
12
+ - inetd\_conf resource error [\#1253](https://github.com/chef/inspec/issues/1253)
13
+ - Process user should eq \["longusername"\]: usernames get truncated with a '+' at the end [\#995](https://github.com/chef/inspec/issues/995)
14
+ - Remove wildcard from windows package detection [\#1259](https://github.com/chef/inspec/pull/1259) ([chris-rock](https://github.com/chris-rock))
15
+ - Fix nil timeout and retries [\#1256](https://github.com/chef/inspec/pull/1256) ([alexpop](https://github.com/alexpop))
16
+ - Supermarket tools get and filter by tool\_type [\#1254](https://github.com/chef/inspec/pull/1254) ([alexpop](https://github.com/alexpop))
17
+ - Fix processes resource user and command truncation [\#1225](https://github.com/chef/inspec/pull/1225) ([alexpop](https://github.com/alexpop))
18
+
19
+ **Closed issues:**
20
+
21
+ - inetd and xinetd resources inconsistencies [\#1252](https://github.com/chef/inspec/issues/1252)
22
+ - TestKitchen - Duplicate testing when verifier specified in suite definition [\#1240](https://github.com/chef/inspec/issues/1240)
23
+ - Document new DCO process in contributing.md [\#1223](https://github.com/chef/inspec/issues/1223)
24
+ - Move InSpec Community to https://community-slack.chef.io/ [\#1222](https://github.com/chef/inspec/issues/1222)
25
+ - Export Docker package for InSpec from Habitat [\#1212](https://github.com/chef/inspec/issues/1212)
26
+ - Test verify action on Windows 2012 fails - \[no implicit conversion of nil into Array\] on default-windows-2012r2 [\#1193](https://github.com/chef/inspec/issues/1193)
27
+ - Add InSpec habitat plan [\#843](https://github.com/chef/inspec/issues/843)
28
+
29
+ **Merged pull requests:**
30
+
31
+ - Use Slack Badge instead of Gitter badge [\#1262](https://github.com/chef/inspec/pull/1262) ([chris-rock](https://github.com/chris-rock))
32
+ - remove accidentally added file [\#1260](https://github.com/chef/inspec/pull/1260) ([chris-rock](https://github.com/chris-rock))
33
+ - overwrite exec for inetd because respec its is executing `exec` [\#1257](https://github.com/chef/inspec/pull/1257) ([chris-rock](https://github.com/chris-rock))
34
+ - Use include instead of match in the error message [\#1248](https://github.com/chef/inspec/pull/1248) ([artem-sidorenko](https://github.com/artem-sidorenko))
35
+ - Code-block directive is not needed here [\#1247](https://github.com/chef/inspec/pull/1247) ([artem-sidorenko](https://github.com/artem-sidorenko))
36
+ - Set the global message to display again [\#1246](https://github.com/chef/inspec/pull/1246) ([ryankeairns](https://github.com/ryankeairns))
37
+ - Ignore RVM files [\#1245](https://github.com/chef/inspec/pull/1245) ([artem-sidorenko](https://github.com/artem-sidorenko))
38
+ - Change global message regarding 10/25 webinar [\#1244](https://github.com/chef/inspec/pull/1244) ([ryankeairns](https://github.com/ryankeairns))
39
+ - Fix issue with registry\_key example [\#1243](https://github.com/chef/inspec/pull/1243) ([seththoenen](https://github.com/seththoenen))
40
+ - Accessing nested mappings in a yam file [\#1242](https://github.com/chef/inspec/pull/1242) ([chriswessells](https://github.com/chriswessells))
41
+ - Fix broken link in README.md [\#1233](https://github.com/chef/inspec/pull/1233) ([swalberg](https://github.com/swalberg))
42
+ - DOCS: fix commit amend dash [\#1232](https://github.com/chef/inspec/pull/1232) ([alexpop](https://github.com/alexpop))
43
+ - Headers and list elements that include more than one `\_` character we… [\#1231](https://github.com/chef/inspec/pull/1231) ([nathenharvey](https://github.com/nathenharvey))
44
+ - Implements profile signing and verification \[Experimental\] [\#1228](https://github.com/chef/inspec/pull/1228) ([metadave](https://github.com/metadave))
45
+ - Document new DCO process [\#1224](https://github.com/chef/inspec/pull/1224) ([chris-rock](https://github.com/chris-rock))
46
+ - adding by\_user permissions support for windows [\#1215](https://github.com/chef/inspec/pull/1215) ([jeremymv2](https://github.com/jeremymv2))
47
+
48
+ ## [v1.2.1](https://github.com/chef/inspec/tree/v1.2.1) (2016-10-15)
49
+ [Full Changelog](https://github.com/chef/inspec/compare/v1.2.0...v1.2.1)
5
50
 
6
51
  **Implemented enhancements:**
7
52
 
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # InSpec: Inspect Your Infrastructure
2
2
 
3
- [![Join the chat at https://gitter.im/chef/inspec](https://badges.gitter.im/chef/inspec.svg)](https://gitter.im/chef/inspec?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
3
+ [![Slack](https://community-slack.chef.io/badge.svg)](https://community-slack.chef.io/)
4
4
  [![Build Status Master](https://travis-ci.org/chef/inspec.svg?branch=master)](https://travis-ci.org/chef/inspec)
5
5
  [![Build Status Master](https://ci.appveyor.com/api/projects/status/github/chef/inspec?branch=master&svg=true&passingText=master%20-%20Ok&pendingText=master%20-%20Pending&failingText=master%20-%20Failing)](https://ci.appveyor.com/project/Chef/inspec/branch/master)
6
6
 
@@ -45,6 +45,10 @@ inspec exec test.rb -t docker://container_id
45
45
 
46
46
  InSpec requires Ruby ( >1.9 ).
47
47
 
48
+ ### Install as package
49
+
50
+ The InSpec package is available for MacOS, RedHat, Ubuntu and Windows. Download the latest package at [InSpec Downloads](https://downloads.chef.io/inspec).
51
+
48
52
  ### Install it via rubygems.org
49
53
 
50
54
  When installing from source, gem dependencies may require ruby build tools to be installed.
@@ -114,7 +118,7 @@ On Windows, you need to install [Ruby](http://rubyinstaller.org/downloads/) with
114
118
 
115
119
  Currently, this method of installation only supports Linux. See the [Habitat site](https://www.habitat.sh/) for more information.
116
120
 
117
- Download the `hab` binary from the [Habitat](https://www.habitat.sh/docs/get-habitat/) site.
121
+ Download the `hab` binary from the [Habitat](https://www.habitat.sh/docs/get-habitat/) site.
118
122
 
119
123
  ```bash
120
124
  hab pkg install chef/inspec
@@ -169,11 +173,12 @@ describe sshd_config do
169
173
  end
170
174
  ```
171
175
 
172
- * Test your `kitchen.yml` file to verify that only Vagrant is configured as the driver.
176
+ * Test your `kitchen.yml` file to verify that only Vagrant is configured as the driver. The %w() formatting will
177
+ pass rubocop lintng and allow you to access nested mappings.
173
178
 
174
179
  ```ruby
175
180
  describe yaml('.kitchen.yml') do
176
- its('driver.name') { should eq('vagrant') }
181
+ its(%w(driver name)) { should eq('vagrant') }
177
182
  end
178
183
  ```
179
184
 
@@ -284,28 +289,13 @@ Windows | 2012+
284
289
 
285
290
  Documentation
286
291
 
292
+ * http://inspec.io/docs/
293
+ * http://inspec.io/docs/reference/resources/
287
294
  * https://github.com/chef/inspec/tree/master/docs
288
295
 
289
- Blogs:
290
-
291
- * [The Road to InSpec](https://www.chef.io/blog/2015/11/04/the-road-to-inspec/)
292
- * [Introduction to InSpec](http://tfitch.com/automation-tools-bootcamp/inspec.html)
293
- * [InSpec Tutorial: Day 1 - Hello World](http://www.anniehedgie.com/inspec-basics-1)
294
- * [InSpec Tutorial: Day 2 - Command Resource Blog Logo](http://www.anniehedgie.com/inspec-basics-2)
295
- * [InSpec Tutorial: Day 3 - File Resource](http://www.anniehedgie.com/inspec-basics-3)
296
- * [InSpec Tutorial: Day 4 - Custom Matchers](http://www.anniehedgie.com/inspec-basics-4)
297
- * [InSpec Tutorial: Day 5 - Creating a Profile](http://www.anniehedgie.com/inspec-basics-5)
298
- * [InSpec Tutorial: Day 6 - Ways to Run It and Places to Store It](http://www.anniehedgie.com/inspec-basics-6)
299
- * [InSpec Tutorial: Day 7 - How to Inherit a Profile from Chef Compliance Server](http://www.anniehedgie.com/inspec-basics-7)
300
- * [Windows infrastructure testing using InSpec – Part I](http://datatomix.com/?p=236)
301
- * [Windows infrastructure testing using InSpec and Profiles – Part II](http://datatomix.com/?p=238)
302
- * [Testing Ansible with Inspec](http://scienceofficersblog.blogspot.de/2016/02/testing-ansible-with-inspec.html)
303
- * [Operating Chef/InSpec in an air gapped environment](https://github.com/jeremymv2/chef-intranet-scaffolding/blob/master/README.md)
304
-
305
- Podcasts:
306
-
307
- * [InSpec Foodfight](http://foodfightshow.org/2016/02/inspec.html)
308
- * [Test Driven Infrastructure With Arthur Maltson And Michael Goetz](https://www.arresteddevops.com/tdi/)
296
+ Tutorials/Blogs/Podcasts:
297
+
298
+ * http://inspec.io/tutorials/
309
299
 
310
300
  ## Share your Profiles
311
301
 
@@ -29,13 +29,13 @@ This InSpec audit resource has the following matchers:
29
29
 
30
30
  <%= partial "/shared/matcher_be" %>
31
31
 
32
- ### be_block_device
32
+ ### be\_block\_device
33
33
 
34
34
  The `be_block_device` matcher tests if the file exists as a block device, such as `/dev/disk0` or `/dev/disk0s9`:
35
35
 
36
36
  it { should be_block_device }
37
37
 
38
- ### be_character_device
38
+ ### be\_character\_device
39
39
 
40
40
  The `be_character_device` matcher tests if the file exists as a character device (that corresponds to a block device), such as `/dev/rdisk0` or `/dev/rdisk0s9`:
41
41
 
@@ -71,7 +71,7 @@ The `be_file` matcher tests if the file exists as a file. This can be useful wit
71
71
 
72
72
  it { should be_file }
73
73
 
74
- ### be_grouped_into
74
+ ### be\_grouped\_into
75
75
 
76
76
  The `be_grouped_into` matcher tests if the file exists as part of the named group:
77
77
 
@@ -83,7 +83,7 @@ The `be_immutable` matcher tests if the file is immutable, i.e. "cannot be chang
83
83
 
84
84
  it { should be_immutable }
85
85
 
86
- ### be_linked_to
86
+ ### be\_linked\_to
87
87
 
88
88
  The `be_linked_to` matcher tests if the file is linked to the named target:
89
89
 
@@ -95,7 +95,7 @@ The `be_mounted` matcher tests if the file is accessible from the file system:
95
95
 
96
96
  it { should be_mounted }
97
97
 
98
- ### be_owned_by
98
+ ### be\_owned\_by
99
99
 
100
100
  The `be_owned_by` matcher tests if the file is owned by the named user, such as `root`:
101
101
 
@@ -64,7 +64,7 @@ The `exist` matcher tests if the site exists:
64
64
 
65
65
  it { should exist }
66
66
 
67
- ### have_app_pool
67
+ ### have\_app\_pool
68
68
 
69
69
  The `have_app_pool` matcher tests if the named application pool exists for the site:
70
70
 
@@ -2,7 +2,7 @@
2
2
  title: About the parse_config_file Resource
3
3
  ---
4
4
 
5
- # parse_config_file
5
+ # parse\_config\_file
6
6
 
7
7
  Use the `parse_config_file` InSpec audit resource to test arbitrary configuration files. It works in the same way as `parse_config`. Instead of using a command output, this resource works with files.
8
8
 
@@ -73,9 +73,8 @@ For example, to get all child items for a registry key:
73
73
  The following example shows how find a property that may exist against multiple registry keys, and then test that property for every registry key in which that property is located:
74
74
 
75
75
  describe registry_key({
76
- hive: HKEY_USERS
77
- }).children(/^S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]{3,}\\Software\\Policies\\Microsoft\\Windows\\Installer/).each
78
- { |key|
76
+ hive: 'HKEY_USERS'
77
+ }).children(/^S-1-5-21-[0-9]+-[0-9]+-[0-9]+-[0-9]{3,}\\Software\\Policies\\Microsoft\\Windows\\Installer/).each { |key|
79
78
  describe registry_key(key) do
80
79
  its('AlwaysInstallElevated') { should eq 'value' }
81
80
  end
@@ -101,7 +100,7 @@ The `have_property` matcher tests if a property exists for a registry key:
101
100
 
102
101
  it { should have_property 'value' }
103
102
 
104
- ### have_property_value
103
+ ### have\_property\_value
105
104
 
106
105
  The `have_property_value` matcher tests if a property value exists for a registry key:
107
106
 
@@ -28,8 +28,6 @@ vs:
28
28
 
29
29
  Ignoring case sensitivity:
30
30
 
31
- .. code-block:: ruby
32
-
33
31
  describe some_resource do
34
32
  its('setting') { should cmp 'raw' }
35
33
  its('setting') { should cmp 'RAW' }
@@ -1,6 +1,6 @@
1
1
  # Example InSpec Profile
2
2
 
3
- This example shows the implementation of an InSpec [profile](../../docs/profiles.rst).
3
+ This example shows the implementation of an InSpec [profile](../../docs/profiles.md).
4
4
 
5
5
  ## Verify a profile
6
6
 
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+ # author: Dave Parfitt
3
+
4
+ libdir = File.dirname(__FILE__)
5
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
6
+
7
+ require 'inspec-artifact/cli'
@@ -0,0 +1 @@
1
+ # TODO
@@ -0,0 +1,284 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ require 'base64'
4
+ require 'openssl'
5
+ require 'pathname'
6
+ require 'set'
7
+ require 'tempfile'
8
+ require 'yaml'
9
+
10
+ # Notes:
11
+ #
12
+ # Generate keys
13
+ # The initial implementation uses 2048 bit RSA key pairs (public + private).
14
+ # Public keys must be available for a customer to install and verify an artifact.
15
+ # Private keys should be stored in a secure location and NOT be distributed.
16
+ # (They're only for creating artifacts).
17
+ #
18
+ #
19
+ # .IAF file format
20
+ # .iaf = "Inspec Artifact File", easy to rename if you'd like something more appropriate.
21
+ # The iaf file wraps a binary artifact with some metadata. The first implementation
22
+ # looks like this:
23
+ #
24
+ # INSPEC-PROFILE-1
25
+ # name_of_signing_key
26
+ # algorithm
27
+ # signature
28
+ # <empty line>
29
+ # binary-blob
30
+ # <eof>
31
+ #
32
+ # Let's look at each line:
33
+ # INSPEC-PROFILE-1:
34
+ # This is the artifact version descriptor. It should't change unless the
35
+ # format of the archive changes.
36
+ #
37
+ # name_of_signing_key
38
+ # The name of the public key that can be used to verify an artifact
39
+ #
40
+ # algorithm
41
+ # The digest used to sign, I picked SHA512 to start with.
42
+ # If we support multiple digests, we'll need to have the verify() method
43
+ # support each digest.
44
+ #
45
+ # signature
46
+ # The result of passing the binary artifact through the digest algorithm above.
47
+ # Result is base64 encoded.
48
+ #
49
+ # <empty line>
50
+ # We use an empty line to separate artifact header from artifact body (binary blob).
51
+ # The artifact body can be anything you like.
52
+ #
53
+ # binary-blob
54
+ # A binary blob, most likely a .tar.gz or tar.xz file. We'll need to pick one and
55
+ # stick with it as part of the "INSPEC-PROFILE-1" artifact version. If we change block
56
+ # format, the artifact version descriptor must be incremented, and the sign()
57
+ # and verify() methods must be updated to support a newer version.
58
+ #
59
+ #
60
+ # Key revocation
61
+ # This implementation doesn't support key revocation. However, a customer
62
+ # can remove the public cert file before installation, and artifacts will then
63
+ # fail verification.
64
+ #
65
+ # Key locations
66
+ # This implementation uses the current working directory to find public and
67
+ # private keys. We should establish a common key directory (similar to /hab/cache/keys
68
+ # or ~/.hab/cache/keys in Habitat).
69
+ #
70
+ # Extracting artifacts outside of Inspec
71
+ # As in Habitat, the artifact format for Inspec allows the use of common
72
+ # Unix tools to read the header and body of an artifact.
73
+ # To extract the header from a .iaf:
74
+ # sed '/^$/q' foo.iaf
75
+ # To extract the raw content from a .iaf:
76
+ # sed '1,/^$/d' foo.iaf
77
+
78
+ module Artifact
79
+ KEY_BITS=2048
80
+ KEY_ALG=OpenSSL::PKey::RSA
81
+
82
+ INSPEC_PROFILE_VERSION_1='INSPEC-PROFILE-1'.freeze
83
+ INSPEC_REPORT_VERSION_1='INSPEC-REPORT-1'.freeze
84
+
85
+ ARTIFACT_DIGEST=OpenSSL::Digest::SHA512
86
+ ARTIFACT_DIGEST_NAME='SHA512'.freeze
87
+
88
+ VALID_PROFILE_VERSIONS=Set.new [INSPEC_PROFILE_VERSION_1]
89
+ VALID_PROFILE_DIGESTS=Set.new [ARTIFACT_DIGEST_NAME]
90
+
91
+ SIGNED_PROFILE_SUFFIX='iaf'.freeze
92
+ SIGNED_REPORT_SUFFIX='iar'.freeze
93
+
94
+ # rubocop:disable Metrics/ClassLength
95
+ class CLI < Inspec::BaseCLI
96
+ namespace 'artifact'
97
+
98
+ # TODO: find another solution, once https://github.com/erikhuda/thor/issues/261 is fixed
99
+ def self.banner(command, _namespace = nil, _subcommand = false)
100
+ "#{basename} #{subcommand_prefix} #{command.usage}"
101
+ end
102
+
103
+ def self.subcommand_prefix
104
+ namespace
105
+ end
106
+
107
+ desc 'generate', 'Generate a RSA key pair for signing and verification'
108
+ option :keyname, type: :string, required: true,
109
+ desc: 'Desriptive name of key'
110
+ option :keydir, type: :string, default: './',
111
+ desc: 'Directory to search for keys'
112
+ def generate_keys
113
+ puts 'Generating keys'
114
+ keygen
115
+ end
116
+
117
+ desc 'sign-profile', 'Create a signed .iaf artifact'
118
+ option :profile, type: :string, required: true,
119
+ desc: 'Path to profile directory'
120
+ option :keyname, type: :string, required: true,
121
+ desc: 'Desriptive name of key'
122
+ def sign_profile
123
+ profile_sign
124
+ end
125
+
126
+ desc 'verify-profile', 'Verify a signed .iaf artifact'
127
+ option :infile, type: :string, required: true,
128
+ desc: '.iaf file to verify'
129
+ def verify_profile
130
+ profile_verify
131
+ end
132
+
133
+ desc 'install-profile', 'Verify and install a signed .iaf artifact'
134
+ option :infile, type: :string, required: true,
135
+ desc: '.iaf file to install'
136
+ option :destdir, type: :string, required: true,
137
+ desc: 'Installation directory'
138
+ def install_profile
139
+ profile_install
140
+ end
141
+
142
+ private
143
+
144
+ def keygen
145
+ key = KEY_ALG.new KEY_BITS
146
+ puts 'Generating private key'
147
+ open "#{options['keyname']}.pem.key", 'w' do |io| io.write key.to_pem end
148
+ puts 'Generating public key'
149
+ open "#{options['keyname']}.pem.pub", 'w' do |io| io.write key.public_key.to_pem end
150
+ end
151
+
152
+ def read_profile_metadata(path_to_profile)
153
+ begin
154
+ p = Pathname.new(path_to_profile)
155
+ p = p.join('inspec.yml')
156
+ if not p.exist?
157
+ fail "#{path_to_profile} doesn't appear to be a valid Inspec profile"
158
+ end
159
+ yaml = YAML.load_file(p.to_s)
160
+ yaml = yaml.to_hash
161
+
162
+ if not yaml.key? 'name'
163
+ fail 'Profile is invalid, name is not defined'
164
+ end
165
+
166
+ if not yaml.key? 'version'
167
+ fail 'Profile is invalid, version is not defined'
168
+ end
169
+ rescue => e
170
+ # rewrap it and pass it up to the CLI
171
+ raise "Error reading Inspec profile metadata: #{e}"
172
+ end
173
+
174
+ yaml
175
+ end
176
+
177
+ def profile_compress(path_to_profile, profile_md, workdir)
178
+ profile_name = profile_md['name']
179
+ profile_version = profile_md['version']
180
+ outfile_name = "#{workdir}/#{profile_name}-#{profile_version}.tar.gz"
181
+ `tar czf #{outfile_name} -C #{path_to_profile} .`
182
+ outfile_name
183
+ end
184
+
185
+ def profile_sign
186
+ Dir.mktmpdir do |workdir|
187
+ puts "Signing #{options['profile']} with key #{options['keyname']}"
188
+ path_to_profile = options['profile']
189
+ profile_md = read_profile_metadata(path_to_profile)
190
+ artifact_filename = "#{profile_md['name']}-#{profile_md['version']}.#{SIGNED_PROFILE_SUFFIX}"
191
+ tarfile = profile_compress(path_to_profile, profile_md, workdir)
192
+ content = IO.binread(tarfile)
193
+ signing_key = KEY_ALG.new File.read "#{options['keyname']}.pem.key"
194
+ sha = ARTIFACT_DIGEST.new
195
+ signature = signing_key.sign sha, content
196
+ # convert the signature to Base64
197
+ signature_base64 = Base64.encode64(signature)
198
+ tar_content = IO.binread(tarfile)
199
+ File.open(artifact_filename, 'wb') do |f|
200
+ f.puts(INSPEC_PROFILE_VERSION_1)
201
+ f.puts(options['keyname'])
202
+ f.puts(ARTIFACT_DIGEST_NAME)
203
+ f.puts(signature_base64)
204
+ f.puts('') # newline separates artifact header with body
205
+ f.write(tar_content)
206
+ end
207
+ puts "Successfully generated #{artifact_filename}"
208
+ end
209
+ end
210
+
211
+ def valid_header?(file_alg, file_version, file_keyname)
212
+ public_keyfile = "#{file_keyname}.pem.pub"
213
+ puts "Looking for #{public_keyfile} to verify artifact"
214
+ if not File.exist? public_keyfile
215
+ fail "Can't find #{public_keyfile}"
216
+ end
217
+
218
+ if not VALID_PROFILE_DIGESTS.member? file_alg
219
+ fail 'Invalid artifact digest algorithm detected'
220
+ end
221
+
222
+ if not VALID_PROFILE_VERSIONS.member? file_version
223
+ fail 'Invalid artifact version detected'
224
+ end
225
+ end
226
+
227
+ def verify(file_to_verifiy, &content_block)
228
+ f = File.open(file_to_verifiy, 'r')
229
+ file_version = f.readline.strip!
230
+ file_keyname = f.readline.strip!
231
+ file_alg = f.readline.strip!
232
+
233
+ file_sig = ''
234
+ # the signature is multi-line
235
+ while (line = f.readline) != "\n"
236
+ file_sig += line
237
+ end
238
+ file_sig.strip!
239
+ f.close
240
+
241
+ valid_header?(file_alg, file_version, file_keyname)
242
+
243
+ public_keyfile = "#{file_keyname}.pem.pub"
244
+ verification_key = KEY_ALG.new File.read public_keyfile
245
+
246
+ f = File.open(file_to_verifiy, 'r')
247
+ while f.readline != "\n" do end
248
+ content = f.read
249
+
250
+ signature = Base64.decode64(file_sig)
251
+ digest = ARTIFACT_DIGEST.new
252
+ if verification_key.verify digest, signature, content
253
+ content_block.yield(content)
254
+ else
255
+ puts 'Artifact is invalid'
256
+ end
257
+ end
258
+
259
+ def profile_verify
260
+ file_to_verifiy = options['infile']
261
+ puts "Verifying #{file_to_verifiy}"
262
+ verify(file_to_verifiy) do ||
263
+ puts 'Artifact is valid'
264
+ end
265
+ end
266
+
267
+ def profile_install
268
+ puts 'Installing profile'
269
+ file_to_verifiy = options['infile']
270
+ dest_dir = options['destdir']
271
+ verify(file_to_verifiy) do |content|
272
+ Dir.mktmpdir do |workdir|
273
+ tmpfile = Pathname.new(workdir).join('artifact_to_install.tar.gz')
274
+ File.write(tmpfile, content)
275
+ puts "Installing to #{dest_dir}"
276
+ `tar xzf #{tmpfile} -C #{dest_dir}`
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ # register the subcommand to Inspec CLI registry
283
+ Inspec::Plugins::CLI.add_subcommand(Artifact::CLI, 'artifact', 'artifact SUBCOMMAND ...', 'Sign, verify and install artifacts', {})
284
+ end
@@ -11,11 +11,11 @@ module Supermarket
11
11
 
12
12
  # displays a list of profiles
13
13
  def self.profiles(supermarket_url = SUPERMARKET_URL)
14
- url = "#{supermarket_url}/api/v1/tools-search"
15
- _success, data = get(url, { q: 'compliance_profile' })
14
+ url = "#{supermarket_url}/api/v1/tools"
15
+ _success, data = get(url, { start: 0, items: 100, order: 'recently_added' })
16
16
  if !data.nil?
17
17
  profiles = JSON.parse(data)
18
- profiles['items'].map { |x|
18
+ profiles['items'].select { |p| p['tool_type'] == 'compliance_profile' }.map { |x|
19
19
  m = %r{^#{supermarket_url}/api/v1/tools/(?<slug>[\w-]+)(/)?$}.match(x['tool'])
20
20
  x['slug'] = m[:slug]
21
21
  x
@@ -21,6 +21,18 @@ module Inspec
21
21
  @opts[:default]
22
22
  end
23
23
 
24
+ def title
25
+ @opts[:title]
26
+ end
27
+
28
+ def description
29
+ @opts[:description]
30
+ end
31
+
32
+ def ruby_var_identifier
33
+ 'attr_' + @name.downcase.strip.gsub(/\s+/, '-').gsub(/[^\w-]/, '')
34
+ end
35
+
24
36
  def to_hash
25
37
  {
26
38
  name: @name,
@@ -28,6 +40,15 @@ module Inspec
28
40
  }
29
41
  end
30
42
 
43
+ def to_ruby
44
+ res = ["#{ruby_var_identifier} = attribute('#{@name}',{"]
45
+ res.push " title: '#{title}'," unless title.to_s.empty?
46
+ res.push " default: '#{default}'," unless default.to_s.empty?
47
+ res.push " description: '#{description}'," unless description.to_s.empty?
48
+ res.push '})'
49
+ res.join("\n")
50
+ end
51
+
31
52
  def to_s
32
53
  "Attribute #{@name} with #{@value}"
33
54
  end
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.2.1'.freeze
7
+ VERSION = '1.3.0'.freeze
8
8
  end
@@ -219,7 +219,7 @@ end
219
219
  # unsupported
220
220
  RSpec::Matchers.define :contain do |_rule|
221
221
  match do |_resource|
222
- fail "[UNSUPPORTED] `contain` matcher. Please use the following syntax `its('content') { should match('value') }`."
222
+ fail "[UNSUPPORTED] `contain` matcher. Please use the following syntax `its('content') { should include('value') }`."
223
223
  end
224
224
  end
225
225
 
@@ -7,7 +7,20 @@
7
7
  require 'shellwords'
8
8
 
9
9
  module Inspec::Resources
10
+ module FilePermissionsSelector
11
+ def select_file_perms_style(os)
12
+ if os.unix?
13
+ UnixFilePermissions.new(inspec)
14
+ elsif os.windows?
15
+ WindowsFilePermissions.new(inspec)
16
+ end
17
+ end
18
+ end
19
+
10
20
  class FileResource < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
21
+ include FilePermissionsSelector
22
+ include MountParser
23
+
11
24
  name 'file'
12
25
  desc 'Use the file InSpec audit resource to test all system file types, including files, directories, symbolic links, named pipes, sockets, character devices, block devices, and doors.'
13
26
  example "
@@ -16,14 +29,16 @@ module Inspec::Resources
16
29
  it { should be_file }
17
30
  it { should be_readable }
18
31
  it { should be_writable }
32
+ it { should be_executable.by_user('root') }
19
33
  it { should be_owned_by 'root' }
20
34
  its('mode') { should cmp '0644' }
21
35
  end
22
36
  "
23
- include MountParser
24
37
 
25
38
  attr_reader :file, :mount_options
26
39
  def initialize(path)
40
+ # select permissions style
41
+ @perms_provider = select_file_perms_style(inspec.os)
27
42
  @file = inspec.backend.file(path)
28
43
  end
29
44
 
@@ -51,20 +66,23 @@ module Inspec::Resources
51
66
 
52
67
  def readable?(by_usergroup, by_specific_user)
53
68
  return false unless exist?
69
+ return skip_resource '`readable?` is not supported on your OS yet.' if @perms_provider.nil?
54
70
 
55
- file_permission_granted?('r', by_usergroup, by_specific_user)
71
+ file_permission_granted?('read', by_usergroup, by_specific_user)
56
72
  end
57
73
 
58
74
  def writable?(by_usergroup, by_specific_user)
59
75
  return false unless exist?
76
+ return skip_resource '`writable?` is not supported on your OS yet.' if @perms_provider.nil?
60
77
 
61
- file_permission_granted?('w', by_usergroup, by_specific_user)
78
+ file_permission_granted?('write', by_usergroup, by_specific_user)
62
79
  end
63
80
 
64
81
  def executable?(by_usergroup, by_specific_user)
65
82
  return false unless exist?
83
+ return skip_resource '`executable?` is not supported on your OS yet.' if @perms_provider.nil?
66
84
 
67
- file_permission_granted?('x', by_usergroup, by_specific_user)
85
+ file_permission_granted?('execute', by_usergroup, by_specific_user)
68
86
  end
69
87
 
70
88
  def mounted?(expected_options = nil, identical = false)
@@ -109,16 +127,14 @@ module Inspec::Resources
109
127
 
110
128
  private
111
129
 
112
- def file_permission_granted?(flag, by_usergroup, by_specific_user)
113
- unless inspec.os.unix?
114
- fail 'Checking file permissions is not supported on your os'
115
- end
116
-
130
+ def file_permission_granted?(access, by_usergroup, by_specific_user)
131
+ fail '`file_permission_granted?` is not supported on your OS' if @perms_provider.nil?
117
132
  if by_specific_user.nil? || by_specific_user.empty?
133
+ fail '`check_file_permission_by_mask` is not supported on your OS' unless inspec.os.unix?
118
134
  usergroup = usergroup_for(by_usergroup, by_specific_user)
119
- check_file_permission_by_mask(usergroup, flag)
135
+ check_file_permission_by_mask(usergroup, access)
120
136
  else
121
- check_file_permission_by_user(by_specific_user, flag)
137
+ @perms_provider.check_file_permission_by_user(by_specific_user, access, source_path)
122
138
  end
123
139
  end
124
140
 
@@ -129,15 +145,44 @@ module Inspec::Resources
129
145
  (file.mode & mask) != 0
130
146
  end
131
147
 
132
- def check_file_permission_by_user(user, flag)
148
+ def usergroup_for(usergroup, specific_user)
149
+ if usergroup == 'others'
150
+ 'other'
151
+ elsif (usergroup.nil? || usergroup.empty?) && specific_user.nil?
152
+ 'all'
153
+ else
154
+ usergroup
155
+ end
156
+ end
157
+ end
158
+
159
+ class FilePermissions
160
+ attr_reader :inspec
161
+ def initialize(inspec)
162
+ @inspec = inspec
163
+ end
164
+ end
165
+
166
+ class UnixFilePermissions < FilePermissions
167
+ def check_file_permission_by_user(user, access_type, path)
168
+ flag = case access_type
169
+ when 'read'
170
+ 'r'
171
+ when 'write'
172
+ 'w'
173
+ when 'execute'
174
+ 'x'
175
+ else
176
+ fail 'Invalid access_type provided'
177
+ end
133
178
  if inspec.os.linux?
134
- perm_cmd = "su -s /bin/sh -c \"test -#{flag} #{source_path}\" #{user}"
179
+ perm_cmd = "su -s /bin/sh -c \"test -#{flag} #{path}\" #{user}"
135
180
  elsif inspec.os.bsd? || inspec.os.solaris?
136
- perm_cmd = "sudo -u #{user} test -#{flag} #{source_path}"
181
+ perm_cmd = "sudo -u #{user} test -#{flag} #{path}"
137
182
  elsif inspec.os.aix?
138
- perm_cmd = "su #{user} -c test -#{flag} #{source_path}"
183
+ perm_cmd = "su #{user} -c test -#{flag} #{path}"
139
184
  elsif inspec.os.hpux?
140
- perm_cmd = "su #{user} -c \"test -#{flag} #{source_path}\""
185
+ perm_cmd = "su #{user} -c \"test -#{flag} #{path}\""
141
186
  else
142
187
  return skip_resource 'The `file` resource does not support `by_user` on your OS.'
143
188
  end
@@ -145,15 +190,22 @@ module Inspec::Resources
145
190
  cmd = inspec.command(perm_cmd)
146
191
  cmd.exit_status == 0 ? true : false
147
192
  end
193
+ end
148
194
 
149
- def usergroup_for(usergroup, specific_user)
150
- if usergroup == 'others'
151
- 'other'
152
- elsif (usergroup.nil? || usergroup.empty?) && specific_user.nil?
153
- 'all'
154
- else
155
- usergroup
156
- end
195
+ class WindowsFilePermissions < FilePermissions
196
+ def check_file_permission_by_user(user, access_type, path)
197
+ access_rule = case access_type
198
+ when 'read'
199
+ '@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'Read\', \'ListDirectory\')'
200
+ when 'write'
201
+ '@(\'FullControl\', \'Modify\', \'Write\')'
202
+ when 'execute'
203
+ '@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'ExecuteFile\')'
204
+ else
205
+ fail 'Invalid access_type provided'
206
+ end
207
+ cmd = inspec.command("@(@((Get-Acl '#{path}').access | Where-Object {$_.AccessControlType -eq 'Allow' -and $_.IdentityReference -eq '#{user}' }) | Where-Object {($_.FileSystemRights.ToString().Split(',') | % {$_.trim()} | ? {#{access_rule} -contains $_}) -ne $null}) | measure | % { $_.Count }")
208
+ cmd.stdout.chomp == '0' ? false : true
157
209
  end
158
210
  end
159
211
  end
@@ -22,6 +22,12 @@ module Inspec::Resources
22
22
  @conf_path = path || '/etc/inetd.conf'
23
23
  end
24
24
 
25
+ # overwrite exec to ensure it works with its
26
+ # TODO: this needs to be fixed in RSpec
27
+ def exec
28
+ read_params['exec']
29
+ end
30
+
25
31
  def method_missing(name)
26
32
  read_params[name.to_s]
27
33
  end
@@ -204,7 +204,7 @@ module Inspec::Resources
204
204
  # Find the package
205
205
  cmd = inspec.command <<-EOF.gsub(/^\s*/, '')
206
206
  Get-ItemProperty (@("#{search_paths.join('", "')}") | Where-Object { Test-Path $_ }) |
207
- Where-Object { $_.DisplayName -like "#{package_name}*" -or $_.PSChildName -like "#{package_name}" } |
207
+ Where-Object { $_.DisplayName -like "#{package_name}" -or $_.PSChildName -like "#{package_name}" } |
208
208
  Select-Object -Property DisplayName,DisplayVersion | ConvertTo-Json
209
209
  EOF
210
210
 
@@ -26,7 +26,7 @@ module Inspec::Resources
26
26
  grep = '(/[^/]*)*'+grep if grep[0] != '/'
27
27
  grep = Regexp.new('^' + grep + '(\s|$)')
28
28
  end
29
- all_cmds = ps_aux
29
+ all_cmds = ps_axo
30
30
  @list = all_cmds.find_all do |hm|
31
31
  hm[:command] =~ grep
32
32
  end
@@ -43,39 +43,37 @@ module Inspec::Resources
43
43
 
44
44
  private
45
45
 
46
- def ps_aux
46
+ def ps_axo
47
47
  os = inspec.os
48
48
 
49
49
  if os.linux?
50
- command = 'ps auxZ'
50
+ command = 'ps axo label,pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user:32,command'
51
51
  regex = /^([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
52
52
  else
53
- command = 'ps aux'
54
- regex = /^([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
53
+ command = 'ps axo pid,pcpu,pmem,vsz,rss,tty,stat,start,time,user,command'
54
+ regex = /^\s*([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+([^ ]+)\s+(.*)$/
55
55
  end
56
56
  build_process_list(command, regex, os)
57
57
  end
58
58
 
59
- Process = Struct.new(:label, :user, :pid,
59
+ Process = Struct.new(:label, :pid,
60
60
  :cpu, :mem, :vsz,
61
61
  :rss, :tty, :stat,
62
- :start, :time, :command)
62
+ :start, :time, :user, :command)
63
63
 
64
64
  def build_process_list(command, regex, os)
65
65
  cmd = inspec.command(command)
66
66
  all = cmd.stdout.split("\n")[1..-1]
67
67
  return [] if all.nil?
68
-
69
68
  lines = all.map do |line|
70
69
  line.match(regex)
71
70
  end.compact
72
-
73
71
  lines.map do |m|
74
72
  a = m.to_a[1..-1] # grab all matching groups
75
73
  a.unshift(nil) unless os.linux?
76
- a[2] = a[2].to_i
74
+ a[1] = a[1].to_i
75
+ a[4] = a[4].to_i
77
76
  a[5] = a[5].to_i
78
- a[6] = a[6].to_i
79
77
  Process.new(*a)
80
78
  end
81
79
  end
data/lib/resources/ssl.rb CHANGED
@@ -41,7 +41,7 @@ class SSL < Inspec.resource(1)
41
41
  'tls1.2',
42
42
  ].freeze
43
43
 
44
- attr_reader :host, :port
44
+ attr_reader :host, :port, :timeout, :retries
45
45
 
46
46
  def initialize(opts = {})
47
47
  @host = opts[:host]
@@ -71,7 +71,7 @@ class SSL < Inspec.resource(1)
71
71
  res = Parallel.map(groups, in_threads: 8) do |proto, e|
72
72
  [proto, SSLShake.hello(x.resource.host, port: x.resource.port,
73
73
  protocol: proto, ciphers: e.map(&:cipher),
74
- timeout: @timeout, retries: @retries)]
74
+ timeout: x.resource.timeout, retries: x.resource.retries)]
75
75
  end
76
76
  Hash[res]
77
77
  }
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.2.1
4
+ version: 1.3.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-10-15 00:00:00.000000000 Z
11
+ date: 2016-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train
@@ -344,17 +344,13 @@ files:
344
344
  - examples/profile/controls/example.rb
345
345
  - examples/profile/controls/gordon.rb
346
346
  - examples/profile/controls/meta.rb
347
- - examples/profile/inspec.lock
348
347
  - examples/profile/inspec.yml
349
348
  - examples/profile/libraries/gordon_config.rb
350
- - examples/ssh/README.md
351
- - examples/ssh/controls/example.rb
352
- - examples/ssh/inspec.lock
353
- - examples/ssh/inspec.yml
354
- - examples/ssh/libraries/.gitkeep
355
- - examples/ssh/libraries/habitat.rb
356
349
  - inspec.gemspec
357
350
  - lib/bundles/README.md
351
+ - lib/bundles/inspec-artifact.rb
352
+ - lib/bundles/inspec-artifact/README.md
353
+ - lib/bundles/inspec-artifact/cli.rb
358
354
  - lib/bundles/inspec-compliance.rb
359
355
  - lib/bundles/inspec-compliance/.kitchen.yml
360
356
  - lib/bundles/inspec-compliance/README.md
@@ -537,7 +533,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
537
533
  version: '0'
538
534
  requirements: []
539
535
  rubyforge_project:
540
- rubygems_version: 2.5.1
536
+ rubygems_version: 2.4.6
541
537
  signing_key:
542
538
  specification_version: 4
543
539
  summary: Infrastructure and compliance testing.
@@ -1,3 +0,0 @@
1
- ---
2
- lockfile_version: 1
3
- depends: []
@@ -1,3 +0,0 @@
1
- # Example InSpec Profile
2
-
3
- This example shows the implementation of an InSpec [profile](../../docs/profiles.rst).
@@ -1,9 +0,0 @@
1
- # encoding: utf-8
2
- # copyright: 2015, The Authors
3
- # license: All rights reserved
4
-
5
- title 'my hab tests'
6
-
7
- describe habitat do
8
- it { should exist }
9
- end
@@ -1,3 +0,0 @@
1
- ---
2
- lockfile_version: 1
3
- depends: []
@@ -1,8 +0,0 @@
1
- name: ssh
2
- title: InSpec Profile
3
- maintainer: The Authors
4
- copyright: The Authors
5
- copyright_email: you@example.com
6
- license: All Rights Reserved
7
- summary: An InSpec Compliance Profile
8
- version: 0.1.0
File without changes
@@ -1,13 +0,0 @@
1
-
2
- class Habitat < Inspec.resource(1)
3
- name "habitat"
4
- example "
5
- describe habitat do
6
- it { should exist }
7
- end
8
- "
9
-
10
- def exist?
11
- inspec.file('hab').exist?
12
- end
13
- end