inspec 0.34.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +323 -6
  3. data/Gemfile +10 -0
  4. data/README.md +1 -1
  5. data/Rakefile +4 -55
  6. data/docs/.gitignore +2 -0
  7. data/docs/README.md +40 -0
  8. data/docs/cli.md +342 -0
  9. data/docs/dsl_inspec.md +245 -0
  10. data/docs/dsl_resource.md +93 -0
  11. data/docs/inspec_and_friends.md +105 -0
  12. data/docs/matchers.md +136 -0
  13. data/docs/plugin_kitchen_inspec.html.md +55 -0
  14. data/docs/profiles.md +285 -0
  15. data/docs/resources/apache_conf.md.erb +75 -0
  16. data/docs/resources/apt.md.erb +84 -0
  17. data/docs/resources/audit_policy.md.erb +61 -0
  18. data/docs/resources/auditd_conf.md.erb +79 -0
  19. data/docs/resources/auditd_rules.md.erb +132 -0
  20. data/docs/resources/bash.md.erb +84 -0
  21. data/docs/resources/bond.md.erb +97 -0
  22. data/docs/resources/bridge.md.erb +67 -0
  23. data/docs/resources/bsd_service.md.erb +76 -0
  24. data/docs/resources/command.md.erb +151 -0
  25. data/docs/resources/csv.md.erb +62 -0
  26. data/docs/resources/directory.md.erb +43 -0
  27. data/docs/resources/etc_group.md.erb +116 -0
  28. data/docs/resources/etc_passwd.md.erb +155 -0
  29. data/docs/resources/etc_shadow.md.erb +149 -0
  30. data/docs/resources/file.md.erb +460 -0
  31. data/docs/resources/gem.md.erb +73 -0
  32. data/docs/resources/group.md.erb +74 -0
  33. data/docs/resources/grub_conf.md.erb +115 -0
  34. data/docs/resources/host.md.erb +85 -0
  35. data/docs/resources/iis_site.md.erb +142 -0
  36. data/docs/resources/inetd_conf.md.erb +99 -0
  37. data/docs/resources/ini.md.erb +69 -0
  38. data/docs/resources/interface.md.erb +66 -0
  39. data/docs/resources/iptables.md.erb +70 -0
  40. data/docs/resources/json.md.erb +76 -0
  41. data/docs/resources/kernel_module.md.erb +60 -0
  42. data/docs/resources/kernel_parameter.md.erb +72 -0
  43. data/docs/resources/launchd_service.md.erb +76 -0
  44. data/docs/resources/limits_conf.md.erb +80 -0
  45. data/docs/resources/login_def.md.erb +77 -0
  46. data/docs/resources/mount.md.erb +83 -0
  47. data/docs/resources/mysql_conf.md.erb +102 -0
  48. data/docs/resources/mysql_session.md.erb +63 -0
  49. data/docs/resources/npm.md.erb +75 -0
  50. data/docs/resources/ntp_conf.md.erb +76 -0
  51. data/docs/resources/oneget.md.erb +67 -0
  52. data/docs/resources/os.md.erb +154 -0
  53. data/docs/resources/os_env.md.erb +98 -0
  54. data/docs/resources/package.md.erb +115 -0
  55. data/docs/resources/parse_config.md.erb +122 -0
  56. data/docs/resources/parse_config_file.md.erb +143 -0
  57. data/docs/resources/pip.md.erb +74 -0
  58. data/docs/resources/port.md.erb +150 -0
  59. data/docs/resources/postgres_conf.md.erb +90 -0
  60. data/docs/resources/postgres_session.md.erb +75 -0
  61. data/docs/resources/powershell.md.erb +116 -0
  62. data/docs/resources/process.md.erb +73 -0
  63. data/docs/resources/registry_key.md.erb +149 -0
  64. data/docs/resources/runit_service.md.erb +76 -0
  65. data/docs/resources/security_policy.md.erb +61 -0
  66. data/docs/resources/service.md.erb +135 -0
  67. data/docs/resources/ssh_config.md.erb +94 -0
  68. data/docs/resources/sshd_config.md.erb +97 -0
  69. data/docs/resources/ssl.md.erb +133 -0
  70. data/docs/resources/sys_info.md.erb +55 -0
  71. data/docs/resources/systemd_service.md.erb +76 -0
  72. data/docs/resources/sysv_service.md.erb +76 -0
  73. data/docs/resources/upstart_service.md.erb +76 -0
  74. data/docs/resources/user.md.erb +154 -0
  75. data/docs/resources/users.md.erb +140 -0
  76. data/docs/resources/vbscript.md.erb +69 -0
  77. data/docs/resources/windows_feature.md.erb +61 -0
  78. data/docs/resources/wmi.md.erb +95 -0
  79. data/docs/resources/xinetd_conf.md.erb +170 -0
  80. data/docs/resources/yaml.md.erb +69 -0
  81. data/docs/resources/yum.md.erb +103 -0
  82. data/docs/ruby_usage.md +154 -0
  83. data/docs/shared/matcher_be.md.erb +1 -0
  84. data/docs/shared/matcher_cmp.md.erb +45 -0
  85. data/docs/shared/matcher_eq.md.erb +3 -0
  86. data/docs/shared/matcher_include.md.erb +1 -0
  87. data/docs/shared/matcher_match.md.erb +1 -0
  88. data/docs/shell.md +150 -0
  89. data/examples/meta-profile/inspec.yml +2 -4
  90. data/inspec.gemspec +1 -1
  91. data/lib/bundles/inspec-compliance/api.rb +28 -18
  92. data/lib/bundles/inspec-compliance/cli.rb +19 -27
  93. data/lib/bundles/inspec-compliance/target.rb +15 -2
  94. data/lib/fetchers/git.rb +4 -0
  95. data/lib/fetchers/local.rb +16 -1
  96. data/lib/fetchers/mock.rb +4 -0
  97. data/lib/fetchers/url.rb +46 -20
  98. data/lib/inspec/base_cli.rb +14 -0
  99. data/lib/inspec/cached_fetcher.rb +67 -0
  100. data/lib/inspec/cli.rb +22 -8
  101. data/lib/inspec/control_eval_context.rb +12 -2
  102. data/lib/inspec/dependencies/{vendor_index.rb → cache.rb} +5 -4
  103. data/lib/inspec/dependencies/dependency_set.rb +8 -14
  104. data/lib/inspec/dependencies/lockfile.rb +5 -13
  105. data/lib/inspec/dependencies/requirement.rb +31 -41
  106. data/lib/inspec/dependencies/resolver.rb +3 -3
  107. data/lib/inspec/dsl.rb +16 -4
  108. data/lib/inspec/errors.rb +1 -0
  109. data/lib/inspec/fetcher.rb +25 -1
  110. data/lib/inspec/objects/each_loop.rb +5 -2
  111. data/lib/inspec/objects/test.rb +8 -2
  112. data/lib/inspec/plugins/fetcher.rb +13 -12
  113. data/lib/inspec/plugins/resource.rb +3 -0
  114. data/lib/inspec/profile.rb +62 -26
  115. data/lib/inspec/profile_context.rb +44 -8
  116. data/lib/inspec/resource.rb +18 -3
  117. data/lib/inspec/rspec_json_formatter.rb +91 -40
  118. data/lib/inspec/rule.rb +45 -2
  119. data/lib/inspec/runner.rb +51 -39
  120. data/lib/inspec/shell.rb +5 -16
  121. data/lib/inspec/version.rb +1 -1
  122. data/lib/resources/apache_conf.rb +1 -1
  123. data/lib/resources/gem.rb +1 -0
  124. data/lib/resources/groups.rb +190 -0
  125. data/lib/resources/oneget.rb +1 -0
  126. data/lib/resources/os.rb +1 -1
  127. data/lib/resources/package.rb +3 -1
  128. data/lib/resources/parse_config.rb +18 -1
  129. data/lib/resources/pip.rb +1 -1
  130. data/lib/resources/security_policy.rb +54 -0
  131. data/lib/resources/ssl.rb +9 -11
  132. data/lib/resources/sys_info.rb +1 -1
  133. data/lib/resources/users.rb +81 -64
  134. metadata +91 -17
  135. data/docs/cli.rst +0 -442
  136. data/docs/dsl_inspec.rst +0 -259
  137. data/docs/dsl_resource.rst +0 -90
  138. data/docs/inspec_and_friends.rst +0 -85
  139. data/docs/matchers.rst +0 -137
  140. data/docs/profiles.rst +0 -169
  141. data/docs/readme.rst +0 -105
  142. data/docs/resources.rst +0 -4836
  143. data/docs/ruby_usage.rst +0 -145
  144. data/docs/shell.rst +0 -130
  145. data/docs/template.rst +0 -51
  146. data/lib/resources/group.rb +0 -137
