mirror of
https://github.com/inspec/inspec
synced 2024-11-15 01:17:08 +00:00
Merge pull request #1482 from chef/adamleff/add-crontab-resource
Adding new crontab resource
This commit is contained in:
commit
09cc7c3a7e
7 changed files with 244 additions and 1 deletions
62
docs/resources/crontab.md.erb
Normal file
62
docs/resources/crontab.md.erb
Normal file
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
title: About the crontab Resource
|
||||
---
|
||||
|
||||
# crontab
|
||||
|
||||
Use the `crontab` InSpec audit resource to test the crontab entries for a particular user on the system.
|
||||
|
||||
## Syntax
|
||||
|
||||
A `crontab` resource block declares a user (which defaults to the current user, if not specified), and then the details to be tested, such as the schedule elements for each crontab entry or the commands itself:
|
||||
|
||||
describe crontab do
|
||||
its('commands') { should include '/some/scheduled/task.sh' }
|
||||
end
|
||||
|
||||
## 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 audit resource.
|
||||
|
||||
### Test that root's crontab has a particular command
|
||||
|
||||
describe crontab('root') do
|
||||
its('commands') { should include '/path/to/some/script' }
|
||||
end
|
||||
|
||||
### Test that myuser's crontab entry for command '/home/myuser/build.sh' runs every minute
|
||||
|
||||
describe crontab('myuser').commands('/home/myuser/build.sh') do
|
||||
its('hours') { should cmp '*' }
|
||||
its('minutes') { should cmp '*' }
|
||||
end
|
||||
|
||||
### Test that the logged-in user's crontab has no tasks set to run on every hour and every minute
|
||||
|
||||
describe crontab.where({'hour' => '*', 'minute' => '*'}) do
|
||||
its('entries.length') { should cmp '0' }
|
||||
end
|
|
@ -79,6 +79,7 @@ require 'resources/bash'
|
|||
require 'resources/bond'
|
||||
require 'resources/bridge'
|
||||
require 'resources/command'
|
||||
require 'resources/crontab'
|
||||
require 'resources/directory'
|
||||
require 'resources/etc_group'
|
||||
require 'resources/file'
|
||||
|
|
83
lib/resources/crontab.rb
Normal file
83
lib/resources/crontab.rb
Normal file
|
@ -0,0 +1,83 @@
|
|||
# encoding: utf-8
|
||||
# author: Adam Leff
|
||||
|
||||
require 'utils/parser'
|
||||
require 'utils/filter'
|
||||
|
||||
module Inspec::Resources
|
||||
class Crontab < Inspec.resource(1)
|
||||
name 'crontab'
|
||||
desc 'Use the crontab InSpec audit resource to test the contents of the crontab for a given user which contains information about scheduled tasks owned by that user.'
|
||||
example "
|
||||
describe crontab('root') do
|
||||
its('commands') { should include '/path/to/some/script' }
|
||||
end
|
||||
|
||||
describe crontab('myuser').commands('/home/myuser/build.sh') do
|
||||
its('hours') { should cmp '*' }
|
||||
its('minutes') { should cmp '*' }
|
||||
end
|
||||
|
||||
describe crontab.where({'hour' => '*', 'minute' => '*'}) do
|
||||
its('entries.length') { should cmp '0' }
|
||||
end
|
||||
"
|
||||
|
||||
attr_reader :params
|
||||
|
||||
include CommentParser
|
||||
|
||||
def initialize(user = nil)
|
||||
@user = user
|
||||
@params = read_crontab
|
||||
|
||||
return skip_resource 'The `crontab` resource is not supported on your OS.' unless inspec.os.unix?
|
||||
end
|
||||
|
||||
def read_crontab
|
||||
inspec.command(crontab_cmd).stdout.lines.map { |l| parse_crontab_line(l) }.compact
|
||||
end
|
||||
|
||||
def parse_crontab_line(l)
|
||||
data, = parse_comment_line(l, comment_char: '#', standalone_comments: false)
|
||||
return nil if data.nil? || data.empty?
|
||||
|
||||
elements = data.split(/\s+/, 6)
|
||||
{
|
||||
'minute' => elements.at(0),
|
||||
'hour' => elements.at(1),
|
||||
'day' => elements.at(2),
|
||||
'month' => elements.at(3),
|
||||
'weekday' => elements.at(4),
|
||||
'command' => elements.at(5),
|
||||
}
|
||||
end
|
||||
|
||||
def crontab_cmd
|
||||
@user.nil? ? 'crontab -l' : "crontab -l -u #{@user}"
|
||||
end
|
||||
|
||||
filter = FilterTable.create
|
||||
filter.add_accessor(:where)
|
||||
.add_accessor(:entries)
|
||||
.add(:minutes, field: 'minute')
|
||||
.add(:hours, field: 'hour')
|
||||
.add(:days, field: 'day')
|
||||
.add(:months, field: 'month')
|
||||
.add(:weekdays, field: 'weekday')
|
||||
.add(:commands, field: 'command')
|
||||
|
||||
# rebuild the crontab line from raw content
|
||||
filter.add(:content) { |t, _|
|
||||
t.entries.map do |e|
|
||||
[e.minute, e.hour, e.day, e.month, e.weekday, e.command].join(' ')
|
||||
end.join("\n")
|
||||
}
|
||||
|
||||
filter.connect(self, :params)
|
||||
|
||||
def to_s
|
||||
@user.nil? ? 'crontab for current user' : "crontab for user #{@user}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -274,7 +274,11 @@ class MockLoader
|
|||
"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'),
|
||||
'modinfo -F version dhcp' => cmd.call('modinfo-f-version-dhcp')
|
||||
'modinfo -F version dhcp' => cmd.call('modinfo-f-version-dhcp'),
|
||||
# crontab display for root / current user
|
||||
'crontab -l' => cmd.call('crontab-root'),
|
||||
# crontab display for non-current user
|
||||
'crontab -l -u foouser' => cmd.call('crontab-foouser')
|
||||
}
|
||||
|
||||
@backend
|
||||
|
|
5
test/unit/mock/cmd/crontab-foouser
Normal file
5
test/unit/mock/cmd/crontab-foouser
Normal file
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# This is a sample crontab file for unit testing, for user 'foouser'
|
||||
#
|
||||
|
||||
* * * * * /path/to/script3
|
10
test/unit/mock/cmd/crontab-root
Normal file
10
test/unit/mock/cmd/crontab-root
Normal file
|
@ -0,0 +1,10 @@
|
|||
#
|
||||
# This is a sample crontab file for unit testing
|
||||
#
|
||||
|
||||
|
||||
# entry number 1
|
||||
0 2 11 9 4 /path/to/script1
|
||||
|
||||
# entry number 2
|
||||
1 3 12 10 5 /path/to/script2 arg1 arg2
|
78
test/unit/resources/crontab_test.rb
Normal file
78
test/unit/resources/crontab_test.rb
Normal file
|
@ -0,0 +1,78 @@
|
|||
# encoding: utf-8
|
||||
# author: Adam Leff
|
||||
|
||||
require 'helper'
|
||||
require 'inspec/resource'
|
||||
|
||||
describe 'Inspec::Resources::Crontab' do
|
||||
let(:crontab) { load_resource('crontab') }
|
||||
|
||||
it 'retrieve minutes via field' do
|
||||
_(crontab.minutes).must_equal [ '0', '1' ]
|
||||
end
|
||||
|
||||
it 'retrieve hours via field' do
|
||||
_(crontab.hours).must_equal [ '2', '3' ]
|
||||
end
|
||||
|
||||
it 'retrieve days via field' do
|
||||
_(crontab.days).must_equal [ '11', '12' ]
|
||||
end
|
||||
|
||||
it 'retrieve months via field' do
|
||||
_(crontab.months).must_equal [ '9', '10' ]
|
||||
end
|
||||
|
||||
it 'retrieve weekdays via field' do
|
||||
_(crontab.weekdays).must_equal [ '4', '5' ]
|
||||
end
|
||||
|
||||
it 'retrieve commands via field' do
|
||||
_(crontab.commands).must_equal [ '/path/to/script1', '/path/to/script2 arg1 arg2' ]
|
||||
end
|
||||
|
||||
it 'returns all params of the file' do
|
||||
_(crontab.params).must_equal([
|
||||
{
|
||||
'minute' => '0',
|
||||
'hour' => '2',
|
||||
'day' => '11',
|
||||
'month' => '9',
|
||||
'weekday' => '4',
|
||||
'command' => '/path/to/script1',
|
||||
},
|
||||
{
|
||||
'minute' => '1',
|
||||
'hour' => '3',
|
||||
'day' => '12',
|
||||
'month' => '10',
|
||||
'weekday' => '5',
|
||||
'command' => '/path/to/script2 arg1 arg2'
|
||||
},
|
||||
])
|
||||
end
|
||||
|
||||
it 'prints a nice to_s string' do
|
||||
_(crontab.to_s).must_equal "crontab for current user"
|
||||
end
|
||||
|
||||
describe 'filter by command' do
|
||||
let(:entry) { crontab.commands(/script2/) }
|
||||
|
||||
it 'returns the correct content' do
|
||||
_(entry.content).must_equal '1 3 12 10 5 /path/to/script2 arg1 arg2'
|
||||
end
|
||||
|
||||
it 'prints a nice to_s string' do
|
||||
_(entry.to_s).must_equal 'crontab for current user with command == /script2/'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'query by user' do
|
||||
let(:crontab) { load_resource('crontab', 'foouser') }
|
||||
|
||||
it 'prints a user-specific to_s string' do
|
||||
_(crontab.to_s).must_equal 'crontab for user foouser'
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue