Merge branch 'main' into inspec-6

This commit is contained in:
Nikita Mathur 2022-08-22 14:55:01 +05:30
commit 1c6bfa8f24
154 changed files with 4575 additions and 582 deletions

View file

@ -1,37 +1,59 @@
# Change Log # Change Log
<!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ --> <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
<!-- latest_release 5.18.3 --> <!-- latest_release 5.20.1 -->
## [v5.18.3](https://github.com/inspec/inspec/tree/v5.18.3) (2022-06-10) ## [v5.20.1](https://github.com/inspec/inspec/tree/v5.20.1) (2022-08-04)
#### Merged Pull Requests #### Merged Pull Requests
- Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras)) - Fix the dependabot adding ffi (1.15.5-x64-unknown) to omnibus bump [#6213](https://github.com/inspec/inspec/pull/6213) ([Vasu1105](https://github.com/Vasu1105))
<!-- latest_release --> <!-- latest_release -->
<!-- release_rollup since=5.17.4 --> <!-- release_rollup since=5.18.14 -->
### Changes since 5.17.4 release ### Changes since 5.18.14 release
#### Merged Pull Requests #### Merged Pull Requests
- Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras)) <!-- 5.18.3 --> - Fix the dependabot adding ffi (1.15.5-x64-unknown) to omnibus bump [#6213](https://github.com/inspec/inspec/pull/6213) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.20.1 -->
- Fixed Lint/DuplicateMethods: Method Inspec::Resources::Service#resource_id is defined at both [#6132](https://github.com/inspec/inspec/pull/6132) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.18.2 --> - Adds podman resources. [#6183](https://github.com/inspec/inspec/pull/6183) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.20.0 -->
- CFINSPEC-291: Fix `processes` resource to consider processes without `path` on Windows [#6100](https://github.com/inspec/inspec/pull/6100) ([ahasunos](https://github.com/ahasunos)) <!-- 5.18.1 --> - CFINSPEC-237 Added enhanced_outcomes option [#6145](https://github.com/inspec/inspec/pull/6145) ([Nik08](https://github.com/Nik08)) <!-- 5.19.0 -->
- CFINSPEC-167: Profile Signing Rollup [#5995](https://github.com/inspec/inspec/pull/5995) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.18.0 --> - CFINSPEC-400 Fix for verify pipeline failure [#6218](https://github.com/inspec/inspec/pull/6218) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.18.17 -->
- Bump berkshelf from 8.0.0 to 8.0.2 in /omnibus [#6114](https://github.com/inspec/inspec/pull/6114) ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 5.17.18 --> - Docs spellcheck [#6214](https://github.com/inspec/inspec/pull/6214) ([IanMadd](https://github.com/IanMadd)) <!-- 5.18.16 -->
- CFINSPEC-262 - Handle resource_id in error situation [#6119](https://github.com/inspec/inspec/pull/6119) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.17.17 --> - Trivial README change to trigger new omnibus build [#6203](https://github.com/inspec/inspec/pull/6203) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 5.18.15 -->
- Handle resource_id in error situations [#6118](https://github.com/inspec/inspec/pull/6118) ([ahasunos](https://github.com/ahasunos)) <!-- 5.17.16 -->
- CFINSPEC-273 Adds resource_id group 12 [#6112](https://github.com/inspec/inspec/pull/6112) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.17.15 -->
- CFINSPEC-270 Adds resource_id group9 [#6111](https://github.com/inspec/inspec/pull/6111) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.17.14 -->
- CFINSPEC-265 Group 4 - Added resource_id in resources [#6109](https://github.com/inspec/inspec/pull/6109) ([Nik08](https://github.com/Nik08)) <!-- 5.17.13 -->
- CFINSPEC-269 Adds resource_id group 8 [#6107](https://github.com/inspec/inspec/pull/6107) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.17.12 -->
- CFINSPEC-268 Adds resource_id group 7 [#6105](https://github.com/inspec/inspec/pull/6105) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.17.11 -->
- Fix the key duplication error warning in the mock_loader.rb [#6120](https://github.com/inspec/inspec/pull/6120) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.17.10 -->
- CFINSPEC-266: resource_ids group 5 [#6103](https://github.com/inspec/inspec/pull/6103) ([ahasunos](https://github.com/ahasunos)) <!-- 5.17.9 -->
- CFINSPEC-262 Adds resource_id group 1 [#6102](https://github.com/inspec/inspec/pull/6102) ([Vasu1105](https://github.com/Vasu1105)) <!-- 5.17.8 -->
- CFINSPEC-267: resource_ids group 6 [#6101](https://github.com/inspec/inspec/pull/6101) ([ahasunos](https://github.com/ahasunos)) <!-- 5.17.7 -->
- CFINSPEC-95: Enhance `x509_certificate` resource [#6041](https://github.com/inspec/inspec/pull/6041) ([ahasunos](https://github.com/ahasunos)) <!-- 5.17.6 -->
- Bump rack from 2.2.3 to 2.2.3.1 in /omnibus [#6098](https://github.com/inspec/inspec/pull/6098) ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 5.17.5 -->
<!-- release_rollup --> <!-- release_rollup -->
<!-- latest_stable_release --> <!-- latest_stable_release -->
## [v5.18.14](https://github.com/inspec/inspec/tree/v5.18.14) (2022-07-13)
#### Merged Pull Requests
- Bump rack from 2.2.3 to 2.2.3.1 in /omnibus [#6098](https://github.com/inspec/inspec/pull/6098) ([dependabot[bot]](https://github.com/dependabot[bot]))
- CFINSPEC-95: Enhance `x509_certificate` resource [#6041](https://github.com/inspec/inspec/pull/6041) ([ahasunos](https://github.com/ahasunos))
- CFINSPEC-267: resource_ids group 6 [#6101](https://github.com/inspec/inspec/pull/6101) ([ahasunos](https://github.com/ahasunos))
- CFINSPEC-262 Adds resource_id group 1 [#6102](https://github.com/inspec/inspec/pull/6102) ([Vasu1105](https://github.com/Vasu1105))
- CFINSPEC-266: resource_ids group 5 [#6103](https://github.com/inspec/inspec/pull/6103) ([ahasunos](https://github.com/ahasunos))
- Fix the key duplication error warning in the mock_loader.rb [#6120](https://github.com/inspec/inspec/pull/6120) ([Vasu1105](https://github.com/Vasu1105))
- CFINSPEC-268 Adds resource_id group 7 [#6105](https://github.com/inspec/inspec/pull/6105) ([Vasu1105](https://github.com/Vasu1105))
- CFINSPEC-269 Adds resource_id group 8 [#6107](https://github.com/inspec/inspec/pull/6107) ([Vasu1105](https://github.com/Vasu1105))
- CFINSPEC-265 Group 4 - Added resource_id in resources [#6109](https://github.com/inspec/inspec/pull/6109) ([Nik08](https://github.com/Nik08))
- CFINSPEC-270 Adds resource_id group9 [#6111](https://github.com/inspec/inspec/pull/6111) ([Vasu1105](https://github.com/Vasu1105))
- CFINSPEC-273 Adds resource_id group 12 [#6112](https://github.com/inspec/inspec/pull/6112) ([Vasu1105](https://github.com/Vasu1105))
- Handle resource_id in error situations [#6118](https://github.com/inspec/inspec/pull/6118) ([ahasunos](https://github.com/ahasunos))
- CFINSPEC-262 - Handle resource_id in error situation [#6119](https://github.com/inspec/inspec/pull/6119) ([Vasu1105](https://github.com/Vasu1105))
- Bump berkshelf from 8.0.0 to 8.0.2 in /omnibus [#6114](https://github.com/inspec/inspec/pull/6114) ([dependabot[bot]](https://github.com/dependabot[bot]))
- CFINSPEC-167: Profile Signing Rollup [#5995](https://github.com/inspec/inspec/pull/5995) ([Vasu1105](https://github.com/Vasu1105))
- CFINSPEC-291: Fix `processes` resource to consider processes without `path` on Windows [#6100](https://github.com/inspec/inspec/pull/6100) ([ahasunos](https://github.com/ahasunos))
- Fixed Lint/DuplicateMethods: Method Inspec::Resources::Service#resource_id is defined at both [#6132](https://github.com/inspec/inspec/pull/6132) ([Vasu1105](https://github.com/Vasu1105))
- Dk/matchers rewrite [#6007](https://github.com/inspec/inspec/pull/6007) ([dkumaras](https://github.com/dkumaras))
- Add inspec-6 branch as release branch [#6136](https://github.com/inspec/inspec/pull/6136) ([clintoncwolfe](https://github.com/clintoncwolfe))
- add ruby test 3.1 in verify pipeline [#5892](https://github.com/inspec/inspec/pull/5892) ([jayashrig158](https://github.com/jayashrig158))
- Updated plugins doc with send_report functionality [#6144](https://github.com/inspec/inspec/pull/6144) ([Nik08](https://github.com/Nik08))
- Bump octokit from 4.23.0 to 4.25.0 in /omnibus [#6146](https://github.com/inspec/inspec/pull/6146) ([dependabot[bot]](https://github.com/dependabot[bot]))
- Fixes for Buildkite Issues [#6161](https://github.com/inspec/inspec/pull/6161) ([Nik08](https://github.com/Nik08))
- CFINSPEC-238 Enhanced Outcomes Design Doc [#6152](https://github.com/inspec/inspec/pull/6152) ([clintoncwolfe](https://github.com/clintoncwolfe))
- Add k8s section to resources index page [#6167](https://github.com/inspec/inspec/pull/6167) ([IanMadd](https://github.com/IanMadd))
- Windows fix for dependent profiles [#6173](https://github.com/inspec/inspec/pull/6173) ([Nik08](https://github.com/Nik08))
- Bump omnibus-software from `a9b13a0` to `7bb8c7b` in /omnibus [#6191](https://github.com/inspec/inspec/pull/6191) ([dependabot[bot]](https://github.com/dependabot[bot]))
- Trial - Update the omnibus/Gemfile.lock (can be the reason for omnibus build failure) [#6195](https://github.com/inspec/inspec/pull/6195) ([Vasu1105](https://github.com/Vasu1105))
- CFINSPEC-239 Attestations Design Doc [#6188](https://github.com/inspec/inspec/pull/6188) ([clintoncwolfe](https://github.com/clintoncwolfe))
<!-- latest_stable_release -->
## [v5.17.4](https://github.com/inspec/inspec/tree/v5.17.4) (2022-05-25) ## [v5.17.4](https://github.com/inspec/inspec/tree/v5.17.4) (2022-05-25)
#### Merged Pull Requests #### Merged Pull Requests
@ -52,7 +74,6 @@
- fixing bad markdown syntax [#6066](https://github.com/inspec/inspec/pull/6066) ([replicajune](https://github.com/replicajune)) - fixing bad markdown syntax [#6066](https://github.com/inspec/inspec/pull/6066) ([replicajune](https://github.com/replicajune))
- Add vale config to docs in inspec repository [#6065](https://github.com/inspec/inspec/pull/6065) ([IanMadd](https://github.com/IanMadd)) - Add vale config to docs in inspec repository [#6065](https://github.com/inspec/inspec/pull/6065) ([IanMadd](https://github.com/IanMadd))
- Remove Hugo version from Netlify config [#6075](https://github.com/inspec/inspec/pull/6075) ([IanMadd](https://github.com/IanMadd)) - Remove Hugo version from Netlify config [#6075](https://github.com/inspec/inspec/pull/6075) ([IanMadd](https://github.com/IanMadd))
<!-- latest_stable_release -->
## [v5.14.0](https://github.com/inspec/inspec/tree/v5.14.0) (2022-04-21) ## [v5.14.0](https://github.com/inspec/inspec/tree/v5.14.0) (2022-04-21)

View file

@ -1,7 +1,7 @@
FROM ubuntu:18.04 FROM ubuntu:18.04
LABEL maintainer="Chef Software, Inc. <docker@chef.io>" LABEL maintainer="Chef Software, Inc. <docker@chef.io>"
ARG VERSION=5.17.4 ARG VERSION=5.18.14
ARG CHANNEL=stable ARG CHANNEL=stable
ENV PATH=/opt/inspec/bin:/opt/inspec/embedded/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ENV PATH=/opt/inspec/bin:/opt/inspec/embedded/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

View file

@ -25,7 +25,7 @@ end
group :test do group :test do
gem "chefstyle", "~> 2.0.3" gem "chefstyle", "~> 2.0.3"
gem "concurrent-ruby", "~> 1.0" gem "concurrent-ruby", "~> 1.0"
gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows gem "html-proofer", "~> 3.19.4", platforms: :ruby # do not attempt to run proofer on windows. Pinned to 3.19.4 as test is breaking in updated versions.
gem "json_schemer", ">= 0.2.1", "< 0.2.19" gem "json_schemer", ">= 0.2.1", "< 0.2.19"
gem "m" gem "m"
gem "minitest-sprint", "~> 1.0" gem "minitest-sprint", "~> 1.0"

View file

@ -56,7 +56,7 @@ inspec exec test.rb -t docker://container_id
Chef InSpec requires Ruby ( >= 2.7 ). Chef InSpec requires Ruby ( >= 2.7 ).
Note: Versions of Chef InSpec 4.0 and later require accepting the EULA to use. Please visit the [license acceptance page](https://docs.chef.io/chef_license_accept.html) on the Chef docs site for more information. All currently supported versions of Chef InSpec (4.0 and later) require accepting the EULA to use. Please visit the [license acceptance page](https://docs.chef.io/chef_license_accept.html) on the Chef docs site for more information.
### Install as package ### Install as package

82
dev-docs/attestations.md Normal file
View file

@ -0,0 +1,82 @@
# Attestations
## Use Cases
As a compliance officer,
I want to mark skipped controls as manually passed or failed
so that I can manually complete the profile.
As a compliance officer,
I want to set an expiration date and a justification for my attestations
so that I can control their application.
As a compliance officer,
I want flexibility in the file format accepted by the attestations system (XLSX, YAML, CSV, JSON),
so that I can use a familiar file format.
When used with Enhanced Outcomes, this becomes handling `Not Reviewed` controls.
## Mechanism
### CLI option desirable
`inspec exec profilename --attestation-file file.???`
The new option is named like `--waiver-file` - singular, with `-file`. You may provide multiple arguments for the option.
The file can be any of the following formats: `YAML`, `XLSX`, `CSV`, or `JSON`.
#### YAML and JSON
An array of Hashes.
#### XLSX and CSV
XLSX is the first sheet in the file.
Both formats assume a header row.
### Fields in the file
#### control_id
_Required_. Matches control ID of the control.
#### justification
_Required_. Free text field, used as an explanation for the control when displayed.
#### evidence_url
_Optional_. URL to some evidence, determined by the user, supports the justification.
#### expiration_date
_Optional_. If present, the attestation expires at the end of the date given.
#### status
_Optional_.
Default `passed`. If the attestation should indicate that the control is a failure, set this to `failed`.
### Implementation
When running, at the **RunData** stage, attestations are handled by the following process:
1. Locate matching controls by matching the control ID.
2. Inject an artificial test result into the control. Use the attestation justification as the result message.
3. If the attestation is expired, set the new test result to Skip.
4. If the attestation is not expired, set the new test result to the status given on the attestation data (default pass).
5. Record a copy of the attestation data structure in the Control RunData structure.
### Compatibility
To support backward compatibility with existing MITRE work, support will be added (but not otherwise documented) for the following fields:
* explanation - the equivalent of justification
* updated (Date) and frequency (string enum) - together, the equivalent of the expiration date.

View file

@ -0,0 +1,54 @@
# Enhanced Outcomes
Enhanced Outcomes refers to the addition of new control outcomes to the InSpec vocabulary.
## Test Outcomes vs. Control Outcomes
It is essential to understand that Enhanced Outcomes refers to new **control outcomes**, the results of running a control.
The results of running a test, a `describe block` are determined by RSpec and are limited to `Pass`, `Fail`, and `Skip`.
Test outcomes are much more difficult to extend than control outcomes.
## New Control Outcomes
Enhanced Outcomes adds three new control outcomes to the existing `Pass`, `Fail`, and `Skip` outcomes.
### Error
In the first iteration of Enhanced Outcomes, the Error outcome is detected:
* if the message of any test includes the text "Control source error" OR
* the result of any test includes a backtrace
Then the entire control should be marked `Error`.
Additional means of detecting error may be developed in the future.
Error's abbreviation is `ERR`. Error's UI color assignment is `Indigo`.
### Not Applicable
If the control is not in `Error` and the impact of the control is `0.0`, then the control's outcome is `Not Applicable`.
Not Applicable's abbreviation is `N/A`. Not Applicable's UI color assignment is `Sky Blue`.
### Not Reviewed
If the control is not in `Error` or `Not Applicable`, and all test results are `Skipped`, then the control outcome is `Not Reviewed`.
Not Reviewed replaces `Skipped` as a control outcome.
Not Reviewed's abbreviation is `N/R`. Not Reviewed's UI color assignment is `Amber`.
## The `--enhanced-outcomes` option
A new CLI option will be introduced for `inspec exec`, `inspec shell`, and `inspec schema` that controls the Enhanced Outcomes functionality.
### InSpec 5
In InSpec 5.x, a user must request the enhanced outcomes functionality explicitly by adding the `--enhanced-outcomes` option.
### InSpec 6
In InSpec 6.x, --enhanced-outcomes will default to `true`. A user may request disabling the enhanced outcomes functionality by adding the `--no-enhanced-outcomes` option.

View file

@ -493,6 +493,24 @@ v0.1.0 - Initial version
v0.2.0 - added `run_data.profiles[0].inputs[0].options.sensitive` v0.2.0 - added `run_data.profiles[0].inputs[0].options.sensitive`
v0.3.0 - added resource_name && params v0.3.0 - added resource_name && params
#### Implement send_report
The primary responsibilty of this function is to implement a logic for sending reporter output through email invocations or making API calls. When this is defined in a reporter, rendering of output is skipped.
```ruby
module InspecPlugins::Sweeten
class Reporter < Inspec.plugin(2, :reporter)
def send_report
# logic for sending reporter output using email invocations or API calls.
end
def render
# this will be skipped, will only run send_report
end
end
end
```
## Implementing Streaming Reporter Plugins ## Implementing Streaming Reporter Plugins
Streaming Reporter plugins offer the opportunity to customize or create a plugin which operates real-time as the Chef Inspec tests runs. Streaming reporters perform streaming using RSpec custom formatters. Streaming Reporter plugins offer the opportunity to customize or create a plugin which operates real-time as the Chef Inspec tests runs. Streaming reporters perform streaming using RSpec custom formatters.

View file

@ -62,7 +62,7 @@ inspec automate SUBCOMMAND
## check ## check
Verify the metadata in the `inspec.yml` file, verify that control blocks have the correct fields (title, description, impact) defined that all controls have visible tests, and the controls are not using deprecated InSpec DSL code. Verify the metadata in the `inspec.yml` file, verify that control blocks have the correct fields (title, description, impact), and define that all controls have visible tests and the controls are not using deprecated InSpec DSL code.
### Syntax ### Syntax
@ -118,9 +118,9 @@ This subcommand has the following additional options:
* `--client-key-pass=CLIENT_CERT_PASSWORD` * `--client-key-pass=CLIENT_CERT_PASSWORD`
Specify client certificate password, if required for SSL authentication (WinRM). Specify client certificate password, if required for SSL authentication (WinRM).
* `--config=CONFIG` * `--config=CONFIG`
Read configuration from JSON file (`-` reads from stdin). Read configuration from the JSON file (`-` reads from stdin).
* `--docker-url` * `--docker-url`
Provides path to Docker API endpoint (Docker). Provides a path to the Docker API endpoint (Docker).
* `--enable-password=ENABLE_PASSWORD` * `--enable-password=ENABLE_PASSWORD`
Password for enable mode on Cisco IOS devices. Password for enable mode on Cisco IOS devices.
* `--format=FORMAT` * `--format=FORMAT`
@ -152,7 +152,7 @@ This subcommand has the following additional options:
* `--ssl`, `--no-ssl` * `--ssl`, `--no-ssl`
Use SSL for transport layer encryption (WinRM). Use SSL for transport layer encryption (WinRM).
* `--ssl-peer-fingerprint` * `--ssl-peer-fingerprint`
Specify ssl peer fingerprint in lieu of certificates, for SSL authentication (WinRM). Specify SSL peer fingerprint in place of certificates for SSL authentication (WinRM).
* `--sudo`, `--no-sudo` * `--sudo`, `--no-sudo`
Run scans with sudo. Only activates on Unix and non-root user. Run scans with sudo. Only activates on Unix and non-root user.
* `--sudo-command=SUDO_COMMAND` * `--sudo-command=SUDO_COMMAND`
@ -174,7 +174,7 @@ This subcommand has the following additional options:
* `--winrm-transport=WINRM_TRANSPORT` * `--winrm-transport=WINRM_TRANSPORT`
Specify which transport to use, defaults to negotiate (WinRM). Specify which transport to use, defaults to negotiate (WinRM).
* `--winrm-shell-type=WINRM_SHELL_TYPE` * `--winrm-shell-type=WINRM_SHELL_TYPE`
Specify which shell type to use (powershell,elevated or cmd), defaults to powershell (WinRM). Specify which shell type to use (powershell, elevated, or cmd), which defaults to powershell (WinRM).
## env ## env
@ -192,7 +192,7 @@ inspec env
Run all test files at the specified locations. Run all test files at the specified locations.
The subcommand loads the given profiles, fetches their dependencies if needed, then connects to the target and executes any controls contained in the profiles. One or more reporters are used to generate the output. The subcommand loads the given profiles, fetches their dependencies if needed, then connects to the target and executes any controls in the profiles. One or more reporters are used to generate the output.
```ruby ```ruby
exit codes: exit codes:
@ -314,13 +314,13 @@ This subcommand has the following additional options:
* `--command-timeout=SECONDS` * `--command-timeout=SECONDS`
Maximum seconds to allow a command to run. Maximum seconds to allow a command to run.
* `--config=CONFIG` * `--config=CONFIG`
Read configuration from JSON file (`-` reads from stdin). Read configuration from the JSON file (`-` reads from stdin).
* `--controls=one two three` * `--controls=one two three`
A list of control names to run, or a list of /regexes/ to match against control names. Ignore all other tests. A list of control names to run or a list of /regexes/ to match against control names. Ignore all other tests.
* `--create-lockfile`, `--no-create-lockfile` * `--create-lockfile`, `--no-create-lockfile`
Write out a lockfile based on this execution (unless one already exists). Write out a lockfile based on this execution (unless one already exists).
* `--distinct-exit`, `--no-distinct-exit` * `--distinct-exit`, `--no-distinct-exit`
Exit with code 101 if any tests fail, and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures. Exit with code 101 if any tests fail and 100 if any are skipped (default). If disabled, exit 0 on skips and 1 for failures.
* `--docker-url` * `--docker-url`
Provides path to Docker API endpoint (Docker). Defaults to unix:///var/run/docker.sock on Unix systems and tcp://localhost:2375 on Windows. Provides path to Docker API endpoint (Docker). Defaults to unix:///var/run/docker.sock on Unix systems and tcp://localhost:2375 on Windows.
* `--enable-password=ENABLE_PASSWORD` * `--enable-password=ENABLE_PASSWORD`
@ -328,7 +328,7 @@ This subcommand has the following additional options:
* `--filter-empty-profiles`, `--no-filter-empty-profiles` * `--filter-empty-profiles`, `--no-filter-empty-profiles`
Filter empty profiles (profiles without controls) from the report. Filter empty profiles (profiles without controls) from the report.
* `--filter-waived-controls` * `--filter-waived-controls`
Do not execute waived controls in InSpec at all. Must use with --waiver-file. Ignores `run` setting of waiver file. Do not execute waived controls in InSpec at all. Must use with --waiver-file. Ignores the `run` setting of the waiver file.
* `--host=HOST` * `--host=HOST`
Specify a remote host which is tested. Specify a remote host which is tested.
* `--input=name1=value1 name2=value2` * `--input=name1=value1 name2=value2`
@ -352,13 +352,13 @@ This subcommand has the following additional options:
* `--proxy-command=PROXY_COMMAND` * `--proxy-command=PROXY_COMMAND`
Specifies the command to use to connect to the server. Specifies the command to use to connect to the server.
* `--reporter=one two:/output/file/path` * `--reporter=one two:/output/file/path`
Enable one or more output reporters: cli, documentation, html, progress, progress-bar, json, json-min, json-rspec, junit, yaml. Enable one or more output reporters: cli, documentation, html2, progress, progress-bar, json, json-min, json-rspec, junit2, yaml.
* `--reporter-backtrace-inclusion`, `--no-reporter-backtrace-inclusion` * `--reporter-backtrace-inclusion`, `--no-reporter-backtrace-inclusion`
Include a code backtrace in report data (default: true). Include a code backtrace in report data (default: true).
* `--reporter-include-source` * `--reporter-include-source`
Include full source code of controls in the CLI report. Include full source code of controls in the CLI report.
* `--reporter-message-truncation=REPORTER_MESSAGE_TRUNCATION` * `--reporter-message-truncation=REPORTER_MESSAGE_TRUNCATION`
Number of characters to truncate failure messages in report data to (default: no truncation). Number of characters to truncate failure messages in report data (default: no truncation).
* `--self-signed`, `--no-self-signed` * `--self-signed`, `--no-self-signed`
Allow remote scans with self-signed certificates (WinRM). Allow remote scans with self-signed certificates (WinRM).
* `--shell`, `--no-shell` * `--shell`, `--no-shell`
@ -370,13 +370,13 @@ This subcommand has the following additional options:
* `--show-progress`, `--no-show-progress` * `--show-progress`, `--no-show-progress`
Show progress while executing tests. Show progress while executing tests.
* `--silence-deprecations=all|GROUP GROUP...` * `--silence-deprecations=all|GROUP GROUP...`
Suppress deprecation warnings. See install_dir/etc/deprecations.json for list of GROUPs or use 'all'. Suppress deprecation warnings. See install_dir/etc/deprecations.json for a list of GROUPs or use 'all'.
* `--ssh-config-file=one two three` * `--ssh-config-file=one two three`
A list of paths to the SSH configuration file, for example: `~/.ssh/config` or `/etc/ssh/ssh_config`. A list of paths to the SSH configuration file, for example: `~/.ssh/config` or `/etc/ssh/ssh_config`.
* `--ssl`, `--no-ssl` * `--ssl`, `--no-ssl`
Use SSL for transport layer encryption (WinRM). Use SSL for transport layer encryption (WinRM).
* `--ssl-peer-fingerprint` * `--ssl-peer-fingerprint`
Specify ssl peer fingerprint in lieu of certificates, for SSL authentication (WinRM). Specify SSL peer fingerprint in place of certificates for SSL authentication (WinRM).
* `--sudo`, `--no-sudo` * `--sudo`, `--no-sudo`
Run scans with sudo. Only activates on Unix and non-root user. Run scans with sudo. Only activates on Unix and non-root user.
* `--sudo-command=SUDO_COMMAND` * `--sudo-command=SUDO_COMMAND`
@ -388,9 +388,9 @@ This subcommand has the following additional options:
* `-t`, `--target=TARGET` * `-t`, `--target=TARGET`
Simple targeting option using URIs, e.g. ssh://user:pass@host:port. Simple targeting option using URIs, e.g. ssh://user:pass@host:port.
* `--target-id=TARGET_ID` * `--target-id=TARGET_ID`
Provide a ID which will be included on reports - deprecated. Provide an ID that is included on reports - deprecated.
* `--tags=one two three` * `--tags=one two three`
A list of tags or a list of regular expressions that match tags. `exec` will run controls referenced by the listed or matching tags. A list of tags or regular expressions that match tags. `exec` will run controls referenced by the listed or matching tags.
* `--user=USER` * `--user=USER`
The login user for a remote scan. The login user for a remote scan.
* `--vendor-cache=VENDOR_CACHE` * `--vendor-cache=VENDOR_CACHE`
@ -403,6 +403,8 @@ This subcommand has the following additional options:
Whether to use disable sspi authentication, defaults to false (WinRM). Whether to use disable sspi authentication, defaults to false (WinRM).
* `--winrm-transport=WINRM_TRANSPORT` * `--winrm-transport=WINRM_TRANSPORT`
Specify which transport to use, defaults to negotiate (WinRM). Specify which transport to use, defaults to negotiate (WinRM).
* `--enhanced-outcomes`
Includes enhanced outcome of controls in report data.
## habitat ## habitat
@ -442,7 +444,7 @@ inspec init TEMPLATE
## json ## json
Read all tests in path and generate a json summary. Read all tests in the path and generate a json summary.
### Syntax ### Syntax
@ -463,7 +465,7 @@ This subcommand has the following additional options:
* `--profiles-path=PROFILES_PATH` * `--profiles-path=PROFILES_PATH`
Folder which contains referenced profiles. Folder which contains referenced profiles.
* `--tags=one two three` * `--tags=one two three`
A list of tags that reference certain controls. Other controls are ignored. A list of tags that reference specific controls. Other controls are ignored.
* `--vendor-cache=VENDOR_CACHE` * `--vendor-cache=VENDOR_CACHE`
Use the given path for caching dependencies. (default: `~/.inspec/cache`). Use the given path for caching dependencies. (default: `~/.inspec/cache`).
@ -503,6 +505,13 @@ This subcommand has the following syntax:
inspec schema NAME inspec schema NAME
``` ```
### Options
This subcommand has the following additional option:
* `--enhanced-outcomes`
Includes enhanced outcome of controls in report data.
## shell ## shell
Open an interactive debugging shell. Open an interactive debugging shell.
@ -540,11 +549,11 @@ This subcommand has the following additional options:
* `--client-key-pass=CLIENT_CERT_PASSWORD` * `--client-key-pass=CLIENT_CERT_PASSWORD`
Specify client certificate password, if required for SSL authentication (WinRM). Specify client certificate password, if required for SSL authentication (WinRM).
* `--config=CONFIG` * `--config=CONFIG`
Read configuration from JSON file (`-` reads from stdin). Read configuration from the JSON file (`-` reads from stdin).
* `--depends=one two three` * `--depends=one two three`
A space-delimited list of local folders containing profiles whose libraries and resources will be loaded into the new shell. A space-delimited list of local folders containing profiles whose libraries and resources will be loaded into the new shell.
* `--distinct-exit`, `--no-distinct-exit` * `--distinct-exit`, `--no-distinct-exit`
Exit with code 100 if any tests fail, and 101 if any are skipped but none failed (default). If disabled, exit 0 on skips and 1 for failures. Exit with code 100 if any tests fail and 101 if any are skipped, but none failed (default). If disabled, exit 0 on skips and 1 for failures.
* `--docker-url` * `--docker-url`
Provides path to Docker API endpoint (Docker). Defaults to unix:///var/run/docker.sock on Unix systems and tcp://localhost:2375 on Windows. Provides path to Docker API endpoint (Docker). Defaults to unix:///var/run/docker.sock on Unix systems and tcp://localhost:2375 on Windows.
* `--enable-password=ENABLE_PASSWORD` * `--enable-password=ENABLE_PASSWORD`
@ -568,7 +577,7 @@ This subcommand has the following additional options:
* `--proxy-command=PROXY_COMMAND` * `--proxy-command=PROXY_COMMAND`
Specifies the command to use to connect to the server. Specifies the command to use to connect to the server.
* `--reporter=one two:/output/file/path` * `--reporter=one two:/output/file/path`
Enable one or more output reporters: cli, documentation, html, progress, json, json-min, json-rspec, junit. Enable one or more output reporters: cli, documentation, html2, progress, json, json-min, json-rspec, junit2.
* `--self-signed`, `--no-self-signed` * `--self-signed`, `--no-self-signed`
Allow remote scans with self-signed certificates (WinRM). Allow remote scans with self-signed certificates (WinRM).
* `--shell`, `--no-shell` * `--shell`, `--no-shell`
@ -582,7 +591,7 @@ This subcommand has the following additional options:
* `--ssl`, `--no-ssl` * `--ssl`, `--no-ssl`
Use SSL for transport layer encryption (WinRM). Use SSL for transport layer encryption (WinRM).
* `--ssl-peer-fingerprint=SSL_PEER_FINGERPRINT` * `--ssl-peer-fingerprint=SSL_PEER_FINGERPRINT`
Specify ssl peer fingerprint in lieu of certificates, for SSL authentication (WinRM). Specify SSL peer fingerprint in place of certificates for SSL authentication (WinRM).
* `--sudo`, `--no-sudo` * `--sudo`, `--no-sudo`
Run scans with sudo. Only activates on Unix and non-root user. Run scans with sudo. Only activates on Unix and non-root user.
* `--sudo-command=SUDO_COMMAND` * `--sudo-command=SUDO_COMMAND`
@ -603,6 +612,8 @@ This subcommand has the following additional options:
Whether to use disable sspi authentication, defaults to false (WinRM). Whether to use disable sspi authentication, defaults to false (WinRM).
* `--winrm-transport=WINRM_TRANSPORT` * `--winrm-transport=WINRM_TRANSPORT`
Specify which transport to use, defaults to negotiate (WinRM). Specify which transport to use, defaults to negotiate (WinRM).
* `--enhanced-outcomes`
Includes enhanced outcome of controls in report data.
## supermarket ## supermarket
@ -640,7 +651,7 @@ inspec vendor PATH
This subcommand has additional options: This subcommand has additional options:
* `--overwrite`, `--no-overwrite` * `--overwrite`, `--no-overwrite`
Overwrite existing vendored dependencies and lockfile. Overwrite existing vendored dependencies and lockfiles.
## version ## version

View file

@ -249,6 +249,7 @@ end
``` ```
This example checks for if certain pip packages are installed, but only if '/root/.aws' exists: This example checks for if certain pip packages are installed, but only if '/root/.aws' exists:
```ruby ```ruby
control 'pip-packages-installed' do control 'pip-packages-installed' do
title 'Check if essential pips are installed' title 'Check if essential pips are installed'
@ -269,14 +270,37 @@ certain controls, which would 100% fail due to the way servers are prepared, but
you know that the same control suites are reused later in different circumstances you know that the same control suites are reused later in different circumstances
by different teams. by different teams.
This example checks whether the Gnome Desktop is installed. If not installed, it resets the impact of the control to the new value which is passed as a hash with the impact key.
Here, it resets it to 0:
```ruby
control 'gnome-destkop-settings' do
impact 0.5
desc 'some good settings'
desc 'check', 'check the settings file for good things'
desc 'fix', 'set the good things in the file /etc/gnome/settings'
tag nist: 'CM-6'
only_if("The Gnome Desktop is not installed, this control is Not Applicable", impact: 0) {
package('gnome-desktop').installed?
}
describe gnome_settings do
it should_be set_well
end
end
```
Some notes about `only_if`: Some notes about `only_if`:
- `only_if` applies to the entire `control`. If the results of the `only_if` * `only_if` applies to the entire `control`. If the results of the `only_if`
block evaluate to false, any Chef InSpec resources mentioned as part of a block evaluate to false, any Chef InSpec resources mentioned as part of a
`describe` block will not be run. Additionally, the contents of the describe `describe` block will not be run. Additionally, the contents of the describe
blocks will not be run. However, bare Ruby expressions and bare Chef InSpec blocks will not be run. However, bare Ruby expressions and bare Chef InSpec
resources (not assocated with a describe block) preceding the only_if statement resources (not assocated with a describe block) preceding the only_if statement
will run will run
* `only_if` also accepts hash with impact key to reset the impact value of the control. Control's impact is helpful in determining it is enhanced outcome.
To illustrate: To illustrate:

View file

@ -19,7 +19,7 @@ is a standalone structure with its own distribution and execution flow.
A profile should have the following structure: A profile should have the following structure:
```YAML ```yaml
examples/profile examples/profile
├── README.md ├── README.md
├── controls ├── controls
@ -67,7 +67,7 @@ Each profile must have an `inspec.yml` file that defines the following informati
`name` is required; all other profile settings are optional. For example: `name` is required; all other profile settings are optional. For example:
```YAML ```yaml
name: ssh name: ssh
title: Basic SSH title: Basic SSH
maintainer: Chef Software, Inc. maintainer: Chef Software, Inc.
@ -89,7 +89,7 @@ inspec_version: "~> 2.1"
The `inspec.yml` also supports embedded ERB in the file. For example: The `inspec.yml` also supports embedded ERB in the file. For example:
```YAML ```yaml
name: dummy name: dummy
title: InSpec Profile title: InSpec Profile
maintainer: The Authors maintainer: The Authors
@ -130,7 +130,7 @@ platforms. The new families can restrict the platform family to `os`, `aws`, `az
For example, to target anything running Debian Linux, use: For example, to target anything running Debian Linux, use:
```YAML ```yaml
name: ssh name: ssh
supports: supports:
- platform-name: debian - platform-name: debian
@ -138,7 +138,7 @@ supports:
To target only Ubuntu version 20.04, use: To target only Ubuntu version 20.04, use:
```YAML ```yaml
name: ssh name: ssh
supports: supports:
- platform-name: ubuntu - platform-name: ubuntu
@ -147,7 +147,7 @@ supports:
To target the entire release of Ubuntu version 20.x, use: To target the entire release of Ubuntu version 20.x, use:
```YAML ```yaml
name: ssh name: ssh
supports: supports:
- platform-name: ubuntu - platform-name: ubuntu
@ -156,7 +156,7 @@ supports:
To target the Red Hat and derivative platforms such as CentOS and Oracle Linux, use: To target the Red Hat and derivative platforms such as CentOS and Oracle Linux, use:
```YAML ```yaml
name: ssh name: ssh
supports: supports:
- platform-family: redhat - platform-family: redhat
@ -164,7 +164,7 @@ supports:
To target the entire Windows 2019 platform family, including Datacenter and Core Servers, use: To target the entire Windows 2019 platform family, including Datacenter and Core Servers, use:
```YAML ```yaml
name: ssh name: ssh
supports: supports:
- platform-name: windows_server_2019* - platform-name: windows_server_2019*
@ -172,7 +172,7 @@ supports:
To target anything running on Amazon AWS, use: To target anything running on Amazon AWS, use:
```YAML ```yaml
name: ssh name: ssh
supports: supports:
- platform: aws - platform: aws
@ -180,7 +180,7 @@ supports:
To target all of these examples in a single `inspec.yml` file, use: To target all of these examples in a single `inspec.yml` file, use:
```YAML ```yaml
name: ssh name: ssh
supports: supports:
- platform-name: debian - platform-name: debian
@ -206,7 +206,7 @@ needs to be specified in the including profiles `inspec.yml` file in the `dep
section. For each profile to be included, a location for the profile from where section. For each profile to be included, a location for the profile from where
to be fetched and a name for the profile should be included. For example: to be fetched and a name for the profile should be included. For example:
```YAML ```yaml
depends: depends:
- name: linux-baseline - name: linux-baseline
url: https://github.com/dev-sec/linux-baseline/archive/master.tar.gz url: https://github.com/dev-sec/linux-baseline/archive/master.tar.gz
@ -221,7 +221,7 @@ Chef InSpec supports a number of dependency sources.
The `path` setting defines a profile that is located on disk. This setting is The `path` setting defines a profile that is located on disk. This setting is
typically used during development of profiles and when debugging profiles. typically used during development of profiles and when debugging profiles.
```YAML ```yaml
depends: depends:
- name: my-profile - name: my-profile
path: /absolute/path path: /absolute/path
@ -235,17 +235,17 @@ The `url` setting specifies a profile that is located at an HTTP- or HTTPS-based
URL. The profile must be accessible via a HTTP GET operation and must be a valid URL. The profile must be accessible via a HTTP GET operation and must be a valid
profile archive (zip, tar, or tar.gz format). profile archive (zip, tar, or tar.gz format).
```YAML ```yaml
depends: depends:
- name: my-profile - name: my-profile
url: https://my.domain/path/to/profile.tgz url: https://my.domain/path/to/profile.tgz
- name: profile-via-git - name: profile-via-git
url: https://github.com/myusername/myprofile-repo/archive/master.tar.gz url: https://github.com/username/myprofile-repo/archive/master.tar.gz
``` ```
`url` also supports basic authentication. `url` also supports basic authentication.
```YAML ```yaml
depends: depends:
- name: my-profile - name: my-profile
url: https://my.domain/path/to/profile.tgz url: https://my.domain/path/to/profile.tgz
@ -260,7 +260,7 @@ optional settings for branch, tag, commit, version, and relative_path. The sourc
location is translated into a URL upon resolution. This type of dependency supports location is translated into a URL upon resolution. This type of dependency supports
version constraints via semantic versioning as git tags. version constraints via semantic versioning as git tags.
```YAML ```yaml
depends: depends:
- name: git-profile - name: git-profile
git: http://url/to/repo git: http://url/to/repo
@ -278,7 +278,7 @@ on Chef Supermarket. The source location is translated into a URL upon resolutio
For example: For example:
```YAML ```yaml
depends: depends:
- name: supermarket-profile - name: supermarket-profile
supermarket: supermarket-username/supermarket-profile supermarket: supermarket-username/supermarket-profile
@ -293,7 +293,7 @@ or Chef Compliance server.
For example: For example:
```YAML ```yaml
depends: depends:
- name: linux - name: linux
compliance: base/linux compliance: base/linux
@ -304,8 +304,7 @@ Any profile with ruby gem dependencies that need to be installed can be specifie
For example, if you required any ruby library in a custom resource that needs a specific gem to be installed, then you can specify those gems in the metadata file. Chef InSpec will prompt to install the gems to `~/.inspec/gems` when you run your profile the first time. To skip the prompt and automatically install, pass the `--auto-install-gems` option to `inspec exec`. For example, if you required any ruby library in a custom resource that needs a specific gem to be installed, then you can specify those gems in the metadata file. Chef InSpec will prompt to install the gems to `~/.inspec/gems` when you run your profile the first time. To skip the prompt and automatically install, pass the `--auto-install-gems` option to `inspec exec`.
```yaml
```YAML
gem_dependencies: gem_dependencies:
- name: "mongo" - name: "mongo"
version: ">= 2.3.12" version: ">= 2.3.12"
@ -412,7 +411,7 @@ for use in your profile. If two of your dependencies provide a resource with
the same name, you can use the `require_resource` DSL function to the same name, you can use the `require_resource` DSL function to
disambiguate the two: disambiguate the two:
```YAML ```yaml
require_resource(profile: 'my_dep', resource: 'my_res', require_resource(profile: 'my_dep', resource: 'my_res',
as: 'my_res2') as: 'my_res2')
``` ```
@ -436,7 +435,7 @@ of a profile. They are accessed by their name relative to this folder with
Here is an example for reading and testing a list of ports. The folder structure is: Here is an example for reading and testing a list of ports. The folder structure is:
```YAML ```yaml
examples/profile examples/profile
├── controls ├── controls
│ ├── example.rb │ ├── example.rb
@ -447,7 +446,7 @@ examples/profile
With `services.yml` containing: With `services.yml` containing:
```YAML ```yaml
- service_name: httpd-alpha - service_name: httpd-alpha
port: 80 port: 80
- service_name: httpd-beta - service_name: httpd-beta

View file

@ -11,17 +11,17 @@ gh_repo = "inspec"
weight = 50 weight = 50
+++ +++
Introduced in Chef InSpec 1.51.6 A `reporter` is a facility for formatting and delivering the results of a Chef InSpec auditing run. Reporters were introduced in Chef InSpec 1.51.6.
A `reporter` is a facility for formatting and delivering the results of a Chef InSpec auditing run. Chef InSpec allows you to output your test results to one or more reporters.
Chef InSpec allows you to output your test results to one or more reporters. Configure the reporter(s) using either the `--reporter` option or as part of the general config file using the `--config` (or `--json-config`, prior to v3.6) option. While you can configure multiple reporters to write to different files, only one reporter can output to the screen(stdout). Configure the reporter(s) using either the `--reporter` option or as part of the general configuration file using the `--config` (or `--json-config`, prior to v3.6) option. While you can configure multiple reporters to write to different files, only one reporter can output to the screen(stdout).
## Syntax ## Syntax
You can specify one or more reporters using the `--reporter` cli flag. You can also specify a output by appending a path separated by a colon. You can specify one or more reporters using the `--reporter` CLI flag. You can also specify an output by appending a path separated by a colon.
Output json to screen. **Output json to screen**
```bash ```bash
inspec exec example_profile --reporter json inspec exec example_profile --reporter json
@ -29,7 +29,7 @@ inspec exec example_profile --reporter json
inspec exec example_profile --reporter json:- inspec exec example_profile --reporter json:-
``` ```
Output yaml to screen **Output yaml to screen.**
```bash ```bash
inspec exec example_profile --reporter yaml inspec exec example_profile --reporter yaml
@ -37,33 +37,33 @@ inspec exec example_profile --reporter yaml
inspec exec example_profile --reporter yaml:- inspec exec example_profile --reporter yaml:-
``` ```
Output cli to screen and write json to a file. **Output cli to screen and write json to a file.**
```bash ```bash
inspec exec example_profile --reporter cli json:/tmp/output.json inspec exec example_profile --reporter cli json:/tmp/output.json
``` ```
Output nothing to screen and write junit and html to a file. **Output nothing to screen and write junit and html to a file.**
```bash ```bash
inspec exec example_profile --reporter junit2:/tmp/junit.xml html:www/index.html inspec exec example_profile --reporter junit2:/tmp/junit.xml html:www/index.html
``` ```
Output json to screen and write to a file. Write junit to a file. **Output json to screen and write to a file. Write junit to a file.**
```bash ```bash
inspec exec example_profile --reporter json junit2:/tmp/junit.xml | tee out.json inspec exec example_profile --reporter json junit2:/tmp/junit.xml | tee out.json
``` ```
If you wish to pass the profiles directly after specifying the reporters you will need to use the end of options flag `--`. If you wish to pass the profiles directly after specifying the reporters, you must use the end of options flag `--`.
```bash ```bash
inspec exec --reporter json junit2:/tmp/junit.xml -- profile1 profile2 inspec exec --reporter json junit2:/tmp/junit.xml -- profile1 profile2
``` ```
If you are using the cli option `--config`, you can also set reporters. Using the CLI option `--config`, you can also set reporters.
Output cli to screen. **Output cli to screen.**
```json ```json
{ {
@ -75,7 +75,7 @@ Output cli to screen.
} }
``` ```
Output cli to screen and write json to a file. **Output cli to screen and write json to a file.**
```json ```json
{ {
@ -91,24 +91,25 @@ Output cli to screen and write json to a file.
} }
``` ```
Output real-time progress to screen with a progress bar. **Output real-time progress to screen with a progress bar.**
```bash ```bash
inspec exec example_profile --reporter progress-bar inspec exec example_profile --reporter progress-bar
``` ```
## Reporter Options ## Reporter Options
The following are CLI options that may be used to modify reporter behavior. Many of these options allow you to limit the size of the report, because some reporters (such as the json-automate reporter) have a limit on the total size of the report that can be processed. The following are CLI options that are used to modify reporter behavior. Many of these options allow you to limit the report size because some reporters (such as the json-automate reporter) limit on the total size of the report that can be processed.
`--diff`, `--no-diff` `--diff`, `--no-diff`
: Include a `diff` comparison of textual differences in failed test output (default: `true`). : Include a `diff` comparison of textual differences in the failed test output (default: `true`).
: Use `--no-diff` to limit the size of the report output when tests contain large amounts of text output. : Use `--no-diff` to limit the size of the report output when tests contain large amounts of text output.
`--filter-empty-profiles` `--filter-empty-profiles`
: Remove empty profiles (those containing zero controls, such as resource packs) from the output of the reporter. : Remove empty profiles (those containing zero controls, such as resource packs) from the reporter's output.
`--reporter-backtrace-inclusion`, `--no-reporter-backtrace-inclusion` `--reporter-backtrace-inclusion`, `--no-reporter-backtrace-inclusion`
@ -126,13 +127,21 @@ The following are CLI options that may be used to modify reporter behavior. Many
: This may be used to limit the size of reports when failure messages are exceptionally large. : This may be used to limit the size of reports when failure messages are exceptionally large.
`--enhanced-outcomes`
: Includes enhanced outcome of controls in report data.
: The control level status outcomes are `Passed`, `Failed`, `Not Applicable (N/A)`, `Not Reviewed (N/R)`, or `Error (ERR)`.
: Only supported for cli, progress-bar, html2, json, json-automate, automate, and yaml reporters.
## Supported Reporters ## Supported Reporters
The following are the current supported reporters: The following are the currently supported reporters:
### cli ### cli
This is the basic text base report. It includes details about which tests passed and failed and includes an overall summary at the end. This is the basic text based report. It includes details about tests that passed and failed and an overall summary at the end.
### json ### json
@ -172,29 +181,39 @@ This reporter outputs the standard JUnit spec in XML format and is recommended f
#### junit #### junit
This legacy reporter outputs nonstandard JUnit XML and is provided only for backwards compatibility. This legacy reporter outputs nonstandard JUnit XML and is provided only for backward compatibility.
### progress ### progress
This reporter is very condensed and gives you a `.`(pass), `f`(fail), or `*`(skip) character per test and a small summary at the end. This reporter is very condensed and provides you a `.`(pass), `f`(fail), or `*`(skip) character per test and a small summary at the end.
### progress-bar ### progress-bar
This reporter outputs real-time progress of a running InSpec profile using a progress bar and prints running control's ID with an indicator of control's status (Passed, failed or skipped). This reporter outputs the real-time progress of a running InSpec profile using a progress bar and prints the running control's ID with an indicator of the control's status (`Passed`, `failed`, or `skipped`).
For example:
![Progress Bar Reporter Outcome](/images/inspec/reporter_outcome_progress_bar.png)
And reporter outcome with `--enhanced-outcomes` option:
![Progress Bar Reporter Outcome with enhanced outcomes](/images/inspec/reporter_outcome_progress_bar_enhanced_outcomes.png)
### json-rspec ### json-rspec
This reporter includes all information from the rspec runner. Unlike the json reporter this includes rspec specific details. This reporter includes all information from the Rspec runner. Unlike the json reporter, this includes Rspec-specific details.
### html ### html
This reporter is the legacy RSpec HTML reporter, which is retained for backwards compatibility. The report generated is not aware of profiles or controls, and only contains unsorted test information. Most users should migrate to the `html2` reporter for more complete data. This reporter is the legacy RSpec HTML reporter retained for backward compatibility. The report generated is unaware of profiles or controls and only contains unsorted test information. Most users should migrate to the `html2` reporter for more complete data.
### html2 ### html2
This reporter is an improved HTML reporter that contains full data about the structure of the profile, controls, and tests. The generated report renders HTML code for viewing your tests in a browser. This reporter is an improved HTML reporter that contains full data about the structure of the profile, controls, and tests. The generated report renders HTML code for viewing your tests in a browser.
The `html2` reporter requires no configuration to function. However, two options--`alternate_css_file` and `alternate_js_file`--are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes. For details, see [our configuration file documentation](/inspec/config/). The `html2` reporter requires no configuration to function. However, options `--alternate_css_file` and `--alternate_js_file` are available for customization. The options are set in the JSON-formatted configuration file that Chef InSpec consumes.
For details, see [our configuration file documentation](/inspec/config/).
For example: For example:
@ -212,17 +231,17 @@ For example:
#### alternate_css_file #### alternate_css_file
Specifies the full path to the location of a CSS file that will be read and inlined into the HTML report. The default CSS will not be included. Specifies the full path to the location of a CSS file that is read and inlined into the HTML report. The default CSS is not included.
#### alternate_js_file #### alternate_js_file
Specifies the full path to the location of a JavaScript file that will be read and inlined into the HTML report. The default JavaScript will not be included. The JavaScript file should implement at least a `pageLoaded()` function, which will be called by the `onload` event of the HTML `body` element. Specifies the full path to the location of a JavaScript file that is read and inlined into the HTML report. The default JavaScript is included. The JavaScript file should implement at least a `pageLoaded()` function, which is called by the `onload` event of the HTML `body` element.
## Automate Reporter ## Automate Reporter
The `automate` reporter type is a special reporter which will send its results over the network to [Chef Automate]({{< relref "/automate/">}}). To use this reporter you must pass in the correct configuration via a json config `--config`. The `automate` reporter type is a special reporter which sends its results over the network to [Chef Automate]({{< relref "/automate/">}}). To use this reporter, you must pass in the correct configuration via a json configuration `--config`.
Example config: Example Configuration:
```json ```json
{ {
@ -241,44 +260,34 @@ Example config:
### Mandatory fields ### Mandatory fields
#### stdout `stdout`
: Either suppress or shows the automate report in the CLI screen on completion.
This will either suppress or show the automate report in the CLI screen on completion `url`
: Automate 2 url. Append `data-collector/v0/` at the end.
#### url `token`
: Automate 2 tokens. You can generate this token by navigating to the **admin** tab of A2 and then clicking **API keys**.
This is your Automate 2 url. Append `data-collector/v0/` at the end.
#### token
This is your Automate 2 token. You can generate this token by navigating to the admin tab of A2 and then api keys.
### Optional fields ### Optional fields
#### insecure `insecure`
: Disables or enables the SSL check when accessing the Automate 2 instance.
This will disable or enable the ssl check when accessing the Automate 2 instance. `node_name`
: Node name which shows up in Automate.
#### node_name `node_uuid`
: Node UUID, which shows up in Chef Automate. Use a single static UUID per node for all your reports. You must specify a `node_uuid` in the Chef InSpec configuration file if running Chef InSpec outside of an audit cookbook or another environment where a `chef_guid` or `node_uuid` is already known to Chef InSpec.
This will be the node name which shows up in Automate. `environment`
: Sets the environment metadata for Automate.
#### node_uuid ## json-Automate Reporter
This will be the node UUID which shows up in Chef Automate. Use a single static UUID The `json-automate` reporter is a special reporter that prepares the data format used by the Automate reporter. `json-automate` does not communicate on the network; instead, it simply produces the JSON report format that Automate would be consuming. Notably, the report is based on the `json` reporter, with the following modifications:
per node for all your reports. You must specify a `node_uuid` in the Chef InSpec
configuration file if running Chef InSpec outside of an audit cookbook or another
environment where a `chef_guid` or `node_uuid` is already known to Chef InSpec.
#### environment - Controls appearing in child profiles are de-duplicated by ID, merging into the parent profile.
- Child profiles are deleted, flattening the report.
This will set the environment metadata for Automate. The `json-automate` reporter is primarily used for internal needs, but some users may find it helpful if they want a JSON-based reporter that merges controls.
## JSON-Automate Reporter
The `json-automate` reporter is a special reporter that prepares the data format used by the Automate reporter. `json-automate` does not communicate on the network; rather it simply produces the JSON report format that Automate would be consuming. Notably, the report is based on the `json` reporter, with the following modifications:
* Controls that appear in child profiles are de-duplicated by ID, merging up into the parent profile.
* Child profiles are deleted, flattening the report.
The `json-automate` reporter is primarily used for internal needs, but some users may find it useful if they want a JSON based reporter that merges controls.

View file

@ -56,3 +56,7 @@ The following resources work on Windows operating systems.
## Habitat ## Habitat
{{< inspec/inspec_resources platform="habitat" >}} {{< inspec/inspec_resources platform="habitat" >}}
## Kubernetes
{{< inspec/inspec_resources platform="k8s" >}}

View file

@ -11,7 +11,7 @@ platform = "linux"
parent = "inspec/resources/os" parent = "inspec/resources/os"
+++ +++
Use the `auditd` Chef InSpec audit resource to test the rules for logging that exist on the system. The audit.rules file is typically located under /etc/audit/ and contains the list of rules that define what is captured in log files. These rules are output using the auditctl -l command. This resource supports versions of `audit` >= 2.3. Use the `auditd` Chef InSpec audit resource to test the rules for logging that exist on the system. The audit.rules file is typically located under /etc/audit/ and contains the list of rules that define what is captured in log files. These rules are output using the `auditctl -l` command. This resource supports versions of `audit` >= 2.3.
## Availability ## Availability

View file

@ -1,226 +0,0 @@
+++
title = "azure_virtual_machine_data_disk resource"
draft = false
gh_repo = "inspec"
platform = "azure"
[menu]
[menu.inspec]
title = "azure_virtual_machine_data_disk"
identifier = "inspec/resources/azure/azure_virtual_machine_data_disk.md azure_virtual_machine_data_disk resource"
parent = "inspec/resources/azure"
+++
Use this resource to ensure that a specific data disk attached to a machine has been created properly.
## Availability
### Installation
{{% inspec/inspec_installation %}}
### Version
This resource first became available in v2.0.16 of InSpec.
## Syntax
The name of the resource group and machine are required to use this resource.
describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'MyVM') do
its('property') { should eq 'value' }
end
where:
- `MyVm` is the name of the virtual machine as seen in Azure. (It is **not** the hostname of the machine)
- `InSpec-Azure` is the name of the resource group that the machine is in.
- `property` is a resource property
- `value` is the expected output from the matcher
## Examples
The following examples show to use this Chef InSpec audit resource.
### Check that the first data disk is of the correct size
describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Linux-Internal-VM').where(number: 1) do
its('size') { should cmp >= 15 }
end
## Resource Parameters
- `group_name`
- `name`
- `apiversion`
## Parameter Examples
The options that can be passed to the resource are as follows.
### `group_name` (required)
Use this parameter to define the Azure Resource Group to be tested.
describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure') do
...
end
### name
Use this parameter to define the name of the Azure resource to test.
describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Windows-Internal-VM') do
...
end
### apiversion
The API Version to use when querying the resource. Defaults to the latest version for the resource.
describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Windows-Internal-VM', apiversion: '2.0') do
...
end
These options can also be set using the environment variables:
- `AZURE_RESOURCE_GROUP_NAME`
- `AZURE_RESOURCE_NAME`
- `AZURE_RESOURCE_API_VERSION`
When the options have been set as well as the environment variables, the environment variables take priority.
## Filter Criteria
- `number`
- `disk`
## Filter Examples
### disk
The zero based index of the disk attached to the machine.
describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Windows-Internal-VM').where(disk: 0)
end
### number
The '1' based index of the disk attached to the machine.
describe azure_virtual_machine_data_disk(group_name: 'InSpec-Azure', name: 'Windows-Internal-VM').where(number: 1)
end
## Properties
- `count`, `disk`, `number`, `name`, `size`, `lun`, `caching`, `create_option`, `is_managed_disk?`, `vhd_uri`, `storage_account_name`, `storage_account_type`, `id`, `subscription_id`, `resource_group`
## Property Examples
### count
Returns the number of data disks attached to the machine
its('count') { should eq 1 }
### name
Returns a string of the name of the disk.
its('name') { should cmp 'linux-external-datadisk-1' }
### size
Returns an integer of size of this disk in GB.
its('size') { should cmp >= 15 }
### lun
The disk number as reported by Azure. Has a zero-based index value.
its('lun') { should cmp 0 }
### caching
String stating the caching that has been set on the disk.
its('caching') { should cmp 'none' }
### create_option
How the disk was created. Typically for data disks, this will be the string value 'Empty'.
its('create_option') { should cmp 'Empty' }
### is_managed_disk?
Boolean stating if the disk is a managed disk or not. If it is not a managed disk then it is one that is stored in a Storage Account.
its('is_managed_disk?') { should cmp 'false' }
### vhd_uri
If this is _not_ a managed disk, then the `vhd_uri` will be the full URI to the disk in the storage account.
its('vhd_uri') { should cmp 'https://primary_storage.blob.core.windows.net/container_name/vm_name.vhd' }
### storage_account_name
If this is _not_ a managed disk this will be the storage account name in which the disk is stored.
This derived from the `vhd_uri`.
its('storage_account_name') { should cmp 'primary_storage' }
### storage_account_type
If this is a managed disk this is the storage account type, e.g. `Standard_LRS`.
its('storage_account_type') { should cmp 'Standard_LRS' }
### id
If this is a managed disk then this is the fully qualified id for the disk in Azure.
its('id') { should cmp '/subscriptions/1234abcd-e567-890f-g123-456h78i9jkl0/resourceGroups/InSpec-Azure' }
### subscription_id
If this is a managed disk, this returns the subscription id of where the disk is stored.
This is derived from the `id`.
its('subscription_id') { should cmp '1234abcd-e567-890f-g123-456h78i9jkl0' }
### resource_group
If this is a managed disk, this returns the resource group in which the disk is stored.
This is derived from the `id`.
its('resource_group') { should cmp 'InSpec-Azure' }
## Matchers
This Chef InSpec audit resource has the following special matchers. For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
The following properties are applied to the virtual machine itself and not specific disks.
### have_data_disks
Returns a boolean denoting if any data disks are attached to the machine.
it { should have_data_disks }
### have_managed_disks
Returns a boolean stating if the machine has Managed Disks for data disks.
it { should have_managed_disks }
## References
- [Azure Ruby SDK - Compute](https://github.com/Azure/azure-sdk-for-ruby/tree/master/management/azure_mgmt_compute)
- [Linux Internal Data Disks](https://github.com/inspec/inspec/blob/main/test/integration/azure/verify/controls/virtual_machine_linux_external_vm_datadisk.rb)
- [Windows Internal Data Disk](https://github.com/inspec/inspec/blob/main/test/integration/azure/verify/controls/virtual_machine_windows_internal_vm_datadisk.rb)

View file

@ -82,7 +82,7 @@ The following examples show how to use this InSpec audit resource.
Filters the results to include only those Users that match the given Filters the results to include only those Users that match the given
name. This is a string value. name. This is a string value.
describe azurerm_ad_users.where{ displayName.eql?('Joe Bloggs') } do describe azurerm_ad_users.where{ displayName.eql?('Haris Shefu') } do
it { should exist } it { should exist }
end end

View file

@ -104,7 +104,7 @@ Indicates the type of database account, e.g. `GlobalDocumentDB`, `MongoDB`
### tags ### tags
Resource tags applied to the ComsosDb Account. Resource tags applied to the Cosmos DB Account.
### properties ### properties

View file

@ -49,7 +49,7 @@ This resource first became available in 1.11.0 of the inspec-azure resource pack
The `resource_group`, `namespace_name`, `event_hub_name` and `authorization_rule_name` must be given as a parameter. The `resource_group`, `namespace_name`, `event_hub_name` and `authorization_rule_name` must be given as a parameter.
describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name 'my-event-hub-ns', event_hub_name: 'myeventhub', authorization_rule_name: 'my-auth-rule') do describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name 'event-hub-namespace', event_hub_name: 'event-hub', authorization_rule_name: 'my-auth-rule') do
it { should exist } it { should exist }
end end
@ -57,13 +57,13 @@ The `resource_group`, `namespace_name`, `event_hub_name` and `authorization_rule
If an Event Hub Authorization Rule is referenced with a valid `Resource Group`, `Namespace Name`, `Event Hub Name` and `Authorization Rule Name` If an Event Hub Authorization Rule is referenced with a valid `Resource Group`, `Namespace Name`, `Event Hub Name` and `Authorization Rule Name`
describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_endpoint: 'myeventhub', authorization_rule: 'my-auth-rule') do describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name: 'event-hub-namespace', event_hub_endpoint: 'event-hub', authorization_rule: 'my-auth-rule') do
it { should exist } it { should exist }
end end
If a Event Hub Authorization Rule is referenced with an invalid `Resource Group`, `Namespace Name`, `Event Hub Name` or `Authorization Rule Name` If a Event Hub Authorization Rule is referenced with an invalid `Resource Group`, `Namespace Name`, `Event Hub Name` or `Authorization Rule Name`
describe azurerm_event_hub_namespace(resource_group: 'invalid-rg', namespace_name: 'i-dont-exist', event_hub_endpoint: 'fakeendpoint', authorization_rule: 'fake-auth-rule') do describe azurerm_event_hub_namespace(resource_group: 'invalid-rg', namespace_name: 'i-do-not-exist', event_hub_endpoint: 'fake-endpoint', authorization_rule: 'fake-auth-rule') do
it { should_not exist } it { should_not exist }
end end
@ -117,7 +117,7 @@ requests are always welcome.
### exists ### exists
describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name 'my-event-hub-ns', event_hub_name: 'myeventhub', authorization_rule_name: 'my-auth-rule') do describe azurerm_event_hub_authorization_rule(resource_group: 'my-rg', namespace_name 'event-hub-namespace', event_hub_name: 'event-hub', authorization_rule_name: 'my-auth-rule') do
it { should exist } it { should exist }
end end

View file

@ -49,7 +49,7 @@ This resource first became available in 1.11.0 of the inspec-azure resource pack
The `resource_group`, `namespace_name` and `event_hub_name` must be given as a parameter. The `resource_group`, `namespace_name` and `event_hub_name` must be given as a parameter.
describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name 'my-event-hub-ns', event_hub_name 'myeventhub') do describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name 'my-event-hub-ns', event_hub_name 'event-hub') do
it { should exist } it { should exist }
end end
@ -57,13 +57,13 @@ The `resource_group`, `namespace_name` and `event_hub_name` must be given as a p
If an Event Hub Event Hub is referenced with a valid `Resource Group`, `Namespace Name` and `Event Hub Name` If an Event Hub Event Hub is referenced with a valid `Resource Group`, `Namespace Name` and `Event Hub Name`
describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_name 'myeventhub') do describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_name 'event-hub') do
it { should exist } it { should exist }
end end
If a Event Hub Event Hub is referenced with an invalid `Resource Group`, `Namespace Name` and `Event Hub Name` If a Event Hub Event Hub is referenced with an invalid `Resource Group`, `Namespace Name` and `Event Hub Name`
describe azurerm_event_hub_event_hub(resource_group: 'invalid-rg', namespace_name: 'i-dont-exist', event_hub_name 'i-dont-exist') do describe azurerm_event_hub_event_hub(resource_group: 'invalid-rg', namespace_name: 'i-do-not-exist', event_hub_name 'i-do-not-exist') do
it { should_not exist } it { should_not exist }
end end
@ -86,7 +86,7 @@ Azure resource ID.
### name ### name
Event Hub name, e.g. `myeventhub`. Event Hub name, e.g. `event-hub`.
### type ### type
@ -116,7 +116,7 @@ requests are always welcome.
### exists ### exists
describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_name: 'myeventhub') do describe azurerm_event_hub_event_hub(resource_group: 'my-rg', namespace_name: 'my-event-hub-ns', event_hub_name: 'event-hub') do
it { should exist } it { should exist }
end end

View file

@ -63,7 +63,7 @@ If an Event Hub Namespace is referenced with a valid `Resource Group` and `Names
If an Event Hub Namespace is referenced with an invalid `Resource Group` or `Namespace Name` If an Event Hub Namespace is referenced with an invalid `Resource Group` or `Namespace Name`
describe azurerm_event_hub_namespace(resource_group: 'invalid-rg', namespace_name: 'i-dont-exist') do describe azurerm_event_hub_namespace(resource_group: 'invalid-rg', namespace_name: 'i-do-not-exist') do
it { should_not exist } it { should_not exist }
end end

View file

@ -63,7 +63,7 @@ If an IoT Hub is referenced with a valid `Resource Group` and `Resource Name`
If an IoT Hub is referenced with an invalid `Resource Group` or `Resource Name` If an IoT Hub is referenced with an invalid `Resource Group` or `Resource Name`
describe azurerm_iothub(resource_group: 'invalid-rg', resource_name: 'i-dont-exist') do describe azurerm_iothub(resource_group: 'invalid-rg', resource_name: 'i-do-not-exist') do
it { should_not exist } it { should_not exist }
end end

View file

@ -50,7 +50,7 @@ This resource first became available in 1.11.0 of the inspec-azure resource pack
The `resource_group`, `resource_name`, `event_hub_endpoint` and `consumer_group` must be given as a parameter. The `resource_group`, `resource_name`, `event_hub_endpoint` and `consumer_group` must be given as a parameter.
describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub', consumer_group: 'my-consumer-group') do describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub', consumer_group: 'my-consumer-group') do
it { should exist } it { should exist }
end end
@ -58,13 +58,13 @@ The `resource_group`, `resource_name`, `event_hub_endpoint` and `consumer_group`
If an IoT Hub Event Hub Consumer Group is referenced with a valid `Resource Group`, `Resource Name`, `Event Hub Endpoint` and `Consumer Group` If an IoT Hub Event Hub Consumer Group is referenced with a valid `Resource Group`, `Resource Name`, `Event Hub Endpoint` and `Consumer Group`
describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub', consumer_group: 'my-consumer-group') do describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub', consumer_group: 'my-consumer-group') do
it { should exist } it { should exist }
end end
If an IoT Hub Event Hub Consumer Group is referenced with an invalid `Resource Group`, `Resource Name`, `Event Hub Endpoint` or `Consumer Group` If an IoT Hub Event Hub Consumer Group is referenced with an invalid `Resource Group`, `Resource Name`, `Event Hub Endpoint` or `Consumer Group`
describe azurerm_iothub_event_hub_consumer_group(resource_group: 'invalid-rg', resource_name: 'invalid-resource', event_hub_endpoint: 'invalideventhub', consumer_group: 'invalid-consumer-group') do describe azurerm_iothub_event_hub_consumer_group(resource_group: 'invalid-rg', resource_name: 'invalid-resource', event_hub_endpoint: 'invalid-event-hub', consumer_group: 'invalid-consumer-group') do
it { should_not exist } it { should_not exist }
end end
@ -123,7 +123,7 @@ requests are always welcome.
### exists ### exists
describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub', consumer_group: 'my-consumer-group') do describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub', consumer_group: 'my-consumer-group') do
it { should exist } it { should exist }
end end

View file

@ -49,7 +49,7 @@ This resource first became available in 1.11.0 of the inspec-azure resource pack
The `resource_group`, `resource_name` and `event_hub_endpoint` must be given as a parameter. The `resource_group`, `resource_name` and `event_hub_endpoint` must be given as a parameter.
describe azurerm_iothub_event_hub_consumer_groups(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub') do describe azurerm_iothub_event_hub_consumer_groups(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub') do
its('names') { should include "my-consumer-group"} its('names') { should include "my-consumer-group"}
its('types') { should include 'Microsoft.Devices/IotHubs/EventHubEndpoints/ConsumerGroups' } its('types') { should include 'Microsoft.Devices/IotHubs/EventHubEndpoints/ConsumerGroups' }
end end
@ -58,7 +58,7 @@ The `resource_group`, `resource_name` and `event_hub_endpoint` must be given as
If a IoT Hub Event Hub Consumer Groups is referenced with a valid `Resource Group`, `Resource Name` and `Event Hub Endpoint` If a IoT Hub Event Hub Consumer Groups is referenced with a valid `Resource Group`, `Resource Name` and `Event Hub Endpoint`
describe azurerm_iothub_event_hub_consumer_groups(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub') do describe azurerm_iothub_event_hub_consumer_groups(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub') do
it { should exist } it { should exist }
end end
@ -126,7 +126,7 @@ requests are always welcome.
### exists ### exists
describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'myeventhub') do describe azurerm_iothub_event_hub_consumer_group(resource_group: 'my-rg', resource_name 'my-iot-hub', event_hub_endpoint: 'event-hub') do
it { should exist } it { should exist }
end end

View file

@ -63,7 +63,7 @@ If a Load Balancer is referenced with a valid `Resource Group` and `Load balance
If a Load Balancer is referenced with an invalid `Resource Group` or `Load balancer Name` If a Load Balancer is referenced with an invalid `Resource Group` or `Load balancer Name`
describe azurerm_load_balancer(resource_group: 'invalid-rg', loadbalancer_name: 'i-dont-exist') do describe azurerm_load_balancer(resource_group: 'invalid-rg', loadbalancer_name: 'i-do-not-exist') do
it { should_not exist } it { should_not exist }
end end

View file

@ -256,12 +256,12 @@ requests are always welcome.
### exists ### exists
# If a management group is found it will exist # If a management group is found it will exist
describe azurerm_management_group(groupd_id: 'MyGroupId') do describe azurerm_management_group(group_id: 'MyGroupId') do
it { should exist } it { should exist }
end end
# management groups that aren't found will not exist # management groups that aren't found will not exist
describe azurerm_management_group(groupd_id: 'DoesNotExist') do describe azurerm_management_group(group_id: 'DoesNotExist') do
it { should_not exist } it { should_not exist }
end end

View file

@ -46,7 +46,7 @@ This resource first became available in 1.6.0 of the inspec-azure resource pack.
## Syntax ## Syntax
An `azurerm_mysql_databases` resource block returns all MySQL Databases on a MySQL Server, within a Rsource Group. An `azurerm_mysql_databases` resource block returns all MySQL Databases on a MySQL Server, within a resource group.
describe azurerm_mysql_databases(resource_group: ..., server_name: ...) do describe azurerm_mysql_databases(resource_group: ..., server_name: ...) do
... ...

View file

@ -63,7 +63,7 @@ If a SQL Server is referenced with a valid `Resource Group` and `Server Name`
If a SQL Server is referenced with an invalid `Resource Group` or `Server Name` If a SQL Server is referenced with an invalid `Resource Group` or `Server Name`
describe azurerm_sql_server(resource_group: 'invalid-rg', server_name: 'i-dont-exist') do describe azurerm_sql_server(resource_group: 'invalid-rg', server_name: 'i-do-not-exist') do
it { should_not exist } it { should_not exist }
end end

View file

@ -62,7 +62,7 @@ If a Network Interface is referenced with a valid `Resource Group` and `Name`
If a Network Interface is referenced with an invalid `Resource Group` or `Name` If a Network Interface is referenced with an invalid `Resource Group` or `Name`
describe azurerm_network_interface(resource_group: 'invalid-rg', name: 'i-dont-exist') do describe azurerm_network_interface(resource_group: 'invalid-rg', name: 'i-do-not-exist') do
it { should_not exist } it { should_not exist }
end end

View file

@ -63,7 +63,7 @@ If a PostgreSQL Server is referenced with a valid `Resource Group` and `Server N
If a PostgreSQL Server is referenced with an invalid `Resource Group` or `Server Name` If a PostgreSQL Server is referenced with an invalid `Resource Group` or `Server Name`
describe azurerm_postgresql_server(resource_group: 'invalid-rg', server_name: 'i-dont-exist') do describe azurerm_postgresql_server(resource_group: 'invalid-rg', server_name: 'i-do-not-exist') do
it { should_not exist } it { should_not exist }
end end

View file

@ -46,7 +46,7 @@ This resource first became available in 1.2.0 of the inspec-azure resource pack.
## Syntax ## Syntax
An `azurerm_sql_databases` resource block returns all SQL Databases on a SQL Server, within a Rsource Group. An `azurerm_sql_databases` resource block returns all SQL Databases on a SQL Server, within a resource group.
describe azurerm_sql_databases(resource_group: ..., server_name: ...) do describe azurerm_sql_databases(resource_group: ..., server_name: ...) do
... ...

View file

@ -63,7 +63,7 @@ If a SQL Server is referenced with a valid `Resource Group` and `Server Name`
If a SQL Server is referenced with an invalid `Resource Group` or `Server Name` If a SQL Server is referenced with an invalid `Resource Group` or `Server Name`
describe azurerm_sql_server(resource_group: 'invalid-rg', server_name: 'i-dont-exist') do describe azurerm_sql_server(resource_group: 'invalid-rg', server_name: 'i-do-not-exist') do
it { should_not exist } it { should_not exist }
end end

View file

@ -123,13 +123,13 @@ The subnet's id.
Id will be in Id will be in
format: format:
'/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/Inspec-Azure-mmclane/providers/Microsoft.Network/virtualNetworks/Inspec-VNet/subnets/Inspec-Subnet' '/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Network/virtualNetworks/Inspec-VNet/subnets/Inspec-Subnet'
### name ### name
The subnets's name. The subnets's name.
its('name') { should eq('MySubnetName') } its('name') { should eq('SubnetName') }
### type ### type

View file

@ -55,7 +55,7 @@ The `resource_group` and 'vnet' must be given as a parameter.
## Examples ## Examples
# Exists if any subnetss exist for a given virtual network in the resource group # Exists if any subnets exist for a given virtual network in the resource group
describe azurerm_subnets(resource_group: 'MyResourceGroup', vnet: 'MyVnetName') do describe azurerm_subnets(resource_group: 'MyResourceGroup', vnet: 'MyVnetName') do
it { should exist } it { should exist }
end end

View file

@ -121,7 +121,7 @@ The virtual network's id.
Id will be in Id will be in
format: format:
'/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/Inspec-Azure-mmclane/providers/Microsoft.Network/virtualNetworks/MyVnetName' '/subscriptions/<subscription-id>/resourceGroups/<resource-group-name>/providers/Microsoft.Network/virtualNetworks/MyVnetName'
### name ### name

View file

@ -100,7 +100,7 @@ The Resource Group as well as the Webapp name.
- `auth_settings` - `auth_settings`
- `configuration` - `configuration`
All of the attributes are avialable via dot notation. This is an example of the currently available attributes. All of the attributes are available via dot notation. This is an example of the currently available attributes.
```ruby ```ruby
control 'azurerm_webapp' do control 'azurerm_webapp' do

View file

@ -167,7 +167,7 @@ By default the command that is ran is shown in the Chef InSpec output. This can
The following examples show how to use `redact_regex`: The following examples show how to use `redact_regex`:
# Example without capture groups # Example without capture groups
describe command('myapp -p secretpassword -d no_redact', redact_regex: /-p .* -d/) do describe command('myapp -p secret_password -d no_redact', redact_regex: /-p .* -d/) do
its('exit_status') { should cmp 0 } its('exit_status') { should cmp 0 }
end end
@ -178,7 +178,7 @@ The following examples show how to use `redact_regex`:
# Example with capture groups # Example with capture groups
# Each set of parenthesis is a capture group. # Each set of parenthesis is a capture group.
# Anything in the two capture groups will not be 'REDACTED' # Anything in the two capture groups will not be 'REDACTED'
describe command('myapp -p secretpassword -d no_redact', redact_regex: /(-p ).*( -d)/) do describe command('myapp -p secret_password -d no_redact', redact_regex: /(-p ).*( -d)/) do
its('exit_status') { should cmp 0 } its('exit_status') { should cmp 0 }
end end

View file

@ -60,7 +60,7 @@ This resource uses package names and perl library paths as resource parameters.
Hint: You can pass multiple paths separated with a colon Hint: You can pass multiple paths separated with a colon
`/path/to/perl5/lib:/usr/share/perl5/vendor_perl/lib/perl5` `/path/to/perl5/lib:/usr/share/perl5/vendor_perl/lib/perl5`
describe cpan('DBD::Pg', '/home/jdoe/perl5/lib/perl5') do describe cpan('DBD::Pg', '/home/username/perl5/lib/perl5') do
it { should be_installed } it { should be_installed }
end end

View file

@ -55,7 +55,7 @@ The following examples show how to use this audit resource.
it { should have_entry "5 * * * * /some/scheduled/task.sh" } it { should have_entry "5 * * * * /some/scheduled/task.sh" }
end end
### Test to ensure myuser's crontab has a particular cron entry ### Test to ensure a user's crontab has a particular cron entry
describe cron('MY_USER') do describe cron('MY_USER') do
it { should have_entry "5 * * * * /some/scheduled/task.sh" } it { should have_entry "5 * * * * /some/scheduled/task.sh" }

View file

@ -55,9 +55,9 @@ The following examples show how to use this Chef InSpec audit resource.
its('commands') { should include '/path/to/some/script -option arg' } its('commands') { should include '/path/to/some/script -option arg' }
end end
### Test that myuser's crontab entry for command '/home/myuser/build.sh' runs every minute ### Test that username's crontab entry for command '/home/username/build.sh' runs every minute
describe crontab('myuser').commands('/home/myuser/build.sh') do describe crontab('username').commands('/home/username/build.sh') do
its('hours') { should cmp '*' } its('hours') { should cmp '*' }
its('minutes') { should cmp '*' } its('minutes') { should cmp '*' }
end end

View file

@ -25,7 +25,7 @@ This resource first became available in v1.21.0 of InSpec.
## Syntax ## Syntax
A `docker` resource block declares allows you to write test for many containers: A `docker` resource block allows you to write tests for many containers:
describe docker.containers do describe docker.containers do
its('images') { should_not include 'u12:latest' } its('images') { should_not include 'u12:latest' }
@ -33,7 +33,7 @@ A `docker` resource block declares allows you to write test for many containers:
or: or:
describe docker.containers.where { names == 'flamboyant_colden' } do describe docker.containers.where { names == 'flamboyant_allen' } do
it { should be_running } it { should be_running }
end end
@ -45,7 +45,7 @@ where
The `docker` resource block also declares allows you to write test for many images: The `docker` resource block also declares allows you to write test for many images:
describe docker.images do describe docker.images do
its('repositories') { should_not include 'inssecure_image' } its('repositories') { should_not include 'insecure_image' }
end end
or if you want to query specific images: or if you want to query specific images:

View file

@ -29,7 +29,7 @@ A `docker_service` resource block declares the service by name:
describe docker_service('foo') do describe docker_service('foo') do
it { should exist } it { should exist }
its('id') { should eq '2ghswegspre1' } its('id') { should eq 'docker-service-id' }
its('repo') { should eq 'alpine' } its('repo') { should eq 'alpine' }
its('tag') { should eq 'latest' } its('tag') { should eq 'latest' }
end end
@ -38,7 +38,7 @@ A `docker_service` resource block declares the service by name:
The resource allows you to pass in a service id: The resource allows you to pass in a service id:
describe docker_service(id: '2ghswegspre1') do describe docker_service(id: 'docker-service-id') do
... ...
end end
@ -56,7 +56,7 @@ The following examples show how to use Chef InSpec `docker_service` resource.
The `id` property returns the service id: The `id` property returns the service id:
its('id') { should eq '2ghswegspre1' } its('id') { should eq 'docker-service-id' }
### image ### image
@ -104,7 +104,7 @@ The `tag` property tests the value of image tag:
describe docker_service('foo') do describe docker_service('foo') do
it { should exist } it { should exist }
its('id') { should eq '2ghswegspre1' } its('id') { should eq 'docker-service-id' }
its('repo') { should eq 'alpine' } its('repo') { should eq 'alpine' }
its('tag') { should eq 'latest' } its('tag') { should eq 'latest' }
end end

View file

@ -77,7 +77,8 @@ The `all_host_names` property returns a two-dimensional string array where each
its('ip_address') { should cmp '127.0.1.154' } its('ip_address') { should cmp '127.0.1.154' }
end end
### Test the primay name for where ip address is '::1' ### Test the primary name for where IP address is '::1'
describe etc_hosts.where { ip_address == '::1' } do describe etc_hosts.where { ip_address == '::1' } do
its('primary_name') { should cmp 'localhost' } its('primary_name') { should cmp 'localhost' }
end end

View file

@ -61,7 +61,7 @@ The `free_kb` property returns the size of available space on the partition in k
its('size_kb') { should be >= 32000 } its('size_kb') { should be >= 32000 }
## percent_free (Integrer) ## percent_free (Integer)
The `percent_free` property returns the available free space on the partition, ranges from 0 to 100. The `percent_free` property returns the available free space on the partition, ranges from 0 to 100.

View file

@ -180,7 +180,7 @@ You can include the username and password in the `proxy` parameter:
The `proxy` parameter also accepts proxy options in hash format: The `proxy` parameter also accepts proxy options in hash format:
describe http('http://localhost:8080/ping', proxy: { uri: 'http://www.example.com:3128', user: 'username', password: 'proxypassword'}) do describe http('http://localhost:8080/ping', proxy: { uri: 'http://www.example.com:3128', user: 'username', password: 'proxy-password'}) do
... ...
end end

View file

@ -70,7 +70,7 @@ For example:
`physical_path` property returns the physical path of the application, such as `'C:\\inetpub\\wwwroot\\myapp'`. `physical_path` property returns the physical path of the application, such as `'C:\\inetpub\\wwwroot\\myapp'`.
its('phyiscal_path') { should eq 'C:\\inetpub\\wwwroot\\myapp' } its('physical_path') { should eq 'C:\\inetpub\\wwwroot\\myapp' }
### protocols ### protocols

View file

@ -55,7 +55,7 @@ Settings inside of sections, such as the following:
In the event a section or setting name has a period in it, the alternate syntax can be used: In the event a section or setting name has a period in it, the alternate syntax can be used:
its(['section.with.a.dot.in.it', 'setting.name.with.dots']) { should cmp 'lotsadots' } its(['section.with.a.dot.in.it', 'setting.name.with.dots']) { should cmp 'lots-of-dots' }
## Properties ## Properties

View file

@ -69,6 +69,6 @@ For a full list of available matchers, please visit our [matchers page](/inspec/
### have_rule ### have_rule
The `have_rule` matcher tests the named rule against the information in the output rule of `'ipftstat -io'`: The `have_rule` matcher tests the named rule against the information in the output rule of `'ipfstat -io'`:
it { should have_rule("RULE") } it { should have_rule("RULE") }

View file

@ -29,16 +29,16 @@ This resource first became available in v1.18.0 of InSpec.
An `key_rsa` resource block declares a `key file` to be tested. An `key_rsa` resource block declares a `key file` to be tested.
describe key_rsa('mycertificate.key') do describe key_rsa('certificate.key') do
it { should be_private } it { should be_private }
it { should be_public } it { should be_public }
its('public_key') { should match "-----BEGIN PUBLIC KEY-----\n3597459df9f3982" } its('public_key') { should match "PUBLIC_KEY" }
its('key_length') { should eq 2048 } its('key_length') { should eq 2048 }
end end
You can use an optional passphrase with `key_rsa` You can use an optional passphrase with `key_rsa`
describe key_rsa('mycertificate.key', 'passphrase') do describe key_rsa('certificate.key', 'passphrase') do
it { should be_private } it { should be_private }
end end
@ -48,23 +48,23 @@ You can use an optional passphrase with `key_rsa`
The `public_key` property returns the public part of the RSA key pair The `public_key` property returns the public part of the RSA key pair
describe key_rsa('/etc/pki/www.mywebsite.com.key') do describe key_rsa('/etc/pki/www.example.com.key') do
its('public_key') { should match "-----BEGIN PUBLIC KEY-----\n3597459df9f3982......" } its('public_key') { should match "RSA_PUBLIC_KEY" }
end end
### private_key (String) ### private_key (String)
The `private_key` property returns the private key or the RSA key pair. The `private_key` property returns the private key or the RSA key pair.
describe key_rsa('/etc/pki/www.mywebsite.com.key') do describe key_rsa('/etc/pki/www.example.com.key') do
its('private_key') { should match "-----BEGIN RSA PRIVATE KEY-----\nMIIJJwIBAAK......" } its('private_key') { should match "RSA_PRIVATE_KEY" }
end end
### key_length ### key_length
The `key_length` property allows testing the number of bits in the key pair. The `key_length` property allows testing the number of bits in the key pair.
describe key_rsa('/etc/pki/www.mywebsite.com.key') do describe key_rsa('/etc/pki/www.example.com.key') do
its('key_length') { should eq 2048 } its('key_length') { should eq 2048 }
end end
@ -76,7 +76,7 @@ For a full list of available matchers, please visit our [matchers page](/inspec/
To verify if a key is public use the following: To verify if a key is public use the following:
describe key_rsa('/etc/pki/www.mywebsite.com.key') do describe key_rsa('/etc/pki/www.example.com.key') do
it { should be_public } it { should be_public }
end end
@ -84,6 +84,6 @@ To verify if a key is public use the following:
This property verifies that the key includes a private key: This property verifies that the key includes a private key:
describe key_rsa('/etc/pki/www.mywebsite.com.key') do describe key_rsa('/etc/pki/www.example.com.key') do
it { should be_private } it { should be_private }
end end

View file

@ -59,7 +59,7 @@ The following examples show how to use this Chef InSpec audit resource.
### Test a specific host and instance ### Test a specific host and instance
sql = mssql_session(user: 'my_user', password: 'password', host: 'mssqlserver', instance: 'foo') sql = mssql_session(user: 'my_user', password: 'password', host: 'ms-sql-server', instance: 'foo')
describe sql.query("SELECT SERVERPROPERTY('ProductVersion') as result").row(0).column('result') do describe sql.query("SELECT SERVERPROPERTY('ProductVersion') as result").row(0).column('result') do
its("value") { should cmp > '12.00.4457' } its("value") { should cmp > '12.00.4457' }

View file

@ -25,15 +25,15 @@ This resource first became available in v1.0.0 of InSpec.
## Syntax ## Syntax
A `oracledb_session` resource block declares the username and password to use for the session with an optional service to connect to, and then the command to be run: A `oracledb_session` resource block declares the username and PASSWORD to use for the session with an optional service to connect to, and then the command to be run:
describe oracledb_session(user: 'username', password: 'password', service: 'ORCL.localdomain').query('QUERY').row(0).column('result') do describe oracledb_session(user: 'username', PASSWORD: 'PASSWORD', service: 'ORCL.localdomain').query('QUERY').row(0).column('result') do
its('value') { should eq('') } its('value') { should eq('') }
end end
where where
- `oracledb_session` declares a username and password with permission to run the query (required), and an optional parameters for host (default: `localhost`), SID (default: `nil`, which uses the default SID, and path to the sqlplus binary (default: `sqlplus`). - `oracledb_session` declares a username and PASSWORD with permission to run the query (required), and an optional parameters for host (default: `localhost`), system identifier (SID) (default: `nil`), which uses the default SID, and path to the sqlplus binary (default: `sqlplus`).
- it is possible to run queries as sysdba/sysoper by using `as_db_role option`, see examples - it is possible to run queries as sysdba/sysoper by using `as_db_role option`, see examples
- SQLcl can be used in place of sqlplus. Use the `sqlcl_bin` option to set the sqlcl binary path instead of `sqlplus_bin`. - SQLcl can be used in place of sqlplus. Use the `sqlcl_bin` option to set the sqlcl binary path instead of `sqlplus_bin`.
- `query('QUERY')` contains the query to be run - `query('QUERY')` contains the query to be run
@ -51,7 +51,7 @@ The following examples show how to use this Chef InSpec audit resource.
### Test for matching databases ### Test for matching databases
sql = oracledb_session(user: 'my_user', pass: 'password') sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD')
describe sql.query('SELECT NAME AS VALUE FROM v$database;').row(0).column('value') do describe sql.query('SELECT NAME AS VALUE FROM v$database;').row(0).column('value') do
its('value') { should cmp 'ORCL' } its('value') { should cmp 'ORCL' }
@ -59,7 +59,7 @@ The following examples show how to use this Chef InSpec audit resource.
### Test for matching databases with custom host, SID and sqlplus binary location ### Test for matching databases with custom host, SID and sqlplus binary location
sql = oracledb_session(user: 'my_user', pass: 'password', host: 'oraclehost', sid: 'mysid', sqlplus_bin: '/u01/app/oracle/product/12.1.0/dbhome_1/bin/sqlplus') sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD', host: 'ORACLE_HOST', sid: 'ORACLE_SID', sqlplus_bin: '/u01/app/oracle/product/12.1.0/dbhome_1/bin/sqlplus')
describe sql.query('SELECT NAME FROM v$database;').row(0).column('name') do describe sql.query('SELECT NAME FROM v$database;').row(0).column('name') do
its('value') { should cmp 'ORCL' } its('value') { should cmp 'ORCL' }
@ -67,9 +67,9 @@ The following examples show how to use this Chef InSpec audit resource.
### Test for table contains a specified value in any row for the given column name ### Test for table contains a specified value in any row for the given column name
sql = oracledb_session(user: 'my_user', pass: 'password', service: 'MYSID') sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD', service: 'ORACLE_SID')
describe sql.query('SELECT * FROM my_table;').column('my_column') do describe sql.query('SELECT * FROM my_table;').column('COLUMN') do
it { should include 'my_value' } it { should include 'my_value' }
end end
@ -77,16 +77,16 @@ The following examples show how to use this Chef InSpec audit resource.
The check will change user (with su) to specified user and run 'sqlplus / as sysdba' (sysoper, sysasm) The check will change user (with su) to specified user and run 'sqlplus / as sysdba' (sysoper, sysasm)
sql = oracledb_session(as_os_user: 'oracle', as_db_role: 'sysdba', service: 'MYSID') sql = oracledb_session(as_os_user: 'oracle', as_db_role: 'sysdba', service: 'ORACLE_SID')
describe sql.query('SELECT tablespace_name AS name FROM dba_tablespaces;').column('name') do describe sql.query('SELECT tablespace_name AS name FROM dba_tablespaces;').column('name') do
it { should include 'MYTABLESPACE' } it { should include 'TABLE_SPACE' }
end end
NOTE: option `as_os_user` available only on unix-like systems and not supported on Windows. Also this option requires that you are running inspec as `root` or with `--sudo` NOTE: option `as_os_user` available only on unix-like systems and not supported on Windows. Also this option requires that you are running inspec as `root` or with `--sudo`
### Test number of rows in the query result ### Test number of rows in the query result
sql = oracledb_session(user: 'my_user', pass: 'password') sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD')
describe sql.query('SELECT * FROM my_table;').rows do describe sql.query('SELECT * FROM my_table;').rows do
its('count') { should eq 20 } its('count') { should eq 20 }
@ -94,7 +94,7 @@ The following examples show how to use this Chef InSpec audit resource.
### Use data out of (remote) DB query to build other tests ### Use data out of (remote) DB query to build other tests
sql = oracledb_session(user: 'my_user', pass: 'password', host: 'my.remote.db', service: 'MYSID') sql = oracledb_session(user: 'USERNAME', pass: 'PASSWORD', host: 'my.remote.db', service: 'ORACLE_SID')
sql.query('SELECT * FROM files;').rows.each do |file_row| sql.query('SELECT * FROM files;').rows.each do |file_row|
describe file(file_row['path']) do describe file(file_row['path']) do

View file

@ -58,7 +58,7 @@ where
### gids ### gids
The `gids` property tests if the group indentifiers in the test match group identifiers in `/etc/passwd`: The `gids` property tests if the group identifiers in the test match group identifiers in `/etc/passwd`:
its('gids') { should include 1234 } its('gids') { should include 1234 }
its('gids') { should cmp 0 } its('gids') { should cmp 0 }

View file

@ -0,0 +1,218 @@
+++
title = "podman resource"
draft = false
gh_repo = "inspec"
platform = "unix"
[menu]
[menu.inspec]
title = "podman"
identifier = "inspec/resources/os/podman.md podman resource"
parent = "inspec/resources/os"
+++
Use the `podman` Chef InSpec audit resource to test the configuration data for the Podman resources.
## Availability
### Installation
This resource is distributed with Chef InSpec and is automatically available for use.
## Syntax
A `podman` resource block allows you to write a test for many `containers`.
```ruby
describe podman.containers do
its('ids') { should include "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" }
its('images) { should include "docker.io/library/ubuntu:latest" }
end
```
Or, if you want to query a specific `container`:
```ruby
describe podman.containers.where(id: "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7") do
its('status') { should include "Up 44 hours ago" }
end
```
> Where
>
> - `.where()` specifies a specific item and value to which the resource parameters are compared.
> - `commands`, `created_at`, `ids`, `images`, `names`, `status`, `image_ids`, `labels`, `mounts`, `networks`, `pods`, `ports`, `running_for`, and `sizes` are valid parameters for `containers`.
The `podman` resource block also allows you to write a test for many `images`.
```ruby
describe podman.images do
its('repositories') { should_not include 'docker.io/library/nginx' }
end
```
Or, if you want to query a specific `image`:
```ruby
describe podman.images.where(id: "c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f") do
it { should exist }
end
```
> Where
>
> - `.where()` specifies a specific filter and expected value, against which parameters are compared.
> - `repositories`, `tags`, `sizes`, `digests`, `history`, `created_at`, `history`, and`created_since` are valid parameters for `images`.
The `podman` resource block also allows you to write a test for many `networks`.
```ruby
describe podman.networks do
its("names") { should include "podman" }
end
```
Or, if you want to query a specific `network`:
```ruby
describe podman.networks.where(id: "c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f") do
it { should exist }
end
```
> Where
>
> - `.where()` specifies a specific filter and expected value, against which parameters are compared.
> - `ids`, `names`, `drivers`, `network_interfaces`, `created`, `subnets`, `ipv6_enabled`, `internal`, `dns_enabled`, `options`, `labels`, and `ipam_options` are valid parameters for `networks`.
The `podman` resource block also allows you to write a test for many `pods`.
```ruby
describe podman.pods do
its("names") { should include "cranky_allen" }
end
```
Or, if you want to query a specific `pod`:
```ruby
describe podman.pods.where(id: "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc") do
it { should exist }
end
```
> Where
>
> - `.where()` may specify a specific filter and expected value, against which parameters are compared.
> - `ids`, `cgroups`, `containers`, `created`, `infraids`, `names`, `namespaces`, `networks`, `status`, and `labels` are valid parameters for `pods`.
## Examples
The following examples show how to use this Chef InSpec audit resource.
### Returns all running containers
```ruby
podman.containers.running?.ids.each do |id|
describe podman.object(id) do
its('State.Health.Status') { should eq 'healthy' }
end
end
```
## Resource Parameter Examples
### containers
`containers` returns information about containers as returned by [podman ps -a](https://docs.podman.io/en/latest/markdown/podman.1.html).
```ruby
describe podman.containers do
its("ids") { should include "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" }
its("labels") { should include "maintainer" => "NGINX Docker Maintainers \u003cdocker-maint@nginx.com\u003e" }
its('names') { should include "sweet_mendeleev" }
its("images") { should include "docker.io/library/nginx:latest" }
end
```
### images
`images` returns information about a Podman image as returned by [podman images -a](https://docs.podman.io/en/latest/markdown/podman-images.1.html).
```ruby
describe podman.images do
its('ids') { should include 'sha256:c7db653c4397e6a4d1e468bb7c6400c022c62623bdb87c173d54bac7995b6d8f ' }
its('sizes') { should_not include '80.3 GB' }
its('repositories") { should include "docker.io/library/nginx"}
end
```
### pods
`pods` returns information about pods as returned by [podman pod ps](https://docs.podman.io/en/latest/markdown/podman-pod-ps.1.html).
```ruby
describe podman.pods do
its("ids") { should include "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc" }
its("containers") { should eq [{ "Id" => "a218dfc58fa28e0c58c55e508e5b57084876b42e894b98073c69c45dea06cbb2", "Names" => "95cadbb84df7-infra", "Status" => "running" } ]}
its("names") { should include "cranky_allen" }
end
```
### networks
`networks` returns information about a Podman network as returned by [podman network ls](https://docs.podman.io/en/latest/markdown/podman-network-ls.1.html).
```ruby
describe podman.networks do
its("names") { should include "podman" }
its("ids") { should include "2f259bab93aaaaa2542ba43ef33eb990d0999ee1b9924b557b7be53c0b7a1bb9" }
its("ipv6_enabled") { should eq [false] }
end
```
### volumes
`volumes` returns information about a Podman volume as returned by [podman volume ls](https://docs.podman.io/en/latest/markdown/podman-volume-ls.1.html).
```ruby
describe podman.volumes do
its('names') { should include 'ae6be9ba838b9b150de47657229bb9b67142dbdb3d1ddbc5efa245cf1e95536a' }
its('drivers') { should include 'local' }
end
```
### info
`info` returns the parsed result of [podman info](https://docs.podman.io/en/latest/markdown/podman-info.1.html).
```ruby
describe podman.info do
its("host.os") { should eq "linux" }
end
```
### version
`version` returns the parsed result of [podman version](https://docs.podman.io/en/latest/markdown/podman-version.1.html)
```ruby
describe podman.version do
its("Client.Version") { should eq "4.1.0"}
its('Server.Version') { should eq '4.1.0'}
end
```
### object('id')
`object` returns low-level information about Podman objects as returned by [podman inspect](https://docs.podman.io/en/latest/markdown/podman-inspect.1.html).
```ruby
describe docker.object(id) do
its('State.Running') { should eq true }
end
```
## Matchers
For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).

View file

@ -0,0 +1,149 @@
+++
title = "podman_container resource"
draft = false
gh_repo = "inspec"
platform = "unix"
[menu]
[menu.inspec]
title = "podman_container"
identifier = "inspec/resources/os/podman_container.md podman_container resource"
parent = "inspec/resources/os"
+++
Use the `podman_container` Chef InSpec audit resource to test the ...
## Availability
### Installation
This resource is distributed with Chef InSpec and is automatically available for use.
## Syntax
A `podman_container` Chef InSpec audit resource ...
```ruby
describe podman_container("sweet_mendeleev") do
it { should exist }
it { should be_running }
its("id") { should eq "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" }
its("image") { should eq "docker.io/library/nginx:latest" }
its("labels") { should include "maintainer"=>"NGINX Docker Maintainers <docker-maint@nginx.com>" }
its("ports") { should eq nil }
end
```
## Resource Parameter Examples
### name
The container name can be provided with the `name` resource parameter.
```ruby
describe podman_container(name: 'an-echo-server') do
it { should exist }
it { should be_running }
end
```
### container ID
Alternatively, you can pass the container ID.
```ruby
describe podman_container(id: '71b5df59442b') do
it { should exist }
it { should be_running }
end
```
## Properties
## Property Examples
The following examples show how to use this Chef InSpec resource.
### id
The `id` property tests the container ID.
```ruby
its('id') { should eq '71b5df59...442b' }
```
### image
The `image` property tests the value of the container image.
```ruby
its('image') { should eq 'docker.io/library/nginx:latest' }
```
### labels
The `labels` property tests the value of container image labels.
```ruby
its('labels') { should eq "maintainer" => "NGINX Docker Maintainers <docker-maint@nginx.com>" }
```
### ports
The `ports` property tests the value of the Podmans ports.
```ruby
its('ports') { should eq '0.0.0.0:1234->1234/tcp' }
```
### command
The `command` property tests the value of the container run command.
```ruby
its('command') { should eq 'nc -ll -p 1234 -e /bin/cat' }
```
## Matchers
For a full list of available matchers, please visit our [matchers page](/inspec/matchers/). The specific matchers of this resource are: `exist` and `be_running`.
### exist
The `exist` matcher specifies if the container exists.
```ruby
it { should exist }
```
### be_running
The `be_running` matcher checks if the container is running.
```ruby
it { should be_running }
```
## Examples
The following examples show how to use this Chef InSpec audit resource.
### Ensures container exists
The below test passes if the container `sweet_mendeleev` exists as part of the Podman instances.
```ruby
describe podman_container('sweet_mendeleev') do
it { should exist }
end
```
### Ensures container is in running status
The below test passes if the container `sweet_mendeleev` exists as part of the Podman instances and the status is running.
```ruby
describe podman_container('sweet_mendeleev') do
it { should be_running }
end
```

View file

@ -0,0 +1,189 @@
+++
title = "podman_image resource"
draft = false
gh_repo = "inspec"
platform = "unix"
[menu]
[menu.inspec]
title = "podman_image"
identifier = "inspec/resources/os/podman_image.md podman_image resource"
parent = "inspec/resources/os"
+++
Use the `podman_image` Chef InSpec audit resource to test the properties of a container image on Podman.
## Availability
### Installation
This resource is distributed with Chef InSpec and is automatically available for use.
## Syntax
A `podman_image` Chef InSpec audit resource aids in testing the properties of a container image on Podman.
```ruby
describe podman_image("docker.io/library/busybox") do
it { should exist }
its("id") { should eq "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" }
its("repo_tags") { should include "docker.io/library/busybox:latest" }
its("size") { should eq 1636053 }
its("os") { should eq "linux" }
end
```
> where
>
> - `id`, `repo_tags`, `size`, and `os` are properties of this resource to fetch the respective value of the container image.
> - `exist` is a matcher of this resource.
### Resource Parameter Examples
- The resource allows you to pass an image name. If the tag is missing for an image, `latest` is assumed as default.
```ruby
describe podman_image("docker.io/library/busybox") do
it { should exist }
end
```
- The resource allows you to pass the repository and tag values as separate values.
```ruby
describe podman_image(repo: "docker.io/library/busybox", tag: "latest") do
it { should exist }
end
```
- The resource allows you to pass with an image ID.
```ruby
describe podman_image(id: "8847e9bf6df8") do
it { should exist }
end
```
## Properties
### id
The `id` property returns the full image ID.
```ruby
its("id") { should eq "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" }
```
### repo_tags
The `repo_tags` property tests the value of the repository name.
```ruby
its("repo_tags") { should include "docker.io/library/busybox:latest" }
```
### size
The `size` property tests the size of the image in bytes
```ruby
its("size") { should eq 1636053 }
```
### digest
The `digest` property tests the value of the image digest.
```ruby
its("digest") { should eq "sha256:3614ca5eacf0a3a1bcc361c939202a974b4902b9334ff36eb29ffe9011aaad83" }
```
### created_at
The `created_at` property tests the time of the image creation.
```ruby
its("created_at") { should eq "2022-06-08T00:39:28.175020858Z" }
```
### version
The `version` property tests the version of the image.
```ruby
its("version") { should eq "20.10.12" }
```
### names_history
The `names_history` property tests the names history of the image.
```ruby
its("names_history") { should include "docker.io/library/busybox:latest" }
```
### repo_digests
The `repo_digests` tests the digest of the repository of the given image.
```ruby
its("repo_digests") { should include "docker.io/library/busybox@sha256:2c5e2045f35086c019e80c86880fd5b7c7a619878b59e3b7592711e1781df51a" }
```
### architecture
The `architecture` tests the architecture of the given image.
```ruby
its("architecture") { should eq "arm64" }
```
### os
The `os` property tests the operating system of the given image.
```ruby
its("os") { should eq "linux" }
```
### virtual_size
The `virtual_size` property tests the virtual size of the given image.
```ruby
its("virtual_size") { should eq 1636053 }
```
## Matchers
For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
### exist
The `exist` matcher tests if the image is available on Podman.
```ruby
it { should exist }
```
## Examples
### Test if an image exists on Podman and verify the various image properties
```ruby
describe podman_image("docker.io/library/busybox") do
it { should exist }
its("id") { should eq "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" }
its("repo_tags") { should include "docker.io/library/busybox:latest" }
its("size") { should eq 1636053 }
its("digest") { should eq "sha256:3614ca5eacf0a3a1bcc361c939202a974b4902b9334ff36eb29ffe9011aaad83" }
its("created_at") { should eq "2022-06-08T00:39:28.175020858Z" }
its("version") { should eq "20.10.12" }
its("names_history") { should include "docker.io/library/busybox:latest" }
its("repo_digests") { should include "docker.io/library/busybox@sha256:2c5e2045f35086c019e80c86880fd5b7c7a619878b59e3b7592711e1781df51a" }
its("architecture") { should eq "arm64" }
its("os") { should eq "linux" }
its("virtual_size") { should eq 1636053 }
its("resource_id") { should eq "docker.io/library/busybox:latest" }
end
```

View file

@ -0,0 +1,189 @@
+++
title = "podman_network resource"
draft = false
gh_repo = "inspec"
platform = "unix"
[menu]
[menu.inspec]
title = "podman_network"
identifier = "inspec/resources/os/podman_network.md podman_network resource"
parent = "inspec/resources/os"
+++
Use the `podman_network` Chef InSpec audit resource to test the properties of existing Podman networks.
## Availability
### Installation
This resource is distributed with Chef InSpec and is automatically available for use.
## Syntax
A `podman_network` Chef InSpec audit resource aids in testing the properties of a Podman network.
```ruby
describe podman_network("minikube") do
it { should exist }
its("id") { should eq "3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f" }
its("name") { should eq "minikube" }
its("ipv6_enabled") { should eq false }
its("network_interface") { should eq "podman1" }
end
```
> where
>
> - `id`, `name`, `ipv6_enabled`, and `network_interface` are properties of this resource to fetch the respective value of the Podman network.
> - `exist` is a matcher of this resource.
### Resource Parameter Examples
- The resource allows you to pass a network name.
```ruby
describe podman_network("minikube") do
it { should exist }
end
```
- The resource allows you to pass with a Network ID.
```ruby
describe podman_network("3a7c94d937d5") do
it { should exist }
end
```
## Properties
### id
The `id` property returns the full Podman Network ID.
```ruby
its("id") { should eq "3c19bafed22355e11a608c4b613d87d06b9cdd37d378e6e0176cbc8e7144d5c6" }
```
### name
The `name` property tests the value of the Podman network name.
```ruby
its("name") { should eq "minikube" }
```
### ipv6_enabled
The `ipv6_enabled` property tests whether ipv6 is enabled on the Podman network.
```ruby
its("ipv6_enabled") { should eq true }
```
### network_interface
The `network_interface` property tests the value of the network interface settings on the Podman network.
```ruby
its("network_interface") { should eq "podman0" }
```
### created
The `created` property tests the timestamp when the Podman network was created.
```ruby
its("created") { should eq "2022-07-06T08:51:11.735432521+05:30" }
```
### subnets
The `subnets` property tests the list of subnets on the Podman network.
```ruby
its("subnets") { should inclue "gateway"=>"192.168.49.1", "subnet"=>"192.168.49.0/24" }
```
### dns_enabled
The `dns_enabled` property tests whether the Podman network has DNS enabled.
```ruby
its("dns_enabled") { should be false }
```
### internal
The `internal` property tests whether the specified Podman network is internal.
```ruby
its("internal") { should eq true }
```
### ipam_options
The `ipam_options` property tests the IPAM options of the given Podman network.
```ruby
its("ipam_options") { should eq "driver" => "host-local" }
```
### labels
The `labels` property tests the labels set for the specified Podman network.
```ruby
its("labels") { should eq "created_by.minikube.sigs.k8s.io"=>"true", "name.minikube.sigs.k8s.io"=>"minikube" }
```
### driver
The `driver` property tests the value of the Podman network driver.
```ruby
its("driver") { should eq "bridge" }
```
### options
The `options` property tests the network options for the specified Podman network.
```ruby
its("options") { should eq nil }
```
## Matchers
For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
### exist
The `exist` matcher tests if the specified network is available on Podman.
```ruby
it { should exist }
```
## Examples
### Tests if a given Podman network exists and verifies the various network properties
```ruby
describe podman_network("minikube") do
it { should exist }
its("id") { should eq "3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f" }
its("name") { should eq "minikube" }
its("ipv6_enabled") { should eq false }
its("network_interface") { should eq "podman1" }
its("subnets") { should include "gateway"=>"192.168.49.1", "subnet"=>"192.168.49.0/24" }
its("dns_enabled") { should eq true }
its("internal") { should eq false }
its("created") { should eq "2022-07-06T08:51:11.735432521+05:30" }
its("ipam_options") { should eq "driver" => "host-local" }
its("labels") { should eq "created_by.minikube.sigs.k8s.io"=>"true", "name.minikube.sigs.k8s.io"=>"minikube" }
its("driver") { should eq "bridge" }
its("options") { should eq nil }
end
```

View file

@ -0,0 +1,210 @@
+++
title = "podman_pod resource"
draft = false
gh_repo = "inspec"
platform = "unix"
[menu]
[menu.inspec]
title = "podman_pod"
identifier = "inspec/resources/os/podman_pod.md podman_pod resource"
parent = "inspec/resources/os"
+++
Use the `podman_pod` Chef InSpec audit resource to test the properties of a pod on Podman.
## Availability
### Installation
This resource is distributed with Chef InSpec and is automatically available for use.
## Syntax
A `podman_pod` Chef InSpec audit resource aids in testing the properties of a pod on Podman.
```ruby
describe podman_pod("nginx-frontend") do
it { should exist }
its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" }
its("name") { should eq "nginx-frontend" }
its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" }
its("create_command") { should include "new:nginx-frontend" }
its("state") { should eq "Running" }
end
```
> where
>
> - `'nginx-frontend'` is the name of the pod. Pod ID and Pod names are valid parameters accepted by `podman_pod`.
> - `'id'`, `'name'`, `'created_at'`, `'create_command'`, and `'state'`, are properties of this resource to fetch the respective value of the podman pod.
> - `exist` is a matcher of this resource.
## Properties
- Properties of the resources are: `'id'`, `'name'`, `'created_at'`, `'create_command'`, `'state'`, `'hostname'`, `'create_cgroup'`, `'cgroup_parent'`, `cgroup_path`, `'create_infra'`, `'infra_container_id'`, `'infra_config'`, `'shared_namespaces'`, `'num_containers'`, and `'containers'`
### `id`
The `id` property returns the id of the pod.
```ruby
its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" }
```
### `name`
The `name` property returns the name of the pod.
```ruby
its("name") { should eq "nginx-frontend" }
```
### `created_at`
The `created_at` property returns the creation date of the pod.
```ruby
its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" }
```
### `create_command`
The `create_command` property returns an array of commands used to create the pod.
```ruby
its("create_command") { should include "new:nginx-frontend" }
```
### `state`
The `state` property returns the state of the pod.
```ruby
its("state") { should eq "Running" }
```
### `hostname`
The `hostname` property returns the hostname of the pod.
```ruby
its("hostname") { should eq "" }
```
### `create_cgroup`
The `create_cgroup` property returns a boolean value for cgroup creation of the pod.
```ruby
its("create_cgroup") { should eq true }
```
### `cgroup_parent`
The `cgroup_parent` property returns the name of the cgroup parent of the pod.
```ruby
its("cgroup_parent") { should eq "user.slice" }
```
### `cgroup_path`
The `cgroup_path` property returns the path of the cgroup parent of the pod.
```ruby
its("cgroup_path") { should eq "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" }
```
### `create_infra`
The `create_infra` property returns a boolean value for the pod infra creation.
```ruby
its("create_infra") { should eq true }
```
### `infra_container_id`
The `infra_container_id` property returns the infra container ID of the pod.
```ruby
its("infra_container_id") { should eq "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" }
```
### `infra_config`
The `infra_config` property returns a hash of the infra configuration of the pod.
```ruby
its("infra_config") { should include "DNSOption" }
```
### `shared_namespaces`
The `shared_namespaces` property returns an array of shared namespaces of the pod.
```ruby
its("shared_namespaces") { should include "ipc" }
```
### `num_containers`
The `num_containers` property returns the number of containers in the pod.
```ruby
its("num_containers") { should eq 2 }
```
### `containers`
The `containers` property returns an array of hashes about the information of containers in the pod.
```ruby
its("containers") { should_not be nil }
```
## Matchers
For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
### exist
The `exist` matcher tests if the pod is available on Podman.
```ruby
it { should exist }
```
## Examples
### Test if a pod exists on Podman and verifies pod properties
```ruby
describe podman_pod("nginx-frontend") do
it { should exist }
its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" }
its("name") { should eq "nginx-frontend" }
its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" }
its("create_command") { should include "new:nginx-frontend" }
its("state") { should eq "Running" }
its("hostname") { should eq "" }
its("create_cgroup") { should eq true }
its("cgroup_parent") { should eq "user.slice" }
its("cgroup_path") { should eq "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" }
its("create_infra") { should eq true }
its("infra_container_id") { should eq "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" }
its("infra_config") { should include "DNSOption" }
its("shared_namespaces") { should include "ipc" }
its("num_containers") { should eq 2 }
its("containers") { should_not be nil }
end
```
### Test if a pod does not exist on Podman
```ruby
describe podman_pod("non_existing_pod") do
it { should_not exist }
end
```

View file

@ -0,0 +1,155 @@
+++
title = "podman_volume resource"
draft = false
gh_repo = "inspec"
platform = "unix"
[menu]
[menu.inspec]
title = "podman_volume"
identifier = "inspec/resources/os/podman_volume.md podman_volume resource"
parent = "inspec/resources/os"
+++
Use the `podman_volume` Chef InSpec audit resource to test the properties of a volume on Podman.
## Availability
### Installation
This resource is distributed with Chef InSpec and is automatically available for use.
## Syntax
A `podman_volume` Chef InSpec audit resource aids in testing the properties of a volume on Podman.
```ruby
describe podman_volume("my_volume") do
it { should exist }
its("name") { should eq "my_volume" }
its("driver") { should eq "local" }
its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" }
its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" }
end
```
> where
>
> - `'name'`, `'driver'`, `'mountpoint'`, and `'created_at'` are properties of this resource to fetch the respective value of the podman volume.
> - `exist` is a matcher of this resource.
## Properties
- Properties of the resources: `name`, `driver`, `mountpoint`, `created_at`, `labels`, `scope`, `options`, `mount_count`, `needs_copy_up`, and `needs_chown`.
### name
The `name` property returns the name of the volume.
```ruby
its("name") { should eq "my_volume" }
```
### driver
The `driver` property returns the value for the volume's driver environment.
```ruby
its("driver") { should eq "local" }
```
### mountpoint
The `mountpoint` property returns the value for the volume's mount path.
```ruby
its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" }
```
### created_at
The `created_at` property returns the creation date of the volume.
```ruby
its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" }
```
### labels
The `labels` property returns the labels associated with the volume.
```ruby
its("labels") { should eq({}) }
```
### scope
The `scope` property returns the scope of the volume.
```ruby
its("scope") { should eq "local" }
```
### options
The `options` property returns the options associated with the volume.
```ruby
its("options") { should eq({}) }
```
### mount_count
The `mount_count` property returns the **MountCount** value from the volume's inspect information.
```ruby
its("mount_count") { should eq 0 }
```
### needs_copy_up
The `needs_copy_up` property returns the **NeedsCopyUp** value from the volume's inspect information.
```ruby
its("needs_copy_up") { should eq true }
```
### needs_chown
The `needs_chown` property returns the **NeedsChown** value from the volume's inspect information.
```ruby
its("needs_chown") { should eq true }
```
## Matchers
For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
### exist
The `exist` matcher tests if the volume is available on Podman.
```ruby
it { should exist }
```
## Examples
### Test if a volume exists on Podman and verifies volume properties
```ruby
describe podman_volume("my_volume") do
it { should exist }
its("name") { should eq "my_volume" }
its("driver") { should eq "local" }
its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" }
its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" }
its("labels") { should eq({}) }
its("scope") { should eq "local" }
its("options") { should eq({}) }
its("mount_count") { should eq 0 }
its("needs_copy_up") { should eq true }
its("needs_chown") { should eq true }
end
```

View file

@ -42,7 +42,7 @@ When using `postfix_conf` with a custom configuration directory, the following s
where where
- `'path'` is the path to your Postfix configuration (ex. '/etc/my/postfix/path/main.cf') - `'path'` is the path to your Postfix configuration (ex. '/etc/path/to/postfix/main.cf')
## Properties ## Properties

View file

@ -36,7 +36,7 @@ where
The following examples show how to use this Chef InSpec resource. The following examples show how to use this Chef InSpec resource.
### Verify that the Admnistrator user has a SID ### Verify that the Administrator user has a SID
describe security_identifier(user: 'Administrator') do describe security_identifier(user: 'Administrator') do
it { should exist } it { should exist }

View file

@ -26,7 +26,7 @@ The format for `/etc/shadow` includes:
These entries are defined as a colon-delimited row in the file, one row per user: These entries are defined as a colon-delimited row in the file, one row per user:
dannos:Gb7crrO5CDF.:10063:0:99999:7::: username:Gb7crrO5CDF.:10063:0:99999:7:::
The `shadow` resource understands this format, allows you to search on the fields, and exposes the selected users' properties. The `shadow` resource understands this format, allows you to search on the fields, and exposes the selected users' properties.

View file

@ -142,7 +142,7 @@ The `badpasswordattempts` property tests the count of bad password attempts for
where `0` is the count of bad passwords for a user. where `0` is the count of bad passwords for a user.
On Linux based operating systems it relies on `lastb` and for Windows it uses information stored for the user object. On Linux based operating systems it relies on `lastb` and for Windows it uses information stored for the user object.
These settings will be resetted to `0` depending on your operating system configuration. These settings will reset to `0` depending on your operating system configuration.
## Examples ## Examples

View file

@ -72,7 +72,7 @@ This helper returns, if any of the supported virtualization platforms was detect
### virtualization.physical_system? Helper ### virtualization.physical_system? Helper
If no virtualization platform is detected, this will return `true`. For unsupported virtualization platforms this can result in false posititves. If no virtualization platform is detected, this will return `true`. For unsupported virtualization platforms this can result in false positives.
### virtualization.system names ### virtualization.system names

View file

@ -70,9 +70,9 @@ The following examples show how to use this Chef InSpec resource.
it { should exist } it { should exist }
end end
## Gathering Tasknames ## Gathering Task Names
Rather then use the GUI you can use the `schtasks.exe` to output a full list of tasks available on the system Rather than use the GUI, you can use the `schtasks.exe` to output a full list of tasks available on the system
`schtasks /query /FO list` `schtasks /query /FO list`

View file

@ -29,13 +29,13 @@ This resource is available from InSpec version 1.18.
An `x509_certificate` resource block declares a certificate `key file` to be tested. An `x509_certificate` resource block declares a certificate `key file` to be tested.
describe x509_certificate('mycertificate.pem') do describe x509_certificate('certificate.pem') do
its('validity_in_days') { should be > 30 } its('validity_in_days') { should be > 30 }
end end
The `filepath` property can also be used. The `filepath` property can also be used.
describe x509_certificate(filepath: 'mycertificate.pem') do describe x509_certificate(filepath: 'certificate.pem') do
its('validity_in_days') { should be > 30 } its('validity_in_days') { should be > 30 }
end end
@ -55,8 +55,8 @@ The `content` value is used if the `content` and `filepath` are specified.
The `subject` (string) property accesses the individual subject elements. The `subject` (string) property accesses the individual subject elements.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('subject.CN') { should eq "www.mywebsite.com" } its('subject.CN') { should eq "www.example.com" }
end end
### subject_dn ### subject_dn
@ -65,15 +65,15 @@ The `subject_dn` (string) property returns the distinguished name of the subject
For example, `/C=US/L=Seattle/O=Chef Software Inc/OU=Chefs/CN=Richard Nixon` For example, `/C=US/L=Seattle/O=Chef Software Inc/OU=Chefs/CN=Richard Nixon`
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('subject_dn') { should match "CN=www.mywebsite.com" } its('subject_dn') { should match "CN=www.example.com" }
end end
### issuer.XX ### issuer.XX
The `issuer` (string) property accesses the individual issuer elements. The `issuer` (string) property accesses the individual issuer elements.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('issuer.CN') { should eq "Acme Trust CA" } its('issuer.CN') { should eq "Acme Trust CA" }
end end
@ -83,7 +83,7 @@ During the certificate signing process, the `issuer_dn` (string) property is the
For example, `/C=US/L=Seattle/CN=Acme Trust CA/emailAddress=support@acmetrust.org` For example, `/C=US/L=Seattle/CN=Acme Trust CA/emailAddress=support@acmetrust.org`
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('issuer_cn') { should match "CN=NAME CA" } its('issuer_cn') { should match "CN=NAME CA" }
end end
@ -91,7 +91,7 @@ For example, `/C=US/L=Seattle/CN=Acme Trust CA/emailAddress=support@acmetrust.or
The `public_key` (string) property returns a base64 encoded public key in PEM format. The `public_key` (string) property returns a base64 encoded public key in PEM format.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('public_key') { should match "-----BEGIN PUBLIC KEY-----\nblah blah blah..." } its('public_key') { should match "-----BEGIN PUBLIC KEY-----\nblah blah blah..." }
end end
@ -99,7 +99,7 @@ The `public_key` (string) property returns a base64 encoded public key in PEM fo
The `key_length` (integer) property calculates the number of bits in the public key. If the length of bits in the public key increases, the public keys are secure. However, at the cost of speed and compatibility. The `key_length` (integer) property calculates the number of bits in the public key. If the length of bits in the public key increases, the public keys are secure. However, at the cost of speed and compatibility.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('key_length') { should be 2048 } its('key_length') { should be 2048 }
end end
@ -107,7 +107,7 @@ The `key_length` (integer) property calculates the number of bits in the public
The `keylength` (integer) property is an alias of the `key_length` property. The `keylength` (integer) property is an alias of the `key_length` property.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('keylength') { should be 2048 } its('keylength') { should be 2048 }
end end
@ -115,7 +115,7 @@ The `keylength` (integer) property is an alias of the `key_length` property.
The `signature_algorithm` (string) property describes the CA's hash function to sign the certificate. The `signature_algorithm` (string) property describes the CA's hash function to sign the certificate.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('signature_algorithm') { should be 'sha256WithRSAEncryption' } its('signature_algorithm') { should be 'sha256WithRSAEncryption' }
end end
@ -123,7 +123,7 @@ The `signature_algorithm` (string) property describes the CA's hash function to
The `validity_in_days` (float) property is used to check the validity of the certificates. The `validity_in_days` (float) property is used to check the validity of the certificates.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('validity_in_days') { should be > 30 } its('validity_in_days') { should be > 30 }
end end
@ -131,7 +131,7 @@ The `validity_in_days` (float) property is used to check the validity of the cer
The `not_before` and `not_after` (time) properties expose the start and end dates of certificate validity. These dates are exposed as Ruby **Time** class and perform date calculations. The `not_before` and `not_after` (time) properties expose the start and end dates of certificate validity. These dates are exposed as Ruby **Time** class and perform date calculations.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('not_before') { should be <= Time.utc.now } its('not_before') { should be <= Time.utc.now }
its('not_after') { should be >= Time.utc.now } its('not_after') { should be >= Time.utc.now }
end end
@ -140,7 +140,7 @@ The `not_before` and `not_after` (time) properties expose the start and end date
The `serial` (integer) property exposes the certificate's serial number. The CA sets the serial number during the signing process and should be unique within that CA. The `serial` (integer) property exposes the certificate's serial number. The CA sets the serial number during the signing process and should be unique within that CA.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('serial') { should eq 9623283588743302433 } its('serial') { should eq 9623283588743302433 }
end end
@ -148,7 +148,7 @@ The `serial` (integer) property exposes the certificate's serial number. The CA
The `version` (integer) property exposes the certificate version. The `version` (integer) property exposes the certificate version.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('version') { should eq 2 } its('version') { should eq 2 }
end end
@ -156,7 +156,7 @@ The `version` (integer) property exposes the certificate version.
The `extensions` (hash) property is mainly used to determine the purpose of the certificate. The `extensions` (hash) property is mainly used to determine the purpose of the certificate.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
# Check what extension categories we have # Check what extension categories we have
its('extensions') { should include 'keyUsage' } its('extensions') { should include 'keyUsage' }
its('extensions') { should include 'extendedKeyUsage' } its('extensions') { should include 'extendedKeyUsage' }
@ -179,16 +179,16 @@ The `extensions` (hash) property is mainly used to determine the purpose of the
The `email` (string) property checks for the email address of the certificate. This is equivalent to invoking the property `subject.emailAddress`. The `email` (string) property checks for the email address of the certificate. This is equivalent to invoking the property `subject.emailAddress`.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('email') { should_not be_empty } its('email') { should_not be_empty }
its('email') { should eq 'admin@mywebsite.com' } its('email') { should eq 'admin@example.com' }
end end
### subject_alt_names ### subject_alt_names
The `subject_alt_names` (string) property checks for the subject alternative names (additional host names) of the certificate. The `subject_alt_names` (string) property checks for the subject alternative names (additional host names) of the certificate.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
its('subject_alt_names') { should include 'DNS:example.com' } its('subject_alt_names') { should include 'DNS:example.com' }
its('subject_alt_names') { should include 'DNS:www.example.com' } its('subject_alt_names') { should include 'DNS:www.example.com' }
end end
@ -203,7 +203,7 @@ The specific matchers of this resource are: `be_valid`, `be_certificate` and `ha
The `be_valid` matcher tests if the specified certificate is valid. The `be_valid` matcher tests if the specified certificate is valid.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
it { should be_valid } it { should be_valid }
end end
@ -211,7 +211,7 @@ The `be_valid` matcher tests if the specified certificate is valid.
The `be_certificate` matcher tests if the specified content or file is a certificate. The `be_certificate` matcher tests if the specified content or file is a certificate.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
it { should be_certificate } it { should be_certificate }
end end
@ -219,7 +219,7 @@ The `be_certificate` matcher tests if the specified content or file is a certifi
The `have_purpose` matcher tests if the certificate meets the specified purpose. The `have_purpose` matcher tests if the certificate meets the specified purpose.
describe x509_certificate('/etc/pki/www.mywebsite.com.pem') do describe x509_certificate('/etc/pki/www.example.com.pem') do
it { should have_purpose('SSL client CA : Yes') } it { should have_purpose('SSL client CA : Yes') }
it { should have_purpose('SSL server CA : Yes') } it { should have_purpose('SSL server CA : Yes') }
end end

View file

@ -112,12 +112,12 @@ $ inspec shell
Welcome to the interactive InSpec Shell Welcome to the interactive InSpec Shell
To find out how to use it, type: help To find out how to use it, type: help
inspec> file('/Users/myuser').directory? inspec> file('/Users/username').directory?
=> true => true
inspec> os_env('HOME') inspec> os_env('HOME')
=> Environment variable HOME => Environment variable HOME
inspec> os_env('HOME').content inspec> os_env('HOME').content
=> /Users/myuser => /Users/username
inspec> exit inspec> exit
``` ```
@ -141,10 +141,10 @@ replaced with the redefinition and the control is re-run.
```bash ```bash
inspec> control 'my_control' do inspec> control 'my_control' do
inspec> describe os_env('HOME') do inspec> describe os_env('HOME') do
inspec> its('content') { should eq '/Users/myuser' } inspec> its('content') { should eq '/Users/username' }
inspec> end inspec> end
inspec> end inspec> end
✔ my_control: Environment variable HOME content should eq "/Users/myuser" ✔ my_control: Environment variable HOME content should eq "/Users/username"
Summary: 1 successful, 0 failures, 0 skipped Summary: 1 successful, 0 failures, 0 skipped
``` ```
@ -173,10 +173,10 @@ If you wish to run a single Chef InSpec command and fetch its results, you
may use the `-c` flag. This is similar to using `bash -c`. may use the `-c` flag. This is similar to using `bash -c`.
```bash ```bash
$ inspec shell -c 'describe file("/Users/myuser") do it { should exist } end' $ inspec shell -c 'describe file("/Users/username") do it { should exist } end'
Target: local:// Target: local://
✔ File /Users/myuser should exist ✔ File /Users/username should exist
Summary: 1 successful, 0 failures, 0 skipped Summary: 1 successful, 0 failures, 0 skipped
``` ```

View file

@ -12,8 +12,7 @@ gh_repo = "inspec"
+++ +++
Waivers is a mechanism to mark controls as "waived" for various reasons, and to Waivers is a mechanism to mark controls as "waived" for various reasons, and to
control the running and/or reporting of those controls. It uses a YAML input file control the running and/or reporting of those controls. A waiver file identifies:
that identifies:
1. which controls are waived 1. which controls are waived
1. a description of why it is waived 1. a description of why it is waived
@ -31,7 +30,7 @@ inspec exec path/to/profile --waiver-file waivers.yaml
## File Format ## File Format
Waiver files are [input files](/inspec/inputs/) with a specific format: Waiver files support YAML, JSON, CSV, XLSX & XLS format.
```yaml ```yaml
control_id: control_id:
@ -40,6 +39,18 @@ control_id:
justification: "reason for waiving this control" justification: "reason for waiving this control"
``` ```
OR
```json
{
"control_id": {
"expiration_date": "YYYY-MM-DD",
"run": false,
"justification": "reason for waiving this control"
}
}
```
- `expiration_date` sets the day that the waiver file will expire in YYYY-MM-DD format. Waiver files expire at 00:00 at the local time of the system on the specified date. Waiver files without an expiration date are permanent. `expiration_date` is optional. - `expiration_date` sets the day that the waiver file will expire in YYYY-MM-DD format. Waiver files expire at 00:00 at the local time of the system on the specified date. Waiver files without an expiration date are permanent. `expiration_date` is optional.
- `run` is optional. If absent or true, the control will run and be - `run` is optional. If absent or true, the control will run and be
reported, but failures in it won't make the overall run fail. If present and false, the control will not be run. You may use any of yes, no, true or false. To avoid confusion, it is good practice to explicitly specify whether the control should run. reported, but failures in it won't make the overall run fail. If present and false, the control will not be run. You may use any of yes, no, true or false. To avoid confusion, it is good practice to explicitly specify whether the control should run.
@ -48,6 +59,8 @@ control_id:
### Examples: ### Examples:
Example in YAML:
```yaml ```yaml
waiver_control_1_2_3: waiver_control_1_2_3:
expiration_date: 2019-10-15 expiration_date: 2019-10-15
@ -58,3 +71,34 @@ xccdf_org.cisecurity.benchmarks_rule_1.1.1.4_Ensure_mounting_of_hfs_filesystems_
justification: "This might be a bug in the test. @qateam" justification: "This might be a bug in the test. @qateam"
run: false run: false
``` ```
Example in JSON:
```json
{
"waiver_control_1_2_3": {
"expiration_date": "2019-10-15T00:00:00.000Z",
"justification": "Not needed until Q3. @secteam"
},
"xccdf_org.cisecurity.benchmarks_rule_1.1.1.4_Ensure_mounting_of_hfs_filesystems_is_disabled": {
"expiration_date": "2020-03-01T00:00:00.000Z",
"justification": "This might be a bug in the test. @qateam",
"run": false
}
}
```
Example in CSV/XLSX/XLS:
These file formats support the following fields in a file:
* `control_id`
_Required_.
* `justification`
_Required_.
* `run`
_Optional_.
* `expiration_date`
_Optional_.
![Waiver File Excel Example](/images/inspec/waivers_file_excel.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -34,6 +34,10 @@ Gem::Specification.new do |spec|
# progress bar streaming reporter plugin support # progress bar streaming reporter plugin support
spec.add_dependency "progress_bar", "~> 1.3.3" spec.add_dependency "progress_bar", "~> 1.3.3"
# roo support for reading excel waiver files
spec.add_dependency "roo", "~> 2.9.0"
spec.add_dependency "roo-xls" # extension for roo to read xls files
# Used for Azure profile until integrated into train # Used for Azure profile until integrated into train
spec.add_dependency "faraday_middleware", ">= 0.12.2", "< 1.1" spec.add_dependency "faraday_middleware", ">= 0.12.2", "< 1.1"

View file

@ -205,6 +205,8 @@ module Inspec
long_desc: "Maximum seconds to allow commands to run during execution. A timed out command is considered an error." long_desc: "Maximum seconds to allow commands to run during execution. A timed out command is considered an error."
option :reporter_include_source, type: :boolean, default: false, option :reporter_include_source, type: :boolean, default: false,
desc: "Include full source code of controls in the CLI report" desc: "Include full source code of controls in the CLI report"
option :enhanced_outcomes, type: :boolean,
desc: "Show enhanced outcomes in output"
end end
def self.help(*args) def self.help(*args)

View file

@ -415,6 +415,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
desc: "Load one or more input files, a YAML file with values for the shell to use" desc: "Load one or more input files, a YAML file with values for the shell to use"
option :input, type: :array, banner: "name1=value1 name2=value2", option :input, type: :array, banner: "name1=value1 name2=value2",
desc: "Specify one or more inputs directly on the command line to the shell, as --input NAME=VALUE. Accepts single-quoted YAML and JSON structures." desc: "Specify one or more inputs directly on the command line to the shell, as --input NAME=VALUE. Accepts single-quoted YAML and JSON structures."
option :enhanced_outcomes, type: :boolean,
desc: "Show enhanced outcomes in output"
def shell_func def shell_func
o = config o = config
deprecate_target_id(config) deprecate_target_id(config)
@ -461,11 +463,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI
pretty_handle_exception(e) pretty_handle_exception(e)
end end
option :enhanced_outcomes, type: :boolean,
desc: "Show enhanced outcomes output"
desc "schema NAME", "print the JSON schema", hide: true desc "schema NAME", "print the JSON schema", hide: true
def schema(name) def schema(name)
require "inspec/schema/output_schema" require "inspec/schema/output_schema"
o = config
puts Inspec::Schema::OutputSchema.json(name) puts Inspec::Schema::OutputSchema.json(name, o)
rescue StandardError => e rescue StandardError => e
puts e puts e
puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}" puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}"

View file

@ -0,0 +1,18 @@
module Inspec
module EnhancedOutcomes
def self.determine_status(results, impact)
if results.any? { |r| !r[:exception].nil? && !r[:backtrace].nil? }
"error"
elsif !impact.nil? && impact.to_f == 0.0
"not_applicable"
elsif results.all? { |r| r[:status] == "skipped" }
"not_reviewed"
elsif results.any? { |r| r[:status] == "failed" }
"failed"
else
"passed"
end
end
end
end

View file

@ -10,5 +10,7 @@ module Inspec
class SecretsBackendNotFound < ArgumentError; end class SecretsBackendNotFound < ArgumentError; end
class ProfileValidationKeyNotFound < ArgumentError; end class ProfileValidationKeyNotFound < ArgumentError; end
class ProfileSigningKeyNotFound < ArgumentError; end class ProfileSigningKeyNotFound < ArgumentError; end
class WaiversFileNotReadable < ArgumentError; end
class WaiversFileDoesNotExist < ArgumentError; end
end end
end end

View file

@ -1,12 +1,13 @@
require "rspec/core" require "rspec/core"
require "rspec/core/formatters/base_formatter" require "rspec/core/formatters/base_formatter"
require "set" unless defined?(Set) require "set" unless defined?(Set)
require "inspec/enhanced_outcomes"
module Inspec::Formatters module Inspec::Formatters
class Base < RSpec::Core::Formatters::BaseFormatter class Base < RSpec::Core::Formatters::BaseFormatter
RSpec::Core::Formatters.register self, :close, :dump_summary, :stop RSpec::Core::Formatters.register self, :close, :dump_summary, :stop
attr_accessor :backend, :run_data attr_accessor :backend, :run_data, :enhanced_outcomes
def initialize(output) def initialize(output)
super(output) super(output)
@ -17,6 +18,7 @@ module Inspec::Formatters
@backend = nil @backend = nil
@all_controls_count = nil @all_controls_count = nil
@control_checks_count_map = {} @control_checks_count_map = {}
@enhanced_outcomes = nil
end end
# RSpec Override: #dump_summary # RSpec Override: #dump_summary
@ -50,7 +52,6 @@ module Inspec::Formatters
else else
hash[:message] = exception_message(e) hash[:message] = exception_message(e)
end end
next if e.is_a? RSpec::Expectations::ExpectationNotMetError next if e.is_a? RSpec::Expectations::ExpectationNotMetError
hash[:exception] = e.class.name hash[:exception] = e.class.name
@ -68,6 +69,8 @@ module Inspec::Formatters
# flesh out the profiles key with additional profile information # flesh out the profiles key with additional profile information
run_data[:profiles] = profiles_info run_data[:profiles] = profiles_info
add_enhanced_outcomes_to_controls if enhanced_outcomes
# add the platform information for this particular target # add the platform information for this particular target
run_data[:platform] = { run_data[:platform] = {
name: platform(:name), name: platform(:name),
@ -110,6 +113,20 @@ module Inspec::Formatters
private private
def add_enhanced_outcomes_to_controls
all_unique_controls.each do |control|
control[:status] = determine_control_enhanced_outcome(control)
end
end
def determine_control_enhanced_outcome(control)
if control[:results]
Inspec::EnhancedOutcomes.determine_status(control[:results], control[:impact])
else
"passed"
end
end
def all_unique_controls def all_unique_controls
unique_controls = Set.new unique_controls = Set.new
run_data[:profiles].each do |profile| run_data[:profiles].each do |profile|
@ -120,10 +137,45 @@ module Inspec::Formatters
end end
def statistics def statistics
error = 0
not_applicable = 0
not_reviewed = 0
failed = 0 failed = 0
skipped = 0
passed = 0 passed = 0
skipped = 0
enhanced_outcomes_summary = {}
if enhanced_outcomes
all_unique_controls.each do |control|
if control[:status] == "error"
error += 1
elsif control[:status] == "not_applicable"
not_applicable += 1
elsif control[:status] == "not_reviewed"
not_reviewed += 1
elsif control[:status] == "failed"
failed += 1
elsif control[:status] == "passed"
passed += 1
end
# added this additionally because stats summary is also used for determining exit code in runner rspec
skipped += 1 if control[:results].any? { |r| r[:status] == "skipped" }
end
total = error + not_applicable + not_reviewed + failed + passed
enhanced_outcomes_summary = {
not_applicable: {
total: not_applicable,
},
not_reviewed: {
total: not_reviewed,
},
error: {
total: error,
},
}
else
all_unique_controls.each do |control| all_unique_controls.each do |control|
next unless control[:results] next unless control[:results]
@ -135,10 +187,9 @@ module Inspec::Formatters
passed += 1 passed += 1
end end
end end
total = failed + passed + skipped total = failed + passed + skipped
end
{ final_summary = {
total: total, total: total,
passed: { passed: {
total: passed, total: passed,
@ -150,6 +201,8 @@ module Inspec::Formatters
total: failed, total: failed,
}, },
} }
final_summary.merge!(enhanced_outcomes_summary)
end end
def exception_message(exception) def exception_message(exception)

View file

@ -7,6 +7,7 @@ module Inspec::Plugin::V2::PluginType
include Inspec::Utils::RunDataFilters include Inspec::Utils::RunDataFilters
attr_reader :run_data attr_reader :run_data
attr_accessor :enhanced_outcomes
def initialize(config) def initialize(config)
@config = config @config = config

View file

@ -11,6 +11,7 @@ module Inspec::Plugin::V2::PluginType
@running_controls_list = [] @running_controls_list = []
@control_checks_count_map = {} @control_checks_count_map = {}
@controls_count = nil @controls_count = nil
@notifications = {}
end end
private private
@ -49,5 +50,58 @@ module Inspec::Plugin::V2::PluginType
@control_checks_count_map = RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_control_checks_count_map @control_checks_count_map = RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_control_checks_count_map
end end
end end
def enhanced_outcomes
@enhanced_outcomes ||= RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.enhanced_outcomes
end
def add_enhanced_outcomes(control_id)
if control_has_error(@notifications[control_id])
"error"
elsif control_has_impact_zero(@notifications[control_id])
"not_applicable"
elsif control_has_all_tests_skipped(@notifications[control_id])
"not_reviewed"
elsif control_has_any_tests_failed(@notifications[control_id])
"failed"
else
"passed"
end
end
def control_has_error(notifications)
notifications.any? do |notification_data|
notification, _status = notification_data
!notification.example.exception.nil? && !(notification.example.exception.is_a? RSpec::Expectations::ExpectationNotMetError) && !notification.example.exception.backtrace.nil?
end
end
def control_has_all_tests_skipped(notifications)
notifications.all? do |notification_data|
_notification, status = notification_data
status == "skipped"
end
end
def control_has_any_tests_failed(notifications)
notifications.any? do |notification_data|
_notification, status = notification_data
status == "failed"
end
end
def control_has_impact_zero(notifications)
notification_data = notifications.first
notification_impact = notification_data.first.example.metadata[:impact]
notification_data && !notification_impact.nil? && notification_impact.to_f == 0.0
end
def collect_notifications(notification, control_id, status)
if @notifications[control_id].nil?
@notifications[control_id] = [[notification, status]]
else
@notifications[control_id].push([notification, status])
end
end
end end
end end

View file

@ -350,22 +350,24 @@ module Inspec
def load_libraries def load_libraries
return @runner_context if @libraries_loaded return @runner_context if @libraries_loaded
locked_dependencies.dep_list.each_with_index do |(_name, dep), i| locked_dependencies.dep_list.each_with_index do |(_name, dep), index|
d = dep.profile d = dep.profile
# this will force a dependent profile load so we are only going to add # this will force a dependent profile load so we are only going to add
# this metadata if the parent profile is supported. # this metadata if the parent profile is supported.
if @supports_platform && !d.supports_platform? if @supports_platform && !d.supports_platform?
# since ruby 1.9 hashes are ordered so we can just use index values here # since ruby 1.9 hashes are ordered so we can just use index values here
# TODO: NO! this is a violation of encapsulation to an extreme # TODO: NO! this is a violation of encapsulation to an extreme
metadata.dependencies[i][:status] = "skipped" if metadata.dependencies[index]
metadata.dependencies[index][:status] = "skipped"
msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'." msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'."
metadata.dependencies[i][:status_message] = msg metadata.dependencies[index][:status_message] = msg
metadata.dependencies[i][:skip_message] = msg # Repeat as skip_message for backward compatibility metadata.dependencies[index][:skip_message] = msg # Repeat as skip_message for backward compatibility
end
next next
elsif metadata.dependencies[i] elsif metadata.dependencies[index]
# Currently wrapper profiles will load all dependencies, and then we # Currently wrapper profiles will load all dependencies, and then we
# load them again when we dive down. This needs to be re-done. # load them again when we dive down. This needs to be re-done.
metadata.dependencies[i][:status] = "loaded" metadata.dependencies[index][:status] = "loaded"
end end
# rubocop:disable Layout/ExtraSpacing # rubocop:disable Layout/ExtraSpacing

View file

@ -7,7 +7,7 @@ require "inspec/reporters/yaml"
module Inspec::Reporters module Inspec::Reporters
# rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/CyclomaticComplexity
def self.render(reporter, run_data) def self.render(reporter, run_data, enhanced_outcomes = false)
name, config = reporter.dup name, config = reporter.dup
config[:run_data] = run_data config[:run_data] = run_data
case name case name
@ -29,6 +29,7 @@ module Inspec::Reporters
activator.activate! activator.activate!
reporter = activator.implementation_class.new(config) reporter = activator.implementation_class.new(config)
end end
reporter.enhanced_outcomes = enhanced_outcomes
# optional send_report method on reporter # optional send_report method on reporter
return reporter.send_report if defined?(reporter.send_report) return reporter.send_report if defined?(reporter.send_report)

View file

@ -5,6 +5,7 @@ module Inspec::Reporters
include Inspec::Utils::RunDataFilters include Inspec::Utils::RunDataFilters
attr_reader :run_data attr_reader :run_data
attr_accessor :enhanced_outcomes
def initialize(config) def initialize(config)
@config = config @config = config

View file

@ -9,6 +9,9 @@ module Inspec::Reporters
"passed" => "\033[0;1;32m", "passed" => "\033[0;1;32m",
"skipped" => "\033[0;37m", "skipped" => "\033[0;37m",
"reset" => "\033[0m", "reset" => "\033[0m",
"error" => "\033[34m",
"not_applicable" => "\033[36m",
"not_reviewed" => "\033[33m",
}.freeze }.freeze
# Most currently available Windows terminals have poor support # Most currently available Windows terminals have poor support
@ -18,6 +21,9 @@ module Inspec::Reporters
"skipped" => "[SKIP]", "skipped" => "[SKIP]",
"passed" => "[PASS]", "passed" => "[PASS]",
"unknown" => "[UNKN]", "unknown" => "[UNKN]",
"error" => "[ERR]",
"not_applicable" => "[N/A]",
"not_reviewed" => "[N/R]",
}.freeze }.freeze
else else
# Extended colors for everyone else # Extended colors for everyone else
@ -26,6 +32,9 @@ module Inspec::Reporters
"passed" => "\033[38;5;41m", "passed" => "\033[38;5;41m",
"skipped" => "\033[38;5;247m", "skipped" => "\033[38;5;247m",
"reset" => "\033[0m", "reset" => "\033[0m",
"error" => "\033[0;38;5;21m",
"not_applicable" => "\033[0;38;5;117m",
"not_reviewed" => "\033[0;38;5;214m",
}.freeze }.freeze
# Groovy UTF-8 characters for everyone else... # Groovy UTF-8 characters for everyone else...
@ -35,6 +44,9 @@ module Inspec::Reporters
"skipped" => "", "skipped" => "",
"passed" => "", "passed" => "",
"unknown" => "?", "unknown" => "?",
"error" => "ERR",
"not_applicable" => "N/A",
"not_reviewed" => "N/R",
}.freeze }.freeze
end end
@ -63,7 +75,11 @@ module Inspec::Reporters
end end
output("") output("")
if enhanced_outcomes
print_control_outcomes_summary
else
print_profile_summary print_profile_summary
end
print_tests_summary print_tests_summary
end end
@ -88,6 +104,7 @@ module Inspec::Reporters
def print_standard_control_results(profile) def print_standard_control_results(profile)
standard_controls_from_profile(profile).each do |control_from_profile| standard_controls_from_profile(profile).each do |control_from_profile|
control = Control.new(control_from_profile) control = Control.new(control_from_profile)
control.enhanced_outcomes = enhanced_outcomes
next if control.results.nil? next if control.results.nil?
output(format_control_header(control)) output(format_control_header(control))
@ -122,7 +139,7 @@ module Inspec::Reporters
end end
def format_control_header(control) def format_control_header(control)
impact = control.impact_string impact = enhanced_outcomes ? control.impact_string_for_enhanced_outcomes : control.impact_string
format_message( format_message(
color: impact, color: impact,
indicator: impact, indicator: impact,
@ -292,6 +309,68 @@ module Inspec::Reporters
} }
end end
def control_outcomes_summary
failed = 0
passed = 0
error = 0
not_reviewed = 0
not_applicable = 0
all_unique_controls.each do |control|
next if control[:status].empty?
if control[:status] == "failed"
failed += 1
elsif control[:status] == "error"
error += 1
elsif control[:status] == "not_reviewed"
not_reviewed += 1
elsif control[:status] == "not_applicable"
not_applicable += 1
else
passed += 1
end
end
total = failed + passed + error + not_reviewed + not_applicable
{
"total" => total,
"failed" => failed,
"passed" => passed,
"error" => error,
"not_reviewed" => not_reviewed,
"not_applicable" => not_applicable,
}
end
def print_control_outcomes_summary
summary = control_outcomes_summary
return unless summary["total"] > 0
success_str = summary["passed"] == 1 ? "1 successful control" : "#{summary["passed"]} successful controls"
failed_str = summary["failed"] == 1 ? "1 control failure" : "#{summary["failed"]} control failures"
error_str = summary["error"] == 1 ? "1 control has error" : "#{summary["error"]} controls have error"
not_rev_str = summary["not_reviewed"] == 1 ? "1 control not reviewed" : "#{summary["not_reviewed"]} controls not reviewed"
not_app_str = summary["not_applicable"] == 1 ? "1 control not applicable" : "#{summary["not_applicable"]} controls not applicable"
success_color = summary["passed"] > 0 ? "passed" : "no_color"
failed_color = summary["failed"] > 0 ? "failed" : "no_color"
error_color = summary["error"] > 0 ? "error" : "no_color"
not_rev_color = summary["not_reviewed"] > 0 ? "not_reviewed" : "no_color"
not_app_color = summary["not_applicable"] > 0 ? "not_applicable" : "no_color"
s = format(
"Profile Summary: %s, %s, %s, %s, %s",
format_with_color(success_color, success_str),
format_with_color(failed_color, failed_str),
format_with_color(not_rev_color, not_rev_str),
format_with_color(not_app_color, not_app_str),
format_with_color(error_color, error_str)
)
output(s) if summary["total"] > 0
end
def print_profile_summary def print_profile_summary
summary = profile_summary summary = profile_summary
return unless summary["total"] > 0 return unless summary["total"] > 0
@ -350,6 +429,7 @@ module Inspec::Reporters
class Control class Control
attr_reader :data attr_reader :data
attr_accessor :enhanced_outcomes
def initialize(control_hash) def initialize(control_hash)
@data = control_hash @data = control_hash
@ -379,6 +459,10 @@ module Inspec::Reporters
id.start_with?("(generated from ") id.start_with?("(generated from ")
end end
def status
data[:status]
end
def title_for_report def title_for_report
# if this is an anonymous control, just grab the resource title from any result entry # if this is an anonymous control, just grab the resource title from any result entry
return results.first[:resource_title] if anonymous? return results.first[:resource_title] if anonymous?
@ -392,10 +476,17 @@ module Inspec::Reporters
# append a failure summary if appropriate. # append a failure summary if appropriate.
title_for_report += " (#{failure_count} failed)" if failure_count > 0 title_for_report += " (#{failure_count} failed)" if failure_count > 0
title_for_report += " (#{skipped_count} skipped)" if skipped_count > 0 title_for_report += " (#{skipped_count} skipped)" if skipped_count > 0
title_for_report title_for_report
end end
def impact_string_for_enhanced_outcomes
if impact.nil?
"unknown"
else
status
end
end
def impact_string def impact_string
if anonymous? if anonymous?
nil nil

View file

@ -114,7 +114,7 @@ module Inspec::Reporters
def profile_controls(profile) def profile_controls(profile)
(profile[:controls] || []).map { |c| (profile[:controls] || []).map { |c|
{ control_hash = {
id: c[:id], id: c[:id],
title: c[:title], title: c[:title],
desc: c.dig(:descriptions, :default), desc: c.dig(:descriptions, :default),
@ -130,6 +130,8 @@ module Inspec::Reporters
waiver_data: c[:waiver_data] || {}, waiver_data: c[:waiver_data] || {},
results: profile_results(c), results: profile_results(c),
} }
control_hash.merge!({ status: c[:status] }) if enhanced_outcomes
control_hash
} }
end end

View file

@ -3,7 +3,9 @@ require "yaml"
module Inspec::Reporters module Inspec::Reporters
class Yaml < Base class Yaml < Base
def render def render
output(Inspec::Reporters::Json.new({ run_data: run_data }).report.to_yaml, false) json_reporter_obj = Inspec::Reporters::Json.new({ run_data: run_data })
json_reporter_obj.enhanced_outcomes = enhanced_outcomes
output(json_reporter_obj.report.to_yaml, false)
end end
def report def report

View file

@ -0,0 +1,353 @@
require "inspec/resources/command"
require "inspec/utils/filter"
require "hashie/mash"
module Inspec::Resources
class Podman < Inspec.resource(1)
# Resource requires an internal name.
name "podman"
# Restrict to only run on the below platforms (if none were given,
# all OS's and cloud API's supported)
supports platform: "unix"
desc "A resource to retrieve information about podman"
example <<~EXAMPLE
describe podman.containers do
its('images') { should include "docker.io/library/ubuntu:latest" }
end
describe podman.images do
its('names') { should_not include "docker.io/library/ubuntu:latest" }
end
describe podman.pods do
its("ids") { should include "95cadbb84df71e6374fceb3fd89ee3b8f2c7e1a831062cd9cea7d0e3e4b1dbcc" }
end
describe podman.info.host do
its("os") { should eq "linux"}
end
describe podman.version do
its("Client.Version") { should eq "4.1.0"}
end
podman.containers.ids.each do |id|
# call podman inspect for a specific container id
describe podman.object(id) do
its("State.OciVersion") { should eq "1.0.2-dev" }
its("State.Running") { should eq true}
end
end
EXAMPLE
def containers
PodmanContainerFilter.new(parse_containers)
end
def images
PodmanImageFilter.new(parse_images)
end
def networks
PodmanNetworkFilter.new(parse_networks)
end
def pods
PodmanPodFilter.new(parse_pods)
end
def volumes
PodmanVolumeFilter.new(parse_volumes)
end
def version
return @version if defined?(@version)
sub_cmd = "version --format json"
output = run_command(sub_cmd)
@version = Hashie::Mash.new(JSON.parse(output))
rescue JSON::ParserError => _e
Hashie::Mash.new({})
end
def info
return @info if defined?(@info)
sub_cmd = "info --format json"
output = run_command(sub_cmd)
@info = Hashie::Mash.new(JSON.parse(output))
rescue JSON::ParserError => _e
Hashie::Mash.new({})
end
# returns information about podman objects
def object(id)
return @inspect if defined?(@inspect)
output = run_command("inspect #{id} --format json")
data = JSON.parse(output)
data = data[0] if data.is_a?(Array)
@inspect = Hashie::Mash.new(data)
rescue JSON::ParserError => _e
Hashie::Mash.new({})
end
def to_s
"Podman"
end
private
# Calls the run_command method to get all podman containers and parse the command output.
# Returns the parsed command output.
def parse_containers
labels = %w{ID Image ImageID Command CreatedAt RunningFor Status Pod Ports Size Names Networks Labels Mounts}
parse_json_command(labels, "ps -a --no-trunc --size")
end
# Calls the run_command method to get all podman images and parse the command output.
# Returns the parsed command output.
def parse_images
labels = %w{ID Repository Tag Size Digest CreatedAt CreatedSince History}
parse_json_command(labels, "images -a --no-trunc")
end
# Calls the run_command method to get all podman network list and parse the command output.
# Returns the parsed command output.
def parse_networks
labels = %w{ID Name Driver Labels Options IPAMOptions Created Internal IPv6Enabled DNSEnabled NetworkInterface Subnets}
parse_json_command(labels, "network ls --no-trunc")
end
# Calls the run_command method to get all podman pod list and parse the command output.
# Returns the parsed command output.
def parse_pods
sub_cmd = "pod ps --no-trunc --format json"
output = run_command(sub_cmd)
parse(output)
end
# Calls the run_command method to get all podman volume list and parse the command output.
# Returns the parsed command output.
def parse_volumes
sub_cmd = "volume ls --format json"
output = run_command(sub_cmd)
parse(output)
end
# Runs the given podman command on the host machine on which podman is installed
# Returns the command output or raises the command execution error.
def run_command(subcommand)
result = inspec.command("podman #{subcommand}")
if result.stderr.empty?
result.stdout
else
raise "Error while running command \'podman #{subcommand}\' : #{result.stderr}"
end
end
def parse_json_command(labels, subcommand)
# build command
format = labels.map { |label| "\"#{label}\": {{json .#{label}}}" }
raw = inspec.command("podman #{subcommand} --format '{#{format.join(", ")}}'").stdout
output = []
raw.each_line do |entry|
# convert all keys to lower_case to work well with ruby and filter table
row = JSON.parse(entry).map do |key, value|
[key.downcase, value]
end.to_h
# ensure all keys are there
row = ensure_keys(row, labels)
output.push(row)
end
output
rescue JSON::ParserError => _e
warn "Could not parse `podman #{subcommand}` output"
[]
end
def ensure_keys(entry, labels)
labels.each do |key|
entry[key.downcase] = nil unless entry.key?(key.downcase)
end
entry
end
# Method to parse JDON content.
# Returns: Parsed data.
def parse(content)
require "json" unless defined?(JSON)
output = JSON.parse(content)
parsed_output = []
output.each do |entry|
entry = entry.map do |k, v|
[k.downcase, v]
end.to_h
parsed_output << entry
end
parsed_output
rescue => e
raise Inspec::Exceptions::ResourceFailed, "Unable to parse command JSON output: #{e.message}"
end
end
# class for podman.containers plural resource
class PodmanContainerFilter
filter = FilterTable.create
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
filter.register_column(:commands, field: "command")
.register_column(:ids, field: "id")
.register_column(:created_at, field: "createdat")
.register_column(:images, field: "image")
.register_column(:names, field: "names")
.register_column(:status, field: "status")
.register_column(:image_ids, field: "image_id")
.register_column(:labels, field: "labels", style: :simple)
.register_column(:mounts, field: "mounts")
.register_column(:networks, field: "networks")
.register_column(:pods, field: "pod")
.register_column(:ports, field: "ports")
.register_column(:sizes, field: "size")
.register_column(:running_for, field: "running_for")
.register_custom_matcher(:running?) do |x|
x.where { status.downcase.start_with?("up") }
end
filter.install_filter_methods_on_resource(self, :containers)
attr_reader :containers
def initialize(containers)
@containers = containers
end
def to_s
"Podman Containers"
end
def resource_id
"Podman Containers"
end
end
# class for podman.images plural resource
class PodmanImageFilter
filter = FilterTable.create
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
filter.register_column(:ids, field: "id")
.register_column(:repositories, field: "repository")
.register_column(:tags, field: "tag")
.register_column(:sizes, field: "size")
.register_column(:digests, field: "digest")
.register_column(:created_at, field: "createdat")
.register_column(:created_since, field: "createdsince")
.register_column(:history, field: "history")
filter.install_filter_methods_on_resource(self, :images)
attr_reader :images
def initialize(images)
@images = images
end
def to_s
"Podman Images"
end
def resource_id
"Podman Images"
end
end
class PodmanNetworkFilter
filter = FilterTable.create
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
.register_column(:ids, field: "id")
.register_column(:names, field: "name")
.register_column(:drivers, field: "driver")
.register_column(:network_interfaces, field: "networkinterface")
.register_column(:created, field: "created")
.register_column(:subnets, field: "subnets")
.register_column(:ipv6_enabled, field: "ipv6enabled")
.register_column(:internal, field: "internal")
.register_column(:dns_enabled, field: "dnsenabled")
.register_column(:ipam_options, field: "ipamoptions")
.register_column(:options, field: "options")
.register_column(:labels, field: "labels")
filter.install_filter_methods_on_resource(self, :networks)
attr_reader :networks
def initialize(networks)
@networks = networks
end
def to_s
"Podman Networks"
end
def resource_id
"Podman Networks"
end
end
class PodmanPodFilter
filter = FilterTable.create
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
.register_column(:ids, field: "id")
.register_column(:cgroups, field: "cgroup")
.register_column(:containers, field: "containers")
.register_column(:created, field: "created")
.register_column(:infraids, field: "infraid")
.register_column(:names, field: "name")
.register_column(:namespaces, field: "namespace")
.register_column(:networks, field: "networks")
.register_column(:status, field: "status")
.register_column(:labels, field: "labels")
filter.install_filter_methods_on_resource(self, :pods)
attr_reader :pods
def initialize(pods)
@pods = pods
end
def to_s
"Podman Pods"
end
def resource_id
"Podman Pods"
end
end
class PodmanVolumeFilter
filter = FilterTable.create
filter.register_custom_matcher(:exists?) { |x| !x.entries.empty? }
.register_column(:names, field: "name")
.register_column(:drivers, field: "driver")
.register_column(:mountpoints, field: "mountpoint")
.register_column(:createdat, field: "createdat")
.register_column(:labels, field: "labels")
.register_column(:scopes, field: "scope")
.register_column(:options, field: "options")
.register_column(:mountcount, field: "mountcount")
.register_column(:needscopyup, field: "needscopyup")
.register_column(:needschown, field: "needschown")
filter.install_filter_methods_on_resource(self, :volumes)
attr_reader :volumes
def initialize(volumes)
@volumes = volumes
end
def to_s
"Podman Volumes"
end
def resource_id
"Podman Volumes"
end
end
end

View file

@ -0,0 +1,84 @@
require "inspec/resources/podman"
require_relative "docker_object"
# Change module if required
module Inspec::Resources
class PodmanContainer < Inspec.resource(1)
include Inspec::Resources::DockerObject
name "podman_container"
supports platform: "unix"
desc "Inspec core resource to retrieve information about podman container"
example <<~EXAMPLE
describe podman_container("sweet_mendeleev") do
it { should exist }
it { should be_running }
its("id") { should eq "591270d8d80d26671fd6ed622f367fbe19004d16e3b519c292313feb5f22e7f7" }
its("image") { should eq "docker.io/library/nginx:latest" }
its("labels") { should include "maintainer"=>"NGINX Docker Maintainers <docker-maint@nginx.com>" }
its("ports") { should eq nil }
end
describe podman_container(id: "591270d8d80d2667") do
it { should exist }
it { should be_running }
end
EXAMPLE
def initialize(opts = {})
skip_resource "The `podman_container` resource is not yet available on your OS." unless inspec.os.unix?
# if a string is provided, we expect it is the name
if opts.is_a?(String)
@opts = { name: opts }
else
@opts = opts
end
end
def running?
status.downcase.start_with?("up") if object_info.entries.length == 1
end
def status
object_info.status[0] if object_info.entries.length == 1
end
def labels
object_info.labels
end
def ports
object_info.ports[0] if object_info.entries.length == 1
end
def command
return unless object_info.entries.length == 1
object_info.commands[0]
end
def image
object_info.images[0] if object_info.entries.length == 1
end
def resource_id
object_info.ids[0] || @opts[:id] || @opts[:name] || ""
end
def to_s
name = @opts[:name] || @opts[:id]
"Podman Container #{name}"
end
private
def object_info
return @info if defined?(@info)
opts = @opts
@info = inspec.podman.containers.where { names == opts[:name] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) }
end
end
end

View file

@ -0,0 +1,108 @@
require "inspec/resources/command"
require_relative "docker_object"
require "inspec/utils/podman"
module Inspec::Resources
class PodmanImage < Inspec.resource(1)
include Inspec::Resources::DockerObject
include Inspec::Utils::Podman
name "podman_image"
supports platform: "unix"
desc "InSpec core resource to retrieve information about podman image"
example <<~EXAMPLE
describe podman_image("docker.io/library/busybox") do
it { should exist }
its("repo_tags") { should include "docker.io/library/busybox:latest" }
its("size") { should eq 1636053 }
its("resource_id") { should eq "docker.io/library/busybox:latest" }
end
describe podman_image("docker.io/library/busybox:latest") do
it { should exist }
end
describe podman_image(repo: "docker.io/library/busybox", tag: "latest") do
it { should exist }
end
describe podman_image(id: "3c19bafed223") do
it { should exist }
end
EXAMPLE
attr_reader :opts, :image_info
def initialize(opts)
skip_resource "The `podman_image` resource is not yet available on your OS." unless inspec.os.unix?
opts = { image: opts } if opts.is_a?(String)
@opts = sanitize_options(opts)
raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
@image_info = get_image_info
end
LABELS = {
"id" => "ID",
"repo_tags" => "RepoTags",
"size" => "Size",
"digest" => "Digest",
"created_at" => "Created",
"version" => "Version",
"names_history" => "NamesHistory",
"repo_digests" => "RepoDigests",
"architecture" => "Architecture",
"os" => "Os",
"virtual_size" => "VirtualSize",
}.freeze
## This creates all the required properties methods dynamically.
LABELS.each do |k, v|
define_method(k) do
image_info[k.to_s]
end
end
def exist?
! image_info.empty?
end
def resource_id
opts[:id] || opts[:image] || ""
end
def to_s
"podman_image #{resource_id}"
end
private
def sanitize_options(opts)
opts.merge!(parse_components_from_image(opts[:image]))
# assume a "latest" tag if we don't have one
opts[:tag] ||= "latest"
# Assemble/reassemble the image from the repo and tag
opts[:image] = "#{opts[:repo]}:#{opts[:tag]}" unless opts[:repo].nil?
opts
end
def get_image_info
current_image = opts[:id] || opts[:image] || opts[:repo] + ":" + opts[:tag]
json_key_label = generate_go_template(LABELS)
podman_inspect_cmd = inspec.command("podman image inspect #{current_image} --format '{#{json_key_label}}'")
if podman_inspect_cmd.exit_status == 0
parse_command_output(podman_inspect_cmd.stdout)
elsif podman_inspect_cmd.stderr =~ /failed to find image/
{}
else
raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman image information for #{current_image}.\nError message: #{podman_inspect_cmd.stderr}"
end
end
end
end

View file

@ -0,0 +1,81 @@
require "inspec/resources/command"
require "inspec/utils/podman"
module Inspec::Resources
class PodmanNetwork < Inspec.resource(1)
include Inspec::Utils::Podman
name "podman_network"
supports platform: "unix"
desc "InSpec core resource to retrive information about the given Podman network"
example <<~EXAMPLE
describe podman_network("podman") do
it { should exist }
end
describe podman_network("3a7c94d937d5f3a0f1a9b1610589945aedfbe56207fd5d32fc8154aa1a8b007f") do
its("driver") { should eq bridge }
end
EXAMPLE
LABELS = {
id: "ID",
name: "Name",
driver: "Driver",
labels: "Labels",
options: "Options",
ipam_options: "IPAMOptions",
internal: "Internal",
created: "Created",
ipv6_enabled: "IPv6Enabled",
dns_enabled: "DNSEnabled",
network_interface: "NetworkInterface",
subnets: "Subnets",
}.freeze
attr_reader :param, :network_info
def initialize(param)
skip_resource "The `podman_network` resource is not yet available on your OS." unless inspec.os.unix?
@param = param
raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
@network_info = get_network_info
end
## This creates all the required properties methods dynamically.
LABELS.each do |k, v|
define_method(k) do
network_info[k.to_s]
end
end
def exist?
!network_info.empty?
end
def resource_id
id || param || ""
end
def to_s
"podman_network #{resource_id}"
end
private
def get_network_info
go_template_format = generate_go_template(LABELS)
result = inspec.command("podman network inspect #{param} --format '{#{go_template_format}}'")
if result.exit_status == 0
parse_command_output(result.stdout)
elsif result.stderr =~ /network not found/
{}
else
raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman network information for #{param}.\nError message: #{result.stderr}"
end
end
end
end

View file

@ -0,0 +1,101 @@
require "inspec/resources/command"
require "inspec/utils/podman"
module Inspec::Resources
class PodmanPod < Inspec.resource(1)
include Inspec::Utils::Podman
name "podman_pod"
supports platform: "unix"
desc "InSpec core resource to retrieve information about podman pod"
example <<~EXAMPLE
describe podman_pod("nginx-frontend") do
it { should exist }
its("id") { should eq "fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4" }
its("name") { should eq "nginx-frontend" }
its("created_at") { should eq "2022-07-14T15:47:47.978078124+05:30" }
its("create_command") { should include "new:nginx-frontend" }
its("state") { should eq "Running" }
its("hostname") { should eq "" }
its("create_cgroup") { should eq true }
its("cgroup_parent") { should eq "user.slice" }
its("cgroup_path") { should eq "user.slice/user-libpod_pod_fcfe4d471cfface0d1b39bce23af7d31ab8736cd68c0360ade0b4afe364f79d4.slice" }
its("create_infra") { should eq true }
its("infra_container_id") { should eq "727538044b32a165934729dc2d47d9d5e981b6496aebfad7de470f7e76ea4251" }
its("infra_config") { should include "DNSOption" }
its("shared_namespaces") { should include "ipc" }
its("num_containers") { should eq 2 }
its("containers") { should_not be nil }
end
describe podman_pod("non-existing-pod") do
it { should_not exist }
end
EXAMPLE
attr_reader :pod_info, :pod_id
def initialize(pod_id)
skip_resource "The `podman_pod` resource is not yet available on your OS." unless inspec.os.unix?
raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
@pod_id = pod_id
@pod_info = get_pod_info
end
LABELS = {
"id" => "ID",
"name" => "Name",
"created_at" => "Created",
"create_command" => "CreateCommand",
"state" => "State",
"hostname" => "Hostname",
"create_cgroup" => "CreateCgroup",
"cgroup_parent" => "CgroupParent",
"cgroup_path" => "CgroupPath",
"create_infra" => "CreateInfra",
"infra_container_id" => "InfraContainerID",
"infra_config" => "InfraConfig",
"shared_namespaces" => "SharedNamespaces",
"num_containers" => "NumContainers",
"containers" => "Containers",
}.freeze
# This creates all the required properties methods dynamically.
LABELS.each do |k, _|
define_method(k) do
pod_info[k.to_s]
end
end
def exist?
!pod_info.empty?
end
def resource_id
pod_id
end
def to_s
"Podman Pod #{resource_id}"
end
private
def get_pod_info
json_key_label = generate_go_template(LABELS)
inspect_pod_cmd = inspec.command("podman pod inspect #{pod_id} --format '{#{json_key_label}}'")
if inspect_pod_cmd.exit_status == 0
parse_command_output(inspect_pod_cmd.stdout)
elsif inspect_pod_cmd.stderr =~ /no pod with name or ID/
{}
else
raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman pod information for #{pod_id}.\nError message: #{inspect_pod_cmd.stderr}"
end
end
end
end

View file

@ -0,0 +1,87 @@
require "inspec/resources/command"
require "inspec/utils/podman"
module Inspec::Resources
class PodmanVolume < Inspec.resource(1)
include Inspec::Utils::Podman
name "podman_volume"
supports platform: "unix"
desc "InSpec core resource to retrieve information about podman volume"
example <<~EXAMPLE
describe podman_volume("my_volume") do
it { should exist }
its("name") { should eq "my_volume" }
its("driver") { should eq "local" }
its("mountpoint") { should eq "/var/home/core/.local/share/containers/storage/volumes/my_volume/_data" }
its("created_at") { should eq "2022-07-14T13:21:19.965421792+05:30" }
its("labels") { should eq({}) }
its("scope") { should eq "local" }
its("options") { should eq({}) }
its("mount_count") { should eq 0 }
its("needs_copy_up") { should eq true }
its("needs_chown") { should eq true }
end
EXAMPLE
attr_reader :volume_info, :volume_name
def initialize(volume_name)
skip_resource "The `podman_volume` resource is not yet available on your OS." unless inspec.os.unix?
raise Inspec::Exceptions::ResourceFailed, "Podman is not running. Please make sure it is installed and running." unless podman_running?
@volume_name = volume_name
@volume_info = get_volume_info
end
LABELS = {
"name" => "Name",
"driver" => "Driver",
"mountpoint" => "Mountpoint",
"created_at" => "CreatedAt",
"labels" => "Labels",
"scope" => "Scope",
"options" => "Options",
"mount_count" => "MountCount",
"needs_copy_up" => "NeedsCopyUp",
"needs_chown" => "NeedsChown",
}.freeze
# This creates all the required properties methods dynamically.
LABELS.each do |k, _|
define_method(k) do
volume_info[k.to_s]
end
end
def exist?
!volume_info.empty?
end
def resource_id
volume_name
end
def to_s
"podman_volume #{resource_id}"
end
private
def get_volume_info
json_key_label = generate_go_template(LABELS)
inspect_volume_cmd = inspec.command("podman volume inspect #{volume_name} --format '{#{json_key_label}}'")
if inspect_volume_cmd.exit_status == 0
parse_command_output(inspect_volume_cmd.stdout)
elsif inspect_volume_cmd.stderr =~ /inspecting object: no such/
{}
else
raise Inspec::Exceptions::ResourceFailed, "Unable to retrieve podman volume information for #{volume_name}.\nError message: #{inspect_volume_cmd.stderr}"
end
end
end
end

View file

@ -8,6 +8,8 @@ require "inspec/impact"
require "inspec/resource" require "inspec/resource"
require "inspec/resources/os" require "inspec/resources/os"
require "inspec/input_registry" require "inspec/input_registry"
require "inspec/waiver_file_reader"
require "inspec/utils/convert"
module Inspec module Inspec
class Rule class Rule
@ -133,10 +135,11 @@ module Inspec
# #
# @param [Type] &block returns true if tests are added, false otherwise # @param [Type] &block returns true if tests are added, false otherwise
# @return [nil] # @return [nil]
def only_if(message = nil) def only_if(message = nil, impact: nil)
return unless block_given? return unless block_given?
return if @__skip_only_if_eval == true return if @__skip_only_if_eval == true
self.impact(impact) if impact && !yield
@__skip_rule[:result] ||= !yield @__skip_rule[:result] ||= !yield
@__skip_rule[:type] = :only_if @__skip_rule[:type] = :only_if
@__skip_rule[:message] = message @__skip_rule[:message] = message
@ -337,17 +340,20 @@ module Inspec
# only_if mechanism) # only_if mechanism)
# Double underscore: not intended to be called as part of the DSL # Double underscore: not intended to be called as part of the DSL
def __apply_waivers def __apply_waivers
input_name = @__rule_id # TODO: control ID slugging control_id = @__rule_id # TODO: control ID slugging
registry = Inspec::InputRegistry.instance waiver_files = Inspec::Config.cached.final_options["waiver_file"] if Inspec::Config.cached.respond_to?(:final_options)
input = registry.inputs_by_profile.dig(__profile_id, input_name)
return unless input && input.has_value? && input.value.is_a?(Hash) waiver_data_by_profile = Inspec::WaiverFileReader.fetch_waivers_by_profile(__profile_id, waiver_files) unless waiver_files.nil?
return unless waiver_data_by_profile && waiver_data_by_profile[control_id] && waiver_data_by_profile[control_id].is_a?(Hash)
# An InSpec Input is a datastructure that tracks a profile parameter # An InSpec Input is a datastructure that tracks a profile parameter
# over time. Its value can be set by many sources, and it keeps a # over time. Its value can be set by many sources, and it keeps a
# log of each "set" event so that when it is collapsed to a value, # log of each "set" event so that when it is collapsed to a value,
# it can determine the correct (highest priority) value. # it can determine the correct (highest priority) value.
# Store in an instance variable for.. later reading??? # Store in an instance variable for.. later reading???
@__waiver_data = input.value @__waiver_data = waiver_data_by_profile[control_id]
__waiver_data["skipped_due_to_waiver"] = false __waiver_data["skipped_due_to_waiver"] = false
__waiver_data["message"] = "" __waiver_data["message"] = ""
@ -376,6 +382,7 @@ module Inspec
# expiration_date. We only care here if it has a "run" key and it # expiration_date. We only care here if it has a "run" key and it
# is false-like, since all non-skipped waiver operations are handled # is false-like, since all non-skipped waiver operations are handled
# during reporting phase. # during reporting phase.
__waiver_data["run"] = Converter.to_boolean(__waiver_data["run"]) if __waiver_data.key?("run")
return unless __waiver_data.key?("run") && !__waiver_data["run"] return unless __waiver_data.key?("run") && !__waiver_data["run"]
# OK, apply a skip. # OK, apply a skip.

View file

@ -1,3 +1,5 @@
require "inspec/enhanced_outcomes"
module Inspec module Inspec
class RunData class RunData
Control = Struct.new( Control = Struct.new(
@ -31,6 +33,10 @@ module Inspec
].each do |field| ].each do |field|
self[field] = raw_ctl_data[field] self[field] = raw_ctl_data[field]
end end
def status
Inspec::EnhancedOutcomes.determine_status(results, impact)
end
end end
end end

View file

@ -16,14 +16,20 @@ module Inspec
:total, :total,
:passed, :passed,
:skipped, :skipped,
:failed :failed,
:not_reviewed,
:not_applicable,
:error
) do ) do
include HashLikeStruct include HashLikeStruct
def initialize(raw_stat_ctl_data) def initialize(raw_stat_ctl_data)
self.total = raw_stat_ctl_data[:total] self.total = raw_stat_ctl_data[:total]
self.passed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:passed][:total]) self.passed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:passed][:total])
self.skipped = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:skipped][:total])
self.failed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:failed][:total]) self.failed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:failed][:total])
self.skipped = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:skipped][:total]) if raw_stat_ctl_data[:skipped]
self.not_reviewed = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:not_reviewed][:total]) if raw_stat_ctl_data[:not_reviewed]
self.not_applicable = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:not_applicable][:total]) if raw_stat_ctl_data[:not_applicable]
self.error = Inspec::RunData::Statistics::Controls::Total.new(raw_stat_ctl_data[:error][:total]) if raw_stat_ctl_data[:error]
end end
end end
class Controls class Controls

View file

@ -60,9 +60,11 @@ module Inspec
end end
if @conf[:waiver_file] if @conf[:waiver_file]
waivers = @conf.delete(:waiver_file) @conf[:waiver_file].each do |file|
@conf[:input_file] ||= [] unless File.file?(file)
@conf[:input_file].concat waivers raise Inspec::Exceptions::WaiversFileDoesNotExist, "Waiver file #{file} does not exist."
end
end
end end
# About reading inputs: # About reading inputs:
@ -133,12 +135,20 @@ module Inspec
all_controls.each do |rule| all_controls.each do |rule|
unless rule.nil? unless rule.nil?
register_rule(rule) register_rule(rule)
checks = ::Inspec::Rule.prepare_checks(rule) total_checks = 0
unless checks.empty? control_describe_checks = ::Inspec::Rule.prepare_checks(rule)
examples = control_describe_checks.flat_map do |m, a, b|
get_check_example(m, a, b)
end.compact
examples.map { |example| total_checks += example.examples.count }
unless control_describe_checks.empty?
# controls with empty tests are avoided # controls with empty tests are avoided
# checks represent tests within control # checks represent tests within control
controls_count += 1 controls_count += 1 if control_checks_count_map[rule.to_s].nil?
control_checks_count_map[rule.to_s] = checks.count control_checks_count_map[rule.to_s] = control_checks_count_map[rule.to_s].to_i + total_checks
end end
end end
end end
@ -158,7 +168,7 @@ module Inspec
return if @conf["reporter"].nil? return if @conf["reporter"].nil?
@conf["reporter"].each do |reporter| @conf["reporter"].each do |reporter|
result = Inspec::Reporters.render(reporter, run_data) result = Inspec::Reporters.render(reporter, run_data, @conf["enhanced_outcomes"])
raise Inspec::ReporterError, "Error generating reporter '#{reporter[0]}'" if result == false raise Inspec::ReporterError, "Error generating reporter '#{reporter[0]}'" if result == false
end end
end end

View file

@ -107,11 +107,11 @@ module Inspec
stats = @formatter.results[:statistics][:controls] stats = @formatter.results[:statistics][:controls]
load_failures = @formatter.results[:profiles]&.select { |p| p[:status] == "failed" }&.any? load_failures = @formatter.results[:profiles]&.select { |p| p[:status] == "failed" }&.any?
skipped = @formatter.results.dig(:profiles, 0, :status) == "skipped" skipped = @formatter.results.dig(:profiles, 0, :status) == "skipped"
if stats[:failed][:total] == 0 && stats[:skipped][:total] == 0 && !skipped && !load_failures if stats[:failed][:total] == 0 && stats[:skipped][:total] == 0 && !skipped && !load_failures && (stats[:error] && stats[:error][:total] == 0) # placed error count condition because of enhanced outcomes
0 0
elsif load_failures elsif load_failures
@conf["distinct_exit"] ? 102 : 1 @conf["distinct_exit"] ? 102 : 1
elsif stats[:failed][:total] > 0 elsif stats[:failed][:total] > 0 || (stats[:error] && stats[:error][:total] > 0)
@conf["distinct_exit"] ? 100 : 1 @conf["distinct_exit"] ? 100 : 1
elsif stats[:skipped][:total] > 0 || skipped elsif stats[:skipped][:total] > 0 || skipped
@conf["distinct_exit"] ? 101 : 0 @conf["distinct_exit"] ? 101 : 0
@ -196,6 +196,7 @@ module Inspec
def configure_output def configure_output
RSpec.configuration.output_stream = $stdout RSpec.configuration.output_stream = $stdout
@formatter = RSpec.configuration.add_formatter(Inspec::Formatters::Base) @formatter = RSpec.configuration.add_formatter(Inspec::Formatters::Base)
@formatter.enhanced_outcomes = @conf.final_options["enhanced_outcomes"]
RSpec.configuration.add_formatter(Inspec::Formatters::ShowProgress, $stderr) if @conf[:show_progress] RSpec.configuration.add_formatter(Inspec::Formatters::ShowProgress, $stderr) if @conf[:show_progress]
set_optional_formatters set_optional_formatters
RSpec.configuration.color = @conf["color"] RSpec.configuration.color = @conf["color"]

View file

@ -111,6 +111,43 @@ module Inspec
}, },
}.freeze }.freeze
CONTROL_ENHANCED_OUTCOME = {
"type" => "object",
"additionalProperties" => false,
"properties" => {
"id" => { "type" => "string" },
"title" => { "type" => %w{string null} },
"desc" => { "type" => %w{string null} },
"descriptions" => { "type" => %w{array} },
"impact" => { "type" => "number" },
"status" => {
"enum" => %w{passed failed not_applicable not_reviewed error},
"description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"),
},
"refs" => REFS,
"tags" => TAGS,
"code" => { "type" => "string" },
"source_location" => {
"type" => "object",
"properties" => {
"ref" => { "type" => "string" },
"line" => { "type" => "number" },
},
},
"results" => { "type" => "array", "items" => RESULT },
"waiver_data" => {
"type" => "object",
"properties" => {
"skipped_due_to_waiver" => { "type" => "string" },
"run" => { "type" => "boolean" },
"message" => { "type" => "string" },
"expiration_date" => { "type" => "string" },
"justification" => { "type" => "string" },
},
},
},
}.freeze
SUPPORTS = { SUPPORTS = {
"type" => "object", "type" => "object",
"additionalProperties" => false, "additionalProperties" => false,
@ -173,6 +210,45 @@ module Inspec
}, },
}.freeze }.freeze
PROFILE_ENHANCED_OUTCOME = {
"type" => "object",
"additionalProperties" => false,
"properties" => {
"name" => { "type" => "string" },
"version" => { "type" => "string", "optional" => true },
"sha256" => { "type" => "string", "optional" => false },
"title" => { "type" => "string", "optional" => true },
"maintainer" => { "type" => "string", "optional" => true },
"copyright" => { "type" => "string", "optional" => true },
"copyright_email" => { "type" => "string", "optional" => true },
"license" => { "type" => "string", "optional" => true },
"summary" => { "type" => "string", "optional" => true },
"status" => { "type" => "string", "optional" => false },
"status_message" => { "type" => "string", "optional" => true },
# skip_message is deprecated, status_message should be used to store the reason for skipping
"skip_message" => { "type" => "string", "optional" => true },
"supports" => {
"type" => "array",
"items" => SUPPORTS,
"optional" => true,
},
"controls" => {
"type" => "array",
"items" => CONTROL_ENHANCED_OUTCOME,
},
"groups" => {
"type" => "array",
"items" => CONTROL_GROUP,
},
"attributes" => { # TODO: rename to inputs, refs #3802
"type" => "array",
# TODO: more detailed specification needed
},
},
}.freeze
EXEC_JSON = { EXEC_JSON = {
"type" => "object", "type" => "object",
"additionalProperties" => false, "additionalProperties" => false,
@ -187,6 +263,20 @@ module Inspec
}, },
}.freeze }.freeze
EXEC_JSON_ENHANCED_OUTCOME = {
"type" => "object",
"additionalProperties" => false,
"properties" => {
"platform" => PLATFORM,
"profiles" => {
"type" => "array",
"items" => PROFILE_ENHANCED_OUTCOME,
},
"statistics" => STATISTICS,
"version" => { "type" => "string" },
},
}.freeze
MIN_CONTROL = { MIN_CONTROL = {
"type" => "object", "type" => "object",
"additionalProperties" => false, "additionalProperties" => false,
@ -228,6 +318,7 @@ module Inspec
LIST = { LIST = {
"exec-json" => EXEC_JSON, "exec-json" => EXEC_JSON,
"exec-jsonmin" => EXEC_JSONMIN, "exec-jsonmin" => EXEC_JSONMIN,
"exec-json-enhanced-outcome" => EXEC_JSON_ENHANCED_OUTCOME,
"platforms" => PLATFORMS, "platforms" => PLATFORMS,
}.freeze }.freeze

View file

@ -19,8 +19,8 @@ module Inspec
# Lists the potential values for a control result # Lists the potential values for a control result
CONTROL_RESULT_STATUS = Primitives::SchemaType.new("Control Result Status", { CONTROL_RESULT_STATUS = Primitives::SchemaType.new("Control Result Status", {
"type" => "string", "type" => "string",
"enum" => %w{passed failed skipped error}, "enum" => %w{passed failed skipped},
}, [], "The status of a control. Should be one of 'passed', 'failed', 'skipped', or 'error'.") }, [], "The status of a control. Should be one of 'passed', 'failed', or 'skipped'.")
# Represents the statistics/result of a control"s execution # Represents the statistics/result of a control"s execution
CONTROL_RESULT = Primitives::SchemaType.new("Control Result", { CONTROL_RESULT = Primitives::SchemaType.new("Control Result", {
@ -75,6 +75,36 @@ module Inspec
}, },
}, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.") }, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.")
# Represents a control produced with enhanced outcomes option
ENHANCED_OUTCOME_CONTROL = Primitives::SchemaType.new("Exec JSON Control", {
"type" => "object",
"additionalProperties" => true,
"required" => %w{id title desc impact refs tags code source_location results},
"properties" => {
"id" => Primitives.desc(Primitives::STRING, "The id."),
"title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."), # Nullable string
"desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."),
"descriptions" => Primitives.desc(Primitives.array(CONTROL_DESCRIPTION.ref), "A set of additional descriptions. Example: the 'fix' text."),
"impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."),
"status" => {
"enum" => %w{passed failed not_applicable not_reviewed error},
"description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"),
},
"refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."),
"tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."),
"code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code."),
"source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."),
"results" => Primitives.desc(Primitives.array(CONTROL_RESULT.ref), %q(
The set of all tests within the control and their results and findings. Example:
For Chef Inspec, if in the control's code we had the following:
describe sshd_config do
its('Port') { should cmp 22 }
end
The findings from this block would be appended to the results, as well as those of any other blocks within the control.
)),
},
}, [CONTROL_DESCRIPTION, Primitives::REFERENCE, Primitives::SOURCE_LOCATION, CONTROL_RESULT], "Describes a control and any findings it has.")
# Based loosely on https://docs.chef.io/inspec/profiles/ as of July 3, 2019 # Based loosely on https://docs.chef.io/inspec/profiles/ as of July 3, 2019
# However, concessions were made to the reality of current reporters, specifically # However, concessions were made to the reality of current reporters, specifically
# with how description is omitted and version/inspec_version aren't as advertised online # with how description is omitted and version/inspec_version aren't as advertised online
@ -112,6 +142,40 @@ module Inspec
}, },
}, [CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT], "Information on the set of controls assessed. Example: it can include the name of the Inspec profile and any findings.") }, [CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT], "Information on the set of controls assessed. Example: it can include the name of the Inspec profile and any findings.")
ENHANCED_OUTCOME_PROFILE = Primitives::SchemaType.new("Exec JSON Profile", {
"type" => "object",
"additionalProperties" => true,
"required" => %w{name sha256 supports attributes groups controls},
# Name is mandatory in inspec.yml.
# supports, controls, groups, and attributes are always present, even if empty
# sha256, status, status_message
"properties" => {
# These are provided in inspec.yml
"name" => Primitives.desc(Primitives::STRING, "The name - must be unique."),
"title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."),
"maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."),
"copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."),
"copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contact information of the copyright holder(s)."),
"depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."),
"parent_profile" => Primitives.desc(Primitives::STRING, "The name of the parent profile if the profile is a dependency of another."),
"license" => Primitives.desc(Primitives::STRING, "The copyright license. Example: the full text or the name, such as 'Apache License, Version 2.0'."),
"summary" => Primitives.desc(Primitives::STRING, "The summary. Example: the Security Technical Implementation Guide (STIG) header."),
"version" => Primitives.desc(Primitives::STRING, "The version of the profile."),
"supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."),
"description" => Primitives.desc(Primitives::STRING, "The description - should be more detailed than the summary."),
"inspec_version" => Primitives.desc(Primitives::STRING, "The version of Inspec."),
# These are generated at runtime, and all except status_message and skip_message are guaranteed
"sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."),
"status" => Primitives.desc(Primitives::STRING, "The status. Example: loaded."), # enum? loaded, failed, skipped
"status_message" => Primitives.desc(Primitives::STRING, "The reason for the status. Example: why it was skipped or failed to load."),
"skip_message" => Primitives.desc(Primitives::STRING, "The reason for skipping if it was skipped."), # Deprecated field - status_message should be used instead.
"controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls including any findings."),
"groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."),
"attributes" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used in the run."),
},
}, [ENHANCED_OUTCOME_CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::SUPPORT], "Information on the set of controls assessed. Example: it can include the name of the Inspec profile and any findings.")
# Result of exec json. Top level value # Result of exec json. Top level value
# TODO: Include the format of top level controls. This was omitted for lack of sufficient examples # TODO: Include the format of top level controls. This was omitted for lack of sufficient examples
OUTPUT = Primitives::SchemaType.new("Exec JSON Output", { OUTPUT = Primitives::SchemaType.new("Exec JSON Output", {
@ -125,6 +189,18 @@ module Inspec
"version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."), "version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."),
}, },
}, [Primitives::PLATFORM, PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.") }, [Primitives::PLATFORM, PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.")
ENHANCED_OUTCOME_OUTPUT = Primitives::SchemaType.new("Exec JSON Output", {
"type" => "object",
"additionalProperties" => true,
"required" => %w{platform profiles statistics version},
"properties" => {
"platform" => Primitives.desc(Primitives::PLATFORM.ref, "Information on the platform the run from the tool that generated the findings was from. Example: the name of the operating system."),
"profiles" => Primitives.desc(Primitives.array(PROFILE.ref), "Information on the run(s) from the tool that generated the findings. Example: the findings."),
"statistics" => Primitives.desc(Primitives::STATISTICS.ref, "Statistics for the run(s) from the tool that generated the findings. Example: the runtime duration."),
"version" => Primitives.desc(Primitives::STRING, "Version number of the tool that generated the findings. Example: '4.18.108' is a version of Chef InSpec."),
},
}, [Primitives::PLATFORM, ENHANCED_OUTCOME_PROFILE, Primitives::STATISTICS], "The top level value containing all of the results.")
end end
end end
end end

View file

@ -30,6 +30,8 @@ module Inspec
"profile-json" => OutputSchema.finalize(Schema::ProfileJson::PROFILE), "profile-json" => OutputSchema.finalize(Schema::ProfileJson::PROFILE),
"exec-json" => OutputSchema.finalize(Schema::ExecJson::OUTPUT), "exec-json" => OutputSchema.finalize(Schema::ExecJson::OUTPUT),
"exec-jsonmin" => OutputSchema.finalize(Schema::ExecJsonMin::OUTPUT), "exec-jsonmin" => OutputSchema.finalize(Schema::ExecJsonMin::OUTPUT),
"profile-json-enhanced-outcomes" => OutputSchema.finalize(Schema::ProfileJson::ENHANCED_OUTCOME_PROFILE),
"exec-json-enhanced-outcomes" => OutputSchema.finalize(Schema::ExecJson::ENHANCED_OUTCOME_OUTPUT),
"platforms" => PLATFORMS, "platforms" => PLATFORMS,
}.freeze }.freeze
@ -37,7 +39,8 @@ module Inspec
LIST.keys LIST.keys
end end
def self.json(name) def self.json(name, opts)
name += "-enhanced-outcomes" if opts["enhanced_outcomes"]
if !LIST.key?(name) if !LIST.key?(name)
raise("Cannot find schema #{name.inspect}.") raise("Cannot find schema #{name.inspect}.")
elsif LIST[name].is_a?(Proc) elsif LIST[name].is_a?(Proc)