@@ -0,0 +1,154 @@
1
+ # Using Ruby in InSpec
2
+
3
+ The InSpec DSL is a Ruby based DSL for writing audit controls, which
4
+ includes audit resources that you can invoke. Core and custom resources
5
+ are written as regular Ruby classes which inherit from
6
+ `Inspec.resource`.
7
+
8
+ Assuming we have a JSON file like this on the node to be tested:
9
+
10
+ ```json
11
+ {
12
+ "keys":[
13
+ {"username":"john", "key":"/opt/keys/johnd.key"},
14
+ {"username":"jane", "key":"/opt/keys/janed.key"},
15
+ {"username":"sunny ", "key":"/opt/keys/sunnym.key"}
16
+ ]
17
+ }
18
+ ```
19
+
20
+ The following example shows how you can use pure Ruby code(variables,
21
+ loops, conditionals, regular expressions, etc) to run a few tests
22
+ against the above JSON file:
23
+
24
+ ```ruby
25
+ control 'check-interns' do
26
+ # use the json inspec resource to get the file
27
+ json_obj = json('/opt/keys/interns.json')
28
+ describe json_obj do
29
+ its('keys') { should_not eq nil }
30
+ end
31
+ if json_obj['keys']
32
+ # loop over the keys array
33
+ json_obj['keys'].each do |intern|
34
+ username = intern['username'].strip
35
+ # check for white spaces chars in usernames
36
+ describe username do
37
+ it { should_not match(/\s/) }
38
+ end
39
+ # check key file owners and permissions
40
+ describe file(intern['key']) do
41
+ it { should be_owned_by username }
42
+ its('mode') { should cmp '0600' }
43
+ end
44
+ end
45
+ end
46
+ end
47
+ ```
48
+
49
+ ## Execution
50
+
51
+ It's important to understand that Ruby code used in custom resources and
52
+ controls DSL is executed on the system that runs InSpec. This allows
53
+ InSpec to work without Ruby and rubygems being required on remote
54
+ targets(servers or containers).
55
+
56
+ For example, using `` `ls ``\` or `system('ls')` will result in the `ls`
57
+ command being run locally and not on the target(remote) system. In order
58
+ to process the output of `ls` executed on the target system, use
59
+ `inspec.command('ls')` or `inspec.powershell('ls')`
60
+
61
+ Similarly, use `inspec.file(PATH)` to access files or directories from
62
+ remote systems in your tests or custom resources.
63
+
64
+
65
+ ## Using rubygems
66
+
67
+ Ruby gems are self-contained programs and libraries. If you create a custom
68
+ resource please vendor gems into the library. This ensures that all resources
69
+ are self-contained and complete and don't need any resolution at runtime. We
70
+ vendor resources and requirements through dependency resolution, which is
71
+ independent of programming languages and their resolver mechanisms.
72
+
73
+ ## Interactive Debugging with Pry
74
+
75
+ Here's a sample InSpec control that users Ruby variables to instantiate
76
+ an InSpec resource once and use the content in multipLe tests.
77
+
78
+ ```ruby
79
+ control 'check-perl' do
80
+ impact 0.3
81
+ title 'Check perl compiled options and permissions'
82
+ perl_out = command('perl -V')
83
+ #require 'pry'; binding.pry;
84
+ describe perl_out do
85
+ its('exit_status') { should eq 0 }
86
+ its('stdout') { should match (/USE_64_BIT_ALL/) }
87
+ its('stdout') { should match (/useposix=true/) }
88
+ its('stdout') { should match (/-fstack-protector/) }
89
+ end
90
+
91
+ # extract an array of include directories
92
+ perl_inc = perl_out.stdout.partition('@INC:').last.strip.split("\n")
93
+ # ensure include directories are only writable by 'owner'
94
+ perl_inc.each do |path|
95
+ describe directory(path.strip) do
96
+ it { should_not be_writable.by('group') }
97
+ it { should_not be_writable.by('other') }
98
+ end
99
+ end
100
+ end
101
+ ```
102
+
103
+ An **advanced** but very useful Ruby tip. In the previous example, I
104
+ commented out the `require 'pry'; binding.pry;` line. If you remove the
105
+ `#` prefix and run the control, the execution will stop at that line and
106
+ give you a `pry` shell. Use that to troubleshoot, print variables, see
107
+ methods available, etc. For the above example:
108
+
109
+ ```ruby
110
+ [1] pry> perl_out.exit_status
111
+ => 0
112
+ [2] pry> perl_out.stderr
113
+ => ""
114
+ [3] pry> ls perl_out
115
+ Inspec::Plugins::Resource#methods: inspect
116
+ Inspec::Resources::Cmd#methods: command exist? exit_status result stderr stdout to_s
117
+ Inspec::Plugins::ResourceCommon#methods: resource_skipped skip_resource
118
+ Inspec::Resource::Registry::Command#methods: inspec
119
+ instance variables: @__backend_runner__ @__resource_name__ @command @result
120
+ [4] pry> perl_out.stdout.partition('@INC:').last.strip.split("\n")
121
+ => ["/Library/Perl/5.18/darwin-thread-multi-2level",
122
+ " /Library/Perl/5.18",
123
+ ...REDACTED...
124
+ [5] pry> exit # or abort
125
+ ```
126
+
127
+ You can use `pry` inside both the controls DSL and resources. Similarly,
128
+ for dev and test, you can use `inspec shell` which is based on `pry`,
129
+ for example:
130
+
131
+ ```ruby
132
+ $ inspec shell
133
+ Welcome to the interactive InSpec Shell
134
+ To find out how to use it, type: help
135
+
136
+ inspec> command('ls /home/gordon/git/inspec/docs').stdout
137
+ => "ctl_inspec.rst\ndsl_inspec.rst\ndsl_resource.rst\n"
138
+ inspec> command('ls').stdout.split("\n")
139
+ => ["ctl_inspec.rst", "dsl_inspec.rst", "dsl_resource.rst"]
140
+
141
+ inspec> help command
142
+ Name: command
143
+
144
+ Description:
145
+ Use the command InSpec audit resource to test an arbitrary command that is run on the system.
146
+
147
+ Example:
148
+ describe command('ls -al /') do
149
+ it { should exist }
150
+ its('stdout') { should match /bin/ }
151
+ its('stderr') { should eq '' }
152
+ its('exit_status') { should eq 0 }
153
+ end
154
+ ```
@@ -0,0 +1 @@
1
+ Use the `be` matcher to use a comparison operator---`=` (equal to), `>` (greater than), `<` (less than), `>=` (greater than or equal to), and `<=` (less than or equal to)---to compare two values: `its('value') { should be >= value }`, `its('value') { should be < value }`, and so on.
@@ -0,0 +1,45 @@
1
+ Use the `cmp` matcher compare two values, such as comparing strings to numbers, comparing a single value to an array of values, comparing an array of strings to a regular expression, improving the printing of octal values, and comparing while ignoring case sensitivity.
2
+
3
+ Compare a single value to an array:
4
+
5
+ describe some_resource do
6
+ its('users') { should cmp 'root' }
7
+ its('users') { should cmp ['root'] }
8
+ end
9
+
10
+ Compare strings and regular expressions:
11
+
12
+ describe some_resource do
13
+ its('setting') { should cmp /raw/i }
14
+ end
15
+
16
+ Compare strings and numbers:
17
+
18
+ describe some_resource do
19
+ its('setting') { should eq '2' }
20
+ end
21
+
22
+ vs:
23
+
24
+ describe some_resource do
25
+ its('setting') { should cmp '2' }
26
+ its('setting') { should cmp 2 }
27
+ end
28
+
29
+ Ignoring case sensitivity:
30
+
31
+ .. code-block:: ruby
32
+
33
+ describe some_resource do
34
+ its('setting') { should cmp 'raw' }
35
+ its('setting') { should cmp 'RAW' }
36
+ end
37
+
38
+ Printing octal values:
39
+
40
+ describe some_resource('/proc/cpuinfo') do
41
+ its('mode') { should cmp '0345' }
42
+ end
43
+
44
+ expected: 0345
45
+ got: 0444
@@ -0,0 +1,3 @@
1
+ Use the `eq` matcher to test the equality of two values: `its('Port') { should eq '22' }`.
2
+
3
+ Using `its('Port') { should eq 22 }` will fail because `22` is not a string value! Use the `cmp` matcher for less restrictive value comparisons.
@@ -0,0 +1 @@
1
+ Use the `include` matcher to verify that a string value is included in a list: `its('list') { should include 'string' }`.
@@ -0,0 +1 @@
1
+ Use the `match` matcher to check if a string matches a regular expression: `its('string') { should_not match /regex/ }`.
data/docs/shell.md ADDED
@@ -0,0 +1,150 @@
1
+ ---
2
+ title: InSpec Shell
3
+ ---
4
+
5
+ # InSpec Shell
6
+
7
+ The InSpec interactive shell is a pry based REPL that can be used to
8
+ quickly run InSpec controls and tests without having to write it to a
9
+ file. Its functionality is similar to `chef shell` - it provides a way
10
+ to exercise the InSpec DSL, its resources, tests and plugins without
11
+ having to create a profile or write a test file. See
12
+ [http://pryrepl.org/](http://pryrepl.org/) for an introduction to what pry is and what it can
13
+ do.
14
+
15
+ ## Launching the shell
16
+
17
+ If you are using InSpec from a platform-specific package (rpm, msi,
18
+ etc.) or from a chef prepared shell in ChefDK, you can directly launch
19
+ InSpec shell against your local machine using the following. See
20
+ <https://docs.chef.io/install_dk.html#set-system-ruby> for details.
21
+
22
+ ```bash
23
+ $ inspec shell
24
+ $ inspec help shell # This will describe inspec shell usage
25
+ ```
26
+
27
+ If you wish to connect to a remote machine (called a target within
28
+ InSpec), you can use the `-t` flag. We support connecting using ssh,
29
+ WinRm and docker. If no target is provided, we implicitly support the
30
+ "local" target - i.e. tests running on the current machine running
31
+ InSpec. For an ssh connection, use `-i` for specifying ssh key files,
32
+ and the `--sudo*` commands for requesting a privelege escalation after
33
+ logging in. For a WinRM connection, use `--path` to change the login
34
+ path, `--ssl` to use SSL for transport layer encryption.
35
+
36
+ ```bash
37
+ $ inspec shell -t ssh://root@192.168.64.2:11022 # Login to remote machine using ssh as root.
38
+ $ inspec shell -t ssh://user@hostname:1234 -i /path/to/user_key # Login to hostname on port 1234 as user using given ssh key.
39
+ $ inspec shell -t winrm://UserName:Password@windowsmachine:1234 # Login to windowsmachine over WinRM as UserName.
40
+ $ inspec shell -t docker://container_id # Login to a docker container.
41
+ ```
42
+
43
+ ## Using Ruby in InSpec shell
44
+
45
+ Since InSpec shell is pry based, you may treat the shell as an
46
+ interactive Ruby session. You may write Ruby expressions and evaluate
47
+ them. Source high-lighting, automatic indentation and command history
48
+ (using the up and down arrow keys) are available to make your experience
49
+ more delightful. You can exit the shell using `exit`.
50
+
51
+ ```bash
52
+ $ inspec shell
53
+ Welcome to the interactive InSpec Shell
54
+ To find out how to use it, type: help
55
+
56
+ inspec> 1 + 2
57
+ => 3
58
+ inspec> exit
59
+ ```
60
+
61
+ ## Using InSpec DSL in InSpec shell
62
+
63
+ InSpec shell will automatically evaluate the result of every command as
64
+ if it were a test file. If you type in a Ruby command that is not an
65
+ InSpec control or test, the shell will evaluate it as if it were a
66
+ regular ruby command.
67
+
68
+ Bare InSpec resources are instantiated and their help text is presented.
69
+ You may also access the resource contents or other matchers that they
70
+ define. Run `help <resource>` to get more help on using a particular
71
+ resource or see the InSpec resources documentation online.
72
+
73
+ ```bash
74
+ $ inspec shell
75
+ Welcome to the interactive InSpec Shell
76
+ To find out how to use it, type: help
77
+
78
+ inspec> file('/Users/ksubramanian').directory?
79
+ => true
80
+ inspec> os_env('HOME')
81
+ => Environment variable HOME
82
+ inspec> os_env('HOME').content
83
+ => /Users/ksubramanian
84
+ inspec> exit
85
+ ```
86
+
87
+ InSpec tests are immediately executed.
88
+
89
+ ```bash
90
+ inspec> describe file('/Users') # Empty test.
91
+ Summary: 0 successful, 0 failures, 0 skipped
92
+ inspec> describe file('/Users') do # Test with one check.
93
+ inspec> it { should exist }
94
+ inspec> end
95
+ ✔ File /Users should exist
96
+
97
+ Summary: 1 successful, 0 failures, 0 skipped
98
+ ```
99
+
100
+ All tests in a control are immediately executed as well. If a control is
101
+ redefined in the shell, the old control's tests are destroyed and
102
+ replaced with the redefinition and the control is re-run.
103
+
104
+ ```bash
105
+ inspec> control 'my_control' do
106
+ inspec> describe os_env('HOME') do
107
+ inspec> its('content') { should eq '/Users/ksubramanian' }
108
+ inspec> end
109
+ inspec> end
110
+ ✔ my_control: Environment variable HOME content should eq "/Users/ksubramanian"
111
+
112
+ Summary: 1 successful, 0 failures, 0 skipped
113
+ ```
114
+
115
+ Syntax errors are illegal tests are also detected and reported.
116
+
117
+ ```bash
118
+ inspec> control 'foo' do
119
+ inspec> thisisnonsense
120
+ inspec> end
121
+ NameError: undefined local variable or method `thisisnonsense' for #<#<Class:0x007fd63b571f98>:0x007fd639825cc8>
122
+ from /usr/local/lib/ruby/gems/2.3.0/gems/rspec-expectations-3.5.0/lib/rspec/matchers.rb:967:in `method_missing'
123
+ inspec> control 'foo' do
124
+ inspec> describe file('wut') do
125
+ inspec> its('thismakesnosense') { should cmp 'fail' }
126
+ inspec> end
127
+ inspec> end
128
+ ✖ foo: File wut thismakesnosense (undefined method `thismakesnosense' for File wut:Inspec::Resource::Registry::File)
129
+
130
+ Summary: 0 successful, 1 failures, 0 skipped
131
+ ```
132
+
133
+ ## Running a single InSpec command
134
+
135
+ If you wish to run a single InSpec command and fetch its results, you
136
+ may use the `-c` flag. This is similar to using `bash -c`.
137
+
138
+ ```bash
139
+ $ inspec shell -c 'describe file("/Users/ksubramanian") do it { should exist } end'}
140
+ Target: local://
141
+
142
+ ✔ File /Users/ksubramanian should exist
143
+
144
+ Summary: 1 successful, 0 failures, 0 skipped
145
+ ```
146
+
147
+ ```bash
148
+ $ inspec shell --format json -c 'describe file("/Users/ksubramanian") do it { should exist } end'
149
+ {"version":"0.30.0","profiles":{"":{"supports":[],"controls":{"(generated from in_memory.rb:1 5aab65c33fb1f133d9244017958eef64)":{"title":null,"desc":null,"impact":0.5,"refs":[],"tags":{},"code":" rule = rule_class.new(id, profile_id, {}) do\n res = describe(*args, &block)\n end\n","source_location":{"ref":"/Users/ksubramanian/repo/chef/inspec/lib/inspec/profile_context.rb","line":184},"results":[{"status":"passed","code_desc":"File /Users/ksubramanian should exist","run_time":0.000747,"start_time":"2016-08-16 11:41:40 -0400"}]}},"groups":{"in_memory.rb":{"title":null,"controls":["(generated from in_memory.rb:1 5aab65c33fb1f133d9244017958eef64)"]}},"attributes":[]}},"other_checks":[],"summary":{"duration":0.001078,"example_count":1,"failure_count":0,"skip_count":0}}}
150
+ ```
@@ -7,12 +7,10 @@ license: Apache 2
7
7
  summary: InSpec Profile that is only consuming dependencies
8
8
  version: 0.2.0
9
9
  depends:
10
- - name: ssh-hardening
11
- supermarket: hardening/ssh-hardening
10
+ - name: hardening/ssh-hardening # defaults to supermarket
12
11
  - name: os-hardening
13
12
  url: https://github.com/dev-sec/tests-os-hardening/archive/master.zip
14
- - name: ssl-benchmark
15
- git: https://github.com/dev-sec/ssl-benchmark.git
13
+ - git: https://github.com/dev-sec/ssl-benchmark.git
16
14
  - name: windows-patch-benchmark
17
15
  git: https://github.com/chris-rock/windows-patch-benchmark.git
18
16
  - name: linux
data/inspec.gemspec CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
25
  spec.require_paths = ['lib']
26
26
 
27
- spec.add_dependency 'train', '>=0.19.0', '<1.0'
27
+ spec.add_dependency 'train', '>=0.19.1', '<1.0'
28
28
  spec.add_dependency 'thor', '~> 0.19'
29
29
  spec.add_dependency 'json', '>= 1.8', '< 3.0'
30
30
  spec.add_dependency 'rainbow', '~> 2'
@@ -9,18 +9,6 @@ module Compliance
9
9
  # API Implementation does not hold any state by itself,
10
10
  # everything will be stored in local Configuration store
11
11
  class API
12
- # login method for pre-1.0 compliance server
13
- def self.legacy_login_post(url, username, password, insecure)
14
- # form request
15
- # TODO: reuse post function
16
- uri = URI.parse(url)
17
- req = Net::HTTP::Post.new(uri.path)
18
- req.basic_auth(username, password)
19
- req.form_data={}
20
-
21
- send_request(uri, req, insecure)
22
- end
23
-
24
12
  # return all compliance profiles available for the user
25
13
  def self.profiles(config)
26
14
  url = "#{config['server']}/user/compliance"
@@ -86,19 +74,19 @@ Please login using `inspec compliance login https://compliance.test --user admin
86
74
  [res.is_a?(Net::HTTPSuccess), res.body]
87
75
  end
88
76
 
89
- def self.post_refresh_token(url, token, insecure)
77
+ # Use username and refresh_toke to get an API access token
78
+ def self.get_token_via_refresh_token(url, refresh_token, insecure)
90
79
  uri = URI.parse("#{url}/login")
91
80
  req = Net::HTTP::Post.new(uri.path)
92
- # req['Authorization'] = "Bearer #{token}"
93
- req.body = { token: token }.to_json
81
+ req.body = { token: refresh_token }.to_json
94
82
  access_token = nil
95
83
  response = Compliance::HTTP.send_request(uri, req, insecure)
96
84
  data = response.body
97
- if !data.nil?
85
+ if response.code == '200'
98
86
  begin
99
87
  tokendata = JSON.parse(data)
100
88
  access_token = tokendata['access_token']
101
- msg = 'Successfully fetched access token'
89
+ msg = 'Successfully fetched API access token'
102
90
  success = true
103
91
  rescue JSON::ParserError => e
104
92
  success = false
@@ -106,7 +94,29 @@ Please login using `inspec compliance login https://compliance.test --user admin
106
94
  end
107
95
  else
108
96
  success = false
109
- msg = 'Invalid refresh_token'
97
+ msg = "Failed to authenticate to #{url} \n\
98
+ Response code: #{response.code}\n Body: #{response.body}"
99
+ end
100
+
101
+ [success, msg, access_token]
102
+ end
103
+
104
+ # Use username and password to get an API access token
105
+ def self.get_token_via_password(url, username, password, insecure)
106
+ uri = URI.parse("#{url}/login")
107
+ req = Net::HTTP::Post.new(uri.path)
108
+ req.body = { userid: username, password: password }.to_json
109
+ access_token = nil
110
+ response = Compliance::HTTP.send_request(uri, req, insecure)
111
+ data = response.body
112
+ if response.code == '200'
113
+ access_token = data
114
+ msg = 'Successfully fetched an API access token valid for 12 hours'
115
+ success = true
116
+ else
117
+ success = false
118
+ msg = "Failed to authenticate to #{url} \n\
119
+ Response code: #{response.code}\n Body: #{response.body}"
110
120
  end
111
121
 
112
122
  [success, msg, access_token]
@@ -22,9 +22,9 @@ module Compliance
22
22
  option :insecure, aliases: :k, type: :boolean,
23
23
  desc: 'Explicitly allows InSpec to perform "insecure" SSL connections and transfers'
24
24
  option :user, type: :string, required: false,
25
- desc: 'Chef Compliance Username (for legacy auth)'
25
+ desc: 'Chef Compliance Username'
26
26
  option :password, type: :string, required: false,
27
- desc: 'Chef Compliance Password (for legacy auth)'
27
+ desc: 'Chef Compliance Password'
28
28
  option :apipath, type: :string, default: '/api',
29
29
  desc: 'Set the path to the API, defaults to /api'
30
30
  option :token, type: :string, required: false,
@@ -38,7 +38,7 @@ module Compliance
38
38
  url = options['server'] + options['apipath']
39
39
  if !options['user'].nil? && !options['password'].nil?
40
40
  # username / password
41
- _success, msg = login_legacy(url, options['user'], options['password'], options['insecure'])
41
+ _success, msg = login_username_password(url, options['user'], options['password'], options['insecure'])
42
42
  elsif !options['user'].nil? && !options['token'].nil?
43
43
  # access token
44
44
  _success, msg = store_access_token(url, options['user'], options['token'], options['insecure'])
@@ -199,7 +199,7 @@ module Compliance
199
199
  private
200
200
 
201
201
  def login_refreshtoken(url, options)
202
- success, msg, access_token = Compliance::API.post_refresh_token(url, options['refresh_token'], options['insecure'])
202
+ success, msg, access_token = Compliance::API.get_token_via_refresh_token(url, options['refresh_token'], options['insecure'])
203
203
  if success
204
204
  config = Compliance::Configuration.new
205
205
  config['server'] = url
@@ -212,25 +212,17 @@ module Compliance
212
212
  [success, msg]
213
213
  end
214
214
 
215
- def login_legacy(url, username, password, insecure)
215
+ def login_username_password(url, username, password, insecure)
216
216
  config = Compliance::Configuration.new
217
- success, data = Compliance::API.legacy_login_post(url+'/oauth/token', username, password, insecure)
218
- if !data.nil?
219
- tokendata = JSON.parse(data)
220
- if tokendata['access_token']
221
- config['server'] = url
222
- config['user'] = username
223
- config['token'] = tokendata['access_token']
224
- config['insecure'] = insecure
225
- config['version'] = Compliance::API.version(url, insecure)
226
- config.store
227
- success = true
228
- msg = 'Successfully authenticated'
229
- else
230
- msg = 'Response does not include a token'
231
- end
232
- else
233
- msg = "Authentication failed for Server: #{url}"
217
+ success, msg, api_token = Compliance::API.get_token_via_password(url, username, password, insecure)
218
+ if success
219
+ config['server'] = url
220
+ config['user'] = username
221
+ config['token'] = api_token
222
+ config['insecure'] = insecure
223
+ config['version'] = Compliance::API.version(url, insecure)
224
+ config.store
225
+ success = true
234
226
  end
235
227
  [success, msg]
236
228
  end
@@ -245,10 +237,10 @@ module Compliance
245
237
  config['version'] = Compliance::API.version(url, insecure)
246
238
  config.store
247
239
 
248
- [true, 'access token stored']
240
+ [true, 'API access token stored']
249
241
  end
250
242
 
251
- # saves the a user refresh token supplied by the user
243
+ # saves a refresh token supplied by the user
252
244
  def store_refresh_token(url, refresh_token, verify, user, insecure)
253
245
  config = Compliance::Configuration.new
254
246
  config['server'] = url
@@ -260,13 +252,13 @@ module Compliance
260
252
  if !verify
261
253
  config.store
262
254
  success = true
263
- msg = 'refresh token stored'
255
+ msg = 'API refresh token stored'
264
256
  else
265
- success, msg, access_token = Compliance::API.post_refresh_token(url, refresh_token, insecure)
257
+ success, msg, access_token = Compliance::API.get_token_via_refresh_token(url, refresh_token, insecure)
266
258
  if success
267
259
  config['token'] = access_token
268
260
  config.store
269
- msg = 'token verified and stored'
261
+ msg = 'API access token verified and stored'
270
262
  end
271
263
  end
272
264
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  require 'uri'
6
6
  require 'inspec/fetcher'
7
+ require 'inspec/errors'
7
8
 
8
9
  # InSpec Target Helper for Chef Compliance
9
10
  # reuses UrlHelper, but it knows the target server and the access token already
@@ -24,11 +25,23 @@ module Compliance
24
25
 
25
26
  # check if we have a compliance token
26
27
  config = Compliance::Configuration.new
27
- return nil if config['token'].nil?
28
+ if config['token'].nil?
29
+ fail Inspec::FetcherFailure, <<EOF
30
+
31
+ Cannot fetch #{uri} because your compliance token has not been
32
+ configured.
33
+
34
+ Please login using
35
+
36
+ inspec compliance login https://your_compliance_server --user admin --insecure --token 'PASTE TOKEN HERE'
37
+ EOF
38
+ end
28
39
 
29
40
  # verifies that the target e.g base/ssh exists
30
41
  profile = uri.host + uri.path
31
- Compliance::API.exist?(config, profile)
42
+ if !Compliance::API.exist?(config, profile)
43
+ fail Inpsec::FetcherFailure, "The compliance profile #{profile} was not found on the configured compliance server"
44
+ end
32
45
  new(target_url(profile, config), config)
33
46
  rescue URI::Error => _e
34
47
  nil
data/lib/fetchers/git.rb CHANGED
@@ -56,6 +56,10 @@ module Fetchers
56
56
  @repo_directory
57
57
  end
58
58
 
59
+ def cache_key
60
+ resolved_ref
61
+ end
62
+
59
63
  def archive_path
60
64
  @repo_directory
61
65
  end
@@ -55,8 +55,23 @@ module Fetchers
55
55
  @target
56
56
  end
57
57
 
58
+ def writable?
59
+ File.directory?(@target)
60
+ end
61
+
62
+ def cache_key
63
+ sha256.to_s
64
+ end
65
+
66
+ def sha256
67
+ return nil if File.directory?(@target)
68
+ @archive_shasum ||= Digest::SHA256.hexdigest File.read(@target)
69
+ end
70
+
58
71
  def resolved_source
59
- { path: @target }
72
+ h = { path: @target }
73
+ h[:sha256] = sha256 if sha256
74
+ h
60
75
  end
61
76
  end
62
77
  end
data/lib/fetchers/mock.rb CHANGED
@@ -27,5 +27,9 @@ module Fetchers
27
27
  def resolved_source
28
28
  { mock_fetcher: true }
29
29
  end
30
+
31
+ def cache_key
32
+ ''
33
+ end
30
34
  end
31
35
  end