mirror of
https://github.com/inspec/inspec
synced 2024-11-13 00:17:08 +00:00
aws_s3_bucket with modified interface (#183)
Signed-off-by: Matthew Dromazos <dromazmj@dukes.jmu.edu> Signed-off-by: Aaron Lippold <lippold@gmail.com> Signed-off-by: Sam Cornwell <14048146+samcornwell@users.noreply.github.com> Signed-off-by: Clinton Wolfe <clintoncwolfe@gmail.com>
This commit is contained in:
parent
e81937413b
commit
5ab68ecf03
10 changed files with 820 additions and 2 deletions
123
docs/resources/aws_s3_bucket.md
Normal file
123
docs/resources/aws_s3_bucket.md
Normal file
|
@ -0,0 +1,123 @@
|
|||
---
|
||||
title: About the aws_s3_bucket Resource
|
||||
---
|
||||
|
||||
# aws_s3_bucket
|
||||
|
||||
Use the `aws_s3_bucket` InSpec audit resource to test properties of a single AWS bucket.
|
||||
|
||||
To test properties of a multiple S3 buckets, use the `aws_s3_buckets` resource.
|
||||
|
||||
<br>
|
||||
|
||||
## Limitations
|
||||
|
||||
S3 bucket security is a complex matter. For details on how AWS evaluates requests for access, please see [the AWS documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/how-s3-evaluates-access-control.html). S3 buckets and the objects they contain support three different types of access control: bucket ACLs, bucket policies, and object ACLs.
|
||||
|
||||
As of January 2018, this resource supports evaluating bucket ACLs and bucket policies. We do not support evaluating object ACLs because it introduces scalability concerns in the AWS API; we recommend using AWS mechanisms such as CloudTrail and Config to detect insecure object ACLs.
|
||||
|
||||
In particular, users of the `be_public` matcher should carefully examine the conditions under which the matcher will detect an insecure bucket. See the `be_public` section under the Matchers section below.
|
||||
|
||||
## Syntax
|
||||
|
||||
An `aws_s3_bucket` resource block declares a bucket by name, and then lists tests to be performed.
|
||||
|
||||
describe aws_s3_bucket(bucket_name: 'test_bucket') do
|
||||
it { should exist }
|
||||
it { should_not be_public }
|
||||
end
|
||||
|
||||
describe aws_s3_bucket('test_bucket') do
|
||||
it { should exist }
|
||||
end
|
||||
|
||||
<br>
|
||||
|
||||
## Examples
|
||||
|
||||
The following examples show how to use this InSpec audit resource.
|
||||
|
||||
### Test a bucket's bucket-level ACL
|
||||
|
||||
describe aws_s3_bucket('test_bucket') do
|
||||
its('bucket_acl.count') { should eq 1 }
|
||||
end
|
||||
|
||||
### Check to see if a bucket has a bucket policy
|
||||
|
||||
describe aws_s3_bucket('test_bucket') do
|
||||
its('bucket_policy') { should be_empty }
|
||||
end
|
||||
|
||||
### Check to see if a bucket appears to be exposed to the public
|
||||
|
||||
# See Limitations section above
|
||||
describe aws_s3_bucket('test_bucket') do
|
||||
it { should_not be_public }
|
||||
end
|
||||
<br>
|
||||
|
||||
## Supported Properties
|
||||
|
||||
### region
|
||||
|
||||
The `region` property identifies the AWS Region in which the S3 bucket is located.
|
||||
|
||||
describe aws_s3_bucket('test_bucket') do
|
||||
# Check if the correct region is set
|
||||
its('region') { should eq 'us-east-1' }
|
||||
end
|
||||
|
||||
## Unsupported Properties
|
||||
|
||||
### bucket_acl
|
||||
|
||||
The `bucket_acl` property is a low-level property that lists the individual Bucket ACL grants that are in effect on the bucket. Other higher-level properties, such as be\_public, are more concise and easier to use. You can use the `bucket_acl` property to investigate which grants are in effect, causing be\_public to fail.
|
||||
|
||||
The value of bucket_acl is an Array of simple objects. Each object has a `permission` property and a `grantee` property. The `permission` property will be a string such as 'READ', 'WRITE' etc (See the [AWS documentation](https://docs.aws.amazon.com/sdkforruby/api/Aws/S3/Client.html#get_bucket_acl-instance_method) for a full list). The `grantee` property contains sub-properties, such as `type` and `uri`.
|
||||
|
||||
|
||||
bucket_acl = aws_s3_bucket('my-bucket')
|
||||
|
||||
# Look for grants to "AllUsers" (that is, the public)
|
||||
all_users_grants = bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AllUsers/
|
||||
end
|
||||
|
||||
# Look for grants to "AuthenticatedUsers" (that is, any authenticated AWS user - nearly public)
|
||||
auth_grants = bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AuthenticatedUsers/
|
||||
end
|
||||
|
||||
### bucket_policy
|
||||
|
||||
The `bucket_policy` is a low-level property that describes the IAM policy document controlling access to the bucket. The `bucket_policy` property returns a Ruby structure that you can probe to check for particular statements. We recommend using a higher-level property, such as `be_public`, which is concise and easier to implement in your policy files.
|
||||
|
||||
The `bucket_policy` property returns an Array of simple objects, each object being an IAM Policy Statement. See the [AWS documentation](https://docs.aws.amazon.com/AmazonS3/latest/dev/example-bucket-policies.html#example-bucket-policies-use-case-2) for details about the structure of this data.
|
||||
|
||||
If there is no bucket policy, this property will return an empty Array.
|
||||
|
||||
bucket_policy = aws_s3_bucket('my-bucket')
|
||||
|
||||
# Look for statements that allow the general public to do things
|
||||
# This may be a false positive; it's possible these statements
|
||||
# could be protected by conditions, such as IP restrictions.
|
||||
public_statements = bucket_policy.select do |s|
|
||||
s.effect == 'Allow' && s.principal == '*'
|
||||
end
|
||||
|
||||
## Matchers
|
||||
|
||||
This InSpec audit resource has the following special matchers. For a full list of available matchers (such as `exist`) please visit our [matchers page](https://www.inspec.io/docs/reference/matchers/).
|
||||
|
||||
### be_public
|
||||
|
||||
The `be_public` matcher tests if the bucket has potentially insecure access controls. This high-level matcher detects several insecure conditions, which may be enhanced in the future. Currently, the matcher reports an insecure bucket if any of the following conditions are met:
|
||||
|
||||
1. A bucket ACL grant exists for the 'AllUsers' group
|
||||
2. A bucket ACL grant exists for the 'AuthenticatedUsers' group
|
||||
3. A bucket policy has an effect 'Allow' and principal '*'
|
||||
|
||||
Note: This resource does not detect insecure object ACLs.
|
||||
|
||||
it { should_not be_public }
|
|
@ -52,4 +52,8 @@ class AWSConnection
|
|||
def iam_client
|
||||
@iam_client ||= Aws::IAM::Client.new
|
||||
end
|
||||
|
||||
def s3_client
|
||||
@s3_client ||= Aws::S3::Client.new
|
||||
end
|
||||
end
|
||||
|
|
100
libraries/aws_s3_bucket.rb
Normal file
100
libraries/aws_s3_bucket.rb
Normal file
|
@ -0,0 +1,100 @@
|
|||
# author: Matthew Dromazos
|
||||
class AwsS3Bucket < Inspec.resource(1)
|
||||
name 'aws_s3_bucket'
|
||||
desc 'Verifies settings for a s3 bucket'
|
||||
example "
|
||||
describe aws_s3_bucket(bucket_name: 'test_bucket') do
|
||||
it { should exist }
|
||||
end
|
||||
"
|
||||
|
||||
include AwsResourceMixin
|
||||
attr_reader :bucket_name, :region
|
||||
|
||||
def to_s
|
||||
"S3 Bucket #{@bucket_name}"
|
||||
end
|
||||
|
||||
def bucket_acl
|
||||
# This is simple enough to inline it.
|
||||
@bucket_acl ||= AwsS3Bucket::BackendFactory.create.get_bucket_acl(bucket: bucket_name).grants
|
||||
end
|
||||
|
||||
def bucket_policy
|
||||
@bucket_policy ||= fetch_bucket_policy
|
||||
end
|
||||
|
||||
# RSpec will alias this to be_public
|
||||
def public?
|
||||
# first line just for formatting
|
||||
false || \
|
||||
bucket_acl.any? { |g| g.grantee.type == 'Group' && g.grantee.uri =~ /AllUsers/ } || \
|
||||
bucket_acl.any? { |g| g.grantee.type == 'Group' && g.grantee.uri =~ /AuthenticatedUsers/ } || \
|
||||
bucket_policy.any? { |s| s.effect == 'Allow' && s.principal == '*' }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_params(raw_params)
|
||||
validated_params = check_resource_param_names(
|
||||
raw_params: raw_params,
|
||||
allowed_params: [:bucket_name],
|
||||
allowed_scalar_name: :bucket_name,
|
||||
allowed_scalar_type: String,
|
||||
)
|
||||
if validated_params.empty? or !validated_params.key?(:bucket_name)
|
||||
raise ArgumentError, 'You must provide a bucket_name to aws_s3_bucket.'
|
||||
end
|
||||
|
||||
validated_params
|
||||
end
|
||||
|
||||
def fetch_from_aws
|
||||
backend = AwsS3Bucket::BackendFactory.create
|
||||
|
||||
# Since there is no basic "get_bucket" API call, use the
|
||||
# region fetch as the existence check.
|
||||
begin
|
||||
@region = backend.get_bucket_location(bucket: bucket_name).location_constraint
|
||||
rescue Aws::S3::Errors::NoSuchBucket
|
||||
@exists = false
|
||||
return
|
||||
end
|
||||
@exists = true
|
||||
end
|
||||
|
||||
def fetch_bucket_policy
|
||||
backend = AwsS3Bucket::BackendFactory.create
|
||||
|
||||
begin
|
||||
# AWS SDK returns a StringIO, we have to read()
|
||||
raw_policy = backend.get_bucket_policy(bucket: bucket_name).policy
|
||||
return JSON.parse(raw_policy.read)['Statement'].map do |statement|
|
||||
lowercase_hash = {}
|
||||
statement.each_key { |k| lowercase_hash[k.downcase] = statement[k] }
|
||||
OpenStruct.new(lowercase_hash)
|
||||
end
|
||||
rescue Aws::S3::Errors::NoSuchBucketPolicy
|
||||
return []
|
||||
end
|
||||
end
|
||||
|
||||
# Uses the SDK API to really talk to AWS
|
||||
class Backend
|
||||
class AwsClientApi
|
||||
BackendFactory.set_default_backend(self)
|
||||
|
||||
def get_bucket_acl(query)
|
||||
AWSConnection.new.s3_client.get_bucket_acl(query)
|
||||
end
|
||||
|
||||
def get_bucket_location(query)
|
||||
AWSConnection.new.s3_client.get_bucket_location(query)
|
||||
end
|
||||
|
||||
def get_bucket_policy(query)
|
||||
AWSConnection.new.s3_client.get_bucket_policy(query)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
BIN
test/integration/default/build/inspec-logo.png
Normal file
BIN
test/integration/default/build/inspec-logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
100
test/integration/default/build/s3.tf
Normal file
100
test/integration/default/build/s3.tf
Normal file
|
@ -0,0 +1,100 @@
|
|||
#=================================================================#
|
||||
# S3 Bucket
|
||||
#=================================================================#
|
||||
|
||||
resource "aws_s3_bucket" "public" {
|
||||
bucket = "inspec-testing-public-${terraform.env}.chef.io"
|
||||
acl = "public-read"
|
||||
}
|
||||
|
||||
output "s3_bucket_public_name" {
|
||||
value = "${aws_s3_bucket.public.id}"
|
||||
}
|
||||
|
||||
output "s3_bucket_public_region" {
|
||||
value = "${aws_s3_bucket.public.region}"
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket" "private" {
|
||||
bucket = "inspec-testing-private-${terraform.env}.chef.io"
|
||||
acl = "private"
|
||||
}
|
||||
|
||||
output "s3_bucket_private_name" {
|
||||
value = "${aws_s3_bucket.private.id}"
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket" "auth" {
|
||||
bucket = "inspec-testing-auth-${terraform.env}.chef.io"
|
||||
acl = "authenticated-read"
|
||||
}
|
||||
|
||||
output "s3_bucket_auth_name" {
|
||||
value = "${aws_s3_bucket.auth.id}"
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket" "private_acl_public_policy" {
|
||||
bucket = "inspec-testing-mixed-01-${terraform.env}.chef.io"
|
||||
acl = "private"
|
||||
}
|
||||
|
||||
output "s3_bucket_private_acl_public_policy_name" {
|
||||
value = "${aws_s3_bucket.private_acl_public_policy.id}"
|
||||
}
|
||||
|
||||
#=================================================================#
|
||||
# S3 Bucket Policies
|
||||
#=================================================================#
|
||||
resource "aws_s3_bucket_policy" "allow" {
|
||||
bucket = "${aws_s3_bucket.public.id}"
|
||||
policy =<<POLICY
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowGetObject",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::${aws_s3_bucket.public.id}/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
POLICY
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_policy" "deny" {
|
||||
bucket = "${aws_s3_bucket.private.id}"
|
||||
policy =<<POLICY
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "DenyGetObject",
|
||||
"Effect": "Deny",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::${aws_s3_bucket.private.id}/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
POLICY
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_policy" "allow-02" {
|
||||
bucket = "${aws_s3_bucket.private_acl_public_policy.id}"
|
||||
policy =<<POLICY
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowGetObject",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::${aws_s3_bucket.private_acl_public_policy.id}/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
POLICY
|
||||
}
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
fixtures = {}
|
||||
[
|
||||
'aws_account_id',
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
fixtures = {}
|
||||
[
|
||||
'iam_user_recall_hit',
|
||||
|
|
113
test/integration/default/verify/controls/aws_s3_bucket.rb
Normal file
113
test/integration/default/verify/controls/aws_s3_bucket.rb
Normal file
|
@ -0,0 +1,113 @@
|
|||
fixtures = {}
|
||||
[
|
||||
's3_bucket_public_name',
|
||||
's3_bucket_private_name',
|
||||
's3_bucket_auth_name',
|
||||
's3_bucket_private_acl_public_policy_name',
|
||||
's3_bucket_public_region',
|
||||
].each do |fixture_name|
|
||||
fixtures[fixture_name] = attribute(
|
||||
fixture_name,
|
||||
default: "default.#{fixture_name}",
|
||||
description: 'See ../build/s3.tf',
|
||||
)
|
||||
end
|
||||
|
||||
control 'aws_s3_bucket recall tests' do
|
||||
#------------------- Exists -------------------#
|
||||
describe aws_s3_bucket(bucket_name: fixtures['s3_bucket_public_name']) do
|
||||
it { should exist }
|
||||
end
|
||||
|
||||
#------------------- Does Not Exist -------------------#
|
||||
describe aws_s3_bucket(bucket_name: 'inspec-testing-NonExistentBucket.chef.io') do
|
||||
it { should_not exist }
|
||||
end
|
||||
end
|
||||
|
||||
control 'aws_s3_bucket properties tests' do
|
||||
#--------------------------- Region --------------------------#
|
||||
describe aws_s3_bucket(bucket_name: fixtures['s3_bucket_public_name']) do
|
||||
its('region') { should eq fixtures['s3_bucket_public_region'] }
|
||||
end
|
||||
|
||||
#------------------- bucket_acl -------------------#
|
||||
describe "Bucket ACL: Public grants on a public bucket" do
|
||||
subject do
|
||||
aws_s3_bucket(bucket_name: fixtures['s3_bucket_public_name']).bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AllUsers/
|
||||
end
|
||||
end
|
||||
it { should_not be_empty }
|
||||
end
|
||||
|
||||
describe "Bucket ACL: Public grants on a private bucket" do
|
||||
subject do
|
||||
aws_s3_bucket(bucket_name: fixtures['s3_bucket_private_name']).bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AllUsers/
|
||||
end
|
||||
end
|
||||
it { should be_empty }
|
||||
end
|
||||
|
||||
describe "Bucket ACL: AuthUser grants on a private bucket" do
|
||||
subject do
|
||||
aws_s3_bucket(bucket_name: fixtures['s3_bucket_private_name']).bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AuthenticatedUsers/
|
||||
end
|
||||
end
|
||||
it { should be_empty }
|
||||
end
|
||||
|
||||
describe "Bucket ACL: AuthUser grants on an AuthUser bucket" do
|
||||
subject do
|
||||
aws_s3_bucket(bucket_name: fixtures['s3_bucket_auth_name']).bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AuthenticatedUsers/
|
||||
end
|
||||
end
|
||||
it { should_not be_empty }
|
||||
end
|
||||
|
||||
#------------------- bucket_policy -------------------#
|
||||
describe "Bucket Policy: Allow GetObject Statement For Everyone on public" do
|
||||
subject do
|
||||
bucket_policy = aws_s3_bucket(bucket_name: fixtures['s3_bucket_public_name']).bucket_policy
|
||||
allow_all = bucket_policy.select { |s| s.effect == 'Allow' && s.principal == '*' }
|
||||
allow_all.count
|
||||
end
|
||||
it { should == 1 }
|
||||
end
|
||||
|
||||
describe "Bucket Policy: Allow GetObject Statement For Everyone on private" do
|
||||
subject do
|
||||
bucket_policy = aws_s3_bucket(bucket_name: fixtures['s3_bucket_private_name']).bucket_policy
|
||||
allow_all = bucket_policy.select { |s| s.effect == 'Allow' && s.principal == '*' }
|
||||
allow_all.count
|
||||
end
|
||||
it { should be_zero }
|
||||
end
|
||||
|
||||
describe "Bucket Policy: Empty policy on auth" do
|
||||
subject do
|
||||
aws_s3_bucket(bucket_name: fixtures['s3_bucket_auth_name']).bucket_policy
|
||||
end
|
||||
it { should be_empty }
|
||||
end
|
||||
end
|
||||
|
||||
control 'aws_s3_bucket matchers test' do
|
||||
|
||||
#------------------------ be_public --------------------------#
|
||||
describe aws_s3_bucket(bucket_name: fixtures['s3_bucket_public_name']) do
|
||||
it { should be_public }
|
||||
end
|
||||
describe aws_s3_bucket(bucket_name: fixtures['s3_bucket_auth_name']) do
|
||||
it { should be_public }
|
||||
end
|
||||
describe aws_s3_bucket(bucket_name: fixtures['s3_bucket_private_name']) do
|
||||
it { should_not be_public }
|
||||
end
|
||||
describe aws_s3_bucket(bucket_name: fixtures['s3_bucket_private_acl_public_policy_name']) do
|
||||
it { should be_public }
|
||||
end
|
||||
end
|
289
test/unit/resources/aws_s3_bucket_test.rb
Normal file
289
test/unit/resources/aws_s3_bucket_test.rb
Normal file
|
@ -0,0 +1,289 @@
|
|||
# encoding: utf-8
|
||||
require 'helper'
|
||||
require 'aws_s3_bucket'
|
||||
|
||||
# MSBSB = MockS3BucketSingleBackend
|
||||
# Abbreviation not used outside this file
|
||||
|
||||
#=============================================================================#
|
||||
# Constructor Tests
|
||||
#=============================================================================#
|
||||
class AwsS3BucketConstructor < Minitest::Test
|
||||
def setup
|
||||
AwsS3Bucket::BackendFactory.select(AwsMSBSB::Basic)
|
||||
end
|
||||
|
||||
def test_constructor_no_args_raises
|
||||
assert_raises(ArgumentError) { AwsS3Bucket.new }
|
||||
end
|
||||
|
||||
def test_constructor_accept_scalar_param
|
||||
AwsS3Bucket.new('some-bucket')
|
||||
end
|
||||
|
||||
def test_constructor_accept_hash
|
||||
AwsS3Bucket.new(bucket_name: 'some-bucket')
|
||||
end
|
||||
|
||||
def test_constructor_reject_unknown_resource_params
|
||||
assert_raises(ArgumentError) { AwsS3Bucket.new(bla: 'blabla') }
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================#
|
||||
# Search / Recall
|
||||
#=============================================================================#
|
||||
class AwsS3BucketPropertiesTest < Minitest::Test
|
||||
def setup
|
||||
AwsS3Bucket::BackendFactory.select(AwsMSBSB::Basic)
|
||||
end
|
||||
|
||||
def test_recall_no_match_is_no_exception
|
||||
refute AwsS3Bucket.new('NonExistentBucket').exists?
|
||||
end
|
||||
|
||||
def test_recall_match_single_result_works
|
||||
assert AwsS3Bucket.new('public').exists?
|
||||
end
|
||||
|
||||
# No need to handle multiple hits; S3 bucket names are globally unique.
|
||||
end
|
||||
|
||||
#=============================================================================#
|
||||
# Properties
|
||||
#=============================================================================#
|
||||
|
||||
class AwsS3BucketPropertiesTest < Minitest::Test
|
||||
def setup
|
||||
AwsS3Bucket::BackendFactory.select(AwsMSBSB::Basic)
|
||||
end
|
||||
|
||||
#---------------------Bucket Name----------------------------#
|
||||
def test_property_bucket_name
|
||||
assert_equal('public', AwsS3Bucket.new('public').bucket_name)
|
||||
end
|
||||
|
||||
#--------------------- Region ----------------------------#
|
||||
def test_property_region
|
||||
assert_equal('us-east-2', AwsS3Bucket.new('public').region)
|
||||
assert_equal('EU', AwsS3Bucket.new('private').region)
|
||||
end
|
||||
|
||||
#---------------------- bucket_acl -------------------------------#
|
||||
def test_property_bucket_acl_structure
|
||||
bucket_acl = AwsS3Bucket.new('public').bucket_acl
|
||||
|
||||
assert_kind_of(Array, bucket_acl)
|
||||
assert(bucket_acl.size > 0)
|
||||
assert(bucket_acl.all? { |g| g.respond_to?(:permission)})
|
||||
assert(bucket_acl.all? { |g| g.respond_to?(:grantee)})
|
||||
assert(bucket_acl.all? { |g| g.grantee.respond_to?(:type)})
|
||||
end
|
||||
|
||||
def test_property_bucket_acl_public
|
||||
bucket_acl = AwsS3Bucket.new('public').bucket_acl
|
||||
|
||||
public_grants = bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AllUsers/
|
||||
end
|
||||
refute_empty(public_grants)
|
||||
end
|
||||
|
||||
def test_property_bucket_acl_private
|
||||
bucket_acl = AwsS3Bucket.new('private').bucket_acl
|
||||
|
||||
public_grants = bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AllUsers/
|
||||
end
|
||||
assert_empty(public_grants)
|
||||
|
||||
auth_users_grants = bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AuthenticatedUsers/
|
||||
end
|
||||
assert_empty(auth_users_grants)
|
||||
end
|
||||
|
||||
def test_property_bucket_acl_auth_users
|
||||
bucket_acl = AwsS3Bucket.new('auth-users').bucket_acl
|
||||
|
||||
public_grants = bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AllUsers/
|
||||
end
|
||||
assert_empty(public_grants)
|
||||
|
||||
auth_users_grants = bucket_acl.select do |g|
|
||||
g.grantee.type == 'Group' && g.grantee.uri =~ /AuthenticatedUsers/
|
||||
end
|
||||
refute_empty(auth_users_grants)
|
||||
end
|
||||
|
||||
#---------------------- bucket_policy -------------------------------#
|
||||
def test_property_bucket_policy_structure
|
||||
bucket_policy = AwsS3Bucket.new('public').bucket_policy
|
||||
assert_kind_of(Array, bucket_policy)
|
||||
assert_kind_of(OpenStruct, bucket_policy.first)
|
||||
[:effect, :principal, :action, :resource].each do |field|
|
||||
assert_respond_to(bucket_policy.first, field)
|
||||
end
|
||||
end
|
||||
|
||||
def test_property_bucket_policy_public
|
||||
bucket_policy = AwsS3Bucket.new('public').bucket_policy
|
||||
allow_all = bucket_policy.select { |s| s.effect == 'Allow' && s.principal == '*' }
|
||||
assert_equal(1, allow_all.count)
|
||||
end
|
||||
|
||||
def test_property_bucket_policy_private
|
||||
bucket_policy = AwsS3Bucket.new('private').bucket_policy
|
||||
allow_all = bucket_policy.select { |s| s.effect == 'Allow' && s.principal == '*' }
|
||||
assert_equal(0, allow_all.count)
|
||||
end
|
||||
|
||||
def test_property_bucket_policy_auth
|
||||
bucket_policy = AwsS3Bucket.new('auth').bucket_policy
|
||||
assert_empty(bucket_policy)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#=============================================================================#
|
||||
# Test Matchers
|
||||
#=============================================================================#
|
||||
|
||||
class AwsS3BucketPropertiesTest < Minitest::Test
|
||||
def setup
|
||||
AwsS3Bucket::BackendFactory.select(AwsMSBSB::Basic)
|
||||
end
|
||||
|
||||
def test_be_public_public_acl
|
||||
assert(AwsS3Bucket.new('public').public?)
|
||||
end
|
||||
def test_be_public_auth_acl
|
||||
assert(AwsS3Bucket.new('auth-users').public?)
|
||||
end
|
||||
def test_be_public_private_acl
|
||||
refute(AwsS3Bucket.new('private').public?)
|
||||
end
|
||||
def test_be_public_public_acl
|
||||
assert(AwsS3Bucket.new('public').public?)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
#=============================================================================#
|
||||
# Test Fixtures
|
||||
#=============================================================================#
|
||||
|
||||
module AwsMSBSB
|
||||
class Basic < AwsS3Bucket::Backend
|
||||
def get_bucket_acl(query)
|
||||
owner_full_control = OpenStruct.new({
|
||||
grantee: OpenStruct.new({
|
||||
type: 'CanonicalUser',
|
||||
}),
|
||||
permission: 'FULL_CONTROL',
|
||||
})
|
||||
|
||||
buckets = {
|
||||
'public' => OpenStruct.new({
|
||||
:grants => [
|
||||
owner_full_control,
|
||||
OpenStruct.new({
|
||||
grantee: OpenStruct.new({
|
||||
type: 'Group',
|
||||
uri: 'http://acs.amazonaws.com/groups/global/AllUsers'
|
||||
}),
|
||||
permission: 'READ',
|
||||
}),
|
||||
]
|
||||
}),
|
||||
'auth-users' => OpenStruct.new({
|
||||
:grants => [
|
||||
owner_full_control,
|
||||
OpenStruct.new({
|
||||
grantee: OpenStruct.new({
|
||||
type: 'Group',
|
||||
uri: 'http://acs.amazonaws.com/groups/global/AuthenticatedUsers'
|
||||
}),
|
||||
permission: 'READ',
|
||||
}),
|
||||
]
|
||||
}),
|
||||
'private' => OpenStruct.new({ :grants => [ owner_full_control ] }),
|
||||
'private-acl-public-policy' => OpenStruct.new({ :grants => [ owner_full_control ] }),
|
||||
}
|
||||
buckets[query[:bucket]]
|
||||
end
|
||||
|
||||
def get_bucket_location(query)
|
||||
buckets = {
|
||||
'public' => OpenStruct.new({ location_constraint: 'us-east-2' }),
|
||||
'private' => OpenStruct.new({ location_constraint: 'EU' }),
|
||||
'auth-users' => OpenStruct.new({ location_constraint: 'ap-southeast-1' }),
|
||||
'private-acl-public-policy' => OpenStruct.new({ location_constraint: 'ap-southeast-2' }),
|
||||
}
|
||||
unless buckets.key?(query[:bucket])
|
||||
raise Aws::S3::Errors::NoSuchBucket.new(nil, nil)
|
||||
end
|
||||
buckets[query[:bucket]]
|
||||
end
|
||||
|
||||
def get_bucket_policy(query)
|
||||
buckets = {
|
||||
'public' => OpenStruct.new({
|
||||
policy: StringIO.new(<<'EOP')
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowGetObject",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::public/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOP
|
||||
}),
|
||||
'private' => OpenStruct.new({
|
||||
policy: StringIO.new(<<'EOP')
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "DenyGetObject",
|
||||
"Effect": "Deny",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::private/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOP
|
||||
}),
|
||||
'private-acl-public-policy' => OpenStruct.new({
|
||||
policy: StringIO.new(<<'EOP')
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AllowGetObject",
|
||||
"Effect": "Allow",
|
||||
"Principal": "*",
|
||||
"Action": "s3:GetObject",
|
||||
"Resource": "arn:aws:s3:::private-acl-public-policy/*"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOP
|
||||
}),
|
||||
# No policies for auth bucket
|
||||
}
|
||||
unless buckets.key?(query[:bucket])
|
||||
raise Aws::S3::Errors::NoSuchBucketPolicy.new(nil, nil)
|
||||
end
|
||||
buckets[query[:bucket]]
|
||||
end
|
||||
end
|
||||
end
|
91
test/unit/resources/aws_vpc.notes
Normal file
91
test/unit/resources/aws_vpc.notes
Normal file
|
@ -0,0 +1,91 @@
|
|||
#=============================================================================#
|
||||
# Search / Recall
|
||||
#=============================================================================#
|
||||
class AwsVpcRecallTest < Minitest::Test
|
||||
def setup
|
||||
AwsVpc::BackendFactory.select(MAVSB::Three)
|
||||
end
|
||||
|
||||
def test_search_miss_is_not_an_exception
|
||||
user = AwsVpc.new('vpc-87654321')
|
||||
refute user.exists?
|
||||
end
|
||||
|
||||
def test_search_hit_via_scalar_works
|
||||
user = AwsVpc.new('')
|
||||
assert user.exists?
|
||||
assert_equal('erin', user.username)
|
||||
end
|
||||
|
||||
def test_search_hit_via_hash_works
|
||||
user = AwsVpc.new(username: 'erin')
|
||||
assert user.exists?
|
||||
assert_equal('erin', user.username)
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================#
|
||||
# Properties
|
||||
#=============================================================================#
|
||||
|
||||
class AwsVpcPropertiesTest < Minitest::Test
|
||||
def setup
|
||||
AwsVpc::BackendFactory.select(MAVSB::Three)
|
||||
end
|
||||
|
||||
#-----------------------------------------------------#
|
||||
# username property
|
||||
#-----------------------------------------------------#
|
||||
def test_property_username_correct_on_hit
|
||||
user = AwsVpc.new(username: 'erin')
|
||||
assert_equal('erin', user.username)
|
||||
end
|
||||
|
||||
#-----------------------------------------------------#
|
||||
# has_console_password property and predicate
|
||||
#-----------------------------------------------------#
|
||||
def test_property_password_positive
|
||||
user = AwsVpc.new(username: 'erin')
|
||||
assert_equal(true, user.has_console_password)
|
||||
assert_equal(true, user.has_console_password?)
|
||||
end
|
||||
|
||||
def test_property_password_negative
|
||||
user = AwsVpc.new(username: 'leslie')
|
||||
assert_equal(false, user.has_console_password)
|
||||
assert_equal(false, user.has_console_password?)
|
||||
end
|
||||
|
||||
#-----------------------------------------------------#
|
||||
# has_mfa_enabled property and predicate
|
||||
#-----------------------------------------------------#
|
||||
def test_property_mfa_positive
|
||||
user = AwsVpc.new(username: 'erin')
|
||||
assert_equal(true, user.has_mfa_enabled)
|
||||
assert_equal(true, user.has_mfa_enabled?)
|
||||
end
|
||||
|
||||
def test_property_mfa_negative
|
||||
user = AwsVpc.new(username: 'leslie')
|
||||
assert_equal(false, user.has_mfa_enabled)
|
||||
assert_equal(false, user.has_mfa_enabled?)
|
||||
end
|
||||
|
||||
#-----------------------------------------------------#
|
||||
# access_keys property
|
||||
#-----------------------------------------------------#
|
||||
def test_property_access_keys_positive
|
||||
keys = AwsVpc.new(username: 'erin').access_keys
|
||||
assert_kind_of(Array, keys)
|
||||
assert_equal(keys.length, 2)
|
||||
# We don't currently promise that the results
|
||||
# will be Inspec resource objects.
|
||||
# assert_kind_of(AwsIamAccessKey, keys.first)
|
||||
end
|
||||
|
||||
def test_property_access_keys_negative
|
||||
keys = AwsVpc.new(username: 'leslie').access_keys
|
||||
assert_kind_of(Array, keys)
|
||||
assert(keys.empty?)
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue