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:
Clinton Wolfe 2018-01-19 11:50:08 -05:00 committed by GitHub
parent e81937413b
commit 5ab68ecf03
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 820 additions and 2 deletions

View 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 }

View file

@ -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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

View 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
}

View file

@ -1,4 +1,3 @@
fixtures = {}
[
'aws_account_id',

View file

@ -1,4 +1,3 @@
fixtures = {}
[
'iam_user_recall_hit',

View 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

View 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

View 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