Merge pull request #6559 from inspec/inspec-6-from-prime-2

Add chef-licensing, syncing from inspec-prime repo inspec-6 branch
This commit is contained in:
Clinton Wolfe 2023-07-07 17:13:26 -04:00 committed by GitHub
commit d64488f333
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
96 changed files with 1640 additions and 1171 deletions

View file

@ -0,0 +1,182 @@
# Documentation available at https://expeditor.chef.io/docs/getting-started/
---
product_key: inspec
rubygems:
- inspec
- inspec-core
- inspec-bin:
gemspec_path: ./inspec-bin/
- inspec-core-bin:
gemspec_path: ./inspec-bin/
pipelines:
- habitat/build:
env:
- HAB_NONINTERACTIVE: "true"
- HAB_NOCOLORING: "true"
- HAB_STUDIO_SECRET_HAB_NONINTERACTIVE: "true"
- docker/build
- omnibus/release:
env:
# The git cache is corrupt more often than not. This always purges the cache.
# https://chefio.atlassian.net/wiki/spaces/RELENGKB/pages/2204336129/Resolving+git+cache+build+errors+in+Omnibus
- EXPIRE_CACHE: 1
- IGNORE_ARTIFACTORY_RUBY_PROXY: true # Artifactory is throwing 500's when downloading some gems like ffi.
- omnibus/adhoc:
definition: .expeditor/release.omnibus.yml
env:
- ADHOC: true
- EXPIRE_CACHE: 1
- verify:
description: Pull Request validation tests
public: true
env:
- LANG: "C.UTF-8"
- SLOW: 1
- NO_AWS: 1
- MT_CPU: 5
- coverage:
description: Unit test coverage
# Private due to use of tokens
trigger: pull_request
env:
- LANG: "C.UTF-8"
- SLOW: 1
- NO_AWS: 1
- MT_CPU: 5
# This has been disabled because it regularly hits Docker API rate limits and fails
# - integration/resources:
# description: Test core resources with test-kitchen.
# definition: .expeditor/integration.resources.yml
# trigger: pull_request
- artifact/habitat:
description: Execute tests against the habitat artifact
definition: .expeditor/artifact.habitat.yml
env:
- HAB_NONINTERACTIVE: "true"
- HAB_NOCOLORING: "true"
- HAB_STUDIO_SECRET_HAB_NONINTERACTIVE: "true"
trigger: pull_request
schedules:
- name: integration_schedule
description: Periodic Integration Testing
cronline: "0 8 * * *"
slack:
notify_channel: inspec-notify
github:
delete_branch_on_merge: true
version_tag_format: v{{version}}
minor_bump_labels:
- "Expeditor: Bump Minor Version"
# allow bumping the major release via label
major_bump_labels:
- "Expeditor: Bump Major Version"
release_branches:
- inspec-6:
version_constraint: 6.*
- main:
version_constraint: 5.*
- inspec-4:
version_constraint: 4.*
changelog:
categories:
- "Type: New Resource": "New Resources"
- "Type: New Feature": "New Features"
- "Type: Enhancement": "Enhancements"
- "Type: Bug": "Bug Fixes"
subscriptions:
- workload: pull_request_merged:{{github_repo}}:{{release_branch}}:*
actions:
- built_in:bump_version:
ignore_labels:
- "Expeditor: Skip All"
- "Expeditor: Skip Version Bump"
only_if_modified:
- .expeditor/*
- docs-chef-io/*
- etc/*
- habitat/*
- inspec-bin/*
- lib/*
- omnibus/*
- support/*
- tasks/*
- test/*
- Gemfile*
- LICENSE
- "*.gemspec"
- "*.md"
- bash:.expeditor/update_version.sh:
only_if: built_in:bump_version
- built_in:update_changelog:
ignore_labels:
- "Expeditor: Skip All"
- "Expeditor: Skip Changelog"
- trigger_pipeline:omnibus/adhoc:
not_if: built_in:bump_version
ignore_labels:
- "Expeditor: Skip Omnibus"
- "Expeditor: Skip All"
- trigger_pipeline:artifact/habitat:
only_if: built_in:bump_version
ignore_labels:
- "Expeditor: Skip Habitat"
- "Expeditor: Skip All"
- trigger_pipeline:omnibus/release:
only_if: built_in:bump_version
ignore_labels:
- "Expeditor: Skip Omnibus"
- "Expeditor: Skip All"
- trigger_pipeline:habitat/build:
only_if: built_in:bump_version
ignore_labels:
- "Expeditor: Skip Habitat"
- "Expeditor: Skip All"
- built_in:build_gem:
only_if:
- built_in:bump_version
- workload: artifact_published:unstable:inspec:{{version_constraint}}
actions:
- trigger_pipeline:docker/build
- bash:.expeditor/buildkite/wwwrelease.sh:
post_commit: true
- workload: artifact_published:current:inspec:{{version_constraint}}
actions:
- built_in:promote_docker_images
- built_in:promote_habitat_packages
- workload: project_promoted:{{agent_id}}:*
actions:
- built_in:promote_artifactory_artifact
- workload: artifact_published:stable:inspec:{{version_constraint}}
actions:
- bash:.expeditor/update_dockerfile.sh
- built_in:rollover_changelog
- built_in:publish_rubygems
- built_in:create_github_release
- built_in:promote_docker_images
- built_in:promote_habitat_packages
- bash:.expeditor/publish-release-notes.sh:
post_commit: true
- purge_packages_chef_io_fastly:{{target_channel}}/inspec/latest:
post_commit: true
- bash:.expeditor/announce-release.sh:
post_commit: true
- built_in:notify_chefio_slack_channels
- workload: pull_request_opened:{{github_repo}}:{{release_branch}}:*
actions:
- post_github_comment:.expeditor/templates/pull_request.mustache:
ignore_team_members:
- inspec/owners
- inspec/inspec-core-team
- built_in:github_auto_assign_author:
only_if_team_member:
- inspec/owners
- inspec/inspec-core-team

View file

@ -9,26 +9,20 @@ expeditor:
steps: steps:
- label: lint-ruby-3.0 - label: lint-ruby-3.1
command: command:
- RAKE_TASK=test:lint /workdir/.expeditor/buildkite/verify.sh - RAKE_TASK=test:lint /workdir/.expeditor/buildkite/verify.sh
expeditor: expeditor:
secrets: true
executor: executor:
docker: docker:
image: ruby:3.0 image: ruby:3.1
- label: run-tests-ruby-2.7
command:
- /workdir/.expeditor/buildkite/verify.sh
expeditor:
executor:
docker:
image: ruby:2.7
- label: run-tests-ruby-3.0 - label: run-tests-ruby-3.0
command: command:
- /workdir/.expeditor/buildkite/verify.sh - /workdir/.expeditor/buildkite/verify.sh
expeditor: expeditor:
secrets: true
executor: executor:
docker: docker:
image: ruby:3.0 image: ruby:3.0
@ -37,6 +31,7 @@ steps:
command: command:
- /workdir/.expeditor/buildkite/verify.sh - /workdir/.expeditor/buildkite/verify.sh
expeditor: expeditor:
secrets: true
executor: executor:
docker: docker:
image: ruby:3.1 image: ruby:3.1
@ -45,6 +40,7 @@ steps:
command: command:
- RAKE_TASK=test:isolated /workdir/.expeditor/buildkite/verify.sh - RAKE_TASK=test:isolated /workdir/.expeditor/buildkite/verify.sh
expeditor: expeditor:
secrets: true
executor: executor:
docker: docker:
image: ruby:3.0 image: ruby:3.0
@ -53,6 +49,7 @@ steps:
command: command:
- RAKE_TASK=test:isolated /workdir/.expeditor/buildkite/verify.sh - RAKE_TASK=test:isolated /workdir/.expeditor/buildkite/verify.sh
expeditor: expeditor:
secrets: true
executor: executor:
docker: docker:
image: ruby:3.1 image: ruby:3.1
@ -61,6 +58,7 @@ steps:
command: command:
- /workdir/.expeditor/buildkite/verify.ps1 - /workdir/.expeditor/buildkite/verify.ps1
expeditor: expeditor:
secrets: true
executor: executor:
docker: docker:
environment: environment:

View file

@ -1,21 +1,61 @@
# 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 unreleased -->
## Unreleased <!-- latest_release 6.4.33 -->
## [v6.4.33](https://github.com/inspec/inspec-prime/tree/v6.4.33) (2023-07-07)
#### Merged Pull Requests #### Merged Pull Requests
- inspec-6 CI - Add secrets: true to private verify pipeline, delete ruby 2.7 config [#6558](https://github.com/inspec/inspec/pull/6558) ([clintoncwolfe](https://github.com/clintoncwolfe)) - Adds test for licensing_config [#57](https://github.com/inspec/inspec-prime/pull/57) ([Vasu1105](https://github.com/Vasu1105))
- forcing private in the configuration file [#6556](https://github.com/inspec/inspec/pull/6556) ([sean-simmons-progress](https://github.com/sean-simmons-progress))
<!-- latest_release --> <!-- latest_release -->
<!-- release_rollup since=5.18.14 --> <!-- release_rollup since=5.18.14 -->
### Changes since 5.18.14 release ### Changes since 5.18.14 release
#### Merged Pull Requests #### Merged Pull Requests
- inspec-6 CI - Add secrets: true to private verify pipeline, delete ruby 2.7 config [#6558](https://github.com/inspec/inspec/pull/6558) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.2.46 --> - inspec-6 CI - Add secrets: true to private verify pipeline, delete ruby 2.7 config [#6558](https://github.com/inspec/inspec/pull/6558) ([clintoncwolfe](https://github.com/clintoncwolfe))
- forcing private in the configuration file [#6556](https://github.com/inspec/inspec/pull/6556) ([sean-simmons-progress](https://github.com/sean-simmons-progress)) <!-- 6.2.46 --> - forcing private in the configuration file [#6556](https://github.com/inspec/inspec/pull/6556) ([sean-simmons-progress](https://github.com/sean-simmons-progress))
- Privatize verify pipeline [#6555](https://github.com/inspec/inspec/pull/6555) ([sean-simmons-progress](https://github.com/sean-simmons-progress)) <!-- 6.2.46 --> - Adds test for licensing_config [#57](https://github.com/inspec/inspec-prime/pull/57) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.4.33 -->
- Prep CI system for inspec-prime merge [#6553](https://github.com/inspec/inspec/pull/6553) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.2.45 --> - Configure to use `Inspec::Log` in Chef Licensing [#67](https://github.com/inspec/inspec-prime/pull/67) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.32 -->
- Crossport public 6549: Drop testing on EOL ruby 2.7, and run linter on Ruby 3.1 [#76](https://github.com/inspec/inspec-prime/pull/76) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.31 -->
- Case correction of product name in licensing config [#78](https://github.com/inspec/inspec-prime/pull/78) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.30 -->
- Foreport - Add postgres support for custom port with a socket connection [#40](https://github.com/inspec/inspec-prime/pull/40) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.29 -->
- Bump omnibus-software from `88169e3` to `4b08f0b` in /omnibus [#73](https://github.com/inspec/inspec-prime/pull/73) ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 6.4.28 -->
- CHEF-3759 Crossport public 6540 Fix for inspec parallel on windows crashing due to error log rename [#74](https://github.com/inspec/inspec-prime/pull/74) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.27 -->
- Foreports #6526 and #6541: Update Docker base image to be ubuntu 22.04 [#64](https://github.com/inspec/inspec-prime/pull/64) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.26 -->
- Crossport Public 6545 Fix for InSpec Parallel hangs on certain CIS profiles [#71](https://github.com/inspec/inspec-prime/pull/71) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.26 -->
- Bump omnibus from `15122f2` to `9c0643a` in /omnibus [#70](https://github.com/inspec/inspec-prime/pull/70) ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 6.4.25 -->
- Bump berkshelf from 8.0.2 to 8.0.7 in /omnibus [#63](https://github.com/inspec/inspec-prime/pull/63) ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 6.4.24 -->
- Foreport #6523: Update RSpec to 3.12 [#65](https://github.com/inspec/inspec-prime/pull/65) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.23 -->
- Bump omnibus from `cf97613` to `15122f2` in /omnibus [#62](https://github.com/inspec/inspec-prime/pull/62) ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 6.4.22 -->
- Bump omnibus-software from `225e357` to `88169e3` in /omnibus [#61](https://github.com/inspec/inspec-prime/pull/61) ([dependabot[bot]](https://github.com/dependabot[bot])) <!-- 6.4.21 -->
- CHEF-3704 Modify help for local licensing service mode and other distros [#59](https://github.com/inspec/inspec-prime/pull/59) ([Nik08](https://github.com/Nik08)) <!-- 6.4.20 -->
- restrict license commands only to inspec distro [#58](https://github.com/inspec/inspec-prime/pull/58) ([sathish-progress](https://github.com/sathish-progress)) <!-- 6.4.19 -->
- CHEF-3184 Error handling for inspec license add command - disabled in local mode [#52](https://github.com/inspec/inspec-prime/pull/52) ([Nik08](https://github.com/Nik08)) <!-- 6.4.18 -->
- CHEF-3403: Default server URL to production value [#50](https://github.com/inspec/inspec-prime/pull/50) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.17 -->
- CHEF-3186: Remove fetching of bearer auth token from vault [#48](https://github.com/inspec/inspec-prime/pull/48) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.16 -->
- CHEF 83 Revert attestations changes [#47](https://github.com/inspec/inspec-prime/pull/47) ([sathish-progress](https://github.com/sathish-progress)) <!-- 6.4.15 -->
- Foreports 6489 (CHEF-1458 Multiple values changes in SimpleConfig library) [#28](https://github.com/inspec/inspec-prime/pull/28) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.14 -->
- Foreport - Add nftables resources (#6499) [#44](https://github.com/inspec/inspec-prime/pull/44) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.13 -->
- Foreport - Update host resource to resolve all ipaddresses (#6481) [#39](https://github.com/inspec/inspec-prime/pull/39) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.12 -->
- Foreport - Bump rack from 2.2.6.2 to 2.2.6.4 in /omnibus (#6490) [#42](https://github.com/inspec/inspec-prime/pull/42) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.11 -->
- Foreport - fix: ensure Invoke-WebRequest headers can be configured (#6484) [#41](https://github.com/inspec/inspec-prime/pull/41) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.10 -->
- Foreport - CHEF-2438 Add train-kubernetes to inspec gemspec (#6512) [#43](https://github.com/inspec/inspec-prime/pull/43) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.9 -->
- Foreport - Clarify key_rsa docs regarding SSH keys (#6507) [#45](https://github.com/inspec/inspec-prime/pull/45) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.8 -->
- CHEF-3105 Fix windows openssl issue [#37](https://github.com/inspec/inspec-prime/pull/37) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.4.7 -->
- CHEF-2743: Set chef executable name to display in help messages of chef-licensing [#34](https://github.com/inspec/inspec-prime/pull/34) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.6 -->
- CHEF-2994: Add license command to list of allowed commands [#35](https://github.com/inspec/inspec-prime/pull/35) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.5 -->
- CHEF-1957: Update chef-licesing api call `license_keys` to `fetch_and_persist` [#30](https://github.com/inspec/inspec-prime/pull/30) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.4 -->
- Remove kitchen group from Gemfile [#31](https://github.com/inspec/inspec-prime/pull/31) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.3 -->
- CHEF-52: Add licensing information to help output [#27](https://github.com/inspec/inspec-prime/pull/27) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.2 -->
- Add command to list license information [#10](https://github.com/inspec/inspec-prime/pull/10) ([ahasunos](https://github.com/ahasunos)) <!-- 6.4.1 -->
- Licensing - Integrates Software Entitlement [#13](https://github.com/inspec/inspec-prime/pull/13) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.4.0 -->
- Integration of chef licensing with inspec [#12](https://github.com/inspec/inspec-prime/pull/12) ([Nik08](https://github.com/Nik08)) <!-- 6.3.0 -->
- CI - Use License Key and API Key Secrets from Vault [#26](https://github.com/inspec/inspec-prime/pull/26) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.2.49 -->
- Update Gemfile to add artifactory as source for chef-licensing gem dependency [#25](https://github.com/inspec/inspec-prime/pull/25) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.2.48 -->
- testing version bump and pipeline creation [#16](https://github.com/inspec/inspec-prime/pull/16) ([sean-simmons-progress](https://github.com/sean-simmons-progress)) <!-- 6.2.47 -->
- CHEF-1267 Add omnibus release and adhoc pipelines [#15](https://github.com/inspec/inspec-prime/pull/15) ([clintoncwolfe](https://github.com/clintoncwolfe)) <!-- 6.2.46 -->
- testing version bump [#9](https://github.com/inspec/inspec-prime/pull/9) ([sean-simmons-progress](https://github.com/sean-simmons-progress)) <!-- 6.2.45 -->
- Forport 6388 [#6477](https://github.com/inspec/inspec/pull/6477) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.2.44 --> - Forport 6388 [#6477](https://github.com/inspec/inspec/pull/6477) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.2.44 -->
- Foreport 6360 [#6476](https://github.com/inspec/inspec/pull/6476) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.2.43 --> - Foreport 6360 [#6476](https://github.com/inspec/inspec/pull/6476) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.2.43 -->
- Foreport-6423 [#6474](https://github.com/inspec/inspec/pull/6474) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.2.42 --> - Foreport-6423 [#6474](https://github.com/inspec/inspec/pull/6474) ([Vasu1105](https://github.com/Vasu1105)) <!-- 6.2.42 -->
@ -5171,4 +5211,4 @@
- make default rake tasks test+lint [\#108](https://github.com/chef/inspec/pull/108) ([arlimus](https://github.com/arlimus)) - make default rake tasks test+lint [\#108](https://github.com/chef/inspec/pull/108) ([arlimus](https://github.com/arlimus))
- Improve unit tests [\#106](https://github.com/chef/inspec/pull/106) ([chris-rock](https://github.com/chris-rock)) - Improve unit tests [\#106](https://github.com/chef/inspec/pull/106) ([chris-rock](https://github.com/chris-rock))
- add to\_s methods to resources, fixes \#98 [\#105](https://github.com/chef/inspec/pull/105) ([chris-rock](https://github.com/chris-rock)) - add to\_s methods to resources, fixes \#98 [\#105](https://github.com/chef/inspec/pull/105) ([chris-rock](https://github.com/chris-rock))
- 0.7.0 release [\#104](https://github.com/chef/inspec/pull/104) ([chris-rock](https://github.com/chris-rock)) - 0.7.0 release [\#104](https://github.com/chef/inspec/pull/104) ([chris-rock](https://github.com/chris-rock))

View file

@ -1,4 +1,4 @@
FROM ubuntu:18.04 FROM --platform=linux/amd64 ubuntu:22.04
LABEL maintainer="Chef Software, Inc. <docker@chef.io>" LABEL maintainer="Chef Software, Inc. <docker@chef.io>"
ARG VERSION=5.18.14 ARG VERSION=5.18.14

17
Gemfile
View file

@ -48,19 +48,6 @@ group :deploy do
gem "inquirer" gem "inquirer"
end end
group :kitchen do source "https://artifactory-internal.ps.chef.co/artifactory/api/gems/omnibus-gems-local/" do
gem "berkshelf" gem "chef-licensing"
# Chef 18 requires ruby 3
if Gem.ruby_version >= Gem::Version.new("3.0.0")
gem "chef", ">= 17.0"
else
# Ruby 2.7 presumably - TODO remove this when 2.7 is sunsetted
gem "chef", "~> 16.0"
end
gem "test-kitchen", ">= 2.8"
gem "kitchen-inspec", ">= 2.0"
gem "kitchen-dokken", ">= 2.11"
gem "git"
end end

View file

@ -1,5 +1,5 @@
# Chef InSpec: Inspect Your Infrastructure # Chef InSpec: Inspect Your Infrastructure
* **Project State: Active** * **Project State: Active**
* **Issues Response SLA: 14 business days** * **Issues Response SLA: 14 business days**
* **Pull Request Response SLA: 14 business days** * **Pull Request Response SLA: 14 business days**

View file

@ -1 +1 @@
6.2.46 6.4.33

View file

@ -1,82 +0,0 @@
# 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`, `CSV`, or `JSON`.
#### YAML and JSON
An array of Hashes.
#### CSV
CSV 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

@ -1,179 +0,0 @@
+++
title = "Attestations"
draft = false
gh_repo = "inspec"
[menu]
[menu.inspec]
title = "Attestations"
identifier = "inspec/reference/attestations.md Attestations"
parent = "inspec/reference"
weight = 140
+++
Attestations is a mechanism to mark the `Not Reviewed (N/R)` tests as `passed` or `failed` manually using an attestations file.
## Example
A fire alarm needs to be audited, but it cannot be reviewed (N/R) through automation. Hence, to audit the fire alarm using an InSpec profile, the outcome of its working must be marked as `passed` or `failed` in a test through manual intervention. By using attestations and passing the status using an attestations file, we can audit the fire alarm.
### Attestations File to an audit fire alarm
```yaml
fire-alarm-1:
expiration_date: 2090-10-1
status: passed
justification: "Fire alarm 1 was tested manually and it works."
fire-alarm-2:
expiration_date: 2090-10-1
status: failed
justification: "Fire alarm 2 was tested manually and it does not work."
```
### InSpec Test
```ruby
control "fire-alarm-1" do
only_if("Fire alarm 1 needs to be tested manually") {
false
}
end
control "fire-alarm-2" do
only_if("Fire alarm 2 needs to be tested manually") {
false
}
end
```
### Running attestations to an audit fire alarm
```bash
inspec exec path/to/fire-alarm-audit-profile --attestation-file attestation.yaml
Profile: InSpec Profile (attestation)
Version: 0.1.0
Target: local://
Target ID: fa3923b9-f806-4cc2-960d-1ddefb4c7654
✔ fire-alarm-1: No-op (1 skipped)
↺ Skipped control due to only_if condition: Fire alarm 1 needs to be tested manually
✔ Control Attested : Fire alarm 1 was tested manually and it works.
× fire-alarm-2: No-op (1 failed) (1 skipped)
↺ Skipped control due to only_if condition: Fire alarm 2 needs to be tested manually
× Control Attested : Fire alarm 2 was tested manually and it does not work.
Profile Summary: 1 successful control, 1 control failure, 0 controls not reviewed, 0 controls not applicable, 0 controls have error
Test Summary: 1 successful, 1 failure, 2 skipped
```
## Attestations Fields
An attestations file identifies:
1. the controls need to be attested.
1. an explanation of why it is manually attested.
1. control status `passed` or `failed` to attest controls.
1. (optional) an URL pointing to a website containing information on control attestation.
1. (optional) an expiration date of attestation.
## Usage
To use attestations, you must have a correctly formatted attestations file and
invoke `inspec exec` with `--attestation-file [path]`.
```bash
inspec exec path/to/profile --attestation-file attestation.yaml
```
## File Format
Attestations files support YAML, JSON, and CSV formats.
```yaml
control_id:
expiration_date: YYYY-MM-DD
status: passed
justification: "reason for attesting this control"
evidence_url: "URL pointing to a website containing information on control attestation"
```
OR
```json
{
"control_id": {
"expiration_date": "YYYY-MM-DD",
"status": "passed",
"justification": "reason for attesting this control",
"evidence_url": "URL pointing to a website containing information on control attestation"
}
}
```
- `status` is mandatory. If absent, the control will not be attested. It can only be `passed` or `failed`.
- `expiration_date` sets the day the attestations file expires in **YYYY-MM-DD** format. Attestations files expire at 00:00 at the local time of the system on the specified date. Attestations files without expiration date are permanent. `expiration_date` is optional.
- `justification` is a text containing the reason why attestations is required. It might as well as include information on who initiated the attestation. If it is absent, it shows a warning message to include justification in the attestations file.
- `evidence_url` is an URL of a website containing information on control attestation. It is optional.
### File Format Examples
#### Example in YAML
```yaml
example-3.0.1:
justification: "Passed by the auditor manually"
evidence_url: "https://www.attestation-info-chef-example/"
expiration_date: 2050-06-01
status: passed
example-3.0.2:
justification: "Failed by the auditor manually"
evidence_url: "https://www.attestation-info-chef-example/"
expiration_date: 2050-07-01
status: failed
```
#### Example in JSON
```json
{
"example-3.0.1": {
"justification": "Passed by the auditor manually",
"evidence_url": "https://www.attestation-info-chef-example/",
"expiration_date": "2050-06-01",
"status": "passed"
},
"example-3.0.2": {
"justification": "Failed by the auditor manually",
"evidence_url": "https://www.attestation-info-chef-example/",
"expiration_date": "2050-07-01",
"status": "failed"
}
}
```
#### Example in CSV
These file formats support the following fields in a file:
- `control_id`
_Required_.
- `justification`
_Required_.
- `status`
_Required_.
- `evidence_url`
_Optional_.
- `expiration_date`
_Optional_.
![Attestations File Example](/images/inspec/attestations_file_excel.png)
{{< note >}}
How is the Attestations mechanism different than Waivers?
The waivers mechanism skips the controls for various reasons which are required for waiving. Whereas attestations mark the skipped controls which are not reviewed as `passed` or `failed` using the status passed through the attestations file by the auditor.
{{< /note >}}

View file

@ -13,7 +13,7 @@ platform = "os"
Use the `key_rsa` Chef InSpec audit resource to test RSA public/private keypairs. Use the `key_rsa` Chef InSpec audit resource to test RSA public/private keypairs.
This resource is mainly useful when used in conjunction with the x509_certificate resource but it can also be used for checking SSH keys. This resource is mainly useful when used in conjunction with the x509_certificate resource, but it can also be used for checking RSA-based SSH keys.
## Availability ## Availability

View file

@ -0,0 +1,140 @@
+++
title = "nftables resource"
draft = false
gh_repo = "inspec"
platform = "linux"
[menu]
[menu.inspec]
title = "nftables"
identifier = "inspec/resources/os/nftables.md nftables resource"
parent = "inspec/resources/os"
+++
Use the `nftables` Chef InSpec audit resource to test rules and sets that are defined using `nftables`, which maintains tables of IP packet filtering rules. There may be more than one table. Each table contains one (or more) chains. A chain is a list of rules that match packets. When a rule matches a packet, the rule defines what target to assign to the packet.
## Availability
### Installation
{{% inspec/inspec_installation %}}
### Version
This resource first became available in v5.21.30 of InSpec.
## Syntax
A `nftables` resource block declares tests for rules in IP tables:
```ruby
describe nftables(family:'name', table:'name', chain: 'name') do
its('PROPERTY') { should eq 'value' }
it { should have_rule('RULE') }
end
describe nftables(family:'name', table:'name', set: 'name') do
its('PROPERTY') { should eq 'value' }
it { should have_element('ELEMENT') }
end
```
where
- `nftables()` has to specify `family` and `table`. It also has to specify one of `chain` or `set` (exclusively).
- `family:'name'` is the name of the `family` the table belongs to, one of `ip`, `ip6`, `inet`, `arp`, `bridge`, `netdev`.
- `table:'name'` is the packet matching table against which the test is run.
- `chain: 'name'` is the name of a user-defined chain.
- `set: 'name'` is the name of a user-defined named set.
- `have_rule('RULE')` tests that the chain has a given rule in the nftables ruleset. This must match the entire line taken from `nftables -nn list chain FAMILY TABLE CHAIN`.
- `have_element('ELEMENT')` tests that element is a member of the nftables named set.
See the [NFT man page](https://www.netfilter.org/projects/nftables/manpage.html) and [nftables wiki](https://wiki.nftables.org/wiki-nftables/index.php/Main_Page) for more information about nftables.
## Properties
### Chain Properties
`hook`
: The hook type. Possible values: `ingress`, `prerouting`, `forward`, `input`, `output`, `postrouting`, and `egress`.
`prio`
: The numerical chain priority.
`policy`
: The policy type. Possible values: `accept`, `drop`.
`type`
: The chain type. Possible values: `filter`, `nat`, and `route`.
### Set Properties
`flags`
: The set flags. Possible values: `constant`, `dynamic`, `interval`, and `timeout`.
`size`
: The maximum number of elements in the set.
`type`
: The data type of set elements. Possible values: `ipv4_addr`, `ipv6_addr`, `ether_addr`, `inet_proto`, `inet_service`, and `mark`.
## Examples
The following examples show how to use this Chef InSpec audit resource.
### Test if the `CHAIN_NAME` chain from the `TABLE_NAME` table has the default `accept` policy
```ruby
describe nftables(family: 'inet', table: 'TABLE_NAME', chain: 'CHAIN_NAME') do
its('policy') { should eq 'accept' }
end
```
### Test the attributes of the `CHAIN_NAME` chain from the `TABLE_NAME` table
```ruby
describe nftables(family: 'inet', table: 'mangle', chain: 'INPUT') do
its('type') { should eq 'filter' }
its('hook') { should eq 'input' }
its('prio') { should eq (-150) } # mangle
its('policy') { should eq 'accept' }
end
```
### Test if there is a rule allowing Postgres (5432/TCP) traffic
```ruby
describe nftables(family: 'inet', table: 'TABLE_NAME', chain: 'CHAIN_NAME') do
it { should have_rule('tcp dport 5432 comment "postgres" accept') }
end
```
Note that the rule specification must exactly match what's in the output of `nftables -nn list chain inet TABLE_NAME CHAIN_NAME`, which will depend on how you've built your rules.
### Test if there is an element `1.1.1.1` in the `SET_NAME` named set
```ruby
describe nftables(family: 'inet', table: 'TABLE_NAME', set: 'SET_NAME') do
it { should have_element('1.1.1.1') }
end
```
## Matchers
For a full list of available matchers, please visit our [matchers page](/inspec/matchers/).
### have_rule
The `have_rule` matcher tests the named rule against the information in the `nftables` ruleset:
```ruby
it { should have_rule('RULE') }
```
### have_element
The `have_element` matcher tests the named set against the information in the `nftables` ruleset:
```ruby
it { should have_element('SET_ELEMENT') }
```

View file

@ -11,6 +11,7 @@ gh_repo = "inspec"
weight = 140 weight = 140
+++ +++
Waivers allow you to waive controls and to dictate the running and/or reporting of those controls. A waiver file identifies: Waivers allow you to waive controls and to dictate the running and/or reporting of those controls. A waiver file identifies:
1. which controls are waived 1. which controls are waived

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,6 +1,6 @@
KsCa3OgmGAdfLvm0JOqzILc2SUL3FKTQ+XCpaFLiiwefhhKirl+ddcpj10g7 wNEzKHmtSf1pIdciEC6DOs5SlOs3IbW1psVFLlmZc0NbnHe6MEahAnKWmHUP
rtufpHQal6qoQ8PoCP4BBLOtrShyXYhOBqoQXLNiMYQAEdWTobOIz6naicE/ 9YrDv2JMQo1I8MM/cez8XDxpK4O5y4HT66RqoAlfBkg82LmYC7f1Cy34ByCj
z23mweizu3C4qS4yG5hz4BwNhWSTVFrkhQZaF6KS5mMBHrULCV3i7Fs0BFWy LBZg5o/IVBGnY+Ksbhtp0mQYEyU048FnXIfh9uOfbKahU8HkPJssTkIw3fjL
lhO3NS9GXaD+1RSKcTsUWTuNlK2R0TWqHgWiDDy+P80XFW3DvBjPweRyghJa Vrd5GQ4ssfW1XXFaxx7DjxWlPmWBVhd8c1Y2RlACZyI+w1DQNYimrWvgiFym
sW+5fwMYGyZPULt8lx8U8Ec05XQiIxeneosRGtdvjIh7JzhJr/UXJsIMhJvV 0VbnndiSX+2x84AZHE9AmsebcAYk9QlqO1N0VeYqBZj45FXLtpsNwYo0amDa
eNWfNJrWJkzzeeSXVV3E/VeYBhZGkI5ra5guf05Ifw== D/wyKGxRQLUYXyd2tDVJMWbeHPHy8UIK17RoSctrEg==

View file

@ -82,8 +82,6 @@
description: Use JUnit2 reporter. description: Use JUnit2 reporter.
inspec-reporter-html2: inspec-reporter-html2:
description: Use HTML reporter. description: Use HTML reporter.
inspec-attestations:
description: Use attestations mechanism with one or more attestations files.
inspec-reporter-progress-bar: inspec-reporter-progress-bar:
description: Use progress bar streaming reporter description: Use progress bar streaming reporter
inspec-reporter-child-status: inspec-reporter-child-status:

View file

@ -1,5 +1,5 @@
# This file managed by automation - do not edit manually # This file managed by automation - do not edit manually
module InspecBin module InspecBin
INSPECBIN_ROOT = File.expand_path("..", __dir__) INSPECBIN_ROOT = File.expand_path("..", __dir__)
VERSION = "6.2.46".freeze VERSION = "6.4.33".freeze
end end

View file

@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "thor", ">= 0.20", "< 2.0" spec.add_dependency "thor", ">= 0.20", "< 2.0"
spec.add_dependency "method_source", ">= 0.8", "< 2.0" spec.add_dependency "method_source", ">= 0.8", "< 2.0"
spec.add_dependency "rubyzip", ">= 1.2.2", "< 3.0" spec.add_dependency "rubyzip", ">= 1.2.2", "< 3.0"
spec.add_dependency "rspec", ">= 3.9", "<= 3.11" spec.add_dependency "rspec", ">= 3.9", "<= 3.12"
spec.add_dependency "rspec-its", "~> 1.2" spec.add_dependency "rspec-its", "~> 1.2"
spec.add_dependency "pry", "~> 0.13" spec.add_dependency "pry", "~> 0.13"
spec.add_dependency "hashie", ">= 3.4", "< 5.0" spec.add_dependency "hashie", ">= 3.4", "< 5.0"

View file

@ -38,9 +38,11 @@ Gem::Specification.new do |spec|
spec.add_dependency "faraday_middleware", ">= 0.12.2", "< 1.1" spec.add_dependency "faraday_middleware", ">= 0.12.2", "< 1.1"
# Train plugins we ship with InSpec # Train plugins we ship with InSpec
spec.add_dependency "train-habitat", "~> 0.1" spec.add_dependency "train-habitat", "~> 0.1"
spec.add_dependency "train-aws", "~> 0.2" spec.add_dependency "train-aws", "~> 0.2"
spec.add_dependency "train-winrm", "~> 0.2" spec.add_dependency "train-winrm", "~> 0.2"
spec.add_dependency "train-kubernetes", "~> 0.1"
spec.add_dependency "mongo", "= 2.13.2" # 2.14 introduces a broken symlink in mongo-2.14.0/spec/support/ocsp spec.add_dependency "mongo", "= 2.13.2" # 2.14 introduces a broken symlink in mongo-2.14.0/spec/support/ocsp
end end

View file

@ -1,7 +1,7 @@
--- ---
driver: driver:
name: dokken name: dokken
chef_version: :latest chef_version: 17
privileged: true # because Docker and SystemD/Upstart privileged: true # because Docker and SystemD/Upstart
transport: transport:
@ -64,7 +64,7 @@ platforms:
- name: opensuse-leap - name: opensuse-leap
driver: driver:
image: dokken/opensuse-leap-15 image: dokken/opensuse-leap-15
pid_one_command: /bin/systemd pid_one_command: /usr/lib/systemd/systemd
- name: ubuntu-16.04 - name: ubuntu-16.04
driver: driver:

View file

@ -4,6 +4,7 @@ libdir = File.dirname(__FILE__)
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir) $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
require "inspec/version" require "inspec/version"
require "inspec/utils/licensing_config"
require "inspec/exceptions" require "inspec/exceptions"
require "inspec/utils/deprecation" require "inspec/utils/deprecation"
require "inspec/profile" require "inspec/profile"
@ -30,4 +31,4 @@ require "inspec/source_reader"
require "inspec/resource" require "inspec/resource"
require "inspec/dependency_loader" require "inspec/dependency_loader"
require "inspec/dependency_installer" require "inspec/dependency_installer"

View file

@ -1,50 +0,0 @@
require "inspec/secrets/yaml"
require "inspec/utils/waivers/csv_file_reader"
require "inspec/utils/waivers/json_file_reader"
# require "inspec/utils/waivers/excel_file_reader"
module Inspec
class AttestationFileReader < WaiverFileReader
# Invoked from rule.rb and streaming reporter base class to fetch attestation data
def self.fetch_attestation_by_profile(profile_id, files)
read_attestation_from_file(profile_id, files) if @attestation_data.nil? || @attestation_data[profile_id].nil?
@attestation_data[profile_id]
end
def self.read_attestation_from_file(profile_id, files)
@attestation_data ||= {}
output = {}
files.each do |file_path|
data = read_from_file(file_path)
output.merge!(data) if !data.nil? && data.is_a?(Hash)
if data.nil?
raise Inspec::Exceptions::AttestationFileNotReadable,
"Cannot find parser for attestation file '#{file_path}'. " \
"Check to make sure file has the appropriate extension."
end
end
@attestation_data[profile_id] = output
end
# Attestation file has different headers than waiver file
# Overriding header validation logic of WaiverFileReader
def self.validate_headers(headers, json_yaml = false)
required_fields = json_yaml ? %w{status} : %w{control_id status}
missing_cols = (required_fields - headers)
missing_cols << "justification" if (!headers.include? "justification") && (!headers.include? "explanation")
Inspec::Log.warn "Missing column headers: #{missing_cols}" unless missing_cols.empty?
Inspec::Log.warn "Invalid column header: Column can't be nil" if headers.include? nil
Inspec::Log.warn "Extra column headers: #{(headers - all_fields)}" unless (headers - all_fields).empty?
end
# defining all fields used in attestation files of different formats
def self.all_fields
%w{control_id justification expiration_date evidence_url status explanation frequency updated}
end
end
end

View file

@ -1,155 +0,0 @@
module Inspec
module Attestations
# Invoked from reporters base classes & run_data.rb to modify run data
def self.attest(run_data)
run_data[:profiles].each do |profile|
profile[:controls].each do |control|
# logic for attestation applied for N/R controls here.
if control[:status] == "not_reviewed" && !control[:attestation_data].empty?
expiry = determine_expiry(control[:attestation_data], control[:id])
# if expiration date parsing was successful
if expiry
control[:attestation_data]["message"] = validate_attestation_expiry(expiry, control[:id])
attestation_result = attestation_check(control[:attestation_data]["message"], control[:attestation_data], control[:id])
if attestation_result
status, attestation_msg = attestation_result
control[:status] = status # N/R status -> to passed/failed based on attestation logic
# replicated test result hash to invoke pass/fail test
control[:results].push({
status: control[:status],
code_desc: attestation_msg,
expectation_message: attestation_msg,
})
end
end
end
end
end
end
# Invoked from streaming reporter base class
def self.attest_streaming_data(attestation_data, status, control_id)
# logic check for N/R controls here for streaming reporters
if status == "not_reviewed" && !attestation_data.blank?
expiry = determine_expiry(attestation_data, control_id)
# if expiration date parsing was successful
if expiry
expiry_message = validate_attestation_expiry(expiry, control_id)
attestation_check(expiry_message, attestation_data, control_id)
end
end
end
def self.validate_attestation_expiry(expiry, control_id)
# logic to check for expiry
if [Date, Time].include?(expiry.class) || (expiry.is_a?(String) && Time.parse(expiry).year != 0)
expiry = expiry.to_time if expiry.is_a? Date
expiry = Time.parse(expiry) if expiry.is_a? String
if expiry < Time.now # If the attestation expired, return - no attestation done
expiry_message = "Attestation expired on #{expiry}"
expiry_message
end
else
ui = Inspec::UI.new
ui.error("Unable to parse attestation expiration date '#{expiry}' for control #{control_id}")
ui.exit(:usage_error)
end
rescue => e
ui = Inspec::UI.new
ui.error("Unable to parse attestation expiration date '#{expiry}' for control #{control_id}. Error: #{e.message}")
ui.exit(:usage_error)
end
def self.attestation_check(expiry_message, attestation_data, control_id)
# logic to update enhanced outcome status
status, msg = nil
if %w{passed failed}.include? attestation_data["status"]
if expiry_message
status = "failed"
msg = "Control not attested : #{expiry_message}"
else
# use justification and evidence url to show information in msg
attestation_message = attestation_data["justification"] || attestation_data["explanation"] || ""
unless attestation_data["evidence_url"].blank?
if attestation_message.blank?
attestation_message = "Evidence URL: #{attestation_data["evidence_url"]}"
else
attestation_message += " | Evidence URL: #{attestation_data["evidence_url"]}"
end
end
status = attestation_data["status"]
if attestation_message.blank?
msg = "Control Attested : No justification provided."
else
msg = "Control Attested : #{attestation_message}"
end
end
else
if attestation_data["status"].blank?
Inspec::Log.warn "No attestation status for control #{control_id}. Use 'passed' or 'failed'."
else
Inspec::Log.warn "Invalid attestation status '#{attestation_data["status"]}' for control #{control_id}. Use 'passed' or 'failed'."
end
return nil
end
[status, msg]
end
def self.determine_expiry(attestation_data, control_id)
if attestation_data["expiration_date"]
attestation_data["expiration_date"]
elsif !attestation_data["updated"].blank? && !attestation_data["frequency"].blank?
begin
calculate_expiry(attestation_data["updated"], attestation_data["frequency"], control_id)
rescue => e
ui = Inspec::UI.new
ui.error("Unable to parse attestation updated date '#{attestation_data["updated"]}' for control #{control_id}. Error: #{e.message}")
ui.exit(:usage_error)
end
end
end
def self.calculate_expiry(updated_date, frequency, control_id)
# logic to find expiration date using frequency and updated date.
if updated_date.is_a?(Date) || (updated_date.is_a?(String) && Date.parse(updated_date).year != 0)
updated_date = Date.parse(updated_date) if updated_date.is_a? String
if updated_date < Time.now.to_date
case frequency
when "annually"
updated_date.to_date.next_year(1)
when "semiannually"
updated_date.next_month(6)
when "quarterly"
updated_date.next_month(3)
when "monthly"
updated_date.next_month(1)
when "every2weeks"
updated_date.next_day(14)
when "weekly"
updated_date.next_day(7)
when "every3days"
updated_date.next_day(3)
when "daily"
updated_date.next_day(1)
else
Inspec::Log.warn "Invalid frequency value '#{frequency}' for control #{control_id}."
updated_date
end
else
updated_date
end
else
ui = Inspec::UI.new
ui.error("Unable to parse attestation updated date '#{updated_date}' for control #{control_id}")
ui.exit(:usage_error)
end
end
end
end

View file

@ -1,9 +1,11 @@
require "thor" # rubocop:disable Chef/Ruby/UnlessDefinedRequire require "thor" # rubocop:disable Chef/Ruby/UnlessDefinedRequire
require "chef-licensing"
require "inspec/log" require "inspec/log"
require "inspec/ui" require "inspec/ui"
require "inspec/config" require "inspec/config"
require "inspec/dist" require "inspec/dist"
require "inspec/utils/deprecation/global_method" require "inspec/utils/deprecation/global_method"
require "inspec/utils/licensing_config"
# Allow end of options during array type parsing # Allow end of options during array type parsing
# https://github.com/erikhuda/thor/issues/631 # https://github.com/erikhuda/thor/issues/631
@ -30,11 +32,34 @@ module Inspec
end end
def self.start(given_args = ARGV, config = {}) def self.start(given_args = ARGV, config = {})
check_license! if config[:enforce_license] || config[:enforce_license].nil? if Inspec::Dist::EXEC_NAME == "inspec"
check_license! if config[:enforce_license] || config[:enforce_license].nil?
fetch_and_persist_license
end
super(given_args, config) super(given_args, config)
end end
def self.fetch_and_persist_license
allowed_commands = ["-h", "--help", "help", "-v", "--version", "version", "license"]
begin
if (allowed_commands & ARGV.map(&:downcase)).empty? && !ARGV.empty?
license_keys = ChefLicensing.fetch_and_persist
# Only if EULA acceptance or license key args are present. And licenses are successfully persisted, do clean exit.
if ARGV.select { |arg| !(arg.include? "--chef-license") }.empty? && !license_keys.blank?
Inspec::UI.new.exit
end
end
rescue ChefLicensing::LicenseKeyFetcher::LicenseKeyNotFetchedError
Inspec::Log.error "#{Inspec::Dist::PRODUCT_NAME} cannot execute without valid licenses."
Inspec::UI.new.exit(:usage_error)
rescue ChefLicensing::Error => e
Inspec::Log.error e.message
Inspec::UI.new.exit(:usage_error)
end
end
# EULA acceptance # EULA acceptance
def self.check_license! def self.check_license!
allowed_commands = ["-h", "--help", "help", "-v", "--version", "version"] allowed_commands = ["-h", "--help", "help", "-v", "--version", "version"]
@ -48,9 +73,6 @@ module Inspec
Inspec::VERSION, Inspec::VERSION,
logger: Inspec::Log logger: Inspec::Log
) )
if license_acceptor_output && ARGV.count == 1 && (ARGV.first.include? "--chef-license")
Inspec::UI.new.exit
end
license_acceptor_output license_acceptor_output
end end
rescue LicenseAcceptance::LicenseNotAcceptedError rescue LicenseAcceptance::LicenseNotAcceptedError
@ -177,8 +199,6 @@ module Inspec
desc: "Load one or more input files, a YAML file with values for the profile to use." desc: "Load one or more input files, a YAML file with values for the profile to use."
option :waiver_file, type: :array, option :waiver_file, type: :array,
desc: "Load one or more waiver files." desc: "Load one or more waiver files."
option :attestation_file, type: :array,
desc: "Load one or more attestation files."
option :attrs, type: :array, option :attrs, type: :array,
desc: "Legacy name for --input-file - deprecated." desc: "Legacy name for --input-file - deprecated."
option :create_lockfile, type: :boolean, option :create_lockfile, type: :boolean,
@ -213,6 +233,30 @@ module Inspec
def self.help(*args) def self.help(*args)
super(*args) super(*args)
if Inspec::Dist::EXEC_NAME == "inspec"
puts <<~CHEF_LICENSE_HELP
Chef Compliance has three tiers of licensing:
* Free-Tier
Users are limited to audit maximum of 10 targets
Entitled for personal or non-commercial use
* Trial
Entitled for unlimited number of targets
Entitled for 30 days only
Entitled for commercial use
* Commercial
Entitled for purchased number of targets
Entitled for period of subscription purchased
Entitled for commercial use
inspec license add: This command helps users to generate or add an additional license (not applicable to local licensing service)
For more information please visit:
www.chef.io/licensing/faqs
CHEF_LICENSE_HELP
end
puts "\nAbout #{Inspec::Dist::PRODUCT_NAME}:" puts "\nAbout #{Inspec::Dist::PRODUCT_NAME}:"
puts " Patents: chef.io/patents\n\n" puts " Patents: chef.io/patents\n\n"
end end

View file

@ -62,6 +62,11 @@ class Inspec::InspecCLI < Inspec::BaseCLI
require "license_acceptance/cli_flags/thor" require "license_acceptance/cli_flags/thor"
include LicenseAcceptance::CLIFlags::Thor include LicenseAcceptance::CLIFlags::Thor
if Inspec::Dist::EXEC_NAME == "inspec"
require "chef-licensing/cli_flags/thor"
include ChefLicensing::CLIFlags::Thor
end
desc "json PATH", "read all tests in the PATH and generate a JSON summary." desc "json PATH", "read all tests in the PATH and generate a JSON summary."
option :output, aliases: :o, type: :string, option :output, aliases: :o, type: :string,
desc: "Save the created profile to a path." desc: "Save the created profile to a path."

View file

@ -12,7 +12,5 @@ module Inspec
class ProfileSigningKeyNotFound < ArgumentError; end class ProfileSigningKeyNotFound < ArgumentError; end
class WaiversFileNotReadable < ArgumentError; end class WaiversFileNotReadable < ArgumentError; end
class WaiversFileDoesNotExist < ArgumentError; end class WaiversFileDoesNotExist < ArgumentError; end
class AttestationFileNotReadable < ArgumentError; end
class AttestationFileDoesNotExist < ArgumentError; end
end end
end end

View file

@ -236,7 +236,6 @@ module Inspec::Formatters
resource_title: example.metadata[:described_class] || example.metadata[:example_group][:description], resource_title: example.metadata[:described_class] || example.metadata[:example_group][:description],
expectation_message: format_expectation_message(example), expectation_message: format_expectation_message(example),
waiver_data: example.metadata[:waiver_data], waiver_data: example.metadata[:waiver_data],
attestation_data: example.metadata[:attestation_data],
# This enforces the resource name as expected based off of the class # This enforces the resource name as expected based off of the class
# name. However, if we wanted the `name` attribute against the class # name. However, if we wanted the `name` attribute against the class
# to be canonical for this case (consider edge cases!) we would use # to be canonical for this case (consider edge cases!) we would use
@ -359,7 +358,6 @@ module Inspec::Formatters
# (that is, per-describe-block) basis, because that is the only granularity # (that is, per-describe-block) basis, because that is the only granularity
# available to us in the RSpec report data structure which we use as a vehicle. # available to us in the RSpec report data structure which we use as a vehicle.
control[:waiver_data] ||= example[:waiver_data] || {} control[:waiver_data] ||= example[:waiver_data] || {}
control[:attestation_data] ||= example[:attestation_data] || {}
end end
end end
end end

View file

@ -1,4 +1,3 @@
require "inspec/attestations"
module Inspec::Plugin::V2::PluginType module Inspec::Plugin::V2::PluginType
class StreamingReporter < Inspec::Plugin::V2::PluginBase class StreamingReporter < Inspec::Plugin::V2::PluginBase
register_plugin_type(:streaming_reporter) register_plugin_type(:streaming_reporter)
@ -14,7 +13,6 @@ module Inspec::Plugin::V2::PluginType
@controls_count = nil @controls_count = nil
@notifications = {} @notifications = {}
@enhanced_outcome_control_wise = {} @enhanced_outcome_control_wise = {}
@attestation_message_control_wise = {}
end end
private private
@ -37,7 +35,7 @@ module Inspec::Plugin::V2::PluginType
unless @control_checks_count_map[control_id].nil? unless @control_checks_count_map[control_id].nil?
@control_checks_count_map[control_id] -= 1 @control_checks_count_map[control_id] -= 1
control_ended = @control_checks_count_map[control_id] == 0 control_ended = @control_checks_count_map[control_id] == 0
# after a control has ended it checks for certain operations, like enhanced outcomes & attestations # after a control has ended it checks for certain operations, like enhanced outcomes
run_control_operations(notification, control_id) if control_ended run_control_operations(notification, control_id) if control_ended
control_ended control_ended
else else
@ -47,7 +45,6 @@ module Inspec::Plugin::V2::PluginType
def run_control_operations(notification, control_id) def run_control_operations(notification, control_id)
check_for_enhanced_outcomes(notification, control_id) check_for_enhanced_outcomes(notification, control_id)
check_for_attestation(notification, control_id)
end end
def check_for_enhanced_outcomes(notification, control_id) def check_for_enhanced_outcomes(notification, control_id)
@ -57,25 +54,12 @@ module Inspec::Plugin::V2::PluginType
end end
end end
def check_for_attestation(notification, control_id)
control_outcome = control_outcome(control_id)
if control_outcome
attestation_result = attest_control(notification, control_id, control_outcome)
unless attestation_result.blank?
@enhanced_outcome_control_wise[control_id] = attestation_result[0]
@attestation_message_control_wise[control_id] = attestation_result[1]
end
end
end
def format_message(indicator, control_id, title, full_description) def format_message(indicator, control_id, title, full_description)
message_to_format = "" message_to_format = ""
message_to_format += "#{indicator} " message_to_format += "#{indicator} "
message_to_format += "#{control_id.to_s.strip.dup.force_encoding(Encoding::UTF_8)} " message_to_format += "#{control_id.to_s.strip.dup.force_encoding(Encoding::UTF_8)} "
message_to_format += "#{title.gsub(/\n*\s+/, " ").to_s.force_encoding(Encoding::UTF_8)} " if title message_to_format += "#{title.gsub(/\n*\s+/, " ").to_s.force_encoding(Encoding::UTF_8)} " if title
message_to_format += "#{full_description.gsub(/\n*\s+/, " ").to_s.force_encoding(Encoding::UTF_8)} " unless title message_to_format += "#{full_description.gsub(/\n*\s+/, " ").to_s.force_encoding(Encoding::UTF_8)} " unless title
# append attestation message if control is attested
message_to_format += "#{@attestation_message_control_wise[control_id].gsub(/\n*\s+/, " ").to_s.force_encoding(Encoding::UTF_8)} " if @attestation_message_control_wise[control_id]
message_to_format message_to_format
end end
@ -147,23 +131,5 @@ module Inspec::Plugin::V2::PluginType
@notifications[control_id].push([notification, status]) @notifications[control_id].push([notification, status])
end end
end end
def attest_control(notification, control_id, control_outcome)
status = control_outcome
attestation_data = read_attestation_file(notification, control_id)
Inspec::Attestations.attest_streaming_data(attestation_data, status, control_id) unless attestation_data.blank?
end
def read_attestation_file(notification, control_id)
# need to re-read the file from config since not using run data for streaming reporters.
profile_id = notification.example.metadata[:profile_id]
attestation_files = Inspec::Config.cached.final_options["attestation_file"] if Inspec::Config.cached.respond_to?(:final_options)
attestation_data_by_profile = Inspec::AttestationFileReader.fetch_attestation_by_profile(profile_id, attestation_files) unless attestation_files.nil?
return unless attestation_data_by_profile && attestation_data_by_profile[control_id] && attestation_data_by_profile[control_id].is_a?(Hash)
attestation_data_by_profile[control_id]
end
end end
end end

View file

@ -1,5 +1,4 @@
require_relative "../utils/run_data_filters" require_relative "../utils/run_data_filters"
require "inspec/attestations"
module Inspec::Reporters module Inspec::Reporters
class Base class Base
@ -13,8 +12,6 @@ module Inspec::Reporters
@run_data = config[:run_data] || {} @run_data = config[:run_data] || {}
apply_run_data_filters_to_hash apply_run_data_filters_to_hash
# only try for attestation when attestation file is passed
Inspec::Attestations.attest(@run_data) if Inspec::Config.cached[:attestation_file]
@output = "" @output = ""
end end

View file

@ -73,6 +73,7 @@ require "inspec/resources/mssql_sys_conf"
require "inspec/resources/mysql" require "inspec/resources/mysql"
require "inspec/resources/mysql_conf" require "inspec/resources/mysql_conf"
require "inspec/resources/mysql_session" require "inspec/resources/mysql_session"
require "inspec/resources/nftables"
require "inspec/resources/nginx" require "inspec/resources/nginx"
require "inspec/resources/nginx_conf" require "inspec/resources/nginx_conf"
require "inspec/resources/npm" require "inspec/resources/npm"

View file

@ -319,15 +319,9 @@ module Inspec::Resources
return nil return nil
end end
resolve_ipv4 = resolve_ipv4.inject(:merge) if resolve_ipv4.is_a?(Array)
# Append the ipv4 addresses # Append the ipv4 addresses
resolve_ipv4.each_value do |ip| resolve_ipv4 = [resolve_ipv4] unless resolve_ipv4.is_a?(Array)
matched = ip.to_s.chomp.match(Resolv::IPv4::Regex) resolve_ipv4.each { |entry| addresses << entry["IPAddress"] }
next if matched.nil? || addresses.include?(matched.to_s)
addresses << matched.to_s
end
# -Type AAAA is the DNS query for IPv6 server Address. # -Type AAAA is the DNS query for IPv6 server Address.
cmd = inspec.command("Resolve-DnsName Type AAAA #{hostname} | ConvertTo-Json") cmd = inspec.command("Resolve-DnsName Type AAAA #{hostname} | ConvertTo-Json")
@ -337,15 +331,9 @@ module Inspec::Resources
return nil return nil
end end
resolve_ipv6 = resolve_ipv6.inject(:merge) if resolve_ipv6.is_a?(Array)
# Append the ipv6 addresses # Append the ipv6 addresses
resolve_ipv6.each_value do |ip| resolve_ipv6 = [resolve_ipv6] unless resolve_ipv6.is_a?(Array)
matched = ip.to_s.chomp.match(Resolv::IPv6::Regex) resolve_ipv6.each { |entry| addresses << entry["IPAddress"] }
next if matched.nil? || addresses.include?(matched.to_s)
addresses << matched.to_s
end
addresses addresses
end end

View file

@ -304,11 +304,11 @@ module Inspec::Resources
# Insecure not supported simply https://stackoverflow.com/questions/11696944/powershell-v3-invoke-webrequest-https-error # Insecure not supported simply https://stackoverflow.com/questions/11696944/powershell-v3-invoke-webrequest-https-error
cmd << "-MaximumRedirection #{max_redirects}" unless max_redirects.nil? cmd << "-MaximumRedirection #{max_redirects}" unless max_redirects.nil?
request_headers["Authorization"] = """ '\"Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(\"#{username}:#{password}\")) +'\"' """ unless username.nil? || password.nil? request_headers["Authorization"] = """ '\"Basic ' + [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(\"#{username}:#{password}\")) +'\"' """ unless username.nil? || password.nil?
request_header_string = nil request_header_array = []
request_headers.each do |k, v| request_headers.each do |k, v|
request_header_string << " #{k} = #{v}" request_header_array << " '#{k}' = '#{v}'"
end end
cmd << "-Headers @{#{request_header_string.join(";")}}" unless request_header_string.nil? cmd << "-Headers @{#{request_header_array.join(";")}}" unless request_header_array.empty?
if params.nil? if params.nil?
cmd << "'#{url}'" cmd << "'#{url}'"
else else

View file

@ -0,0 +1,251 @@
require "inspec/resources/command"
require "json" unless defined?(JSON)
# @see https://wiki.nftables.org/
# @see https://www.netfilter.org/projects/nftables/manpage.html
# rubocop:disable Style/ClassVars
module Inspec::Resources
class NfTables < Inspec.resource(1)
name "nftables"
supports platform: "linux"
desc "Use the nftables InSpec audit resource to test rules and sets that are defined in nftables, which maintains tables of IP packet filtering rules. There may be more than one table. Each table contains one (or more) chains. A chain is a list of rules that match packets. When the rule matches, the rule defines what target to assign to the packet."
example <<~EXAMPLE
describe nftables(family:'inet', table:'filter', chain: 'INPUT') do
its('type') { should eq 'filter' }
its('hook') { should eq 'input' }
its('prio') { should eq 0 } # filter
its('policy') { should eq 'drop' }
it { should have_rule('tcp dport { 22, 80, 443 } accept') }
end
describe nftables(family: 'inet', table: 'filter', set: 'OPEN_PORTS') do
its('type') { should eq 'ipv4_addr . inet_proto . inet_service' }
its('flags') { should include 'interval' }
it { should have_element('1.1.1.1 . tcp . 25-27') }
end
EXAMPLE
@@bin = nil
@@nft_params = {}
@@nft_params["json"] = ""
@@nft_params["stateless"] = ""
@@nft_params["num"] = ""
def initialize(params = {})
@family = params[:family] || nil
@table = params[:table] || nil
@chain = params[:chain] || nil
@set = params[:set] || nil
@ignore_comments = params[:ignore_comments] || false
unless @@bin
@@bin = find_nftables_or_error
end
# Some old versions of `nft` do not support JSON output or stateless modifier
res = inspec.command("#{@@bin} --version").stdout
version = Gem::Version.new(/^nftables v(\S+) .*/.match(res)[1])
case
when version < Gem::Version.new("0.8.0")
@@nft_params["num"] = "-nn"
when version < Gem::Version.new("0.9.0")
@@nft_params["stateless"] = "-s"
@@nft_params["num"] = "-nn"
when version < Gem::Version.new("0.9.3")
@@nft_params["json"] = "-j"
@@nft_params["stateless"] = "-s"
@@nft_params["num"] = "-nn"
when version >= Gem::Version.new("0.9.3")
@@nft_params["json"] = "-j"
@@nft_params["stateless"] = "-s"
@@nft_params["num"] = "-y"
## --terse
end
# family and table attributes are mandatory
fail_resource "nftables family and table are mandatory." if @family.nil? || @family.empty? || @table.nil? || @table.empty?
# chain name or set name has to be specified and are mutually exclusive
fail_resource "You must specify either a chain or a set name." if (@chain.nil? || @chain.empty?) && (@set.nil? || @set.empty?)
fail_resource "You must specify either a chain or a set name, not both." if !(@chain.nil? || @chain.empty?) && !(@set.nil? || @set.empty?)
# we're done if we are on linux
return if inspec.os.linux?
# ensures, all calls are aborted for non-supported os
@nftables_cache = {}
skip_resource "The `nftables` resource is not supported on your OS yet."
end
# Let's have a generic method to retrieve attributes for chains and sets
def _get_attr(name)
# Some attributes are valid for chains only, for sets only or for both
valid = {
"chains" => %w{hook policy prio type},
"sets" => %w{flags size type},
}
target_obj = @set.nil? ? "chains" : "sets"
if valid[target_obj].include?(name)
attrs = @set.nil? ? retrieve_chain_attrs : retrieve_set_attrs
else
raise Inspec::Exceptions::ResourceSkipped, "`#{name}` attribute is not valid for #{target_obj}"
end
# flags attribute is an array, if not retrieved ensure we return an empty array
# otherwise return an empty string
default = name == "flags" ? [] : ""
val = attrs.key?(name) ? attrs[name] : default
# When set type is has multiple data types it's retrieved as an array, make humans life easier
# by returning a string representation
if name == "type" && target_obj == "sets" && val.is_a?(Array)
return val.join(" . ")
end
val
end
# Create a method for each attribute
%i{flags hook policy prio size type}.each do |attr_method|
define_method attr_method do
_get_attr(attr_method.to_s)
end
end
def has_rule?(rule = nil, _family = nil, _table = nil, _chain = nil)
# checks if the rule is part of the chain
# for now, we expect an exact match
retrieve_chain_rules.any? { |line| line.casecmp(rule) == 0 }
end
def has_element?(element = nil, _family = nil, _table = nil, _chain = nil)
# checks if the element is part of the set
# for now, we expect an exact match
retrieve_set_elements.any? { |line| line.casecmp(element) == 0 }
end
def retrieve_set_elements
idx = "set_#{@family}_#{@table}_#{@set}"
return @nftables_cache[idx] if defined?(@nftables_cache) && @nftables_cache.key?(idx)
@nftables_cache = {} unless defined?(@nftables_cache)
elem_cmd = "list set #{@family} #{@table} #{@set}"
nftables_cmd = format("%s %s %s", @@bin, @@nft_params["stateless"], elem_cmd).strip
cmd = inspec.command(nftables_cmd)
return [] if cmd.exit_status.to_i != 0
@nftables_cache[idx] = cmd.stdout.gsub("\t", "").split("\n").reject { |line| line =~ /^(table|set|type|size|flags|typeof|auto-merge)/ || line =~ /^}$/ }.map { |line| line.sub("elements = {", "").sub("}", "").split(",") }.flatten.map(&:strip)
end
def retrieve_chain_rules
idx = "rule_#{@family}_#{@table}_#{@chain}"
return @nftables_cache[idx] if defined?(@nftables_cache) && @nftables_cache.key?(idx)
@nftables_cache = {} unless defined?(@nftables_cache)
# construct nftables command to read all rules of the given chain
chain_cmd = "list chain #{@family} #{@table} #{@chain}"
nftables_cmd = format("%s %s %s %s", @@bin, @@nft_params["stateless"], @@nft_params["num"], chain_cmd).strip
cmd = inspec.command(nftables_cmd)
return [] if cmd.exit_status.to_i != 0
rules = cmd.stdout.gsub("\t", "").split("\n").reject { |line| line =~ /^(table|chain)/ || line =~ /^}$/ }
if @ignore_comments
# split rules, returns array or rules without any comment
@nftables_cache[idx] = remove_comments_from_rules(rules)
else
# split rules, returns array or rules
@nftables_cache[idx] = rules.map(&:strip)
end
end
def retrieve_chain_attrs
idx = "chain_attrs_#{@family}_#{@table}_#{@chain}"
return @nftables_cache[idx] if defined?(@nftables_cache) && @nftables_cache.key?(idx)
@nftables_cache = {} unless defined?(@nftables_cache)
chain_cmd = "list chain #{@family} #{@table} #{@chain}"
nftables_cmd = format("%s %s %s %s", @@bin, @@nft_params["stateless"], @@nft_params["json"], chain_cmd).strip
cmd = inspec.command(nftables_cmd)
return {} if cmd.exit_status.to_i != 0
if @@nft_params["json"].empty?
res = cmd.stdout.gsub("\t", "").split("\n").select { |line| line =~ /^type/ }[0]
parsed = /type (\S+) hook (\S+) priority (\S+); policy (\S+);/.match(res)
@nftables_cache[idx] = { "type" => parsed[1], "hook" => parsed[2], "prio" => parsed[3].to_i, "policy" => parsed[4] }
else
@nftables_cache[idx] = JSON.parse(cmd.stdout)["nftables"].select { |line| line.key?("chain") }[0]["chain"]
end
end
def retrieve_set_attrs
idx = "set_attrs_#{@family}_#{@table}_#{@chain}"
return @nftables_cache[idx] if defined?(@nftables_cache) && @nftables_cache.key?(idx)
@nftables_cache = {} unless defined?(@nftables_cache)
chain_cmd = "list set #{@family} #{@table} #{@set}"
nftables_cmd = format("%s %s %s %s", @@bin, @@nft_params["stateless"], @@nft_params["json"], chain_cmd).strip
cmd = inspec.command(nftables_cmd)
return {} if cmd.exit_status.to_i != 0
if @@nft_params["json"].empty?
type = ""
size = 0
flags = []
res = cmd.stdout.gsub("\t", "").split("\n").select { |line| line =~ /^(type|size|flags)/ }
res.each do |line|
parsed = /^type (.*)/.match(line)
if parsed
type = parsed[1]
end
parsed = /^flags (.*)/.match(line)
if parsed
flags = parsed[1].split(",")
end
parsed = /^size (.*)/.match(line)
if parsed
size = parsed[1].to_i
end
end
@nftables_cache[idx] = { "type" => type, "size" => size, "flags" => flags }
else
@nftables_cache[idx] = JSON.parse(cmd.stdout)["nftables"].select { |line| line.key?("set") }[0]["set"]
end
end
def resource_id
to_s || "nftables"
end
def to_s
format("nftables (%s %s %s %s)", @family && "family: #{@family}", @table && "table: #{@table}", @chain && "chain: #{@chain}", @set && "set: #{@set}").strip
end
private
def remove_comments_from_rules(rules)
rules.each do |rule|
next if rule.nil?
rule.gsub!(/ comment "([^"]*)"/, "")
rule.strip
end
rules
end
def find_nftables_or_error
%w{/usr/sbin/nft /sbin/nft nft}.each do |cmd|
return cmd if inspec.command(cmd).exist?
end
raise Inspec::Exceptions::ResourceFailed, "Could not find `nft`"
end
end
end