View file

@ -31,6 +31,28 @@ module Inspec
}, },
}, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION], "The set of all tests within the control.") }, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION], "The set of all tests within the control.")
# Represents a control with enhanced outcomes status information
ENHANCED_OUTCOME_CONTROL = Primitives::SchemaType.new("Profile JSON Control", {
"type" => "object",
"additionalProperties" => true,
"required" => %w{id title desc impact tags code},
"properties" => {
"id" => Primitives.desc(Primitives::STRING, "The id."),
"title" => Primitives.desc({ "type" => %w{string null} }, "The title - is nullable."),
"desc" => Primitives.desc({ "type" => %w{string null} }, "The description for the overarching control."),
"descriptions" => Primitives.desc(CONTROL_DESCRIPTIONS.ref, "A set of additional descriptions. Example: the 'fix' text."),
"impact" => Primitives.desc(Primitives::IMPACT, "The impactfulness or severity."),
"status" => {
"enum" => %w{passed failed not_applicable not_reviewed error},
"description" => Primitives.desc(Primitives::STRING, "The enhanced outcome status of the control"),
},
"refs" => Primitives.desc(Primitives.array(Primitives::REFERENCE.ref), "The set of references to external documents."),
"tags" => Primitives.desc(Primitives::TAGS, "A set of tags - usually metadata."),
"code" => Primitives.desc(Primitives::STRING, "The raw source code of the control. Note that if this is an overlay control, it does not include the underlying source code."),
"source_location" => Primitives.desc(Primitives::SOURCE_LOCATION.ref, "The explicit location of the control within the source code."),
},
}, [CONTROL_DESCRIPTIONS, Primitives::REFERENCE, Primitives::SOURCE_LOCATION], "The set of all tests within the control.")
# A profile that has not been run. # A profile that has not been run.
PROFILE = Primitives::SchemaType.new("Profile JSON Profile", { PROFILE = Primitives::SchemaType.new("Profile JSON Profile", {
"type" => "object", "type" => "object",
@ -55,6 +77,30 @@ module Inspec
"depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."), # Can have depends, but NOT a parentprofile "depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."), # Can have depends, but NOT a parentprofile
}, },
}, [Primitives::SUPPORT, CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR], "Information on the set of controls that can be assessed. Example: it can include the name of the Inspec profile.") }, [Primitives::SUPPORT, CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR], "Information on the set of controls that can be assessed. Example: it can include the name of the Inspec profile.")
ENHANCED_OUTCOME_PROFILE = Primitives::SchemaType.new("Profile JSON Profile", {
"type" => "object",
"additionalProperties" => true, # Anything in the yaml will be put in here. LTTODO: Make this stricter!
"required" => %w{name supports controls groups sha256},
"properties" => {
"name" => Primitives.desc(Primitives::STRING, "The name - must be unique."),
"supports" => Primitives.desc(Primitives.array(Primitives::SUPPORT.ref), "The set of supported platform targets."),
"controls" => Primitives.desc(Primitives.array(CONTROL.ref), "The set of controls - contains no findings as the assessment has not yet occurred."),
"groups" => Primitives.desc(Primitives.array(Primitives::CONTROL_GROUP.ref), "A set of descriptions for the control groups. Example: the ids of the controls."),
"inputs" => Primitives.desc(Primitives.array(Primitives::INPUT), "The input(s) or attribute(s) used to be in the run."),
"sha256" => Primitives.desc(Primitives::STRING, "The checksum of the profile."),
"status" => Primitives.desc(Primitives::STRING, "The status. Example: skipped."),
"generator" => Primitives.desc(Primitives::GENERATOR.ref, "The tool that generated this file. Example: Chef Inspec."),
"version" => Primitives.desc(Primitives::STRING, "The version of the profile."),
# Other properties possible in inspec docs, but that aren"t guaranteed
"title" => Primitives.desc(Primitives::STRING, "The title - should be human readable."),
"maintainer" => Primitives.desc(Primitives::STRING, "The maintainer(s)."),
"copyright" => Primitives.desc(Primitives::STRING, "The copyright holder(s)."),
"copyright_email" => Primitives.desc(Primitives::STRING, "The email address or other contract information of the copyright holder(s)."),
"depends" => Primitives.desc(Primitives.array(Primitives::DEPENDENCY.ref), "The set of dependencies this profile depends on. Example: an overlay profile is dependent on another profile."), # Can have depends, but NOT a parentprofile
},
}, [Primitives::SUPPORT, ENHANCED_OUTCOME_CONTROL, Primitives::CONTROL_GROUP, Primitives::DEPENDENCY, Primitives::GENERATOR], "Information on the set of controls that can be assessed. Example: it can include the name of the Inspec profile.")
end end
end end
end end

