inspec 1.34.1 → 1.35.1

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: f8652931e666396eaecf1bdfd94e9979cd8dea97
4
- data.tar.gz: 1769b0dea0a7fe11e89943ada4951b1b1d219545
3
+ metadata.gz: 75f7ef6bcd93aae1ea6bda13e5d50675fa234afa
4
+ data.tar.gz: a43bc69d1420af04b82860164873a42f5fc0ba4b
5
5
  SHA512:
6
- metadata.gz: c85ea367f1d2d196cae095cbc3e9479947bb1876718ff55d68db0d506226f5ea2ddccec00bf36e7d2fd296e49337d4c376889bf3aa88680845b51dd09f19b765
7
- data.tar.gz: ef7c008ff3915b2f80bf3fa60a4f302bb0a245cd50cfc6a293e3a788d64ff275b7f642f4dc21594b274c6e1c4aa033ad765c458751e4c2a1778ffffc1bcb48d0
6
+ metadata.gz: 0f7bffcdfa35005e4805026dd0028031713f1ecb30b7fae10d811c79bde5840599df4e35dc9631ffea18d637c768abed92dbe05392d1a7c77efaf1332629e445
7
+ data.tar.gz: b24fc841af39b05e38ee9537e76da3d1b68bcdd07349e59af1c6b140a5298cec578fef54e1167d7b48b348829d02a59054c785a4fb265fd7cce9767868d3f1b5
@@ -1,24 +1,40 @@
1
1
  # Change Log
2
2
 
3
- <!-- latest_release 1.33.15 -->
4
- ## [v1.33.15](https://github.com/chef/inspec/tree/v1.33.15) (2017-08-23)
3
+ <!-- latest_release 1.34.10 -->
4
+ ## [v1.34.10](https://github.com/chef/inspec/tree/v1.34.10) (2017-08-31)
5
5
 
