mirror of
https://github.com/inspec/inspec
synced 2024-11-23 13:13:22 +00:00
New Resource: Chocolatey Package (#2793)
* Adds chocolatey package resource * Adds docs for chocolatey_package resource * Differentiate chocolatey package from windows feature Suggested by @frezbo Signed-off-by: David Alexander <opensource@thelonelyghost.com>
This commit is contained in:
parent
3acbb47287
commit
3b97e16b97
6 changed files with 171 additions and 0 deletions
58
docs/resources/chocolatey_package.md.erb
Normal file
58
docs/resources/chocolatey_package.md.erb
Normal file
|
@ -0,0 +1,58 @@
|
|||
---
|
||||
title: About the chocolatey_package Resource
|
||||
platform: windows
|
||||
---
|
||||
|
||||
# chocolatey_package
|
||||
|
||||
Use the `chocolatey_package` InSpec audit resource to test if the named [Chocolatey](https://chocolatey.org/) package and/or package version is installed on the system.
|
||||
|
||||
<br>
|
||||
|
||||
## Syntax
|
||||
|
||||
A `chocolatey_package` resource block declares the name of a Chocolatey package to be tested:
|
||||
|
||||
describe chocolatey_package('name') do
|
||||
it { should be_installed }
|
||||
end
|
||||
|
||||
where
|
||||
|
||||
* `('name')` must specify the (case-sensitive) name of a package, such as `'nssm'`
|
||||
* `be_installed` is a valid matcher for this resource
|
||||
|
||||
<br>
|
||||
|
||||
## Examples
|
||||
|
||||
The following examples show how to use this InSpec audit resource
|
||||
|
||||
### Test if NSSM version 2.1.0 is installed
|
||||
|
||||
describe chocolatey_package('nssm') do
|
||||
it { should be_installed }
|
||||
its('version') { should eq '2.1.0' }
|
||||
end
|
||||
|
||||
## Matchers
|
||||
|
||||
For a full list of available matchers, please visit our [matchers page](https://www.inspec.io/docs/reference/matchers).
|
||||
|
||||
### be_installed
|
||||
|
||||
The `be_installed` matcher tests if the named package is installed at all.
|
||||
|
||||
it { should be_installed }
|
||||
|
||||
### version
|
||||
|
||||
The `version` matcher tests if the named package version is on the system:
|
||||
|
||||
its('version') { should eq '2.1.0' }
|
||||
|
||||
You can also use the `cmp OPERATOR` matcher to perform comparisons using the version attribute:
|
||||
|
||||
its('version') { should cmp >= '1.93.4-13debug84' }
|
||||
|
||||
`cmp` understands version numbers using Gem::Version, and can use the operators `==, <, <=, >=, and >`. It will compare versions by each segment, not as a string - so '7.4' is smaller than '7.30', for example.
|
|
@ -95,6 +95,7 @@ require 'resources/auditd_conf'
|
|||
require 'resources/bash'
|
||||
require 'resources/bond'
|
||||
require 'resources/bridge'
|
||||
require 'resources/chocolatey_package'
|
||||
require 'resources/command'
|
||||
require 'resources/cran'
|
||||
require 'resources/cpan'
|
||||
|
|
78
lib/resources/chocolatey_package.rb
Normal file
78
lib/resources/chocolatey_package.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
# encoding: utf-8
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Check for Chocolatey packages to be installed
|
||||
module Inspec::Resources
|
||||
class ChocoPkg < Inspec.resource(1)
|
||||
name 'chocolatey_package'
|
||||
supports platform: 'windows'
|
||||
desc 'Use the chocolatey_package Inspec audit resource to test if the named package and/or package version is installed on the system.'
|
||||
example <<-EOH
|
||||
describe chocolatey_package('git') do
|
||||
it { should be_installed }
|
||||
its('version') { should eq '2.15.1' }
|
||||
end
|
||||
EOH
|
||||
|
||||
attr_reader :package_name
|
||||
|
||||
def initialize(package_name, _opts = {})
|
||||
raise 'Chocolatey is not installed' unless inspec.command('choco').exist?
|
||||
@package_name = package_name
|
||||
@cache = base_data.update(generate_cache)
|
||||
end
|
||||
|
||||
def installed?
|
||||
@cache[:installed]
|
||||
end
|
||||
|
||||
def info
|
||||
@cache.dup
|
||||
end
|
||||
|
||||
def respond_to_missing?(method_name, *)
|
||||
@cache.key?(method_name) || super
|
||||
end
|
||||
|
||||
def method_missing(method_name, *args, &block)
|
||||
if @cache.key?(method_name)
|
||||
@cache.fetch(method_name)
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Chocolatey package #{package_name}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def base_data
|
||||
{
|
||||
name: package_name,
|
||||
version: nil,
|
||||
installed: false,
|
||||
type: 'chocolatey',
|
||||
}
|
||||
end
|
||||
|
||||
def generate_cache
|
||||
command = <<-EOH
|
||||
(choco list --local-only --exact --include-programs --limit-output '#{package_name.gsub("'", "\`'")}') -Replace "\\|", "=" | ConvertFrom-StringData | ConvertTo-JSON
|
||||
EOH
|
||||
|
||||
cmd = inspec.powershell(command.strip)
|
||||
|
||||
return {} if cmd.exit_status != 0 || cmd.stdout.strip.empty?
|
||||
out = JSON.parse(cmd.stdout)
|
||||
|
||||
return {
|
||||
version: out.fetch(package_name),
|
||||
installed: true,
|
||||
}
|
||||
rescue JSON::ParserError, KeyError
|
||||
return {}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -261,6 +261,10 @@ class MockLoader
|
|||
'/test/path/pip show django' => cmd.call('pip-show-non-standard-django'),
|
||||
"Get-Package -Name 'Mozilla Firefox' | ConvertTo-Json" => cmd.call('get-package-firefox'),
|
||||
"Get-Package -Name 'Ruby 2.1.6-p336-x64' | ConvertTo-Json" => cmd.call('get-package-ruby'),
|
||||
'Get-Command "choco"' => empty.call,
|
||||
'bash -c \'type "choco"\'' => cmd_exit_1.call,
|
||||
'(choco list --local-only --exact --include-programs --limit-output \'nssm\') -Replace "\|", "=" | ConvertFrom-StringData | ConvertTo-JSON' => cmd.call('choco-list-nssm'),
|
||||
'(choco list --local-only --exact --include-programs --limit-output \'git\') -Replace "\|", "=" | ConvertFrom-StringData | ConvertTo-JSON' => empty.call,
|
||||
"New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Service -Value (Get-Service -Name 'dhcp'| Select-Object -Property Name, DisplayName, Status) -PassThru | Add-Member -MemberType NoteProperty -Name WMI -Value (Get-WmiObject -Class Win32_Service | Where-Object {$_.Name -eq 'dhcp' -or $_.DisplayName -eq 'dhcp'} | Select-Object -Property StartMode) -PassThru | ConvertTo-Json" => cmd.call('get-service-dhcp'),
|
||||
"New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Pip -Value (Invoke-Command -ScriptBlock {where.exe pip}) -PassThru | Add-Member -MemberType NoteProperty -Name Python -Value (Invoke-Command -ScriptBlock {where.exe python}) -PassThru | ConvertTo-Json" => cmd.call('get-windows-pip-package'),
|
||||
"Get-WindowsFeature | Where-Object {$_.Name -eq 'dhcp' -or $_.DisplayName -eq 'dhcp'} | Select-Object -Property Name,DisplayName,Description,Installed,InstallState | ConvertTo-Json" => cmd.call('get-windows-feature'),
|
||||
|
|
3
test/unit/mock/cmd/choco-list-nssm
Normal file
3
test/unit/mock/cmd/choco-list-nssm
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"nssm": "2.24.101"
|
||||
}
|
27
test/unit/resources/chocolatey_package_test.rb
Normal file
27
test/unit/resources/chocolatey_package_test.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# encoding: utf-8
|
||||
# author: David Alexander <opensource@thelonelyghost.com>
|
||||
|
||||
require 'helper'
|
||||
require 'inspec/resource'
|
||||
|
||||
def skip(*args)
|
||||
# noop
|
||||
end
|
||||
|
||||
describe 'Inspec::Resources::ChocoPkg' do
|
||||
it 'can parse output from `choco` when package is installed' do
|
||||
pkg = { name: 'git', installed: false, version: nil, type: 'chocolatey' }
|
||||
resource = MockLoader.new(:windows).load_resource('chocolatey_package', 'git')
|
||||
_(resource.installed?).must_equal pkg[:installed]
|
||||
_(resource.version).must_equal pkg[:version]
|
||||
_(resource.info).must_equal pkg
|
||||
end
|
||||
|
||||
it 'can parse output from `choco` when package not installed' do
|
||||
pkg = { name: 'nssm', installed: true, version: '2.24.101', type: 'chocolatey' }
|
||||
resource = MockLoader.new(:windows).load_resource('chocolatey_package', 'nssm')
|
||||
_(resource.installed?).must_equal pkg[:installed]
|
||||
_(resource.version).must_equal pkg[:version]
|
||||
_(resource.info).must_equal pkg
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue