mirror of
https://github.com/inspec/inspec
synced 2024-09-21 06:51:56 +00:00
Merge pull request #1306 from username-is-already-taken2/digitalgaz/windows_task
Adding windows_task resource
This commit is contained in:
commit
a990d20fcd
8 changed files with 281 additions and 1 deletions
103
docs/resources/windows_task.md.erb
Normal file
103
docs/resources/windows_task.md.erb
Normal file
|
@ -0,0 +1,103 @@
|
|||
---
|
||||
title: About the windows_task Resource
|
||||
---
|
||||
|
||||
# windows_task
|
||||
|
||||
Use the `windows_task` Inspec audit resource to test a scheduled tasks configuration on a Windows platform.
|
||||
Microsoft and application vendors use scheduled tasks to perform a varity of system maintaince tasks but system administrators can schedule their own.
|
||||
|
||||
## Syntax
|
||||
|
||||
A `windows_task` resource block declares the name of the task (as its full path) and tests its configuration:
|
||||
|
||||
describe windows_task('task name uri' do
|
||||
its('parameter') { should eq 'value' }
|
||||
it { should be_enabled }
|
||||
end
|
||||
|
||||
where
|
||||
|
||||
* `'parameter'` must be a valid parameter defined within this resource ie `logon_mode`, `last_result`, `task_to_run`, `run_as_user`
|
||||
* `'value'` will be used to compare the value gather from your chosen parameter
|
||||
* `'be_enabled'` is an example of a valid matcher that checks the state of a task, other examples are `exist` or `be_disabled`
|
||||
|
||||
## Matchers
|
||||
|
||||
This InSpec audit resource has the following matchers:
|
||||
|
||||
### be
|
||||
|
||||
<%= partial "/shared/matcher_be" %>
|
||||
|
||||
### cmp
|
||||
|
||||
<%= partial "/shared/matcher_cmp" %>
|
||||
|
||||
### eq
|
||||
|
||||
<%= partial "/shared/matcher_eq" %>
|
||||
|
||||
### include
|
||||
|
||||
<%= partial "/shared/matcher_include" %>
|
||||
|
||||
### match
|
||||
|
||||
<%= partial "/shared/matcher_match" %>
|
||||
|
||||
## Examples
|
||||
|
||||
The following examples show how to use this InSpec resource.
|
||||
|
||||
### Test's that a task is enabled
|
||||
```
|
||||
describe windows_task('\Microsoft\Windows\Time Synchronization\SynchronizeTime') do
|
||||
it { should be_enabled }
|
||||
end
|
||||
```
|
||||
|
||||
### Test's that a task is disabled
|
||||
```
|
||||
describe windows_task('\Microsoft\Windows\AppID\PolicyConverter') do
|
||||
it { should be_disabled }
|
||||
end
|
||||
```
|
||||
|
||||
### Test's the configuration parameters of a task
|
||||
```
|
||||
describe windows_task('\Microsoft\Windows\AppID\PolicyConverter') do
|
||||
its('logon_mode') { should eq 'Interactive/Background' }
|
||||
its('last_result') { should eq '1' }
|
||||
its('task_to_run') { should cmp '%Windir%\system32\appidpolicyconverter.exe' }
|
||||
its('run_as_user') { should eq 'LOCAL SERVICE' }
|
||||
end
|
||||
```
|
||||
|
||||
### Test's that a task is defined
|
||||
```
|
||||
describe windows_task('\Microsoft\Windows\Defrag\ScheduledDefrag') do
|
||||
it { should exist }
|
||||
end
|
||||
```
|
||||
|
||||
## Gathering Tasknames
|
||||
Rather then use the GUI you can use the `schtasks.exe` to output a full list of tasks available on the system
|
||||
|
||||
`schtasks /query /FO list`
|
||||
|
||||
rather than use the `list` output you can use `CSV` if it is easier.
|
||||
|
||||
Please make sure you use the full TaskName (include the prefix `\`) within your control
|
||||
|
||||
```
|
||||
C:\>schtasks /query /FO list
|
||||
...
|
||||
Folder: \Microsoft\Windows\Diagnosis
|
||||
HostName: XPS15
|
||||
TaskName: \Microsoft\Windows\Diagnosis\Scheduled
|
||||
Next Run Time: N/A
|
||||
Status: Ready
|
||||
Logon Mode: Interactive/Background
|
||||
...
|
||||
```
|
|
@ -125,6 +125,7 @@ require 'resources/sys_info'
|
|||
require 'resources/users'
|
||||
require 'resources/vbscript'
|
||||
require 'resources/windows_feature'
|
||||
require 'resources/windows_task'
|
||||
require 'resources/xinetd'
|
||||
require 'resources/wmi'
|
||||
require 'resources/yum'
|
||||
|
|
106
lib/resources/windows_task.rb
Normal file
106
lib/resources/windows_task.rb
Normal file
|
@ -0,0 +1,106 @@
|
|||
# encoding: utf-8
|
||||
# author: Gary Bright @username-is-already-taken2
|
||||
# author: Chris Beard @cdbeard2016
|
||||
module Inspec::Resources
|
||||
class WindowsTasks < Inspec.resource(1)
|
||||
name 'windows_task'
|
||||
desc 'Use the windows_task InSpec audit resource to test task schedules on Microsoft Windows.'
|
||||
example "
|
||||
describe windows_task('\\Microsoft\\Windows\\Time Synchronization\\SynchronizeTime') do
|
||||
it { should be_enabled }
|
||||
end
|
||||
|
||||
describe windows_task('\\Microsoft\\Windows\\AppID\\PolicyConverter') do
|
||||
it { should be_disabled }
|
||||
end
|
||||
|
||||
describe windows_task('\\Microsoft\\Windows\\Defrag\\ScheduledDefrag') do
|
||||
it { should exist }
|
||||
end
|
||||
|
||||
describe windows_task('\\Microsoft\\Windows\\AppID\\PolicyConverter') do
|
||||
its('logon_mode') { should eq 'Interactive/Background' }
|
||||
its('last_result') { should eq '1' }
|
||||
its('task_to_run') { should cmp '%Windir%\\system32\\appidpolicyconverter.exe' }
|
||||
its('run_as_user') { should eq 'LOCAL SERVICE' }
|
||||
end
|
||||
"
|
||||
|
||||
def initialize(taskuri)
|
||||
@taskuri = taskuri
|
||||
@cache = nil
|
||||
|
||||
# verify that this resource is only supported on Windows
|
||||
return skip_resource 'The `windows_task` resource is not supported on your OS.' unless inspec.os.windows?
|
||||
end
|
||||
|
||||
def exists?
|
||||
return true unless info.nil? || info[:uri].nil?
|
||||
false
|
||||
end
|
||||
|
||||
# rubocop:disable Style/WordArray
|
||||
def enabled?
|
||||
return false if info.nil? || info[:state].nil?
|
||||
['Ready', 'Running'].include?(info[:state])
|
||||
end
|
||||
|
||||
def disabled?
|
||||
return false if info.nil? || info[:state].nil?
|
||||
info[:scheduled_task_state] == 'Disabled' || info[:state] == 'Disabled'
|
||||
end
|
||||
|
||||
def logon_mode
|
||||
info[:logon_mode]
|
||||
end
|
||||
|
||||
def last_result
|
||||
info[:last_result]
|
||||
end
|
||||
|
||||
def task_to_run
|
||||
info[:task_to_run].to_s.strip
|
||||
end
|
||||
|
||||
def run_as_user
|
||||
info[:run_as_user]
|
||||
end
|
||||
|
||||
def type
|
||||
info[:type] unless info.nil?
|
||||
end
|
||||
|
||||
def info
|
||||
return @cache unless @cache.nil?
|
||||
# PowerShell v5 has Get-ScheduledTask cmdlet,
|
||||
# _using something with backward support to v3_
|
||||
# script = "Get-ScheduledTask | ? { $_.URI -eq '#{@taskuri}' } | Select-Object URI,@{N='State';E={$_.State.ToString()}} | ConvertTo-Json"
|
||||
|
||||
# Using schtasks as suggested by @modille but aligning property names to match cmdlet to future proof.
|
||||
script = "schtasks /query /v /fo csv /tn '#{@taskuri}' | ConvertFrom-Csv | Select @{N='URI';E={$_.TaskName}},@{N='State';E={$_.Status.ToString()}},'Logon Mode','Last Result','Task To Run','Run As User','Scheduled Task State' | ConvertTo-Json -Compress"
|
||||
|
||||
cmd = inspec.powershell(script)
|
||||
|
||||
begin
|
||||
params = JSON.parse(cmd.stdout)
|
||||
rescue JSON::ParserError => _e
|
||||
return nil
|
||||
end
|
||||
|
||||
@cache = {
|
||||
uri: params['URI'],
|
||||
state: params['State'],
|
||||
logon_mode: params['Logon Mode'],
|
||||
last_result: params['Last Result'],
|
||||
task_to_run: params['Task To Run'],
|
||||
run_as_user: params['Run As User'],
|
||||
scheduled_task_state: params['Scheduled Task State'],
|
||||
type: 'windows-task',
|
||||
}
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Windows Task '#{@taskuri}'"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -262,7 +262,11 @@ class MockLoader
|
|||
'hostname' => cmd.call('hostname'),
|
||||
# hostname windows
|
||||
'$env:computername' => cmd.call('$env-computername'),
|
||||
}
|
||||
# windows_task doesnt exist
|
||||
"schtasks /query /v /fo csv /tn 'does-not-exist' | ConvertFrom-Csv | Select @{N='URI';E={$_.TaskName}},@{N='State';E={$_.Status.ToString()}},'Logon Mode','Last Result','Task To Run','Run As User','Scheduled Task State' | ConvertTo-Json -Compress" => cmd.call('schtasks-error'),
|
||||
# windows_task exist
|
||||
"schtasks /query /v /fo csv /tn 'WeLovePizza' | ConvertFrom-Csv | Select @{N='URI';E={$_.TaskName}},@{N='State';E={$_.Status.ToString()}},'Logon Mode','Last Result','Task To Run','Run As User','Scheduled Task State' | ConvertTo-Json -Compress" => cmd.call('schtasks-success'),
|
||||
}
|
||||
|
||||
@backend
|
||||
end
|
||||
|
|
42
test/integration/default/windows_task_spec.rb
Normal file
42
test/integration/default/windows_task_spec.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
# author: Gary Bright
|
||||
# author: Chris Beard
|
||||
|
||||
return unless os.windows?
|
||||
|
||||
if os.release.to_f == 6.3
|
||||
# describe a task that exists on Windows 2012 R2
|
||||
# Test the task without the resource
|
||||
describe powershell('Test-Path $env:windir\System32\schtasks.exe') do
|
||||
its('stdout') { should match(/^True/) }
|
||||
end
|
||||
|
||||
describe windows_task('\Microsoft\Windows\AppID\PolicyConverter') do
|
||||
it { should exist }
|
||||
it { should be_disabled }
|
||||
its('logon_mode') { should eq 'Interactive/Background' }
|
||||
its('last_result') { should eq '1' }
|
||||
its('task_to_run') { should cmp '%Windir%\system32\appidpolicyconverter.exe' }
|
||||
its('run_as_user') { should eq 'LOCAL SERVICE' }
|
||||
end
|
||||
|
||||
describe windows_task('\Microsoft\Windows\AppID\SmartScreenSpecific') do
|
||||
it { should be_enabled }
|
||||
end
|
||||
|
||||
# Testing regular expressions
|
||||
describe windows_task('\Microsoft\Windows\AppID\VerifiedPublisherCertStoreCheck') do
|
||||
its('task_to_run') { should match(/^[a-zA-Z0-9%]*\\[a-zA-Z0-9]*\\[a-zA-Z0-9\.]*$/) }
|
||||
end
|
||||
end
|
||||
|
||||
if os.release.to_f == 6.1
|
||||
# describe a task that exists on Windows 2008 R2
|
||||
describe windows_task('\Microsoft\Windows\Defrag\ScheduledDefrag') do
|
||||
it { should exist }
|
||||
it { should be_enabled }
|
||||
its('logon_mode') { should eq 'Interactive/Background' }
|
||||
its('last_result') { should eq '1' }
|
||||
its('task_to_run') { should cmp '%windir%\system32\defrag.exe -c' }
|
||||
its('run_as_user') { should eq 'SYSTEM' }
|
||||
end
|
||||
end
|
1
test/unit/mock/cmd/schtasks-error
Normal file
1
test/unit/mock/cmd/schtasks-error
Normal file
|
@ -0,0 +1 @@
|
|||
ERROR: The system cannot find the path specified.
|
1
test/unit/mock/cmd/schtasks-success
Normal file
1
test/unit/mock/cmd/schtasks-success
Normal file
|
@ -0,0 +1 @@
|
|||
{"URI":"\\Microsoft\\Windows\\Time Synchronization\\SynchronizeTime","State":"Ready","Logon Mode":"Interactive/Background","Last Result":"1056","Task To Run":"%windir%\\system32\\sc.exe start w32time task_started","Run As User":"LOCAL SERVICE","Scheduled Task State":"Enabled"}
|
22
test/unit/resources/windows_task_test.rb
Normal file
22
test/unit/resources/windows_task_test.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
# author: Gary Bright
|
||||
# author: Chris Beard
|
||||
require 'helper'
|
||||
require 'inspec/resource'
|
||||
|
||||
describe 'Inspec::Resources::WindowsTasks' do
|
||||
it 'verify parsing a windows task that does not exist' do
|
||||
resource_fail = MockLoader.new(:windows).load_resource('windows_task', 'does-not-exist')
|
||||
_(resource_fail.exists?).must_equal false
|
||||
end
|
||||
it 'verify parsing a windows task which is valid' do
|
||||
resource = MockLoader.new(:windows).load_resource('windows_task', 'WeLovePizza')
|
||||
_(resource.enabled?).must_equal true
|
||||
_(resource.disabled?).must_equal false
|
||||
_(resource.logon_mode).must_equal 'Interactive/Background'
|
||||
_(resource.last_result).must_equal '1056'
|
||||
_(resource.task_to_run).must_equal '%windir%\\system32\\sc.exe start w32time task_started'
|
||||
_(resource.run_as_user).must_equal 'LOCAL SERVICE'
|
||||
_(resource.type).must_equal 'windows-task'
|
||||
_(resource.to_s).must_equal "Windows Task 'WeLovePizza'"
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue