mirror of
https://github.com/inspec/inspec
synced 2024-11-26 14:40:26 +00:00
8683c54510
* Search and replace filtertable methods to use new names, and rely on automatic methods * Remove spurious exists? matchers - see https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/exist-matcher * Revert removing exists? - we'll do it on a separate PR * Gah, didn't save before resolving conflict * Add back name column on aws cloudtrail trails Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
167 lines
6.1 KiB
Ruby
167 lines
6.1 KiB
Ruby
# encoding: utf-8
|
|
|
|
require 'utils/parser'
|
|
require 'utils/filter'
|
|
|
|
module Inspec::Resources
|
|
class Crontab < Inspec.resource(1)
|
|
name 'crontab'
|
|
supports platform: 'unix'
|
|
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(user: '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
|
|
|
|
describe crontab.where { command =~ /a partial command string/ } do
|
|
its('entries.length') { should cmp 1 }
|
|
end
|
|
|
|
describe crontab(path: '/etc/cron.d/some_crontab') do
|
|
its('commands') { should include '/path/to/some/script' }
|
|
end
|
|
"
|
|
|
|
attr_reader :params
|
|
|
|
include CommentParser
|
|
|
|
def initialize(opts = nil)
|
|
if opts.respond_to?(:fetch)
|
|
Hash[opts.map { |k, v| [k.to_sym, v] }]
|
|
@user = opts.fetch(:user, nil)
|
|
@path = opts.fetch(:path, nil)
|
|
raise Inspec::Exceptions::ResourceFailed, 'A user or path must be supplied.' if @user.nil? && @path.nil?
|
|
else
|
|
@user = opts
|
|
@path = nil
|
|
end
|
|
@params = read_crontab
|
|
end
|
|
|
|
def read_crontab
|
|
ct = is_system_crontab? ? inspec.file(@path).content : inspec.command(crontab_cmd).stdout
|
|
ct.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?
|
|
|
|
is_system_crontab? ? parse_system_crontab(data) : parse_user_crontab(data)
|
|
end
|
|
|
|
def crontab_cmd
|
|
@user.nil? ? 'crontab -l' : "crontab -l -u #{@user}"
|
|
end
|
|
|
|
filter = FilterTable.create
|
|
filter.register_column(:minutes, field: 'minute')
|
|
.register_column(:hours, field: 'hour')
|
|
.register_column(:days, field: 'day')
|
|
.register_column(:months, field: 'month')
|
|
.register_column(:weekdays, field: 'weekday')
|
|
.register_column(:user, field: 'user')
|
|
.register_column(:commands, field: 'command')
|
|
|
|
# rebuild the crontab line from raw content
|
|
filter.register_custom_property(:content) { |t, _|
|
|
t.entries.map do |e|
|
|
[e.minute, e.hour, e.day, e.month, e.weekday, e.user, e.command].compact.join(' ')
|
|
end.join("\n")
|
|
}
|
|
|
|
filter.install_filter_methods_on_resource(self, :params)
|
|
|
|
def to_s
|
|
if is_system_crontab?
|
|
"crontab for path #{@path}"
|
|
elsif is_user_crontab?
|
|
"crontab for user #{@user}"
|
|
else
|
|
'crontab for current user'
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def is_system_crontab?
|
|
!@path.nil?
|
|
end
|
|
|
|
def is_user_crontab?
|
|
!@user.nil?
|
|
end
|
|
|
|
def parse_system_crontab(data)
|
|
case data
|
|
when /@hourly .*/
|
|
elements = data.split(/\s+/, 3)
|
|
{ 'minute' => '0', 'hour' => '*', 'day' => '*', 'month' => '*', 'weekday' => '*', 'user' => elements.at(1), 'command' => elements.at(2) }
|
|
when /@(midnight|daily) .*/
|
|
elements = data.split(/\s+/, 3)
|
|
{ 'minute' => '0', 'hour' => '0', 'day' => '*', 'month' => '*', 'weekday' => '*', 'user' => elements.at(1), 'command' => elements.at(2) }
|
|
when /@weekly .*/
|
|
elements = data.split(/\s+/, 3)
|
|
{ 'minute' => '0', 'hour' => '0', 'day' => '*', 'month' => '*', 'weekday' => '0', 'user' => elements.at(1), 'command' => elements.at(2) }
|
|
when /@monthly ./
|
|
elements = data.split(/\s+/, 3)
|
|
{ 'minute' => '0', 'hour' => '0', 'day' => '1', 'month' => '*', 'weekday' => '*', 'user' => elements.at(1), 'command' => elements.at(2) }
|
|
when /@(annually|yearly) .*/
|
|
elements = data.split(/\s+/, 3)
|
|
{ 'minute' => '0', 'hour' => '0', 'day' => '1', 'month' => '1', 'weekday' => '*', 'user' => elements.at(1), 'command' => elements.at(2) }
|
|
when /@reboot .*/
|
|
elements = data.split(/\s+/, 3)
|
|
{ 'minute' => '-1', 'hour' => '-1', 'day' => '-1', 'month' => '-1', 'weekday' => '-1', 'user' => elements.at(1), 'command' => elements.at(2) }
|
|
else
|
|
elements = data.split(/\s+/, 7)
|
|
{
|
|
'minute' => elements.at(0),
|
|
'hour' => elements.at(1),
|
|
'day' => elements.at(2),
|
|
'month' => elements.at(3),
|
|
'weekday' => elements.at(4),
|
|
'user' => elements.at(5),
|
|
'command' => elements.at(6),
|
|
}
|
|
end
|
|
end
|
|
|
|
def parse_user_crontab(data)
|
|
case data
|
|
when /@hourly .*/
|
|
{ 'minute' => '0', 'hour' => '*', 'day' => '*', 'month' => '*', 'weekday' => '*', 'user' => @user, 'command' => data.split(/\s+/, 2).at(1) }
|
|
when /@(midnight|daily) .*/
|
|
{ 'minute' => '0', 'hour' => '0', 'day' => '*', 'month' => '*', 'weekday' => '*', 'user' => @user, 'command' => data.split(/\s+/, 2).at(1) }
|
|
when /@weekly .*/
|
|
{ 'minute' => '0', 'hour' => '0', 'day' => '*', 'month' => '*', 'weekday' => '0', 'user' => @user, 'command' => data.split(/\s+/, 2).at(1) }
|
|
when /@monthly ./
|
|
{ 'minute' => '0', 'hour' => '0', 'day' => '1', 'month' => '*', 'weekday' => '*', 'user' => @user, 'command' => data.split(/\s+/, 2).at(1) }
|
|
when /@(annually|yearly) .*/
|
|
{ 'minute' => '0', 'hour' => '0', 'day' => '1', 'month' => '1', 'weekday' => '*', 'user' => @user, 'command' => data.split(/\s+/, 2).at(1) }
|
|
when /@reboot .*/
|
|
{ 'minute' => '-1', 'hour' => '-1', 'day' => '-1', 'month' => '-1', 'weekday' => '-1', 'user' => @user, 'command' => data.split(/\s+/, 2).at(1) }
|
|
else
|
|
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),
|
|
'user' => @user,
|
|
'command' => elements.at(5),
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|