6
- #### Enhancements
7
- - Refine the profile/test summary output of the CLI formatter [#2094](https://github.com/chef/inspec/pull/2094) ([adamleff](https://github.com/adamleff))
6
+ #### New Resources
7
+ - etc_hosts resource: test the contents of the /etc/hosts file [#2065](https://github.com/chef/inspec/pull/2065) ([dromazmj](https://github.com/dromazmj))
8
8
  <!-- latest_release -->
9
9
 
10
- <!-- release_rollup since=1.33.12 -->
11
- ### Changes since 1.33.12 release
10
+ <!-- release_rollup since=1.34.1 -->
11
+ ### Changes since 1.34.1 release
12
12
 
13
13
  #### Enhancements
14
- - Refine the profile/test summary output of the CLI formatter [#2094](https://github.com/chef/inspec/pull/2094) ([adamleff](https://github.com/adamleff)) <!-- 1.33.15 -->
14
+ - port resource: support ss instead of netstat [#2110](https://github.com/chef/inspec/pull/2110) ([adamleff](https://github.com/adamleff)) <!-- 1.34.8 -->
15
+ - pip resource: support non-default pip locations, such as virtualenvs [#2097](https://github.com/chef/inspec/pull/2097) ([tonybaloney](https://github.com/tonybaloney)) <!-- 1.34.7 -->
16
+
17
+ #### Bug Fixes
18
+ - Support mixed-case group entries [#2101](https://github.com/chef/inspec/pull/2101) ([adamleff](https://github.com/adamleff)) <!-- 1.34.6 -->
19
+ - http resource: prevent repeat calls during a control with multiple tests [#2108](https://github.com/chef/inspec/pull/2108) ([mivok](https://github.com/mivok)) <!-- 1.34.5 -->
20
+ - auditd_rules resource: fix get_keys error on lines that have no keys [#2103](https://github.com/chef/inspec/pull/2103) ([jburns12](https://github.com/jburns12)) <!-- 1.34.4 -->
15
21
 
16
22
  #### Merged Pull Requests
17
- - Add slack notifications for Travis CI builds to master [#2092](https://github.com/chef/inspec/pull/2092) ([adamleff](https://github.com/adamleff)) <!-- 1.33.14 -->
18
- - Update CHANGELOG (add fix author) [#2091](https://github.com/chef/inspec/pull/2091) ([n-rodriguez](https://github.com/n-rodriguez)) <!-- 1.33.13 -->
23
+ - Add sensitive flag to resources to restrict logging output [#2017](https://github.com/chef/inspec/pull/2017) ([arothian](https://github.com/arothian)) <!-- 1.34.3 -->
24
+
25
+ #### New Resources
26
+ - etc_hosts resource: test the contents of the /etc/hosts file [#2065](https://github.com/chef/inspec/pull/2065) ([dromazmj](https://github.com/dromazmj)) <!-- 1.34.10 -->
27
+ - Add support for XML files [#2107](https://github.com/chef/inspec/pull/2107) ([jonathanmorley](https://github.com/jonathanmorley)) <!-- 1.34.9 -->
28
+ - aide_conf resource: test configuration of the AIDE file integrity tool [#2063](https://github.com/chef/inspec/pull/2063) ([jburns12](https://github.com/jburns12)) <!-- 1.34.2 -->
19
29
  <!-- release_rollup -->
20
30
 
21
31
  <!-- latest_stable_release -->
32
+ ## [v1.34.1](https://github.com/chef/inspec/tree/v1.34.1) (2017-08-24)
33
+
34
+ #### Enhancements
35
+ - Refine the profile/test summary output of the CLI formatter [#2094](https://github.com/chef/inspec/pull/2094) ([adamleff](https://github.com/adamleff))
36
+ <!-- latest_stable_release -->
37
+
22
38
  ## [v1.33.12](https://github.com/chef/inspec/tree/v1.33.12) (2017-08-18)
23
39
 
24
40
  #### Bug Fixes
@@ -34,7 +50,6 @@
34
50
  #### Merged Pull Requests
35
51
  - add functional tests for inspec check [#2077](https://github.com/chef/inspec/pull/2077) ([chris-rock](https://github.com/chris-rock))
36
52
  - Move bug fixes in CHANGELOG to correct header [#2089](https://github.com/chef/inspec/pull/2089) ([adamleff](https://github.com/adamleff))
37
- <!-- latest_stable_release -->
38
53
 
39
54
  ## [v1.33.1](https://github.com/chef/inspec/tree/v1.33.1) (2017-08-10)
40
55
 
@@ -71,6 +71,16 @@ describe.one do
71
71
  end
72
72
  ```
73
73
 
74
+ #### Sensitive resources
75
+
76
+ In some scenarios, you may be writing checks involving resources with sensitive content (e.g. a file resource). In the case of failures, it may be desired to suppress output. This can be done by adding the `:sensitive` flag to the resource definition
77
+
78
+ ```ruby
79
+ describe file('/tmp/mysecretfile'), :sensitive do
80
+ its('content') { should contain 'secret_info' }
81
+ end
82
+ ```
83
+
74
84
  ## Examples
75
85
 
76
86
  The following examples show simple compliance tests using a single `control` block.
@@ -0,0 +1,81 @@
1
+ ---
2
+ title: About the aide_conf Resource
3
+ ---
4
+
5
+ # aide_conf
6
+
7
+ Use the `aide_conf` InSpec audit resource to test the rules established for the file integrity tool AIDE. Controlled by the aide.conf file typically at /etc/aide.conf.
8
+
9
+ ## Syntax
10
+
11
+ An `aide_conf` resource block can be used to determine if the selection lines contain one (or more) directories whose files should be added to the aide database:
12
+
13
+ describe aide_conf('path') do
14
+ its('selection_lines') { should include '/sbin' }
15
+ end
16
+
17
+ where
18
+
19
+ * `'selection_lines'` refers to all selection lines found in the aide.conf file
20
+ * `('path')` is the non-default path to the `aide.conf` file (optional)
21
+ * `should include 'value'` is the value that is expected
22
+
23
+ Use the where clause to match a selection_line to one rule or a particular set of rules found in the aide.conf file:
24
+
25
+ describe aide_conf.where { selection_line == '/bin' } do
26
+ its('rules.flatten') { should include 'r' }
27
+ end
28
+
29
+ describe aide_conf.where { selection_line == '/sbin' } do
30
+ its('rules') { should include ['p', 'i', 'l', 'n', 'u', 'g', 'sha512'] }
31
+ end
32
+
33
+ ## Matchers
34
+
35
+ This InSpec audit resource has the following matchers:
36
+
37
+ ### be
38
+
39
+ <%= partial "/shared/matcher_be" %>
40
+
41
+ ### cmp
42
+
43
+ <%= partial "/shared/matcher_cmp" %>
44
+
45
+ ### eq
46
+
47
+ <%= partial "/shared/matcher_eq" %>
48
+
49
+ ### include
50
+
51
+ <%= partial "/shared/matcher_include" %>
52
+
53
+ ### all_have_rule
54
+
55
+ The usage of all_have_rule will return whether or not all selection lines in audit.conf contain a particular rule:
56
+
57
+ describe aide_conf.all_have_rule('sha512') do
58
+ it { should eq true }
59
+ end
60
+
61
+ ## Examples
62
+
63
+ The following examples show how to use this InSpec audit resource.
64
+
65
+ ### Test if all selection lines contain the xattr rule
66
+
67
+ describe aide_conf.all_have_rule('xattr') do
68
+ it { should eq true }
69
+ end
70
+
71
+ ### Test whether selection line for /bin contains a particular rule
72
+
73
+ describe aide_conf.where { selection_line == '/bin' } do
74
+ its('rules.flatten') { should include 'r' }
75
+ end
76
+
77
+ ### Test whether selection line for /sbin consists of a particular set of rules
78
+
79
+ describe aide_conf.where { selection_line == '/sbin' } do
80
+ its('rules') { should include ['r', 'sha512'] }
81
+ end
@@ -0,0 +1,62 @@
1
+ ---
2
+ title: About the etc_hosts Resource
3
+ ---
4
+
5
+ # etc_hosts
6
+
7
+ Use the `etc_hosts` InSpec audit resource to test rules set to match IP addresses with hostnames.
8
+ ## Syntax
9
+
10
+ An etc/hosts rule specifies an IP address and what its hostname is along with optional aliases it can have.
11
+
12
+ ## Syntax
13
+
14
+ Use the where clause to match a property to one or more rules in the hosts file.
15
+
16
+ describe etc_hosts.where { ip_address == 'value' } do
17
+ its('primary_name') { should cmp 'hostname' }
18
+ its('all_host_names') { should cmp 'list' }
19
+ end
20
+
21
+ Use the optional constructor parameter to give an alternative path to hosts file
22
+
23
+ describe etc_hosts('path/to/hosts').where { ip_address == 'value' } do
24
+ its('primary_name') { should cmp 'hostname' }
25
+ its('all_host_names') { should cmp 'list' }
26
+ end
27
+
28
+ where
29
+
30
+ * `ip_address` is the ip address of the hostname in either ipv4 or ipv6 format.
31
+ * `primary_name` is the name associated with the ip address.
32
+ * `all_host_names` is a list including the primary_name as the first entry followed by any aliase names the host has.
33
+
34
+ ## Supported Properties
35
+
36
+ 'ip_address', 'primary_name', 'all_host_names'
37
+
38
+ ## Property Examples and Return Types
39
+
40
+ ### ip_address
41
+
42
+ `ip_address` returns a string array of ip addresses specified in the etc/hosts file.
43
+
44
+ describe etc_hosts.where { primary_name == 'localhost' } do
45
+ its('ip_address') { should cmp '127.0.1.154' }
46
+ end
47
+
48
+ ### primary_name
49
+
50
+ `primary_name` returns a string array of primary_names specified in the etc/hosts file.
51
+
52
+ describe etc_hosts.where { ip_address == '::1' } do
53
+ its('primary_name') { should cmp 'localhost' }
54
+ end
55
+
56
+ ### all_host_names
57
+
58
+ `all_host_names` returns a two dimensional string array where each entry has the primary_name first followed by any aliases.
59
+
60
+ describe etc_hosts.where { ip_address == '127.0.1.154' } do
61
+ its('all_host_names') { should eq [['localhost', 'localhost.localdomain', 'localhost4', 'localhost4.localdomain4'], ['localhost', 'localhost.localdomain', 'localhost6', 'localhost6.localdomain6']] }
62
+ end
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: About the xml Resource
3
+ ---
4
+
5
+ # xml
6
+
7
+ Use the `xml` InSpec audit resource to test data in an XML file.
8
+
9
+ ## Syntax
10
+
11
+ An `xml` resource block declares the data to be tested. Assume the following XML file:
12
+
13
+ <root>
14
+ <name>hello</name>
15
+ <meta>
16
+ <creator>John Doe</creator>
17
+ </meta>
18
+ <array>
19
+ <element>one</element>
20
+ <element>two</element>
21
+ </array>
22
+ </root>
23
+
24
+ This file can be queried using:
25
+
26
+ describe xml('/path/to/name.xml') do
27
+ its('root/name') { should eq ['hello'] }
28
+ its('root/meta/creator') { should eq ['John Doe'] }
29
+ its('root/array[2]/element]) { should eq ['two'] }
30
+ end
31
+
32
+ where
33
+
34
+ * `root/name` is an XPath expression
35
+ * `should eq ['foo']` tests a value of `root/name` as read from an XML file versus the value declared in the test
36
+
37
+ ## Matchers
38
+
39
+ This InSpec audit resource has the following matchers:
40
+
41
+ ### be
42
+
43
+ <%= partial "/shared/matcher_be" %>
44
+
45
+ ### cmp
46
+
47
+ <%= partial "/shared/matcher_cmp" %>
48
+
49
+ ### eq
50
+
51
+ <%= partial "/shared/matcher_eq" %>
52
+
53
+ ### include
54
+
55
+ <%= partial "/shared/matcher_include" %>
56
+
57
+ ### match
58
+
59
+ <%= partial "/shared/matcher_match" %>
60
+
61
+ ### name
62
+
63
+ The `name` matcher tests the value of `name` as read from a JSON file versus the value declared in the test:
64
+
65
+ its('name') { should eq 'foo' }
66
+
67
+ ## Examples
68
+
69
+ The following examples show how to use this InSpec audit resource.
70
+
71
+ ### Test an AppPool's presence in an applicationHost.config file
72
+
73
+ describe xml('applicationHost.config') do
74
+ its('configuration/system.applicationHost/applicationPools/add@name') { should contain('my_pool') }
75
+ end
@@ -0,0 +1,29 @@
1
+ # Example InSpec Profile with Sensitive failures
2
+
3
+ This profile demostrates resources flagged as sensitive
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ $ inspec exec examples/profile-sensitive
9
+ ....
10
+
11
+ bob should
12
+ ∅ eq "billy"
13
+
14
+ expected: "billy"
15
+ got: "bob"
16
+
17
+ (compared using ==)
18
+
19
+ sensitivepassword should
20
+ ∅ eq "secret"
21
+ *** sensitive output suppressed ***
22
+ bob should
23
+ ✔ eq "bob"
24
+ sensitivepassword should
25
+ ✔ eq "sensitivepassword"
26
+
27
+ Test Summary: 2 successful, 2 failures, 0 skipped
28
+
29
+ ```
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ describe 'bob' do
4
+ it { should eq 'billy' }
5
+ end
6
+
7
+ describe 'sensitivepassword', :sensitive do
8
+ it { should eq 'secret' }
9
+ end
@@ -0,0 +1,9 @@
1
+ # encoding: utf-8
2
+
3
+ describe 'bob' do
4
+ it { should eq 'bob' }
5
+ end
6
+
7
+ describe 'sensitivepassword', :sensitive do
8
+ it { should eq 'sensitivepassword' }
9
+ end
@@ -0,0 +1,8 @@
1
+ name: profile-sensitive
2
+ title: InSpec Sensitive Profile
3
+ maintainer: The Authors
4
+ copyright: The Authors
5
+ copyright_email: you@example.com
6
+ license: Apache-2.0
7
+ summary: An InSpec Compliance Profile
8
+ version: 0.1.0
@@ -72,6 +72,7 @@ module Inspec
72
72
  end
73
73
  end
74
74
 
75
+ require 'resources/aide_conf'
75
76
  require 'resources/apache'
76
77
  require 'resources/apache_conf'
77
78
  require 'resources/apt'
@@ -89,6 +90,7 @@ require 'resources/docker'
89
90
  require 'resources/docker_image'
90
91
  require 'resources/docker_container'
91
92
  require 'resources/etc_group'
93
+ require 'resources/etc_hosts'
92
94
  require 'resources/file'
93
95
  require 'resources/gem'
94
96
  require 'resources/groups'
@@ -157,3 +159,4 @@ require 'resources/json'
157
159
  require 'resources/yaml'
158
160
  require 'resources/csv'
159
161
  require 'resources/ini'
162
+ require 'resources/xml'
@@ -52,7 +52,12 @@ class InspecRspecMiniJson < RSpec::Core::Formatters::JsonFormatter
52
52
  format_example(example).tap do |hash|
53
53
  e = example.exception
54
54
  next unless e
55
- hash[:message] = exception_message(e)
55
+
56
+ if example.metadata[:sensitive]
57
+ hash[:message] = '*** sensitive output suppressed ***'
58
+ else
59
+ hash[:message] = exception_message(e)
60
+ end
56
61
 
57
62
  next if e.is_a? RSpec::Expectations::ExpectationNotMetError
58
63
  hash[:exception] = e.class.name
@@ -341,7 +346,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
341
346
  # This method is called through the RSpec Formatter interface for every
342
347
  # example found in the test suite.
343
348
  #
344
- # Within #format_example we are getting and example and:
349
+ # Within #format_example we are getting an example and:
345
350
  # * if this is an example, within a control, within a profile then we want
346
351
  # to display the profile header, display the control, and then display
347
352
  # the example.
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.34.1'.freeze
7
+ VERSION = '1.35.1'.freeze
8
8
  end
@@ -0,0 +1,162 @@
1
+ # encoding: utf-8
2
+ # author: Jen Burns, burnsjennifere@gmail.com
3
+
4
+ require 'utils/filter'
5
+ require 'utils/parser'
6
+
7
+ # rubocop:disable Metrics/ClassLength
8
+ module Inspec::Resources
9
+ class AideConf < Inspec.resource(1)
10
+ name 'aide_conf'
11
+ desc 'Use the aide_conf InSpec audit resource to test the rules established for
12
+ the file integrity tool AIDE. Controlled by the aide.conf file typically at /etc/aide.conf.'
13
+ example "
14
+ describe aide_conf do
15
+ its('selection_lines') { should include '/sbin' }
16
+ end
17
+
18
+ describe aide_conf.where { selection_line == '/bin' } do
19
+ its('rules.flatten') { should include 'r' }
20
+ end
21
+
22
+ describe aide_conf.all_have_rule('sha512') do
23
+ it { should eq true }
24
+ end
25
+ "
26
+
27
+ attr_reader :params
28
+
29
+ include CommentParser
30
+
31
+ def initialize(aide_conf_path = nil)
32
+ return skip_resource 'The `aide_conf` resource is not supported on your OS.' unless inspec.os.linux?
33
+ @conf_path = aide_conf_path || '/etc/aide.conf'
34
+ @content = nil
35
+ @rules = nil
36
+ read_content
37
+ end
38
+
39
+ def all_have_rule(rule)
40
+ # Case when file didn't exist or perms didn't allow an open
41
+ return false if @content.nil?
42
+
43
+ lines = @params.reject { |line| line['rules'].include? rule }
44
+ lines.empty?
45
+ end
46
+
47
+ filter = FilterTable.create
48
+ filter.add_accessor(:where)
49
+ .add_accessor(:entries)
50
+ .add(:selection_lines, field: 'selection_line')
51
+ .add(:rules, field: 'rules')
52
+
53
+ filter.connect(self, :params)
54
+
55
+ private
56
+
57
+ def read_content
58
+ return @content unless @content.nil?
59
+ @rules = {}
60
+
61
+ file = inspec.file(@conf_path)
62
+ if !file.file?
63
+ return skip_resource "Can't find file \"#{@conf_path}\""
64
+ end
65
+ raw_conf = file.content
66
+ if raw_conf.nil?
67
+ return skip_resource "File can't be opened or is empty \"#{@conf_path}\""
68
+ end
69
+ if raw_conf.empty? && !file.empty?
70
+ return skip_resource "Can't read file \"#{@conf_path}\""
71
+ end
72
+
73
+ # If there is a file and it contains content, continue
74
+ @content = filter_comments(inspec.file(@conf_path).content.lines)
75
+ @params = parse_conf(@content)
76
+ end
77
+
78
+ def filter_comments(data)
79
+ content = []
80
+ data.each do |line|
81
+ content_line, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
82
+ content.push(content_line)
83
+ end
84
+ content
85
+ end
86
+
87
+ def parse_conf(content)
88
+ params = []
89
+ content.each do |line|
90
+ param = parse_line(line)
91
+ if !param['selection_line'].nil?
92
+ params.push(param)
93
+ end
94
+ end
95
+ params
96
+ end
97
+
98
+ def parse_line(line)
99
+ line_and_rules = {}
100
+ # Case when line is a rule line
101
+ if line.include?(' = ')
102
+ parse_rule_line(line)
103
+ # Case when line is a selection line
104
+ elsif line.start_with?('/', '!', '=')
105
+ line_and_rules = parse_selection_line(line)
106
+ end
107
+ line_and_rules
108
+ end
109
+
110
+ def parse_rule_line(line)
111
+ line.gsub!(/\s+/, '')
112
+ rule_line_arr = line.split('=')
113
+ rules_list = rule_line_arr.last.split('+')
114
+ rule_name = rule_line_arr.first
115
+ rules_list.each_index do |i|
116
+ # Cases where rule respresents one or more other rules
117
+ if @rules.key?(rules_list[i])
118
+ rules_list[i] = @rules[rules_list[i]]
119
+ end
120
+ rules_list[i] = handle_multi_rule(rules_list, i)
121
+ end
122
+ @rules[rule_name] = rules_list.flatten
123
+ end
124
+
125
+ def parse_selection_line(line)
126
+ selec_line_arr = line.split(' ')
127
+ selection_line = selec_line_arr.first
128
+ selection_line.chop! if selection_line.end_with?('/')
129
+ rule_list = selec_line_arr.last.split('+')
130
+ rule_list.each_index do |i|
131
+ hash_list = @rules[rule_list[i]]
132
+ # Cases where rule respresents one or more other rules
133
+ if !hash_list.nil?
134
+ rule_list[i] = hash_list
135
+ end
136
+ rule_list[i] = handle_multi_rule(rule_list, i)
137
+ end
138
+ rule_list.flatten!
139
+ {
140
+ 'selection_line' => selection_line,
141
+ 'rules' => rule_list,
142
+ }
143
+ end
144
+
145
+ def handle_multi_rule(rule_list, i)
146
+ # Rules that represent multiple rules (R,L,>)
147
+ r_rules = %w{p i l n u g s m c md5}
148
+ l_rules = %w{p i l n u g}
149
+ grow_log_rules = %w{p l u g i n S}
150
+
151
+ case rule_list[i]
152
+ when 'R'
153
+ return r_rules
154
+ when 'L'
155
+ return l_rules
156
+ when '>'
157
+ return grow_log_rules
158
+ end
159
+ rule_list[i]
160
+ end
161
+ end
162
+ end
@@ -177,7 +177,7 @@ module Inspec::Resources
177
177
 
178
178
  # NB only in file lines
179
179
  def get_key(line)
180
- line.match(/-k ([^ ]+)/)[1]
180
+ line.match(/-k ([^ ]+)/)[1] if line.include?('-k ')
181
181
  end
182
182
 
183
183
  # NOTE there are NO precautions wrt. filenames containing spaces in auditctl
@@ -0,0 +1,81 @@
1
+ # encoding: utf-8
2
+ # author: Matthew Dromazos
3
+
4
+ require 'utils/parser'
5
+
6
+ class EtcHosts < Inspec.resource(1)
7
+ name 'etc_hosts'
8
+ desc 'Use the etc_hosts InSpec audit resource to find an
9
+ ip_address and its associated hosts'
10
+ example "
11
+ describe etc_hosts.where { ip_address == '127.0.0.1' } do
12
+ its('ip_address') { should cmp '127.0.0.1' }
13
+ its('primary_name') { should cmp 'localhost' }
14
+ its('all_host_names') { should eq [['localhost', 'localhost.localdomain', 'localhost4', 'localhost4.localdomain4']] }
15
+ end
16
+ "
17
+
18
+ attr_reader :params
19
+
20
+ include CommentParser
21
+
22
+ def initialize(hosts_path = nil)
23
+ return skip_resource 'The `etc_hosts` resource is not supported on your OS.' unless inspec.os.linux? || inspec.os.windows?
24
+ @conf_path = hosts_path || default_hosts_file_path
25
+ @content = nil
26
+ @params = nil
27
+ read_content
28
+ end
29
+
30
+ filter = FilterTable.create
31
+ filter.add_accessor(:where)
32
+ .add_accessor(:entries)
33
+ .add(:ip_address, field: 'ip_address')
34
+ .add(:primary_name, field: 'primary_name')
35
+ .add(:all_host_names, field: 'all_host_names')
36
+
37
+ filter.connect(self, :params)
38
+
39
+ private
40
+
41
+ def default_hosts_file_path
42
+ inspec.os.windows? ? 'C:\windows\system32\drivers\etc\hosts' : '/etc/hosts'
43
+ end
44
+
45
+ def read_content
46
+ @content = ''
47
+ @params = {}
48
+ @content = read_file(@conf_path)
49
+ @params = parse_conf(@content)
50
+ end
51
+
52
+ def parse_conf(content)
53
+ content.map do |line|
54
+ data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
55
+ parse_line(data) unless data == ''
56
+ end.compact
57
+ end
58
+
59
+ def parse_line(line)
60
+ line_parts = line.split
61
+ return nil unless line_parts.length >= 2
62
+ {
63
+ 'ip_address' => line_parts[0],
64
+ 'primary_name' => line_parts[1],
65
+ 'all_host_names' => line_parts[1..-1],
66
+ }
67
+ end
68
+
69
+ def read_file(conf_path = @conf_path)
70
+ file = inspec.file(conf_path)
71
+ if !file.file?
72
+ return skip_resource "Can't find file. \"#{@conf_path}\""
73
+ end
74
+
75
+ raw_conf = file.content
76
+ if raw_conf.empty? && !file.empty?
77
+ return skip_resource("Could not read file contents\" #{@conf_path}\"")
78
+ end
79
+ raw_conf.lines
80
+ end
81
+ end
@@ -90,7 +90,6 @@ module Inspec::Resources
90
90
 
91
91
  def initialize(groupname)
92
92
  @group = groupname
93
- @group = @group.downcase unless inspec.os.windows?
94
93
 
95
94
  # select group manager
96
95
  @group_provider = select_group_manager(inspec.os)
@@ -55,6 +55,7 @@ module Inspec::Resources
55
55
  private
56
56
 
57
57
  def response
58
+ return @response if @response
58
59
  conn = Faraday.new url: @url, headers: @headers, params: @params, ssl: { verify: @ssl_verify }
59
60
 
60
61
  # set basic authentication
@@ -7,6 +7,7 @@
7
7
  # it { should be_installed }
8
8
  # end
9
9
  #
10
+
10
11
  module Inspec::Resources
11
12
  class PipPackage < Inspec.resource(1)
12
13
  name 'pip'
@@ -15,10 +16,17 @@ module Inspec::Resources
15
16
  describe pip('Jinja2') do
16
17
  it { should be_installed }
17
18
  end
19
+
20
+ describe pip('django', '/path/to/virtualenv/bin/pip') do
21
+ it { should be_installed }
22
+ its('version') { should eq('1.11.4')}
23
+ end
18
24
  "
19
25
 
20
- def initialize(package_name)
26
+ def initialize(package_name, pip_path = nil)
21
27
  @package_name = package_name
28
+ @pip_cmd = pip_path || default_pip_path
29
+ return skip_resource 'pip not found' unless inspec.command(@pip_cmd).exist?
22
30
  end
23
31
 
24
32
  def info
@@ -26,7 +34,7 @@ module Inspec::Resources
26
34
 
27
35
  @info = {}
28
36
  @info[:type] = 'pip'
29
- cmd = inspec.command("#{pip_cmd} show #{@package_name}")
37
+ cmd = inspec.command("#{@pip_cmd} show #{@package_name}")
30
38
  return @info if cmd.exit_status != 0
31
39
 
32
40
  params = SimpleConfig.new(
@@ -54,28 +62,28 @@ module Inspec::Resources
54
62
 
55
63
  private
56
64
 
57
- def pip_cmd
65
+ def default_pip_path
66
+ return 'pip' unless inspec.os.windows?
67
+
58
68
  # Pip is not on the default path for Windows, therefore we do some logic
59
69
  # to find the binary on Windows
60
- if inspec.os.windows?
61
- # we need to detect the pip command on Windows
62
- cmd = inspec.command('New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Pip -Value (Invoke-Command -ScriptBlock {where.exe pip}) -PassThru | Add-Member -MemberType NoteProperty -Name Python -Value (Invoke-Command -ScriptBlock {where.exe python}) -PassThru | ConvertTo-Json')
63
- begin
64
- paths = JSON.parse(cmd.stdout)
65
- # use pip if it on system path
66
- pipcmd = paths['Pip']
67
- # calculate path on windows
68
- if defined?(paths['Python']) && pipcmd.nil?
69
- pipdir = paths['Python'].split('\\')
70
- # remove python.exe
71
- pipdir.pop
72
- pipcmd = pipdir.push('Scripts').push('pip.exe').join('/')
73
- end
74
- rescue JSON::ParserError => _e
75
- return nil
70
+ cmd = inspec.command('New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Pip -Value (Invoke-Command -ScriptBlock {where.exe pip}) -PassThru | Add-Member -MemberType NoteProperty -Name Python -Value (Invoke-Command -ScriptBlock {where.exe python}) -PassThru | ConvertTo-Json')
71
+ begin
72
+ paths = JSON.parse(cmd.stdout)
73
+ # use pip if it on system path
74
+ pipcmd = paths['Pip']
75
+ # calculate path on windows
76
+ if defined?(paths['Python']) && pipcmd.nil?
77
+ pipdir = paths['Python'].split('\\')
78
+ # remove python.exe
79
+ pipdir.pop
80
+ pipcmd = pipdir.push('Scripts').push('pip.exe').join('/')
76
81
  end
82
+ rescue JSON::ParserError => _e
83
+ return nil
77
84
  end
78
- pipcmd || 'pip'
85
+
86
+ pipcmd
79
87
  end
80
88
  end
81
89
  end
@@ -264,10 +264,34 @@ module Inspec::Resources
264
264
  end
265
265
 
266
266
  # extract port information from netstat
267
- class LinuxPorts < PortsInfo
267
+ class LinuxPorts < PortsInfo # rubocop:disable Metrics/ClassLength
268
+ ALLOWED_PROTOCOLS = %w{tcp tcp6 udp udp6}.freeze
269
+
268
270
  def info
271
+ ports_via_ss || ports_via_netstat
272
+ end
273
+
274
+ def ports_via_ss
275
+ return nil unless inspec.command('ss').exist?
276
+
277
+ cmd = inspec.command('ss -tulpen')
278
+ return nil unless cmd.exit_status.to_i.zero?
279
+
280
+ ports = []
281
+
282
+ cmd.stdout.each_line do |line|
283
+ parsed_line = parse_ss_line(line)
284
+ ports << parsed_line unless parsed_line.nil?
285
+ end
286
+
287
+ ports
288
+ end
289
+
290
+ def ports_via_netstat
291
+ return nil unless inspec.command('netstat').exist?
292
+
269
293
  cmd = inspec.command('netstat -tulpen')
270
- return nil if cmd.exit_status.to_i != 0
294
+ return nil unless cmd.exit_status.to_i.zero?
271
295
 
272
296
  ports = []
273
297
  # parse all lines
@@ -362,6 +386,66 @@ module Inspec::Resources
362
386
  'pid' => pid,
363
387
  }
364
388
  end
389
+
390
+ def parse_ss_line(line)
391
+ parsed = line.split(/\s+/, 7)
392
+
393
+ # ss only returns "tcp" and "udp" as the protocol. However, netstat would return
394
+ # "tcp6" and "udp6" as necessary. In order to maintain backward compatibility, we
395
+ # will manually modify the protocol value if the line we're parsing is an IPv6
396
+ # entry.
397
+ process_info = parsed[6]
398
+ protocol = parsed[0]
399
+ protocol += '6' if process_info.include?('v6only:1')
400
+ return nil unless ALLOWED_PROTOCOLS.include?(protocol)
401
+
402
+ # parse the Local Address:Port
403
+ # examples:
404
+ # *:22
405
+ # :::22
406
+ # 10.0.2.15:1234
407
+ # ::ffff:10.0.2.15:9300
408
+ # fe80::a00:27ff:fe32:ed09%enp0s3:9200
409
+ parsed_net_address = parsed[4].match(/(\S+):(\*|\d+)$/)
410
+ return nil if parsed_net_address.nil?
411
+ host = parsed_net_address[1]
412
+ port = parsed_net_address[2]
413
+ return nil if host.nil? && port.nil?
414
+
415
+ # For backward compatibility with the netstat output, ensure the
416
+ # port is stored as an integer
417
+ port = port.to_i
418
+
419
+ # for those "v4-but-listed-in-v6" entries, strip off the
420
+ # leading IPv6 value at the beginning
421
+ # example: ::ffff:10.0.2.15:9200
422
+ host.delete!('::ffff:') if host.start_with?('::ffff:')
423
+
424
+ # if there's an interface name in the local address, which is common for
425
+ # IPv6 listeners, strip that out too.
426
+ # example: fe80::a00:27ff:fe32:ed09%enp0s3
427
+ host = host.split('%').first
428
+
429
+ # if host is "*", replace with "0.0.0.0" to maintain backward compatibility with
430
+ # the netstat-provided data
431
+ host = '0.0.0.0' if host == '*'
432
+
433
+ # parse the process name from the processes information
434
+ process_match = parsed[6].match(/users:\(\(\"(\S+)\"/)
435
+ process = process_match.nil? ? nil : process_match[1]
436
+
437
+ # parse the PID from the processes information
438
+ pid_match = parsed[6].match(/pid=(\d+)/)
439
+ pid = pid_match.nil? ? nil : pid_match[1].to_i
440
+
441
+ {
442
+ 'port' => port,
443
+ 'address' => host,
444
+ 'protocol' => protocol,
445
+ 'process' => process,
446
+ 'pid' => pid,
447
+ }
448
+ end
365
449
  end
366
450
 
367
451
  # extracts information from sockstat
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ # author: Jonathan Morley
3
+
4
+ module Inspec::Resources
5
+ class XmlConfig < JsonConfig
6
+ name 'xml'
7
+ desc 'Use the xml InSpec resource to test configuration data in an XML file'
8
+ example "
9
+ describe xml('default.xml') do
10
+ its('key/sub_key') { should eq(['value']) }
11
+ end
12
+ "
13
+
14
+ def parse(content)
15
+ require 'rexml/document'
16
+ REXML::Document.new(content)
17
+ end
18
+
19
+ def value(key)
20
+ REXML::XPath.each(@params, key.first.to_s).map(&:text)
21
+ end
22
+
23
+ def to_s
24
+ "XML #{@path}"
25
+ end
26
+ end
27
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.34.1
4
+ version: 1.35.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-23 00:00:00.000000000 Z
11
+ date: 2017-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train
@@ -312,6 +312,7 @@ files:
312
312
  - docs/migration.md
313
313
  - docs/plugin_kitchen_inspec.md
314
314
  - docs/profiles.md
315
+ - docs/resources/aide_conf.md.erb
315
316
  - docs/resources/apache_conf.md.erb
316
317
  - docs/resources/apt.md.erb
317
318
  - docs/resources/audit_policy.md.erb
@@ -330,6 +331,7 @@ files:
330
331
  - docs/resources/docker_container.md.erb
331
332
  - docs/resources/docker_image.md.erb
332
333
  - docs/resources/etc_group.md.erb
334
+ - docs/resources/etc_hosts.md.erb
333
335
  - docs/resources/file.md.erb
334
336
  - docs/resources/gem.md.erb
335
337
  - docs/resources/group.md.erb
@@ -393,6 +395,7 @@ files:
393
395
  - docs/resources/wmi.md.erb
394
396
  - docs/resources/x509_certificate.md.erb
395
397
  - docs/resources/xinetd_conf.md.erb
398
+ - docs/resources/xml.md.erb
396
399
  - docs/resources/yaml.md.erb
397
400
  - docs/resources/yum.md.erb
398
401
  - docs/resources/zfs_dataset.md.erb
@@ -437,6 +440,10 @@ files:
437
440
  - examples/profile-attribute/README.md
438
441
  - examples/profile-attribute/controls/example.rb
439
442
  - examples/profile-attribute/inspec.yml
443
+ - examples/profile-sensitive/README.md
444
+ - examples/profile-sensitive/controls/sensitive-failures.rb
445
+ - examples/profile-sensitive/controls/sensitive.rb
446
+ - examples/profile-sensitive/inspec.yml
440
447
  - examples/profile/README.md
441
448
  - examples/profile/controls/example.rb
442
449
  - examples/profile/controls/gordon.rb
@@ -545,6 +552,7 @@ files:
545
552
  - lib/inspec/source_reader.rb
546
553
  - lib/inspec/version.rb
547
554
  - lib/matchers/matchers.rb
555
+ - lib/resources/aide_conf.rb
548
556
  - lib/resources/apache.rb
549
557
  - lib/resources/apache_conf.rb
550
558
  - lib/resources/apt.rb
@@ -563,6 +571,7 @@ files:
563
571
  - lib/resources/docker_container.rb
564
572
  - lib/resources/docker_image.rb
565
573
  - lib/resources/etc_group.rb
574
+ - lib/resources/etc_hosts.rb
566
575
  - lib/resources/file.rb
567
576
  - lib/resources/gem.rb
568
577
  - lib/resources/groups.rb
@@ -623,6 +632,7 @@ files:
623
632
  - lib/resources/wmi.rb
624
633
  - lib/resources/x509_certificate.rb
625
634
  - lib/resources/xinetd.rb
635
+ - lib/resources/xml.rb
626
636
  - lib/resources/yaml.rb
627
637
  - lib/resources/yum.rb
628
638
  - lib/resources/zfs_dataset.rb