View file

@ -81,7 +81,8 @@ module Inspec::Resources
# Socket path and empty host in the connection string establishes socket connection # Socket path and empty host in the connection string establishes socket connection
# Socket connection only enabled for non-windows platforms # Socket connection only enabled for non-windows platforms
# Windows does not support unix domain sockets # Windows does not support unix domain sockets
"psql -d postgresql://#{@user}:#{@pass}@/#{dbs}?host=#{@socket_path} -A -t -w -c #{escaped_query(query)}" option_port = @port.nil? ? "" : "-p #{@port}" # add explicit port if specified
"psql -d postgresql://#{@user}:#{@pass}@/#{dbs}?host=#{@socket_path} #{option_port} -A -t -w -c #{escaped_query(query)}"
else else
# Host in connection string establishes tcp/ip connection # Host in connection string establishes tcp/ip connection
if inspec.os.windows? if inspec.os.windows?

View file

@ -9,7 +9,6 @@ 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/waiver_file_reader"
require "inspec/attestation_file_reader"
require "inspec/utils/convert" require "inspec/utils/convert"
module Inspec module Inspec
@ -17,7 +16,6 @@ module Inspec
include ::RSpec::Matchers include ::RSpec::Matchers
attr_reader :__waiver_data attr_reader :__waiver_data
attr_reader :__attestation_data
attr_accessor :resource_dsl, :na_impact_freeze attr_accessor :resource_dsl, :na_impact_freeze
attr_reader :__profile_id attr_reader :__profile_id
@ -53,7 +51,6 @@ module Inspec
# By applying waivers *after* the instance eval, we assure that # By applying waivers *after* the instance eval, we assure that
# waivers have higher precedence than only_if. # waivers have higher precedence than only_if.
__apply_waivers __apply_waivers
__add_attestation_data
rescue SystemStackError, StandardError => e rescue SystemStackError, StandardError => e
# We've encountered an exception while trying to eval the code inside the # We've encountered an exception while trying to eval the code inside the
@ -425,19 +422,6 @@ module Inspec
__waiver_data["skipped_due_to_waiver"] = true __waiver_data["skipped_due_to_waiver"] = true
end end
# fetches attestation data for the rule which is used in runner_rspec.rb to assign it inside metadata
def __add_attestation_data
# this adds attestation data to a rule, accesible on run data layer.
control_id = @__rule_id
attestation_files = Inspec::Config.cached.final_options["attestation_file"] if Inspec::Config.cached.respond_to?(:final_options)
attestation_data_by_profile = Inspec::AttestationFileReader.fetch_attestation_by_profile(__profile_id, attestation_files) unless attestation_files.nil?
return unless attestation_data_by_profile && attestation_data_by_profile[control_id] && attestation_data_by_profile[control_id].is_a?(Hash)
@__attestation_data = attestation_data_by_profile[control_id]
end
# #
# Takes a block and returns a block that will run the given block # Takes a block and returns a block that will run the given block
# with access to the resource_dsl of the current class. This is to # with access to the resource_dsl of the current class. This is to

View file

@ -29,9 +29,6 @@ module Inspec
def initialize(raw_run_data) def initialize(raw_run_data)
@raw_run_data = raw_run_data @raw_run_data = raw_run_data
# only try for attestation when attestation file is passed
Inspec::Attestations.attest(@raw_run_data) if Inspec::Config.cached[:attestation_file]
self.controls = @raw_run_data[:controls].map { |c| Inspec::RunData::Control.new(c) } self.controls = @raw_run_data[:controls].map { |c| Inspec::RunData::Control.new(c) }
self.profiles = @raw_run_data[:profiles].map { |p| Inspec::RunData::Profile.new(p) } self.profiles = @raw_run_data[:profiles].map { |p| Inspec::RunData::Profile.new(p) }
self.statistics = Inspec::RunData::Statistics.new(@raw_run_data[:statistics]) self.statistics = Inspec::RunData::Statistics.new(@raw_run_data[:statistics])

View file

@ -13,8 +13,7 @@ module Inspec
:source_location, # Complex local :source_location, # Complex local
:tags, # Hash with custom keys :tags, # Hash with custom keys
:title, # String :title, # String
:waiver_data, # Complex local :waiver_data # Complex local
:attestation_data # Complex local
) do ) do
include HashLikeStruct include HashLikeStruct
def initialize(raw_ctl_data) def initialize(raw_ctl_data)
@ -22,7 +21,6 @@ module Inspec
self.results = (raw_ctl_data[:results] || []).map { |r| Inspec::RunData::Result.new(r) } self.results = (raw_ctl_data[:results] || []).map { |r| Inspec::RunData::Result.new(r) }
self.source_location = Inspec::RunData::Control::SourceLocation.new(raw_ctl_data[:source_location] || {}) self.source_location = Inspec::RunData::Control::SourceLocation.new(raw_ctl_data[:source_location] || {})
self.waiver_data = Inspec::RunData::Control::WaiverData.new(raw_ctl_data[:waiver_data] || {}) self.waiver_data = Inspec::RunData::Control::WaiverData.new(raw_ctl_data[:waiver_data] || {})
self.attestation_data = Inspec::RunData::Control::AttestationData.new(raw_ctl_data[:attestation_data] || {})
[ [
:code, # String :code, # String
@ -86,26 +84,6 @@ module Inspec
}.each { |f| self[f] = raw_wv_data[f.to_s] } }.each { |f| self[f] = raw_wv_data[f.to_s] }
end end
end end
AttestationData = Struct.new(
:expiration_date,
:justification,
:evidence_url,
:status,
:message
) do
include HashLikeStruct
def initialize(raw_attestation_data)
# These have string keys in the raw data!
%i{
expiration_date
justification
evidence_url
status
message
}.each { |f| self[f] = raw_attestation_data[f.to_s] }
end
end
end end
end end
end end

View file

@ -11,6 +11,7 @@ require "inspec/dependencies/cache"
require "inspec/dist" require "inspec/dist"
require "inspec/reporters" require "inspec/reporters"
require "inspec/runner_rspec" require "inspec/runner_rspec"
require "chef-licensing"
# spec requirements # spec requirements
module Inspec module Inspec
@ -69,16 +70,6 @@ module Inspec
} }
end end
if @conf[:attestation_file]
Inspec.with_feature("inspec-attestations") {
@conf[:attestation_file].each do |file|
unless File.file?(file)
raise Inspec::Exceptions::AttestationFileDoesNotExist, "Attestation file #{file} does not exist."
end
end
}
end
# About reading inputs: # About reading inputs:
# @conf gets passed around a lot, eventually to # @conf gets passed around a lot, eventually to
# Inspec::InputRegistry.register_external_inputs. # Inspec::InputRegistry.register_external_inputs.
@ -171,17 +162,23 @@ module Inspec
end end
def run(with = nil) def run(with = nil)
ChefLicensing.check_software_entitlement! if Inspec::Dist::EXEC_NAME == "inspec"
Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}" Inspec::Log.debug "Starting run with targets: #{@target_profiles.map(&:to_s)}"
load load
run_tests(with) run_tests(with)
rescue ChefLicensing::SoftwareNotEntitled
Inspec::Log.error "License is not entitled to use InSpec."
Inspec::UI.new.exit(:license_not_entitled)
rescue ChefLicensing::Error => e
Inspec::Log.error e.message
Inspec::UI.new.exit(:usage_error)
end end
def render_output(run_data) def render_output(run_data)
return if @conf["reporter"].nil? return if @conf["reporter"].nil?
@conf["reporter"].each do |reporter| @conf["reporter"].each do |reporter|
# if attestation file is used then we need enhanced outcomes enhanced_outcome_flag = @conf["enhanced_outcomes"]
enhanced_outcome_flag = @conf["attestation_file"] ? true : @conf["enhanced_outcomes"]
result = Inspec::Reporters.render(reporter, run_data, enhanced_outcome_flag) result = Inspec::Reporters.render(reporter, run_data, enhanced_outcome_flag)
raise Inspec::ReporterError, "Error generating reporter '#{reporter[0]}'" if result == false raise Inspec::ReporterError, "Error generating reporter '#{reporter[0]}'" if result == false
end end

View file

@ -199,8 +199,7 @@ module Inspec
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)
# if attestation file is used then we need enhanced outcomes @formatter.enhanced_outcomes = @conf.final_options["enhanced_outcomes"]
@formatter.enhanced_outcomes = @conf.final_options["attestation_file"] ? true : @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"]
@ -232,7 +231,6 @@ module Inspec
metadata[:code] = rule.instance_variable_get(:@__code) metadata[:code] = rule.instance_variable_get(:@__code)
metadata[:source_location] = rule.instance_variable_get(:@__source_location) metadata[:source_location] = rule.instance_variable_get(:@__source_location)
metadata[:waiver_data] = rule.__waiver_data metadata[:waiver_data] = rule.__waiver_data
metadata[:attestation_data] = rule.__attestation_data # data fetched from rule object
end end
end end
end end

View file

@ -1,3 +1,6 @@
require "chef-licensing"
require "inspec/dist"
autoload :Pry, "pry" autoload :Pry, "pry"
module Inspec module Inspec
@ -10,6 +13,7 @@ module Inspec
end end
def start def start
ChefLicensing.check_software_entitlement! if Inspec::Dist::EXEC_NAME == "inspec"
# This will hold a single evaluation binding context as opened within # This will hold a single evaluation binding context as opened within
# the instance_eval context of the anonymous class that the profile # the instance_eval context of the anonymous class that the profile
# context creates to evaluate each individual test file. We want to # context creates to evaluate each individual test file. We want to
@ -18,6 +22,12 @@ module Inspec
@ctx_binding = @runner.eval_with_virtual_profile("binding") @ctx_binding = @runner.eval_with_virtual_profile("binding")
configure_pry configure_pry
@ctx_binding.pry @ctx_binding.pry
rescue ChefLicensing::SoftwareNotEntitled
Inspec::Log.error "License is not entitled to use InSpec."
Inspec::UI.new.exit(:license_not_entitled)
rescue ChefLicensing::Error => e
Inspec::Log.error e.message
Inspec::UI.new.exit(:usage_error)
end end
def configure_pry # rubocop:disable Metrics/AbcSize def configure_pry # rubocop:disable Metrics/AbcSize

View file

@ -33,6 +33,8 @@ module Inspec
EXIT_GEM_DEPENDENCY_LOAD_ERROR = 4 EXIT_GEM_DEPENDENCY_LOAD_ERROR = 4
EXIT_BAD_SIGNATURE = 5 EXIT_BAD_SIGNATURE = 5
EXIT_LICENSE_NOT_ACCEPTED = 172 EXIT_LICENSE_NOT_ACCEPTED = 172
EXIT_LICENSE_NOT_ENTITLED = 173
EXIT_LICENSE_NOT_SET = 174
EXIT_FAILED_TESTS = 100 EXIT_FAILED_TESTS = 100
EXIT_SKIPPED_TESTS = 101 EXIT_SKIPPED_TESTS = 101
EXIT_TERMINATED_BY_CTL_C = 130 EXIT_TERMINATED_BY_CTL_C = 130

View file

@ -0,0 +1,9 @@
require_relative "../log"
require "chef-licensing"
ChefLicensing.configure do |config|
config.chef_product_name = "InSpec"
config.chef_entitlement_id = "3ff52c37-e41f-4f6c-ad4d-365192205968"
config.chef_executable_name = "inspec"
config.license_server_url = "https://licensing.chef.co/License"
config.logger = Inspec::Log
end

View file

@ -57,11 +57,18 @@ class SimpleConfig
m = opts[:assignment_regex].match(line) m = opts[:assignment_regex].match(line)
return nil if m.nil? return nil if m.nil?
values = parse_values(m, opts[:key_values])
if opts[:multiple_values] if opts[:multiple_values]
@vals[m[1]] ||= [] @vals[m[1]] ||= []
@vals[m[1]].push(parse_values(m, opts[:key_values])) if opts[:multiple_value_regex] # can be used only if multiple values is set as true
value_to_array = values.split(opts[:multiple_value_regex])
@vals[m[1]].concat(value_to_array)
else
@vals[m[1]].push(values)
end
else else
@vals[m[1]] = parse_values(m, opts[:key_values]) @vals[m[1]] = values
end end
end end
@ -116,6 +123,7 @@ class SimpleConfig
key_values: 1, # default for key=value, may require for 'key val1 val2 val3' key_values: 1, # default for key=value, may require for 'key val1 val2 val3'
standalone_comments: false, standalone_comments: false,
multiple_values: false, multiple_values: false,
multiple_value_regex: nil,
} }
end end
end end

View file

@ -1,3 +1,3 @@
module Inspec module Inspec
VERSION = "6.2.46".freeze VERSION = "6.4.33".freeze
end end

View file

@ -148,7 +148,7 @@ RSpec::Matchers.define :be_resolvable do
end end
end end
# matcher for iptables and ip6tables # matcher for iptables, ip6tables and nftables
RSpec::Matchers.define :have_rule do |rule| RSpec::Matchers.define :have_rule do |rule|
match do |tables| match do |tables|
tables.has_rule?(rule) tables.has_rule?(rule)
@ -163,6 +163,13 @@ RSpec::Matchers.define :have_rule do |rule|
end end
end end
# matcher for nftables sets
RSpec::Matchers.define :have_element do |elem|
match do |sets|
sets.has_element?(elem)
end
end
# `be_in` matcher # `be_in` matcher
# You can use it in the following cases: # You can use it in the following cases:
# - check if an item or array is included in a given array # - check if an item or array is included in a given array

View file

@ -0,0 +1,16 @@
# License Plugin
## license list
Implements the `inspec license list` CLI command.
## license add
Implements the `inspec license add` CLI command.
### What This Plugin Does
This plugin consists of the following subcommands:
1. `add`: helps to add a new license
2. `list`: helps to list all the licenses for the current user

View file

@ -0,0 +1,6 @@
Gem::Specification.new do |spec|
spec.name = "inspec-license"
spec.summary = "Plugin to list user licenses."
spec.description = ""
spec.license = "Apache-2.0"
end

View file

@ -0,0 +1,14 @@
module InspecPlugins
module License
class Plugin < ::Inspec.plugin(2)
plugin_name :"inspec-license"
if Inspec::Dist::EXEC_NAME == "inspec"
cli_command :license do
require_relative "inspec-license/cli"
InspecPlugins::License::CLI
end
end
end
end
end

View file

@ -0,0 +1,23 @@
require "chef-licensing"
module InspecPlugins::License
class CLI < Inspec.plugin(2, :cli_command)
include Inspec::Dist
subcommand_desc "license SUBCOMMAND [options]", "Manage #{PRODUCT_NAME} license"
desc "list", "List licenses (not applicable to local licensing service)"
def list
ChefLicensing.list_license_keys_info
end
desc "add", "Add a new license (not applicable to local licensing service)"
def add
ChefLicensing.add_license
rescue ChefLicensing::LicenseKeyFetcher::LicenseKeyAddNotAllowed => e
Inspec::Log.error e.message
Inspec::UI.new.exit(Inspec::UI::EXIT_LICENSE_NOT_SET)
rescue ChefLicensing::Error => e
Inspec::Log.error e.message
Inspec::UI.new.exit(Inspec::UI::EXIT_LICENSE_NOT_SET)
end
end
end

View file

@ -1,7 +1,7 @@
module InspecPlugins::Parallelism module InspecPlugins::Parallelism
class StreamingReporter < Inspec.plugin(2, :streaming_reporter) class StreamingReporter < Inspec.plugin(2, :streaming_reporter)
# Registering these methods with RSpec::Core::Formatters class is mandatory # Registering these methods with RSpec::Core::Formatters class is mandatory
RSpec::Core::Formatters.register self, :example_passed, :example_failed, :example_pending RSpec::Core::Formatters.register self, :example_passed, :example_failed, :example_pending, :close
def initialize(output) def initialize(output)
@status_mapping = {} @status_mapping = {}
@ -21,6 +21,11 @@ module InspecPlugins::Parallelism
set_example(notification, "skipped") set_example(notification, "skipped")
end end
def close(notification)
# HACK: if we've reached the end of the execution, send a special marker, to ease EOF detection on Windows
puts "EOF_MARKER"
end
private private
def set_example(notification, status) def set_example(notification, status)
@ -46,10 +51,6 @@ module InspecPlugins::Parallelism
display_name = control_id.to_s.lstrip.force_encoding(Encoding::UTF_8) unless title display_name = control_id.to_s.lstrip.force_encoding(Encoding::UTF_8) unless title
puts "#{@control_counter}/#{stat}/#{controls_count}/#{display_name}" puts "#{@control_counter}/#{stat}/#{controls_count}/#{display_name}"
# HACK: if we've reached the end of the execution, send a special marker, to ease EOF detection on Windows
if @control_counter == controls_count
puts "EOF_MARKER"
end
end end
def set_status_mapping(control_id, status) def set_status_mapping(control_id, status)

View file

@ -23,8 +23,9 @@ module InspecPlugins
def run def run
validate_thor_options validate_thor_options
validate_invocations! validate_invocations!
catch_ctl_c_and_exit unless run_in_background runner = Runner.new(invocations, cli_options_to_parallel_cmd, sub_cmd)
Runner.new(invocations, cli_options_to_parallel_cmd, sub_cmd).run catch_ctl_c_and_exit(runner) unless run_in_background
runner.run
end end
def dry_run def dry_run
@ -34,11 +35,17 @@ module InspecPlugins
private private
def catch_ctl_c_and_exit def catch_ctl_c_and_exit(runner)
puts "Press CTL+C to stop\n" puts "Press CTL+C to stop\n"
trap("SIGINT") do trap("SIGINT") do
puts "\n" puts "\n"
puts "Shutting down jobs..." puts "Shutting down jobs..."
if Inspec.locally_windows?
runner.kill_child_processes
sleep 1
puts "Renaming error log files..."
runner.rename_error_log_files
end
exit Inspec::UI::EXIT_TERMINATED_BY_CTL_C exit Inspec::UI::EXIT_TERMINATED_BY_CTL_C
end end
end end

View file

@ -12,6 +12,7 @@ module InspecPlugins
@sub_cmd = sub_cmd @sub_cmd = sub_cmd
@total_jobs = cli_options["jobs"] || Concurrent.physical_processor_count @total_jobs = cli_options["jobs"] || Concurrent.physical_processor_count
@child_tracker = {} @child_tracker = {}
@child_tracker_persisted = {}
@run_in_background = cli_options["bg"] @run_in_background = cli_options["bg"]
unless run_in_background unless run_in_background
@ui = InspecPlugins::Parallelism::SuperReporter.make(cli_options["ui"], total_jobs, invocations) @ui = InspecPlugins::Parallelism::SuperReporter.make(cli_options["ui"], total_jobs, invocations)
@ -34,6 +35,10 @@ module InspecPlugins
cleanup_child_processes cleanup_child_processes
sleep 0.1 sleep 0.1
end end
# Requires renaming operations on windows only
# Do Rename and delete operations after all child processes have exited successfully
rename_error_log_files if Inspec.locally_windows?
cleanup_empty_error_log_files cleanup_empty_error_log_files
cleanup_daemon_process if run_in_background cleanup_daemon_process if run_in_background
end end
@ -63,6 +68,36 @@ module InspecPlugins
end end
end end
def kill_child_processes
@child_tracker.each do |pid, info|
Process.kill("SIGKILL", pid)
rescue Exception => e
$stderr.puts "Error while shutting down process #{pid}: #{e.message}"
end
# Waiting for child processes to die after they have been killed
wait_for_child_processes_to_die
end
def wait_for_child_processes_to_die
until @child_tracker.empty?
begin
exited_pid = Process.waitpid(-1, Process::WNOHANG)
@child_tracker.delete exited_pid if exited_pid && exited_pid > 0
sleep 1
rescue Errno::ECHILD
Inspec::Log.info "Processes shutdown complete!"
rescue Exception => e
Inspec::Log.debug "Error while waiting for child processes to shutdown: #{e.message}"
end
end
end
def rename_error_log_files
@child_tracker_persisted.each do |pid, info|
rename_error_log(info[:error_log_file], pid)
end
end
def should_start_more_jobs? def should_start_more_jobs?
@child_tracker.length < total_jobs && !invocations.empty? @child_tracker.length < total_jobs && !invocations.empty?
end end
@ -71,22 +106,25 @@ module InspecPlugins
invocation = invocations.shift[:value] invocation = invocations.shift[:value]
child_reader, parent_writer = IO.pipe child_reader, parent_writer = IO.pipe
# Construct command-line invocation
child_pid = nil
error_log_file_name = "#{Time.now.nsec}.err"
begin begin
logs_dir_path = log_path || Dir.pwd
log_dir = File.join(logs_dir_path, "logs")
FileUtils.mkdir_p(log_dir)
error_log_file = File.open("#{log_dir}/#{Time.now.nsec}.err", "a+")
cmd = "#{$0} #{sub_cmd} #{invocation}" cmd = "#{$0} #{sub_cmd} #{invocation}"
log_msg = "#{Time.now.iso8601} Start Time: #{Time.now}\n#{Time.now.iso8601} Arguments: #{invocation}\n" log_msg = "#{Time.now.iso8601} Start Time: #{Time.now}\n#{Time.now.iso8601} Arguments: #{invocation}\n"
child_pid = Process.spawn(cmd, out: parent_writer, err: error_log_file_name) child_pid = Process.spawn(cmd, out: parent_writer, err: error_log_file.path)
# Rename error log file if exist
rename_error_log(error_log_file_name, child_pid) if File.exist?(error_log_file_name)
# Logging # Logging
create_logs(child_pid, nil, $stderr)
create_logs(child_pid, log_msg) create_logs(child_pid, log_msg)
@child_tracker[child_pid] = { io: child_reader } @child_tracker[child_pid] = { io: child_reader }
# This is used to rename error log files after all child processes are exited
@child_tracker_persisted[child_pid] = { error_log_file: error_log_file }
@ui.child_spawned(child_pid, invocation) @ui.child_spawned(child_pid, invocation)
# Close the file to unlock the error log files opened by processes
error_log_file.close
rescue StandardError => e rescue StandardError => e
$stderr.puts "#{Time.now.iso8601} Error Message: #{e.message}" $stderr.puts "#{Time.now.iso8601} Error Message: #{e.message}"
$stderr.puts "#{Time.now.iso8601} Error Backtrace: #{e.backtrace}" $stderr.puts "#{Time.now.iso8601} Error Backtrace: #{e.backtrace}"
@ -198,7 +236,7 @@ module InspecPlugins
def create_logs(child_pid, run_log , stderr = nil) def create_logs(child_pid, run_log , stderr = nil)
logs_dir_path = log_path || Dir.pwd logs_dir_path = log_path || Dir.pwd
log_dir = File.join(logs_dir_path, "logs") log_dir = File.join(logs_dir_path, "logs")
FileUtils.mkdir_p(log_dir) unless File.directory?(log_dir) FileUtils.mkdir_p(log_dir)
if stderr if stderr
log_file = File.join(log_dir, "#{child_pid}.err") unless File.exist?("#{child_pid}.err") log_file = File.join(log_dir, "#{child_pid}.err") unless File.exist?("#{child_pid}.err")
@ -209,11 +247,18 @@ module InspecPlugins
end end
end end
def rename_error_log(error_log_file_name, child_pid) def rename_error_log(error_log_file, child_pid)
logs_dir_path = log_path || Dir.pwd logs_dir_path = log_path || Dir.pwd
log_dir = File.join(logs_dir_path, "logs") log_dir = File.join(logs_dir_path, "logs")
FileUtils.mkdir_p(log_dir) unless File.directory?(log_dir) FileUtils.mkdir_p(log_dir)
File.rename(error_log_file_name, "#{log_dir}/#{child_pid}.err")
if error_log_file.closed? && File.exist?(error_log_file.path)
begin
File.rename("#{error_log_file.path}", "#{log_dir}/#{child_pid}.err")
rescue
$stderr.puts "Cannot rename error log file #{error_log_file.path} for child pid #{child_pid}"
end
end
end end
end end
end end

View file

@ -89,7 +89,8 @@ module InspecPlugins::Parallelism
# Loop over slots # Loop over slots
slots.each_index do |idx| slots.each_index do |idx|
if slots[idx].nil? if slots[idx].nil?
line += "idle".center(slot_width) # line += "idle".center(slot_width)
# Need to improve UI
elsif slots[idx] == "exited" elsif slots[idx] == "exited"
line += "Done".center(slot_width) line += "Done".center(slot_width)
else else

View file

@ -1,18 +1,17 @@
GIT GIT
remote: https://github.com/chef/omnibus-software.git remote: https://github.com/chef/omnibus-software.git
revision: 4b08f0bc0688f750bc55a49b8103b2d12815399e
revision: 225e3576c48fcd0155f6049cb032b2370eccf29a
branch: main branch: main
specs: specs:
omnibus-software (23.2.286) omnibus-software (23.7.293)
omnibus (>= 9.0.0) omnibus (>= 9.0.0)
GIT GIT
remote: https://github.com/chef/omnibus.git remote: https://github.com/chef/omnibus.git
revision: cf9761311577e24819625aa1d932f828740e33b4 revision: 9c0643a3a44f3e7119789c2093bfc8edd78c74ff
branch: main branch: main
specs: specs:
omnibus (9.0.17) omnibus (9.0.21)
aws-sdk-s3 (~> 1.116.0) aws-sdk-s3 (~> 1.116.0)
chef-cleanroom (~> 1.0) chef-cleanroom (~> 1.0)
chef-utils (>= 15.4) chef-utils (>= 15.4)
@ -30,33 +29,33 @@ GIT
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
addressable (2.8.1) addressable (2.8.4)
public_suffix (>= 2.0.2, < 6.0) public_suffix (>= 2.0.2, < 6.0)
artifactory (3.0.15) artifactory (3.0.15)
awesome_print (1.9.2) awesome_print (1.9.2)
aws-eventstream (1.2.0) aws-eventstream (1.2.0)
aws-partitions (1.716.0) aws-partitions (1.783.0)
aws-sdk-core (3.170.0) aws-sdk-core (3.176.1)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0) aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5) aws-sigv4 (~> 1.5)
jmespath (~> 1, >= 1.6.1) jmespath (~> 1, >= 1.6.1)
aws-sdk-kms (1.62.0) aws-sdk-kms (1.68.0)
aws-sdk-core (~> 3, >= 3.165.0) aws-sdk-core (~> 3, >= 3.176.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.116.0) aws-sdk-s3 (1.116.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1) aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4) aws-sigv4 (~> 1.4)
aws-sdk-secretsmanager (1.64.0) aws-sdk-secretsmanager (1.77.0)
aws-sdk-core (~> 3, >= 3.127.0) aws-sdk-core (~> 3, >= 3.174.0)
aws-sigv4 (~> 1.1) aws-sigv4 (~> 1.1)
aws-sigv4 (1.5.2) aws-sigv4 (1.6.0)
aws-eventstream (~> 1, >= 1.0.2) aws-eventstream (~> 1, >= 1.0.2)
bcrypt_pbkdf (1.1.0) bcrypt_pbkdf (1.1.0)
bcrypt_pbkdf (1.1.0-x64-mingw32) bcrypt_pbkdf (1.1.0-x64-mingw32)
bcrypt_pbkdf (1.1.0-x86-mingw32) bcrypt_pbkdf (1.1.0-x86-mingw32)
berkshelf (8.0.2) berkshelf (8.0.7)
chef (>= 15.7.32) chef (>= 15.7.32)
chef-config chef-config
cleanroom (~> 1.0) cleanroom (~> 1.0)
@ -70,54 +69,57 @@ GEM
solve (~> 4.0) solve (~> 4.0)
thor (>= 0.20) thor (>= 0.20)
builder (3.2.4) builder (3.2.4)
chef (17.10.0) chef (18.2.7)
addressable addressable
aws-sdk-s3 (~> 1.91) aws-sdk-s3 (~> 1.91)
aws-sdk-secretsmanager (~> 1.46) aws-sdk-secretsmanager (~> 1.46)
chef-config (= 17.10.0) chef-config (= 18.2.7)
chef-utils (= 17.10.0) chef-utils (= 18.2.7)
chef-vault chef-vault
chef-zero (>= 14.0.11) chef-zero (>= 14.0.11)
corefoundation (~> 0.3.4) corefoundation (~> 0.3.4)
diff-lcs (>= 1.2.4, < 1.6.0, != 1.4.0) diff-lcs (>= 1.2.4, < 1.6.0, != 1.4.0)
erubis (~> 2.7) erubis (~> 2.7)
ffi (>= 1.5.0) ffi (>= 1.15.5)
ffi-libarchive (~> 1.0, >= 1.0.3) ffi-libarchive (~> 1.0, >= 1.0.3)
ffi-yajl (~> 2.2) ffi-yajl (~> 2.2)
iniparse (~> 1.4) iniparse (~> 1.4)
inspec-core (~> 4.23) inspec-core (>= 5)
license-acceptance (>= 1.0.5, < 3) license-acceptance (>= 1.0.5, < 3)
mixlib-archive (>= 0.4, < 2.0) mixlib-archive (>= 0.4, < 2.0)
mixlib-authentication (>= 2.1, < 4) mixlib-authentication (>= 2.1, < 4)
mixlib-cli (>= 2.1.1, < 3.0) mixlib-cli (>= 2.1.1, < 3.0)
mixlib-log (>= 2.0.3, < 4.0) mixlib-log (>= 2.0.3, < 4.0)
mixlib-shellout (>= 3.1.1, < 4.0) mixlib-shellout (>= 3.1.1, < 4.0)
net-sftp (>= 2.1.2, < 4.0) net-ftp
ohai (~> 17.0) net-sftp (>= 2.1.2, < 5.0)
ohai (~> 18.0)
plist (~> 3.2) plist (~> 3.2)
proxifier (~> 1.0) proxifier2 (~> 1.1)
syslog-logger (~> 1.6) syslog-logger (~> 1.6)
train-core (~> 3.2, >= 3.2.28) train-core (~> 3.10)
train-rest (>= 0.4.1)
train-winrm (>= 0.2.5) train-winrm (>= 0.2.5)
unf_ext (>= 0.0.8.2)
uuidtools (>= 2.1.5, < 3.0) uuidtools (>= 2.1.5, < 3.0)
vault (~> 0.16) vault (~> 0.16)
chef (17.10.0-universal-mingw32) chef (18.2.7-x64-mingw-ucrt)
addressable addressable
aws-sdk-s3 (~> 1.91) aws-sdk-s3 (~> 1.91)
aws-sdk-secretsmanager (~> 1.46) aws-sdk-secretsmanager (~> 1.46)
chef-config (= 17.10.0) chef-config (= 18.2.7)
chef-powershell (~> 1.0.12) chef-powershell (~> 18.0.0)
chef-utils (= 17.10.0) chef-utils (= 18.2.7)
chef-vault chef-vault
chef-zero (>= 14.0.11) chef-zero (>= 14.0.11)
corefoundation (~> 0.3.4) corefoundation (~> 0.3.4)
diff-lcs (>= 1.2.4, < 1.6.0, != 1.4.0) diff-lcs (>= 1.2.4, < 1.6.0, != 1.4.0)
erubis (~> 2.7) erubis (~> 2.7)
ffi (>= 1.5.0) ffi (>= 1.15.5)
ffi-libarchive (~> 1.0, >= 1.0.3) ffi-libarchive (~> 1.0, >= 1.0.3)
ffi-yajl (~> 2.2) ffi-yajl (~> 2.2)
iniparse (~> 1.4) iniparse (~> 1.4)
inspec-core (~> 4.23) inspec-core (>= 5)
iso8601 (>= 0.12.1, < 0.14) iso8601 (>= 0.12.1, < 0.14)
license-acceptance (>= 1.0.5, < 3) license-acceptance (>= 1.0.5, < 3)
mixlib-archive (>= 0.4, < 2.0) mixlib-archive (>= 0.4, < 2.0)
@ -125,17 +127,20 @@ GEM
mixlib-cli (>= 2.1.1, < 3.0) mixlib-cli (>= 2.1.1, < 3.0)
mixlib-log (>= 2.0.3, < 4.0) mixlib-log (>= 2.0.3, < 4.0)
mixlib-shellout (>= 3.1.1, < 4.0) mixlib-shellout (>= 3.1.1, < 4.0)
net-sftp (>= 2.1.2, < 4.0) net-ftp
ohai (~> 17.0) net-sftp (>= 2.1.2, < 5.0)
ohai (~> 18.0)
plist (~> 3.2) plist (~> 3.2)
proxifier (~> 1.0) proxifier2 (~> 1.1)
syslog-logger (~> 1.6) syslog-logger (~> 1.6)
train-core (~> 3.2, >= 3.2.28) train-core (~> 3.10)
train-rest (>= 0.4.1)
train-winrm (>= 0.2.5) train-winrm (>= 0.2.5)
unf_ext (>= 0.0.8.2)
uuidtools (>= 2.1.5, < 3.0) uuidtools (>= 2.1.5, < 3.0)
vault (~> 0.16) vault (~> 0.16)
win32-api (~> 1.5.3) win32-api (~> 1.10.0)
win32-certstore (~> 0.6.2) win32-certstore (~> 0.6.15)
win32-event (~> 0.6.1) win32-event (~> 0.6.1)
win32-eventlog (= 0.6.3) win32-eventlog (= 0.6.3)
win32-mmap (~> 0.4.1) win32-mmap (~> 0.4.1)
@ -145,22 +150,22 @@ GEM
win32-taskscheduler (~> 2.0) win32-taskscheduler (~> 2.0)
wmi-lite (~> 1.0) wmi-lite (~> 1.0)
chef-cleanroom (1.0.5) chef-cleanroom (1.0.5)
chef-config (17.10.0) chef-config (18.2.7)
addressable addressable
chef-utils (= 17.10.0) chef-utils (= 18.2.7)
fuzzyurl fuzzyurl
mixlib-config (>= 2.2.12, < 4.0) mixlib-config (>= 2.2.12, < 4.0)
mixlib-shellout (>= 2.0, < 4.0) mixlib-shellout (>= 2.0, < 4.0)
tomlrb (~> 1.2) tomlrb (~> 1.2)
chef-powershell (1.0.13) chef-powershell (18.0.2)
ffi (~> 1.15) ffi (~> 1.15)
ffi-yajl (~> 2.4) ffi-yajl (~> 2.4)
chef-telemetry (1.1.1) chef-telemetry (1.1.1)
chef-config chef-config
concurrent-ruby (~> 1.0) concurrent-ruby (~> 1.0)
chef-utils (17.10.0) chef-utils (18.2.7)
concurrent-ruby concurrent-ruby
chef-vault (4.1.10) chef-vault (4.1.11)
chef-zero (15.0.11) chef-zero (15.0.11)
ffi-yajl (~> 2.2) ffi-yajl (~> 2.2)
hashie (>= 2.0, < 5.0) hashie (>= 2.0, < 5.0)
@ -175,25 +180,19 @@ GEM
contracts (0.16.1) contracts (0.16.1)
corefoundation (0.3.13) corefoundation (0.3.13)
ffi (>= 1.15.0) ffi (>= 1.15.0)
date (3.3.3)
diff-lcs (1.5.0) diff-lcs (1.5.0)
domain_name (0.5.20190701)
unf (>= 0.0.5, < 1.0.0)
ed25519 (1.3.0) ed25519 (1.3.0)
erubi (1.12.0) erubi (1.12.0)
erubis (2.7.0) erubis (2.7.0)
faraday (1.4.3) faraday (2.7.7)
faraday-em_http (~> 1.0) faraday-net_http (>= 2.0, < 3.1)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.1)
multipart-post (>= 1.2, < 3)
ruby2_keywords (>= 0.0.4) ruby2_keywords (>= 0.0.4)
faraday-em_http (1.0.0) faraday-follow_redirects (0.3.0)
faraday-em_synchrony (1.0.0) faraday (>= 1, < 3)
faraday-excon (1.1.0) faraday-net_http (3.0.2)
faraday-net_http (1.0.1)
faraday-net_http_persistent (1.2.0)
faraday_middleware (1.2.0)
faraday (~> 1.0)
ffi (1.15.5) ffi (1.15.5)
ffi (1.15.5-x64-mingw-ucrt) ffi (1.15.5-x64-mingw-ucrt)
ffi (1.15.5-x64-mingw32) ffi (1.15.5-x64-mingw32)
@ -211,13 +210,16 @@ GEM
builder (>= 2.1.2) builder (>= 2.1.2)
rexml (~> 3.0) rexml (~> 3.0)
hashie (4.1.0) hashie (4.1.0)
http-accept (1.7.0)
http-cookie (1.0.5)
domain_name (~> 0.5)
httpclient (2.8.3) httpclient (2.8.3)
iniparse (1.5.0) iniparse (1.5.0)
inspec-core (4.56.20) inspec-core (5.22.3)
addressable (~> 2.4) addressable (~> 2.4)
chef-telemetry (~> 1.0, >= 1.0.8) chef-telemetry (~> 1.0, >= 1.0.8)
faraday (>= 0.9.0, < 1.5) faraday (>= 1, < 3)
faraday_middleware (~> 1.0) faraday-follow_redirects (~> 0.3)
hashie (>= 3.4, < 5.0) hashie (>= 3.4, < 5.0)
license-acceptance (>= 0.2.13, < 3.0) license-acceptance (>= 0.2.13, < 3.0)
method_source (>= 0.8, < 2.0) method_source (>= 0.8, < 2.0)
@ -233,7 +235,7 @@ GEM
sslshake (~> 1.2) sslshake (~> 1.2)
thor (>= 0.20, < 2.0) thor (>= 0.20, < 2.0)
tomlrb (>= 1.2, < 2.1) tomlrb (>= 1.2, < 2.1)
train-core (~> 3.0) train-core (~> 3.10)
tty-prompt (~> 0.17) tty-prompt (~> 0.17)
tty-table (~> 0.10) tty-table (~> 0.10)
iostruct (0.0.5) iostruct (0.0.5)
@ -249,7 +251,7 @@ GEM
tomlrb (>= 1.2, < 3.0) tomlrb (>= 1.2, < 3.0)
tty-box (~> 0.6) tty-box (~> 0.6)
tty-prompt (~> 0.20) tty-prompt (~> 0.20)
license_scout (1.3.4) license_scout (1.3.6)
ffi-yajl (~> 2.2) ffi-yajl (~> 2.2)
mixlib-shellout (>= 2.2, < 4.0) mixlib-shellout (>= 2.2, < 4.0)
toml-rb (>= 1, < 3) toml-rb (>= 1, < 3)
@ -258,6 +260,9 @@ GEM
little-plugger (~> 1.1) little-plugger (~> 1.1)
multi_json (~> 1.14) multi_json (~> 1.14)
method_source (1.0.0) method_source (1.0.0)
mime-types (3.4.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.0218.1)
minitar (0.9) minitar (0.9)
mixlib-archive (1.1.7) mixlib-archive (1.1.7)
mixlib-log mixlib-log
@ -288,20 +293,26 @@ GEM
molinillo (0.8.0) molinillo (0.8.0)
multi_json (1.15.0) multi_json (1.15.0)
multipart-post (2.3.0) multipart-post (2.3.0)
net-ftp (0.2.0)
net-protocol
time
net-protocol (0.2.1)
timeout
net-scp (4.0.0) net-scp (4.0.0)
net-ssh (>= 2.6.5, < 8.0.0) net-ssh (>= 2.6.5, < 8.0.0)
net-sftp (3.0.0) net-sftp (4.0.0)
net-ssh (>= 5.0.0, < 7.0.0) net-ssh (>= 5.0.0, < 8.0.0)
net-ssh (6.1.0) net-ssh (7.1.0)
net-ssh-gateway (2.0.0) net-ssh-gateway (2.0.0)
net-ssh (>= 4.0.0) net-ssh (>= 4.0.0)
netrc (0.11.0)
nori (2.6.0) nori (2.6.0)
octokit (4.25.1) octokit (4.25.1)
faraday (>= 1, < 3) faraday (>= 1, < 3)
sawyer (~> 0.9) sawyer (~> 0.9)
ohai (17.9.0) ohai (18.1.3)
chef-config (>= 14.12, < 18) chef-config (>= 14.12, < 19)
chef-utils (>= 16.0, < 18) chef-utils (>= 16.0, < 19)
ffi (~> 1.9) ffi (~> 1.9)
ffi-yajl (~> 2.2) ffi-yajl (~> 2.2)
ipaddress ipaddress
@ -312,24 +323,41 @@ GEM
plist (~> 3.1) plist (~> 3.1)
train-core train-core
wmi-lite (~> 1.0) wmi-lite (~> 1.0)
parallel (1.22.1) parallel (1.23.0)
parslet (1.8.2) parslet (1.8.2)
pastel (0.8.0) pastel (0.8.0)
tty-color (~> 0.5) tty-color (~> 0.5)
pedump (0.6.5) pedump (0.6.6)
awesome_print awesome_print
iostruct (>= 0.0.4) iostruct (>= 0.0.4)
multipart-post (>= 2.0.0) multipart-post (>= 2.0.0)
rainbow rainbow
zhexdump (>= 0.0.2) zhexdump (>= 0.0.2)
plist (3.7.0) plist (3.7.0)
proxifier (1.0.3) proxifier2 (1.1.0)
pry (0.14.1) pry (0.14.2)
coderay (~> 1.1) coderay (~> 1.1)
method_source (~> 1.0) method_source (~> 1.0)
public_suffix (5.0.1) public_suffix (5.0.1)
rack (2.2.6.2) rack (2.2.7)
rainbow (3.1.1) rainbow (3.1.1)
rest-client (2.1.0)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rest-client (2.1.0-x64-mingw32)
ffi (~> 1.9)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
rest-client (2.1.0-x86-mingw32)
ffi (~> 1.9)
http-accept (>= 1.7.0, < 2.0)
http-cookie (>= 1.0.2, < 2.0)
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
retryable (3.0.5) retryable (3.0.5)
rexml (3.2.5) rexml (3.2.5)
rspec (3.11.0) rspec (3.11.0)
@ -338,17 +366,17 @@ GEM
rspec-mocks (~> 3.11.0) rspec-mocks (~> 3.11.0)
rspec-core (3.11.0) rspec-core (3.11.0)
rspec-support (~> 3.11.0) rspec-support (~> 3.11.0)
rspec-expectations (3.11.0) rspec-expectations (3.11.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0) rspec-support (~> 3.11.0)
rspec-its (1.3.0) rspec-its (1.3.0)
rspec-core (>= 3.0.0) rspec-core (>= 3.0.0)
rspec-expectations (>= 3.0.0) rspec-expectations (>= 3.0.0)
rspec-mocks (3.11.1) rspec-mocks (3.11.2)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.11.0) rspec-support (~> 3.11.0)
rspec-support (3.11.0) rspec-support (3.11.1)
ruby-progressbar (1.11.0) ruby-progressbar (1.13.0)
ruby2_keywords (0.0.5) ruby2_keywords (0.0.5)
rubyntlm (0.6.3) rubyntlm (0.6.3)
rubyzip (2.3.2) rubyzip (2.3.2)
@ -381,17 +409,24 @@ GEM
winrm (~> 2.0) winrm (~> 2.0)
winrm-elevated (~> 1.0) winrm-elevated (~> 1.0)
winrm-fs (~> 1.1) winrm-fs (~> 1.1)
thor (1.2.1) thor (1.2.2)
time (0.2.2)
date
timeout (0.4.0)
toml-rb (2.2.0) toml-rb (2.2.0)
citrus (~> 3.0, > 3.0) citrus (~> 3.0, > 3.0)
tomlrb (1.3.0) tomlrb (1.3.0)
train-core (3.10.7) train-core (3.10.8)
addressable (~> 2.5) addressable (~> 2.5)
ffi (!= 1.13.0) ffi (!= 1.13.0)
json (>= 1.8, < 3.0) json (>= 1.8, < 3.0)
mixlib-shellout (>= 2.0, < 4.0) mixlib-shellout (>= 2.0, < 4.0)
net-scp (>= 1.2, < 5.0) net-scp (>= 1.2, < 5.0)
net-ssh (>= 2.9, < 8.0) net-ssh (>= 2.9, < 8.0)
train-rest (0.5.0)
aws-sigv4 (~> 1.5)
rest-client (~> 2.1)
train-core (~> 3.0)
train-winrm (0.2.13) train-winrm (0.2.13)
winrm (>= 2.3.6, < 3.0) winrm (>= 2.3.6, < 3.0)
winrm-elevated (~> 1.2.2) winrm-elevated (~> 1.2.2)
@ -414,13 +449,16 @@ GEM
pastel (~> 0.8) pastel (~> 0.8)
strings (~> 0.2.0) strings (~> 0.2.0)
tty-screen (~> 0.8) tty-screen (~> 0.8)
unf (0.1.4)
unf_ext
unf_ext (0.0.8.2)
unicode-display_width (2.4.2) unicode-display_width (2.4.2)
unicode_utils (1.4.0) unicode_utils (1.4.0)
uuidtools (2.2.0) uuidtools (2.2.0)
vault (0.17.0) vault (0.17.0)
aws-sigv4 aws-sigv4
webrick (1.7.0) webrick (1.8.1)
win32-api (1.5.3-universal-mingw32) win32-api (1.10.1-universal-mingw32)
win32-certstore (0.6.15) win32-certstore (0.6.15)
chef-powershell (>= 1.0.12) chef-powershell (>= 1.0.12)
ffi ffi