View file

@ -5,4 +5,12 @@ module Converter
val = val.to_i if val =~ /^\d+$/ val = val.to_i if val =~ /^\d+$/
val val
end end
def self.to_boolean(value)
if ["true", "True", "TRUE", true, "yes", "y", "YES", "Y"].include? value
true
elsif ["false", "False", "FALSE", false, "no", "n", "NO", "N"].include? value
false
end
end
end end

View file

@ -0,0 +1,24 @@
require "inspec/resources/command"
module Inspec
module Utils
module Podman
def podman_running?
inspec.command("podman version").exit_status == 0
end
# Generates the template in this format using labels hash: "\"id\": {{json .ID}}, \"name\": {{json .Name}}",
def generate_go_template(labels)
(labels.map { |k, v| "\"#{k}\": {{json .#{v}}}" }).join(", ")
end
def parse_command_output(output)
require "json" unless defined?(JSON)
JSON.parse(output)
rescue JSON::ParserError => _e
warn "Could not parse the command output"
{}
end
end
end
end

View file

@ -0,0 +1,34 @@
require "csv" unless defined?(CSV)
module Waivers
class CSVFileReader
def self.resolve(path)
return nil unless File.file?(path)
@headers ||= []
fetch_data(path)
end
def self.fetch_data(path)
waiver_data_hash = {}
CSV.foreach(path, headers: true) do |row|
row_hash = row.to_hash
@headers = row_hash.keys if @headers.empty?
control_id = row_hash["control_id"]
# delete keys and values not required in final hash
row_hash.delete("control_id")
row_hash.delete_if { |k, v| k.nil? || v.nil? }
waiver_data_hash[control_id] = row_hash if control_id && !row_hash.blank?
end
waiver_data_hash
rescue CSV::MalformedCSVError => e
raise "Error reading InSpec waivers in CSV: #{e}"
end
def self.headers
@headers
end
end
end

Some files were not shown because too many files have changed in this diff Show more