Merge pull request #3895 from mitre/al-mitre/more-permissive-than

Signed-off-by: Aaron Lippold <lippold@gmail.com>
This commit is contained in:
Clinton Wolfe 2019-04-16 14:41:43 -04:00 committed by GitHub
commit 0c884b1415
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 60 additions and 0 deletions

View file

@ -119,6 +119,8 @@ not
Without the zero prefix for the octal value, InSpec will interpret it as the _decimal_ value 644, which is octal 1024 or `-----w-r-T`, and any test for a file that is `-rw-r--r--` will fail.
Note: see the [`be_more_permissive_than(mode)`](#be_more_permissive_than?(mode)) matcher for upper and lower bounds on file mode.
### mtime
The `mtime` property tests if the file modification time for the file matches the specified value. The mtime, where supported, is returned as the number of seconds since the epoch.
@ -541,3 +543,14 @@ The `exist` matcher tests if the named file exists:
The `have_mode` matcher tests if a file has a mode assigned to it:
it { should have_mode }
### `be_more_permissive_than(mode)`
`be_more_permissive_than(mode)` takes the maximum desired mode - in `octal format`
('0644' or '0777') - of your file as a `String` and returns a `Boolean`. It will
return `true` if your file has a mode with greater permissions than specified.
describe file('/etc/passwd') do
it { should_not be_more_permissive_than('0644') }
it { should be_more_permissive_than('0000') }
end

View file

@ -133,6 +133,35 @@ module Inspec::Resources
alias sticky? sticky
def more_permissive_than?(max_mode = nil)
raise Inspec::Exceptions::ResourceFailed, 'The file' + file.path + 'doesn\'t seem to exist' unless exist?
raise ArgumentError, 'You must proivde a value for the `maximum allowable permission` for the file.' if max_mode.nil?
raise ArgumentError, 'You must proivde the `maximum permission target` as a `String`, you provided: ' + max_mode.class.to_s unless max_mode.is_a?(String)
raise ArgumentError, 'The value of the `maximum permission target` should be a valid file mode in 4-ditgit octal format: for example, `0644` or `0777`' unless /(0)?([0-7])([0-7])([0-7])/.match?(max_mode)
# Using the files mode and a few bit-wise calculations we can ensure a
# file is no more permisive than desired.
#
# 1. Calculate the inverse of the desired mode (e.g., 0644) by XOR it with
# 0777 (all 1s). We are interested in the bits that are currently 0 since
# it indicates that the actual mode is more permissive than the desired mode.
# Conversely, we dont care about the bits that are currently 1 because they
# cannot be any more permissive and we can safely ignore them.
#
# 2. Calculate the above result of ANDing the actual mode and the inverse
# mode. This will determine if any of the bits that would indicate a more
# permissive mode are set in the actual mode.
#
# 3. If the result is 0000, the files mode is equal
# to or less permissive than the desired mode (PASS). Otherwise, the files
# mode is more permissive than the desired mode (FAIL).
max_mode = max_mode.rjust(4, '0')
binary_desired_mode = format('%04b', max_mode).to_i(2)
desired_mode_inverse = (binary_desired_mode ^ 0b111111111)
(desired_mode_inverse & file.mode).zero? ? false : true
end
def to_s
"File #{source_path}"
end
@ -212,6 +241,10 @@ module Inspec::Resources
raise '`check_file_permission_by_mask` is not supported on Windows'
end
def more_permissive_than?(*)
raise Inspec::Exceptions::ResourceSkipped, 'The `more_permissive_than?` matcher is not supported on your OS yet.'
end
def check_file_permission_by_user(access_type, user, path)
access_rule = translate_perm_names(access_type)
access_rule = convert_to_powershell_array(access_rule)

View file

@ -22,6 +22,7 @@ describe Inspec::Resources::FileResource do
resource.stubs(:file_permission_granted?).with('write', 'by_usergroup', 'by_specific_user').returns('test_result')
resource.stubs(:file_permission_granted?).with('execute', 'by_usergroup', 'by_specific_user').returns('test_result')
_(resource.content).must_equal 'content'
_(resource.more_permissive_than?('000').must_equal false)
_(resource.exist?).must_equal true
_(resource.mounted?).must_equal true
_(resource.to_s).must_equal 'File /fakepath/fakefile'
@ -34,6 +35,8 @@ describe Inspec::Resources::FileResource do
_(resource.suid).must_equal true
_(resource.sgid).must_equal true
_(resource.sticky).must_equal true
proc { resource.send(:more_permissive_than?, nil) }.must_raise(ArgumentError)
proc { resource.send(:more_permissive_than?, 0700) }.must_raise(ArgumentError)
end
it 'responds on Windows' do
resource = MockLoader.new(:windows).load_resource('file', 'C:/fakepath/fakefile')
@ -79,3 +82,14 @@ describe Inspec::Resources::FileResource do
proc { resource.send(:contain, nil) }.must_raise(RuntimeError)
end
end
describe Inspec::Resources::FileResource do
let(:file) { stub(unix_mode_mask: 000, mode: 644) }
it 'responds on Ubuntu' do
resource = MockLoader.new(:ubuntu1404).load_resource('file', '/fakepath/fakefile')
_(resource.more_permissive_than?('755').must_equal false)
_(resource.more_permissive_than?('644').must_equal false)
_(resource.more_permissive_than?('640').must_equal true)
proc { resource.send(:more_permissive_than?, '0888') }.must_raise(ArgumentError)
end
end