View file

@ -53,6 +53,8 @@ dependency "shebang-cleanup"
# Ensure our SSL cert files are accessible to ruby. # Ensure our SSL cert files are accessible to ruby.
dependency "openssl-customization" dependency "openssl-customization"
dependency "ruby-msys2-devkit" if windows?
package :rpm do package :rpm do
signing_passphrase ENV["OMNIBUS_RPM_SIGNING_PASSPHRASE"] signing_passphrase ENV["OMNIBUS_RPM_SIGNING_PASSPHRASE"]
compression_level 1 compression_level 1

View file

@ -7,3 +7,5 @@ override "ruby", version: "3.1.2"
# Mac m1 # Mac m1
override "openssl", version: "1.1.1m" if mac_os_x? override "openssl", version: "1.1.1m" if mac_os_x?
override "ruby-msys2-devkit", version: "3.1.2-1"

View file

@ -0,0 +1,442 @@
{
"Content": "<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 2em;\n background-color: #fdfdff;\n border-radius: 0.5em;\n box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n div {\n margin: 0 auto;\n width: auto;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is for use in illustrative examples in documents. You may use this\n domain in literature without prior coordination or asking for permission.</p>\n <p><a href=\"https://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n",
"ParsedHtml": {
"Script": {},
"all": [
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject"
],
"body": {},
"activeElement": null,
"images": [],
"applets": [],
"links": [
"System.__ComObject"
],
"forms": [],
"anchors": [],
"title": "Example Domain",
"scripts": [],
"designMode": "Inherit",
"selection": {},
"readyState": "complete",
"frames": {},
"embeds": [],
"plugins": [],
"alinkColor": "#0000ff",
"bgColor": "#f0f0f2",
"fgColor": "#000000",
"linkColor": "#0000ff",
"vlinkColor": "#800080",
"referrer": null,
"location": {},
"lastModified": "03/07/2021 23:17:45",
"url": "about:blank",
"domain": null,
"cookie": null,
"expando": true,
"charset": "unicode",
"defaultCharset": "windows-1252",
"mimeType": "Chrome HTML Document",
"fileSize": null,
"fileCreatedDate": null,
"fileModifiedDate": null,
"fileUpdatedDate": null,
"security": "This type of document does not have a security certificate.",
"protocol": "Unknown Protocol",
"nameProp": "Example Domain",
"onhelp": null,
"onclick": null,
"ondblclick": null,
"onkeyup": null,
"onkeydown": null,
"onkeypress": null,
"onmouseup": null,
"onmousedown": null,
"onmousemove": null,
"onmouseout": null,
"onmouseover": null,
"onreadystatechange": null,
"onafterupdate": null,
"onrowexit": null,
"onrowenter": null,
"ondragstart": null,
"onselectstart": null,
"parentWindow": {},
"styleSheets": [
"System.__ComObject"
],
"onbeforeupdate": null,
"onerrorupdate": null,
"documentElement": {},
"uniqueID": "ms__id1",
"onrowsdelete": null,
"onrowsinserted": null,
"oncellchange": null,
"ondatasetchanged": null,
"ondataavailable": null,
"ondatasetcomplete": null,
"onpropertychange": null,
"dir": null,
"oncontextmenu": null,
"onstop": null,
"parentDocument": null,
"enableDownload": null,
"baseUrl": null,
"inheritStyleSheets": null,
"onbeforeeditfocus": null,
"onselectionchange": null,
"namespaces": {},
"media": null,
"oncontrolselect": null,
"URLUnencoded": "about:blank",
"onmousewheel": null,
"doctype": null,
"implementation": {},
"onfocusin": null,
"onfocusout": null,
"onactivate": null,
"ondeactivate": null,
"onbeforeactivate": null,
"onbeforedeactivate": null,
"compatMode": "CSS1Compat",
"nodeType": 9,
"parentNode": null,
"childNodes": [
"System.__ComObject",
"System.__ComObject"
],
"attributes": null,
"nodeName": "#document",
"nodeValue": null,
"firstChild": {},
"lastChild": {},
"previousSibling": null,
"nextSibling": null,
"ownerDocument": null,
"IHTMLDocument2_Script": {},
"IHTMLDocument2_all": [
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject",
"System.__ComObject"
],
"IHTMLDocument2_body": {},
"IHTMLDocument2_activeElement": null,
"IHTMLDocument2_images": [],
"IHTMLDocument2_applets": [],
"IHTMLDocument2_links": [
"System.__ComObject"
],
"IHTMLDocument2_forms": [],
"IHTMLDocument2_anchors": [],
"IHTMLDocument2_title": "Example Domain",
"IHTMLDocument2_scripts": [],
"IHTMLDocument2_designMode": "Inherit",
"IHTMLDocument2_selection": {},
"IHTMLDocument2_readyState": "complete",
"IHTMLDocument2_frames": {},
"IHTMLDocument2_embeds": [],
"IHTMLDocument2_plugins": [],
"IHTMLDocument2_alinkColor": "#0000ff",
"IHTMLDocument2_bgColor": "#f0f0f2",
"IHTMLDocument2_fgColor": "#000000",
"IHTMLDocument2_linkColor": "#0000ff",
"IHTMLDocument2_vlinkColor": "#800080",
"IHTMLDocument2_referrer": null,
"IHTMLDocument2_location": null,
"IHTMLDocument2_lastModified": "03/07/2021 23:17:45",
"IHTMLDocument2_url": "about:blank",
"IHTMLDocument2_domain": null,
"IHTMLDocument2_cookie": null,
"IHTMLDocument2_expando": true,
"IHTMLDocument2_charset": "unicode",
"IHTMLDocument2_defaultCharset": "windows-1252",
"IHTMLDocument2_mimeType": "Chrome HTML Document",
"IHTMLDocument2_fileSize": null,
"IHTMLDocument2_fileCreatedDate": null,
"IHTMLDocument2_fileModifiedDate": null,
"IHTMLDocument2_fileUpdatedDate": null,
"IHTMLDocument2_security": "This type of document does not have a security certificate.",
"IHTMLDocument2_protocol": "Unknown Protocol",
"IHTMLDocument2_nameProp": "Example Domain",
"IHTMLDocument2_onhelp": null,
"IHTMLDocument2_onclick": null,
"IHTMLDocument2_ondblclick": null,
"IHTMLDocument2_onkeyup": null,
"IHTMLDocument2_onkeydown": null,
"IHTMLDocument2_onkeypress": null,
"IHTMLDocument2_onmouseup": null,
"IHTMLDocument2_onmousedown": null,
"IHTMLDocument2_onmousemove": null,
"IHTMLDocument2_onmouseout": null,
"IHTMLDocument2_onmouseover": null,
"IHTMLDocument2_onreadystatechange": null,
"IHTMLDocument2_onafterupdate": null,
"IHTMLDocument2_onrowexit": null,
"IHTMLDocument2_onrowenter": null,
"IHTMLDocument2_ondragstart": null,
"IHTMLDocument2_onselectstart": null,
"IHTMLDocument2_parentWindow": {},
"IHTMLDocument2_styleSheets": [
"System.__ComObject"
],
"IHTMLDocument2_onbeforeupdate": null,
"IHTMLDocument2_onerrorupdate": null,
"IHTMLDocument3_documentElement": {},
"IHTMLDocument3_uniqueID": "ms__id2",
"IHTMLDocument3_onrowsdelete": null,
"IHTMLDocument3_onrowsinserted": null,
"IHTMLDocument3_oncellchange": null,
"IHTMLDocument3_ondatasetchanged": null,
"IHTMLDocument3_ondataavailable": null,
"IHTMLDocument3_ondatasetcomplete": null,
"IHTMLDocument3_onpropertychange": null,
"IHTMLDocument3_dir": null,
"IHTMLDocument3_oncontextmenu": null,
"IHTMLDocument3_onstop": null,
"IHTMLDocument3_parentDocument": null,
"IHTMLDocument3_enableDownload": null,
"IHTMLDocument3_baseUrl": null,
"IHTMLDocument3_childNodes": [
"System.__ComObject",
"System.__ComObject"
],
"IHTMLDocument3_inheritStyleSheets": null,
"IHTMLDocument3_onbeforeeditfocus": null,
"IHTMLDocument4_onselectionchange": null,
"IHTMLDocument4_namespaces": {},
"IHTMLDocument4_media": null,
"IHTMLDocument4_oncontrolselect": null,
"IHTMLDocument4_URLUnencoded": "about:blank",
"IHTMLDocument5_onmousewheel": null,
"IHTMLDocument5_doctype": null,
"IHTMLDocument5_implementation": {},
"IHTMLDocument5_onfocusin": null,
"IHTMLDocument5_onfocusout": null,
"IHTMLDocument5_onactivate": null,
"IHTMLDocument5_ondeactivate": null,
"IHTMLDocument5_onbeforeactivate": null,
"IHTMLDocument5_onbeforedeactivate": null,
"IHTMLDocument5_compatMode": "CSS1Compat",
"IHTMLDOMNode_nodeType": null,
"IHTMLDOMNode_parentNode": null,
"IHTMLDOMNode_childNodes": null,
"IHTMLDOMNode_attributes": null,
"IHTMLDOMNode_nodeName": null,
"IHTMLDOMNode_nodeValue": null,
"IHTMLDOMNode_firstChild": null,
"IHTMLDOMNode_lastChild": null,
"IHTMLDOMNode_previousSibling": null,
"IHTMLDOMNode_nextSibling": null,
"IHTMLDOMNode2_ownerDocument": null
},
"Forms": [],
"InputFields": [],
"Links": [
{
"innerHTML": "More information...",
"innerText": "More information...",
"outerHTML": "<A href=\"https://www.iana.org/domains/example\">More information...</A>",
"outerText": "More information...",
"tagName": "A",
"href": "https://www.iana.org/domains/example"
}
],
"Images": [],
"Scripts": [],
"AllElements": [
{
"innerHTML": null,
"innerText": null,
"outerHTML": null,
"outerText": null,
"tagName": "!"
},
{
"innerHTML": "<HEAD><TITLE>Example Domain</TITLE>\r\n<META charset=utf-8>\r\n<META name=viewport content=\"width=device-width, initial-scale=1\">\r\n<STYLE type=text/css>\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 2em;\n background-color: #fdfdff;\n border-radius: 0.5em;\n box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n div {\n margin: 0 auto;\n width: auto;\n }\n }\n </STYLE>\r\n</HEAD>\r\n<BODY>\r\n<DIV>\r\n<H1>Example Domain</H1>\r\n<P>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</P>\r\n<P><A href=\"https://www.iana.org/domains/example\">More information...</A></P></DIV></BODY>",
"innerText": "Example DomainExample Domain\r\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\r\nMore information...",
"outerHTML": "<HTML><HEAD><TITLE>Example Domain</TITLE>\r\n<META charset=utf-8>\r\n<META name=viewport content=\"width=device-width, initial-scale=1\">\r\n<STYLE type=text/css>\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 2em;\n background-color: #fdfdff;\n border-radius: 0.5em;\n box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n div {\n margin: 0 auto;\n width: auto;\n }\n }\n </STYLE>\r\n</HEAD>\r\n<BODY>\r\n<DIV>\r\n<H1>Example Domain</H1>\r\n<P>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</P>\r\n<P><A href=\"https://www.iana.org/domains/example\">More information...</A></P></DIV></BODY></HTML>",
"outerText": "Example DomainExample Domain\r\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\r\nMore information...",
"tagName": "HTML"
},
{
"innerHTML": "<TITLE>Example Domain</TITLE>\r\n<META charset=utf-8>\r\n<META name=viewport content=\"width=device-width, initial-scale=1\">\r\n<STYLE type=text/css>\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 2em;\n background-color: #fdfdff;\n border-radius: 0.5em;\n box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n div {\n margin: 0 auto;\n width: auto;\n }\n }\n </STYLE>",
"innerText": "Example Domain",
"outerHTML": "<HEAD><TITLE>Example Domain</TITLE>\r\n<META charset=utf-8>\r\n<META name=viewport content=\"width=device-width, initial-scale=1\">\r\n<STYLE type=text/css>\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 2em;\n background-color: #fdfdff;\n border-radius: 0.5em;\n box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n div {\n margin: 0 auto;\n width: auto;\n }\n }\n </STYLE>\r\n</HEAD>",
"outerText": "Example Domain",
"tagName": "HEAD"
},
{
"innerHTML": "Example Domain",
"innerText": "Example Domain",
"outerHTML": "<TITLE>Example Domain</TITLE>",
"outerText": "Example Domain",
"tagName": "TITLE"
},
{
"innerHTML": null,
"innerText": null,
"outerHTML": "\r\n<META charset=utf-8>",
"outerText": null,
"tagName": "META",
"charset": "utf-8"
},
{
"innerHTML": null,
"innerText": null,
"outerHTML": null,
"outerText": null,
"tagName": "META"
},
{
"innerHTML": null,
"innerText": null,
"outerHTML": "\r\n<META name=viewport content=\"width=device-width, initial-scale=1\">",
"outerText": null,
"tagName": "META",
"name": "viewport",
"content": "width=device-width, initial-scale=1"
},
{
"innerHTML": "\r\n\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 2em;\n background-color: #fdfdff;\n border-radius: 0.5em;\n box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n div {\n margin: 0 auto;\n width: auto;\n }\n }\n ",
"innerText": null,
"outerHTML": "\r\n<STYLE type=text/css>\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 2em;\n background-color: #fdfdff;\n border-radius: 0.5em;\n box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n div {\n margin: 0 auto;\n width: auto;\n }\n }\n </STYLE>",
"outerText": null,
"tagName": "STYLE",
"type": "text/css"
},
{
"innerHTML": "<DIV>\r\n<H1>Example Domain</H1>\r\n<P>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</P>\r\n<P><A href=\"https://www.iana.org/domains/example\">More information...</A></P></DIV>",
"innerText": "Example Domain\r\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\r\nMore information...",
"outerHTML": "\r\n<BODY><DIV>\r\n<H1>Example Domain</H1>\r\n<P>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</P>\r\n<P><A href=\"https://www.iana.org/domains/example\">More information...</A></P></DIV></BODY>",
"outerText": "Example Domain\r\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\r\nMore information...",
"tagName": "BODY"
},
{
"innerHTML": "<H1>Example Domain</H1>\r\n<P>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</P>\r\n<P><A href=\"https://www.iana.org/domains/example\">More information...</A></P>",
"innerText": "Example Domain\r\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\r\nMore information...",
"outerHTML": "\r\n<DIV><H1>Example Domain</H1>\r\n<P>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</P>\r\n<P><A href=\"https://www.iana.org/domains/example\">More information...</A></P></DIV>",
"outerText": "Example Domain\r\nThis domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.\r\nMore information...",
"tagName": "DIV"
},
{
"innerHTML": "Example Domain",
"innerText": "Example Domain",
"outerHTML": "\r\n<H1>Example Domain</H1>",
"outerText": "Example Domain",
"tagName": "H1"
},
{
"innerHTML": "This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.",
"innerText": "This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.",
"outerHTML": "\r\n<P>This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.</P>",
"outerText": "This domain is for use in illustrative examples in documents. You may use this domain in literature without prior coordination or asking for permission.",
"tagName": "P"
},
{
"innerHTML": "<A href=\"https://www.iana.org/domains/example\">More information...</A>",
"innerText": "More information...",
"outerHTML": "\r\n<P><A href=\"https://www.iana.org/domains/example\">More information...</A></P>",
"outerText": "More information...",
"tagName": "P"
},
{
"innerHTML": "More information...",
"innerText": "More information...",
"outerHTML": "<A href=\"https://www.iana.org/domains/example\">More information...</A>",
"outerText": "More information...",
"tagName": "A",
"href": "https://www.iana.org/domains/example"
}
],
"StatusCode": 200,
"StatusDescription": "OK",
"RawContentStream": {
"CanRead": true,
"CanSeek": true,
"CanTimeout": false,
"CanWrite": true,
"Length": 1256,
"Capacity": 10000,
"Position": 0,
"ReadTimeout": null,
"WriteTimeout": null
},
"RawContentLength": 1256,
"RawContent": "HTTP/1.1 200 OK\r\nAge: 156043\r\nVary: Accept-Encoding\r\nX-Cache: HIT\r\nAccept-Ranges: bytes\r\nContent-Length: 1256\r\nCache-Control: max-age=604800\r\nContent-Type: text/html; charset=UTF-8\r\nDate: Mon, 08 Mar 2021 07:17:44 GMT\r\nExpires: Mon, 15 Mar 2021 07:17:44 GMT\r\nETag: \"3147526947\"\r\nLast-Modified: Thu, 17 Oct 2019 07:18:26 GMT\r\nServer: ECS (oxr/836D)\r\n\r\n<!doctype html>\n<html>\n<head>\n <title>Example Domain</title>\n\n <meta charset=\"utf-8\" />\n <meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n <style type=\"text/css\">\n body {\n background-color: #f0f0f2;\n margin: 0;\n padding: 0;\n font-family: -apple-system, system-ui, BlinkMacSystemFont, \"Segoe UI\", \"Open Sans\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n \n }\n div {\n width: 600px;\n margin: 5em auto;\n padding: 2em;\n background-color: #fdfdff;\n border-radius: 0.5em;\n box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n }\n a:link, a:visited {\n color: #38488f;\n text-decoration: none;\n }\n @media (max-width: 700px) {\n div {\n margin: 0 auto;\n width: auto;\n }\n }\n </style> \n</head>\n\n<body>\n<div>\n <h1>Example Domain</h1>\n <p>This domain is for use in illustrative examples in documents. You may use this\n domain in literature without prior coordination or asking for permission.</p>\n <p><a href=\"https://www.iana.org/domains/example\">More information...</a></p>\n</div>\n</body>\n</html>\n",
"BaseResponse": {
"IsMutuallyAuthenticated": false,
"Cookies": [],
"Headers": [
"Age",
"Vary",
"X-Cache",
"Accept-Ranges",
"Content-Length",
"Cache-Control",
"Content-Type",
"Date",
"Expires",
"ETag",
"Last-Modified",
"Server"
],
"SupportsHeaders": true,
"ContentLength": 1256,
"ContentEncoding": "",
"ContentType": "text/html; charset=UTF-8",
"CharacterSet": "UTF-8",
"Server": "ECS (oxr/836D)",
"LastModified": "/Date(1571296706000)/",
"StatusCode": 200,
"StatusDescription": "OK",
"ProtocolVersion": {
"Major": 1,
"Minor": 1,
"Build": -1,
"Revision": -1,
"MajorRevision": -1,
"MinorRevision": -1
},
"ResponseUri": "https://www.example.com/",
"Method": "GET",
"IsFromCache": false
},
"Headers": {
"Age": "156043",
"Vary": "Accept-Encoding",
"X-Cache": "HIT",
"Accept-Ranges": "bytes",
"Content-Length": "1256",
"Cache-Control": "max-age=604800",
"Content-Type": "text/html; charset=UTF-8",
"Date": "Mon, 08 Mar 2021 07:17:44 GMT",
"Expires": "Mon, 15 Mar 2021 07:17:44 GMT",
"ETag": "\"3147526947\"",
"Last-Modified": "Thu, 17 Oct 2019 07:18:26 GMT",
"Server": "ECS (oxr/836D)"
}
}

7
test/fixtures/cmd/nftables-chain vendored Normal file
View file

@ -0,0 +1,7 @@
table inet filter {
chain INPUT {
type filter hook input priority 0; policy accept;
iifname "eth0" tcp dport 80 accept comment "http on 80"
jump derby-cognos-web
}
}

1
test/fixtures/cmd/nftables-chain-json vendored Normal file
View file

@ -0,0 +1 @@
{"nftables": [{"metainfo": {"version": "1.0.2", "release_name": "Lester Gooch", "json_schema_version": 1}}, {"chain": {"family": "inet", "table": "filter", "name": "INPUT", "handle": 1, "type": "filter", "hook": "input", "prio": 0, "policy": "accept"}}, {"rule": {"family": "inet", "table": "filter", "chain": "INPUT", "handle": 4, "comment": "http on 80", "expr": [{"match": {"op": "==", "left": {"meta": {"key": "iifname"}}, "right": "eth0"}}, {"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 80}}, {"accept": null}]}}, {"rule": {"family": "inet", "table": "filter", "chain": "INPUT", "handle": 5, "expr": [{"jump": {"target": "derby-cognos-web"}}]}}]}

8
test/fixtures/cmd/nftables-set vendored Normal file
View file

@ -0,0 +1,8 @@
table inet filter {
set OPEN_PORTS {
type ipv4_addr
size 65536
flags interval
elements = { 1.1.1.1 }
}
}

1
test/fixtures/cmd/nftables-set-json vendored Normal file
View file

@ -0,0 +1 @@
{"nftables": [{"metainfo": {"version": "1.0.2", "release_name": "Lester Gooch", "json_schema_version": 1}}, {"set": {"family": "inet", "name": "OPEN_PORTS", "table": "filter", "type": "ipv4_addr", "handle": 3, "size": 65536, "flags": ["interval"], "elem": ["1.1.1.1"]}}]}

1
test/fixtures/cmd/nftables-version vendored Normal file
View file

@ -0,0 +1 @@
nftables v1.0.2 (Lester Gooch)

View file

@ -1,93 +0,0 @@
# Error
control "tmp-1.0.1" do
impact 0.7
describe "a.1" do
it { should_bot "a.1" }
end
end
control "tmp-1.0.2" do
impact 0.0
describe "a.2" do
it { should_bot "a.2" }
end
end
# Not Applicable
control "tmp-2.0.1" do
impact 0.0
describe "b.1" do
it { should cmp "b.1" }
end
end
control "tmp-2.0.2" do
impact 0.0
only_if { false }
describe "b.2" do
it { should cmp "b.2" }
end
end
# Not Reviewed
control "tmp-3.0.1" do
only_if { false }
describe "c.1" do
it { should cmp "c.1" }
end
end
control "tmp-3.0.2" do
only_if { false }
describe "c.2" do
it { should_bot "c.2" }
end
end
control "tmp-3.0.3" do
only_if { false }
describe "c.2" do
it { should_bot "c.2" }
end
end
control "tmp-3.0.4" do
only_if { false }
describe "c.2" do
it { should_bot "c.2" }
end
end
# Failed
control "tmp-4.0" do
impact 0.7
describe "d.1" do
it { should_not cmp "d.1" }
it { should cmp "d.1" }
end
end
# Passed
control "tmp-5.0" do
impact 0.7
describe "e.1" do
it { should cmp "e.1" }
end
end
# Example of setting impact using code and marking it N/A
control "tmp-6.0.1" do
impact 0.5
only_if("Some reason for N/A", impact: 0.0) { false }
describe "f.1" do
it { should cmp "f.1" }
end
end
# Example of setting impact using code and not marked as N/A
control "tmp-6.0.2" do
only_if(impact: 0.5) { false }
describe "f.2" do
it { should cmp "f.2" }
end
end

View file

@ -1,5 +0,0 @@
control_id,justification,explanation,evidence_url,status,expiration_date,updated,frequency
tmp-3.0.1,Sound reasoning,,Dummy url,failed,2001-06-01T00:00:00.000Z,,,
tmp-3.0.2,,Unassailable thinking,Dummy url,failed,,2021-06-01T00:00:00.000Z,semiannually
tmp-4.0,Sheer cleverness,,Dummy url,passed,2050-06-01T00:00:00.000Z,,,
tmp-6.0.2,Sheer cleverness,,Dummy url,passed,2050-06-01T00:00:00.000Z,,,
1 control_id,justification,explanation,evidence_url,status,expiration_date,updated,frequency
2 tmp-3.0.1,Sound reasoning,,Dummy url,failed,2001-06-01T00:00:00.000Z,,,
3 tmp-3.0.2,,Unassailable thinking,Dummy url,failed,,2021-06-01T00:00:00.000Z,semiannually
4 tmp-4.0,Sheer cleverness,,Dummy url,passed,2050-06-01T00:00:00.000Z,,,
5 tmp-6.0.2,Sheer cleverness,,Dummy url,passed,2050-06-01T00:00:00.000Z,,,

View file

@ -1,27 +0,0 @@
{
"tmp-3.0.1": {
"explanation": "Sound reasoning",
"evidence_url": "Dummy url",
"status": "failed",
"updated": "2021-06-01T00:00:00.000Z",
"frequency": "semiannually"
},
"tmp-3.0.2": {
"justification": "Unassailable thinking",
"evidence_url": "Dummy url",
"expiration_date": "2001-06-01T00:00:00.000Z",
"status": "passed"
},
"tmp-4.0": {
"justification": "Sheer cleverness",
"evidence_url": "Dummy url",
"expiration_date": "2050-06-01T00:00:00.000Z",
"status": "passed"
},
"tmp-6.0.2": {
"justification": "Sheer cleverness",
"evidence_url": "Dummy url",
"expiration_date": "2050-06-01T00:00:00.000Z",
"status": "passed"
}
}

View file

@ -1,24 +0,0 @@
tmp-3.0.1:
explanation: Sound reasoning
evidence_url: Dummy url
status: failed
updated: 2021-06-01
frequency: semiannually
tmp-3.0.2:
justification: Unassailable thinking
evidence_url: Dummy url
expiration_date: 2001-06-01
status: passed
tmp-4.0:
justification: Sheer cleverness
evidence_url: Dummy url
expiration_date: 2050-06-01
status: passed
tmp-6.0.2:
justification: Sheer cleverness
evidence_url: Dummy url
expiration_date: 2050-06-01
status: passed

View file

@ -1,5 +0,0 @@
tmp-3.0.2:
justification: Unassailable thinking
evidence_url: Dummy url
expiration_date: bad date
status: passed

View file

@ -1,6 +0,0 @@
tmp-3.0.1:
justification: Sound reasoning
evidence_url: Dummy url
status: failed
updated: bad date
frequency: semiannually

View file

@ -1,6 +0,0 @@
tmp-3.0.4:
justification: Sound reasoning
evidence_url: Dummy url
status: failed
updated: 2021-06-01
frequency: biweekly

View file

@ -1,5 +0,0 @@
tmp-3.0.3:
justification: Unassailable thinking
evidence_url: Dummy url
expiration_date: 2050-06-01
status: pass

View file

@ -1,7 +0,0 @@
tmp-3.0.1:
expiration_date: 2050-06-01
status: passed
tmp-3.0.2:
evidence_url: Dummy url
expiration_date: 2050-06-01
status: passed

View file

@ -1,4 +0,0 @@
tmp-3.0.3:
justification: Unassailable thinking
evidence_url: Dummy url
expiration_date: 2050-06-01

View file

@ -1,8 +0,0 @@
control_id_random,justification_random,run_random,expiration_date_random,,,
03_waivered_no_expiry_ran_passes,Sound reasoning,TRUE,,,,
04_waivered_no_expiry_ran_fails,Unassailable thinking,TRUE,2077-11-10T00:00:00Z,,,
,,,,,,
05_waivered_no_expiry_not_ran,Sheer cleverness,FALSE,,,,
06_waivered_expiry_in_past_ran_passes,Necessity,TRUE,,,,
14_waivered_expiry_in_future_z_not_ran,Lack of imagination,FALSE,2077-11-10T00:00:00Z,,,
random contorl id with no data,,,,,,random data in csv!
1 control_id_random justification_random run_random expiration_date_random
2 03_waivered_no_expiry_ran_passes Sound reasoning TRUE
3 04_waivered_no_expiry_ran_fails Unassailable thinking TRUE 2077-11-10T00:00:00Z
4
5 05_waivered_no_expiry_not_ran Sheer cleverness FALSE
6 06_waivered_expiry_in_past_ran_passes Necessity TRUE
7 14_waivered_expiry_in_future_z_not_ran Lack of imagination FALSE 2077-11-10T00:00:00Z
8 random contorl id with no data random data in csv!

View file

@ -1,21 +0,0 @@
{
"tmp-3.0.1": {
"justification": "Sound reasoning",
"evidence_url": "Dummy url",
"expiration_date": "2050-06-01T00:00:00.000Z",
"status": "passed",
"random": "haha"
},
"tmp-3.0.2": {
"justification": "Unassailable thinking",
"evidence_url": "Dummy url",
"expiration_date": "2050-06-01T00:00:00.000Z",
"status": "passed"
},
"tmp-4.0": {
"justification": "Sheer cleverness",
"evidence_url": "Dummy url",
"expiration_date": "2050-06-01T00:00:00.000Z",
"status": "passed"
}
}

View file

@ -1,18 +0,0 @@
tmp-3.0.1:
justification: Sound reasoning
evidence_url: Dummy url
expiration_date: 2050-06-01
status: passed
random: haha
tmp-3.0.2:
justification: Unassailable thinking
evidence_url: Dummy url
expiration_date: 2050-06-01
status: passed
tmp-4.0:
justification: Sheer cleverness
evidence_url: Dummy url
expiration_date: 2050-06-01
status: passed

View file

@ -1,10 +0,0 @@
name: attestation
title: InSpec Profile
maintainer: The Authors
copyright: The Authors
copyright_email: you@example.com
license: Apache-2.0
summary: An InSpec Compliance Profile
version: 0.1.0
supports:
platform: os

View file

@ -1,177 +0,0 @@
require "functional/helper"
describe "attestations" do
include FunctionalHelper
parallelize_me!
let(:attestation_profile) { "#{profile_path}/attestation" }
let(:run_result) { run_inspec_process(cmd) }
let(:cmd) { "exec #{attestation_profile} --attestation-file #{attestation_profile}/files/#{attestation_file}" }
attr_accessor :out
def inspec(commandline, prefix = nil)
@stdout = @stderr = nil
self.out = super
end
def stdout
@stdout ||= out.stdout
.force_encoding(Encoding::UTF_8)
end
def stderr
@stderr ||= out.stderr
.force_encoding(Encoding::UTF_8)
end
describe "with a attestation file that does not exist" do
let(:attestation_file) { "no_file.yaml" }
it "raise file does not exist standard error" do
result = run_result
assert_includes result.stderr, "no_file.yaml does not exist"
assert_equal 1, result.exit_status
end
end
describe "with a attestation file that has wrong headers - yaml format" do
let(:attestation_file) { "wrong-headers.yaml" }
it "raise file does not exist standard error" do
result = run_result
assert_includes result.stdout, "Extra column headers: [\"random\"]"
end
end
describe "with a attestation file that has wrong headers - csv format" do
let(:attestation_file) { "wrong-headers.csv" }
it "raise file does not exist standard error" do
result = run_result
assert_includes result.stdout, "Missing column headers: [\"control_id\", \"status\", \"justification\"]"
assert_includes result.stdout, "Extra column headers: [\"control_id_random\", \"justification_random\", \"run_random\", \"expiration_date_random\", nil]\n"
end
end
describe "with a attestation file that has wrong headers - json format" do
let(:attestation_file) { "wrong-headers.json" }
it "raise file does not exist standard error" do
result = run_result
assert_includes result.stdout, "Extra column headers: [\"random\"]"
end
end
describe "running attestation on a profile - yaml" do
let(:attestation_file) { "attestations.yaml" }
it "attests N/R controls correctly" do
result = run_result
assert_includes result.stdout, "tmp-3.0.1: No-op (1 failed)"
refute_includes result.stdout, "N/R tmp-3.0.2: No-op"
refute_includes result.stdout, "N/R tmp-6.0.2: No-op"
end
it "does not attests non N/R controls" do
result = run_result
assert_includes result.stdout, "tmp-4.0: d.1 (1 failed)"
end
end
describe "running attestation on a profile - json" do
let(:attestation_file) { "attestations.json" }
it "attests N/R controls correctly" do
result = run_result
assert_includes result.stdout, "tmp-3.0.1: No-op (1 failed)"
refute_includes result.stdout, "N/R tmp-3.0.2: No-op"
refute_includes result.stdout, "N/R tmp-6.0.2: No-op"
end
it "does not attests non N/R controls" do
result = run_result
assert_includes result.stdout, "tmp-4.0: d.1 (1 failed)"
end
end
describe "running attestation on a profile - csv" do
let(:attestation_file) { "attestations.csv" }
it "attests N/R controls correctly" do
result = run_result
assert_includes result.stdout, "tmp-3.0.1: No-op (1 failed)"
refute_includes result.stdout, "N/R tmp-3.0.2: No-op"
refute_includes result.stdout, "N/R tmp-6.0.2: No-op"
end
it "does not attests non N/R controls" do
result = run_result
assert_includes result.stdout, "tmp-4.0: d.1 (1 failed)"
end
end
describe "running attestation on profile with streaming reporter" do
let(:attestation_file) { "#{attestation_profile}/files/attestations.yaml" }
it "attests controls correctly" do
inspec("exec " + "#{attestation_profile}" + " --attestation-file #{attestation_file}" + " --no-create-lockfile" + " --no-color" + " --reporter progress-bar")
if windows?
_(stderr).must_match(/\[FAIL\]\s*tmp-3.0.1\s*No-op Skipped control due to only_if condition. Control not attested : Attestation expired on 2021-12-01/)
_(stderr).must_match(/\[FAIL\]\s*tmp-3.0.2\s*No-op Skipped control due to only_if condition. Control not attested : Attestation expired on 2001-06-01/)
_(stderr).must_match(/\[PASS\]\s*tmp-6.0.2\s*No-op Skipped control due to only_if condition. Control Attested : Sheer cleverness | Evidence URL: Dummy url/)
_(stderr).must_match(/\[FAIL\]\s*tmp-4.0\s*d.1 is expected to cmp == \"d.1\"/)
else
_(stderr).must_match(/\[FAILED\]\s*tmp-3.0.1\s*No-op Skipped control due to only_if condition. Control not attested : Attestation expired on 2021-12-01/)
_(stderr).must_match(/\[FAILED\]\s*tmp-3.0.2\s*No-op Skipped control due to only_if condition. Control not attested : Attestation expired on 2001-06-01/)
_(stderr).must_match(/\[PASSED\]\s*tmp-6.0.2\s*No-op Skipped control due to only_if condition. Control Attested : Sheer cleverness | Evidence URL: Dummy url/)
_(stderr).must_match(/\[FAILED\]\s*tmp-4.0\s*d.1 is expected to cmp == \"d.1\"/)
end
end
end
describe "an attestation file with invalid dates" do
let(:attestation_file) { "bad-date.yaml" }
it "gracefully errors" do
result = run_result
assert_includes result.stdout, "ERROR"
end
end
describe "an attestation file with invalid update dates" do
let(:attestation_file) { "bad-update-date.yaml" }
it "gracefully errors" do
result = run_result
assert_includes result.stdout, "ERROR"
end
end
describe "an attestation file with invalid status" do
let(:attestation_file) { "invalid-status.yaml" }
it "throws warning" do
result = run_result
assert_includes result.stdout, "Invalid attestation status 'pass' for control tmp-3.0.3. Use 'passed' or 'failed'."
end
end
describe "an attestation file with no status" do
let(:attestation_file) { "no-status.yaml" }
it "throws warning" do
result = run_result
assert_includes result.stdout, "No attestation status for control tmp-3.0.3. Use 'passed' or 'failed'."
end
end
describe "an attestation file with invalid frequency value" do
let(:attestation_file) { "invalid-frequency.yaml" }
it "throws warning" do
result = run_result
assert_includes result.stdout, "Invalid frequency value 'biweekly' for control tmp-3.0.4."
end
end
describe "an attestation file with no justification" do
let(:attestation_file) { "no-justification.yaml" }
it "throws warning and shows proper message for justification absence" do
result = run_result
assert_includes result.stdout, "Missing column headers: [\"justification\"]"
assert_includes result.stdout, "Control Attested : No justification provided."
end
end
end

View file

@ -105,6 +105,7 @@ describe "inputs" do
# require inspec # require inspec
require "inspec" require "inspec"
require "inspec/runner" require "inspec/runner"
require "inspec/utils/licensing_config"
# inject pretty-printed runner opts # inject pretty-printed runner opts
runner_args = #{options.inspect} runner_args = #{options.inspect}

View file

@ -1,12 +1,14 @@
require "functional/helper" require "functional/helper"
require "inspec/runner" require "inspec/runner"
require "inspec/resources/file" require "inspec/resources/file"
require "inspec/utils/licensing_config"
describe "inspec report tests" do describe "inspec report tests" do
include FunctionalHelper include FunctionalHelper
describe "report" do describe "report" do
it "loads a json report" do it "loads a json report" do
WebMock.allow_net_connect!
o = { "reporter" => ["json"], "report" => true } o = { "reporter" => ["json"], "report" => true }
runner = ::Inspec::Runner.new(o) runner = ::Inspec::Runner.new(o)
runner.add_target(example_profile) runner.add_target(example_profile)

View file

@ -385,6 +385,24 @@ class MockLoader
# ip6tables # ip6tables
"/usr/sbin/ip6tables -S" => cmd.call("ip6tables-s"), "/usr/sbin/ip6tables -S" => cmd.call("ip6tables-s"),
%{sh -c 'type "/usr/sbin/ip6tables"'} => empty.call, %{sh -c 'type "/usr/sbin/ip6tables"'} => empty.call,
# nftables (version)
"/usr/sbin/nft --version" => cmd.call("nftables-version"),
# nftables (chain with json output)
"/usr/sbin/nft -s -j list chain inet filter INPUT" => cmd.call("nftables-chain-json"),
"/usr/sbin/nft -j list chain inet filter INPUT" => cmd.call("nftables-chain-json"),
# nftables (chain)
"/usr/sbin/nft -s list chain inet filter INPUT" => cmd.call("nftables-chain"),
"/usr/sbin/nft -s -y list chain inet filter INPUT" => cmd.call("nftables-chain"),
"/usr/sbin/nft -s -nn list chain inet filter INPUT" => cmd.call("nftables-chain"),
"/usr/sbin/nft -y list chain inet filter INPUT" => cmd.call("nftables-chain"),
"/usr/sbin/nft list chain inet filter INPUT" => cmd.call("nftables-chain"),
# nftables (set with json output)
"/usr/sbin/nft -s -j list set inet filter OPEN_PORTS" => cmd.call("nftables-set-json"),
"/usr/sbin/nft -j list set inet filter OPEN_PORTS" => cmd.call("nftables-set-json"),
# nftables (set)
"/usr/sbin/nft -s list set inet filter OPEN_PORTS" => cmd.call("nftables-set"),
"/usr/sbin/nft list set inet filter OPEN_PORTS" => cmd.call("nftables-set"),
%{sh -c 'type "/usr/sbin/nft"'} => empty.call,
# ipnat # ipnat
"/usr/sbin/ipnat -l" => cmd.call("ipnat-l"), "/usr/sbin/ipnat -l" => cmd.call("ipnat-l"),
%{type "/usr/sbin/ipnat"} => empty.call, %{type "/usr/sbin/ipnat"} => empty.call,
@ -631,6 +649,7 @@ class MockLoader
# http resource - windows # http resource - windows
"\n$body = \n $Body = $body | ConvertFrom-Json\n #convert to hashtable\n $HashTable = @{}\n foreach ($property in $Body.PSObject.Properties) {\n $HashTable[$property.Name] = $property.Value\n }\n $response = Invoke-WebRequest -Method HEAD -TimeoutSec 120 'https://www.example.com' -Body $HashTable -UseBasicParsing\n $response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error" => cmd.call("http-windows-remote-no-options"), "\n$body = \n $Body = $body | ConvertFrom-Json\n #convert to hashtable\n $HashTable = @{}\n foreach ($property in $Body.PSObject.Properties) {\n $HashTable[$property.Name] = $property.Value\n }\n $response = Invoke-WebRequest -Method HEAD -TimeoutSec 120 'https://www.example.com' -Body $HashTable -UseBasicParsing\n $response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error" => cmd.call("http-windows-remote-no-options"),
"\n$body = \n $Body = $body | ConvertFrom-Json\n #convert to hashtable\n $HashTable = @{}\n foreach ($property in $Body.PSObject.Properties) {\n $HashTable[$property.Name] = $property.Value\n }\n $response = Invoke-WebRequest -Method GET -TimeoutSec 120 'https://www.example.com' -Body $HashTable -UseBasicParsing\n $response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error" => cmd.call("http-windows-remote-head"), "\n$body = \n $Body = $body | ConvertFrom-Json\n #convert to hashtable\n $HashTable = @{}\n foreach ($property in $Body.PSObject.Properties) {\n $HashTable[$property.Name] = $property.Value\n }\n $response = Invoke-WebRequest -Method GET -TimeoutSec 120 'https://www.example.com' -Body $HashTable -UseBasicParsing\n $response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error" => cmd.call("http-windows-remote-head"),
"\n$body = \n $Body = $body | ConvertFrom-Json\n #convert to hashtable\n $HashTable = @{}\n foreach ($property in $Body.PSObject.Properties) {\n $HashTable[$property.Name] = $property.Value\n }\n $response = Invoke-WebRequest -Method GET -TimeoutSec 120 -Headers @{ 'X-Test-Header' = 'test/value'; 'foo' = 'bar'} 'https://www.example.com' -Body $HashTable -UseBasicParsing\n $response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error" => cmd.call("http-windows-remote-get-headers"),
"\n$body = '{ \"a\" : \"1\", \"b\" : \"five\" }'\n $Body = $body | ConvertFrom-Json\n #convert to hashtable\n $HashTable = @{}\n foreach ($property in $Body.PSObject.Properties) {\n $HashTable[$property.Name] = $property.Value\n }\n $response = Invoke-WebRequest -Method POST -TimeoutSec 120 'https://www.example.com' -Body $HashTable -UseBasicParsing\n $response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error" => cmd.call("http-windows-remote-head"), "\n$body = '{ \"a\" : \"1\", \"b\" : \"five\" }'\n $Body = $body | ConvertFrom-Json\n #convert to hashtable\n $HashTable = @{}\n foreach ($property in $Body.PSObject.Properties) {\n $HashTable[$property.Name] = $property.Value\n }\n $response = Invoke-WebRequest -Method POST -TimeoutSec 120 'https://www.example.com' -Body $HashTable -UseBasicParsing\n $response | Select-Object -Property * | ConvertTo-json # We use `Select-Object -Property * ` to get around an odd PowerShell error" => cmd.call("http-windows-remote-head"),
# elasticsearch resource # elasticsearch resource
"curl -H 'Content-Type: application/json' http://localhost:9200/_nodes" => cmd.call("elasticsearch-cluster-nodes-default"), "curl -H 'Content-Type: application/json' http://localhost:9200/_nodes" => cmd.call("elasticsearch-cluster-nodes-default"),

View file

@ -1,2 +1,3 @@
default["osprepare"]["docker"] = false default["osprepare"]["docker"] = false
default["osprepare"]["application"] = true default["osprepare"]["application"] = true
default["osprepare"]["nftables"] = false

View file

@ -26,7 +26,11 @@ include_recipe("os_prepare::service")
include_recipe("os_prepare::package") include_recipe("os_prepare::package")
include_recipe("os_prepare::registry_key") include_recipe("os_prepare::registry_key")
include_recipe("os_prepare::iis") include_recipe("os_prepare::iis")
include_recipe("os_prepare::iptables") if node["osprepare"]["nftables"]
include_recipe("os_prepare::nftables")
else
include_recipe("os_prepare::iptables")
end
include_recipe("os_prepare::x509") include_recipe("os_prepare::x509")
include_recipe("os_prepare::dh_params") include_recipe("os_prepare::dh_params")

View file

@ -0,0 +1,12 @@
if platform_family?("rhel", "debian", "fedora", "suse")
package "nftables"
execute "nft flush ruleset"
execute "nft add table inet filter"
execute 'nft add chain inet filter INPUT \{ type filter hook input priority 0\; policy accept\; \}'
execute "nft add chain inet filter derby-cognos-web"
execute 'nft add set inet filter OPEN_PORTS \{ type ipv4_addr\; size 65536\; flags interval\; \}'
execute 'nft add rule inet filter INPUT iifname eth0 tcp dport 80 accept comment \"http on 80\"'
execute "nft add rule inet filter INPUT jump derby-cognos-web"
execute 'nft add rule inet filter derby-cognos-web tcp dport 80 accept comment "derby-cognos-web"'
execute 'nft add element inet filter OPEN_PORTS \{ 1.1.1.1/32 \}'
end

View file

@ -2,6 +2,10 @@ unless ENV['IPV6']
$stderr.puts "\033[1;33mTODO: Not running #{__FILE__.split("/").last} because we are running without IPv6\033[0m" $stderr.puts "\033[1;33mTODO: Not running #{__FILE__.split("/").last} because we are running without IPv6\033[0m"
return return
end end
if ENV['NFTABLES']
$stderr.puts "\033[1;33mTODO: Not running #{__FILE__.split("/").last} because we are running with nftables\033[0m"
return
end
case os[:family] case os[:family]
when 'ubuntu', 'fedora', 'debian', 'suse' when 'ubuntu', 'fedora', 'debian', 'suse'

View file

@ -1,3 +1,8 @@
if ENV['NFTABLES']
$stderr.puts "\033[1;33mTODO: Not running #{__FILE__.split("/").last} because we are running with nftables\033[0m"
return
end
case os[:family] case os[:family]
when 'ubuntu', 'fedora', 'debian', 'suse' when 'ubuntu', 'fedora', 'debian', 'suse'
describe iptables do describe iptables do

View file

@ -0,0 +1,23 @@
unless ENV['NFTABLES']
$stderr.puts "\033[1;33mTODO: Not running #{__FILE__.split("/").last} because we are running with iptables\033[0m"
return
end
case os[:family]
when 'ubuntu', 'fedora', 'debian', 'suse', 'redhat', 'centos'
describe nftables(family: 'inet', table: 'filter', chain: 'INPUT') do
its('type') { should eq 'filter' }
its('hook') { should eq 'input' }
its('prio') { should eq 0 }
its('policy') { should eq 'accept' }
it { should have_rule('iifname "eth0" tcp dport 80 accept comment "http on 80"') }
it { should_not have_rule('iifname "eth1" tcp dport 80 accept') }
end
describe nftables(family: 'inet', table: 'filter', set: 'OPEN_PORTS') do
its('type') { should eq 'ipv4_addr' }
its('flags') { should include 'interval' }
it { should have_element('1.1.1.1') }
it { should_not have_element('2.2.2.2') }
end
end

View file

@ -323,7 +323,7 @@ class PluginLoaderTests < Minitest::Test
skip "not valid in this env" unless using_bundler? skip "not valid in this env" unless using_bundler?
with_empty_registry do with_empty_registry do
exp = %i{ train-aws train-habitat train-winrm } exp = %i{ train-aws train-habitat train-kubernetes train-winrm }
exp_err = "" exp_err = ""
assert_detect_system_plugins exp, exp_err do |loader| assert_detect_system_plugins exp, exp_err do |loader|

View file

@ -35,7 +35,7 @@ describe "Inspec::Resources::Host" do
resource = MockLoader.new(:windows).load_resource("host", "microsoft.com") resource = MockLoader.new(:windows).load_resource("host", "microsoft.com")
_(resource.resolvable?).must_equal true _(resource.resolvable?).must_equal true
_(resource.reachable?).must_equal false _(resource.reachable?).must_equal false
_(resource.ipaddress).must_equal ["134.170.188.221", "2404:6800:4009:827::200e"] _(resource.ipaddress).must_equal ["134.170.185.46", "134.170.188.221", "2404:6800:4009:827::200e"]
_(resource.to_s).must_equal "Host microsoft.com" _(resource.to_s).must_equal "Host microsoft.com"
_(resource.resource_id).must_equal "microsoft.com" _(resource.resource_id).must_equal "microsoft.com"
end end
@ -107,7 +107,7 @@ describe "Inspec::Resources::Host" do
resource = MockLoader.new(:windows).load_resource("host", "microsoft.com", port: 1234, protocol: "tcp") resource = MockLoader.new(:windows).load_resource("host", "microsoft.com", port: 1234, protocol: "tcp")
_(resource.resolvable?).must_equal true _(resource.resolvable?).must_equal true
_(resource.reachable?).must_equal true _(resource.reachable?).must_equal true
_(resource.ipaddress).must_equal ["134.170.188.221", "2404:6800:4009:827::200e"] _(resource.ipaddress).must_equal ["134.170.185.46", "134.170.188.221", "2404:6800:4009:827::200e"]
_(resource.to_s).must_equal "Host microsoft.com port 1234 proto tcp" _(resource.to_s).must_equal "Host microsoft.com port 1234 proto tcp"
_(resource.resource_id).must_equal "microsoft.com-1234-tcp" _(resource.resource_id).must_equal "microsoft.com-1234-tcp"
end end
@ -379,7 +379,7 @@ describe Inspec::Resources::UnixHostProvider do
it "checks ipv4_address and ipv6_address properties on windows" do it "checks ipv4_address and ipv6_address properties on windows" do
resource = MockLoader.new(:windows).load_resource("host", "microsoft.com") resource = MockLoader.new(:windows).load_resource("host", "microsoft.com")
_(resource.ipv4_address).must_equal ["134.170.188.221"] _(resource.ipv4_address).must_equal ["134.170.185.46", "134.170.188.221"]
_(resource.ipv4_address).must_include "134.170.188.221" _(resource.ipv4_address).must_include "134.170.188.221"
_(resource.ipv6_address).must_equal ["2404:6800:4009:827::200e"] _(resource.ipv6_address).must_equal ["2404:6800:4009:827::200e"]
_(resource.ipv6_address).must_include "2404:6800:4009:827::200e" _(resource.ipv6_address).must_include "2404:6800:4009:827::200e"

View file

@ -314,4 +314,25 @@ describe "Inspec::Resources::Http" do
_(worker.resource_id).must_equal "https://www.example.com" _(worker.resource_id).must_equal "https://www.example.com"
end end
end end
describe "Windows-Get-With-Headers" do
let(:backend) { MockLoader.new(:windows).backend }
let(:http_method) { "GET" }
let(:url) { "https://www.example.com" }
let(:opts) { { headers: { "X-Test-Header" => "test/value", "foo" => "bar" } } }
let(:worker) { Inspec::Resources::Http::Worker::Remote.new(backend, http_method, url, opts) }
describe "simple HTTP request with headers" do
it "returns correct data" do
Inspec::Resources::Cmd.any_instance
.stubs(:exist?)
.returns(true)
_(worker.status).must_equal 200
_(worker.response_headers["Content-Type"]).must_equal "text/html; charset=UTF-8"
_(worker.resource_id).must_equal "https://www.example.com"
expected_cmd = "Invoke-WebRequest -Method GET -TimeoutSec 120 -Headers @{ 'X-Test-Header' = 'test/value'; 'foo' = 'bar'} 'https://www.example.com' -Body $HashTable -UseBasicParsing"
_(worker.send(:load_powershell_command)).must_include expected_cmd
end
end
end
end end

View file

@ -0,0 +1,27 @@
require "helper"
require "inspec/resource"
require "inspec/resources/nftables"
describe "Inspec::Resources::NfTables" do
# ubuntu
it "verify nftables chain on ubuntu" do
resource = MockLoader.new(:ubuntu).load_resource("nftables", { family: "inet", table: "filter", chain: "INPUT" })
_(resource.type).must_equal "filter"
_(resource.hook).must_equal "input"
_(resource.prio).must_equal 0
_(resource.policy).must_equal "accept"
_(resource.has_rule?('iifname "eth0" tcp dport 80 accept comment "http on 80"')).must_equal true
_(resource.has_rule?('iifname "eth1" tcp dport 80 accept')).must_equal false
_(resource.resource_id).must_equal "nftables (family: inet table: filter chain: INPUT )"
end
it "verify nftables set on ubuntu" do
resource = MockLoader.new(:ubuntu).load_resource("nftables", { family: "inet", table: "filter", set: "OPEN_PORTS" })
_(resource.type).must_equal "ipv4_addr"
_(resource.flags).must_include "interval"
_(resource.size).must_equal 65536
_(resource.has_element?("1.1.1.1")).must_equal true
_(resource.has_element?("2.2.2.2")).must_equal false
_(resource.resource_id).must_equal "nftables (family: inet table: filter set: OPEN_PORTS)"
end
end

View file

@ -39,7 +39,11 @@ describe "Inspec::Resources::PostgresSession" do
end end
it "verify postgres_session create_psql_cmd in socket connection" do it "verify postgres_session create_psql_cmd in socket connection" do
resource = load_resource("postgres_session", "myuser", "mypass", "127.0.0.1", 5432, "/var/run/postgresql") resource = load_resource("postgres_session", "myuser", "mypass", "127.0.0.1", 5432, "/var/run/postgresql")
_(resource.send(:create_psql_cmd, "SELECT * FROM STUDENTS;", ["testdb"])).must_equal "psql -d postgresql://myuser:mypass@/testdb?host=/var/run/postgresql -A -t -w -c SELECT\\ \\*\\ FROM\\ STUDENTS\\;" _(resource.send(:create_psql_cmd, "SELECT * FROM STUDENTS;", ["testdb"])).must_equal "psql -d postgresql://myuser:mypass@/testdb?host=/var/run/postgresql -p 5432 -A -t -w -c SELECT\\ \\*\\ FROM\\ STUDENTS\\;"
end
it "verify postgres_session create_psql_cmd in socket connection" do
resource = load_resource("postgres_session", "myuser", "mypass", "127.0.0.1", 1234, "/var/run/postgresql")
_(resource.send(:create_psql_cmd, "SELECT * FROM STUDENTS;", ["testdb"])).must_equal "psql -d postgresql://myuser:mypass@/testdb?host=/var/run/postgresql -p 1234 -A -t -w -c SELECT\\ \\*\\ FROM\\ STUDENTS\\;"
end end
it "fails when no connection established in linux" do it "fails when no connection established in linux" do

View file

@ -4,6 +4,7 @@ require "helper"
require "inspec/secrets" require "inspec/secrets"
require "inspec/runner" require "inspec/runner"
require "inspec/fetcher/mock" require "inspec/fetcher/mock"
require "inspec/utils/licensing_config"
describe Inspec::Runner do describe Inspec::Runner do
let(:runner) { Inspec::Runner.new({ command_runner: :generic, reporter: [] }) } let(:runner) { Inspec::Runner.new({ command_runner: :generic, reporter: [] }) }
@ -73,6 +74,7 @@ describe Inspec::Runner do
describe "testing runner.run exit codes" do describe "testing runner.run exit codes" do
it "returns proper exit code when no profile is added" do it "returns proper exit code when no profile is added" do
WebMock.allow_net_connect!
_(runner.run).must_equal 0 _(runner.run).must_equal 0
end end
end end

View file

@ -0,0 +1,18 @@
require "helper"
require "inspec/utils/licensing_config"
describe "ChefLicensing::Config" do
it "returns the default chef product name as foo" do
expect(ChefLicensing::Config.chef_product_name).must_equal("InSpec")
end
it "returns the default chef_entitlement_id" do
expect(ChefLicensing::Config.chef_entitlement_id).must_equal("3ff52c37-e41f-4f6c-ad4d-365192205968")
end
it "returns the default chef_executable_name" do
expect(ChefLicensing::Config.chef_executable_name).must_equal("inspec")
end
# TODO: Need to add the test for license_server_url.
end

View file

@ -101,4 +101,9 @@ describe "SimpleConfig Default Parser" do
cur = SimpleConfig.new("1:2:3", assignment_regex: /^(.*):(.*):(.*)$/, key_values: 4) cur = SimpleConfig.new("1:2:3", assignment_regex: /^(.*):(.*):(.*)$/, key_values: 4)
_(cur.params).must_equal({ "1" => ["2", "3", nil, nil] }) _(cur.params).must_equal({ "1" => ["2", "3", nil, nil] })
end end
it "supports :mulitple values and returns array of values" do
cur = SimpleConfig.new("foo: bar\nbiz: baz boz bop\nbiz: cdsdcs cdscs csc\nbiz: ada\nfoz: foo [!deny] bar", assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/, multiple_values: true, multiple_value_regex: /\s+/)
_(cur.params).must_equal({ "foo" => ["bar"], "biz" => %w{baz boz bop cdsdcs cdscs csc ada}, "foz" => ["foo", "[!deny]", "bar"] })
end
end end