diff --git a/docs/resources/aws_s3_buckets.md.erb b/docs/resources/aws_s3_buckets.md.erb new file mode 100644 index 000000000..4415909d9 --- /dev/null +++ b/docs/resources/aws_s3_buckets.md.erb @@ -0,0 +1,53 @@ +--- +title: About the aws_s3_buckets Resource +--- + +# aws\_s3\_buckets + +Use the `aws_s3_buckets` InSpec audit resource to list all buckets in a single account. + +Use the `aws_s3_bucket` InSpec audit resource to perform in-depth auditing of a single S3 bucket. + +
+ +## Syntax + +An `aws_s3_buckets` resource block takes no arguments + + describe aws_s3_buckets do + it { should exist } + end + +
+ +## Examples + +The following examples show how to use this InSpec audit resource. + +As this is the initial release of `aws_s3_buckets`, its limited functionality precludes examples. + +
+ +## Matchers + +### exists + +The control will pass if the resource contains at least one bucket. + + # Test if there are any buckets + describe aws_s3_buckets + it { should exist } + end + +## Properties + +### bucket\_names + +Provides an array of strings containing the names of the buckets. + + # Examine what buckets have been created. + describe aws_s3_buckets do + its('bucket_names') { should eq ['my_bucket'] } + # OR + its('bucket_names') { should include 'my_bucket' } + end diff --git a/lib/resource_support/aws.rb b/lib/resource_support/aws.rb index 1ca0df14e..c412d2468 100644 --- a/lib/resource_support/aws.rb +++ b/lib/resource_support/aws.rb @@ -36,6 +36,7 @@ require 'resources/aws/aws_rds_instance' require 'resources/aws/aws_route_table' require 'resources/aws/aws_s3_bucket' require 'resources/aws/aws_s3_bucket_object' +require 'resources/aws/aws_s3_buckets' require 'resources/aws/aws_security_group' require 'resources/aws/aws_security_groups' require 'resources/aws/aws_sns_subscription' diff --git a/lib/resources/aws/aws_s3_buckets.rb b/lib/resources/aws/aws_s3_buckets.rb new file mode 100644 index 000000000..e9426a746 --- /dev/null +++ b/lib/resources/aws/aws_s3_buckets.rb @@ -0,0 +1,51 @@ +# author: Matthew Dromazos +# author: Sam Cornwell +class AwsS3Buckets < Inspec.resource(1) + name 'aws_s3_buckets' + desc 'Verifies settings for AWS S3 Buckets in bulk' + example " + describe aws_s3_bucket do + its('bucket_names') { should eq ['my_bucket'] } + end + " + supports platform: 'aws' + + include AwsPluralResourceMixin + + # Underlying FilterTable implementation. + filter = FilterTable.create + filter.add_accessor(:where) + .add_accessor(:entries) + .add(:exists?) { |x| !x.entries.empty? } + .add(:bucket_names, field: :name) + filter.connect(self, :table) + + def to_s + 'S3 Buckets' + end + + def validate_params(resource_params) + unless resource_params.empty? + raise ArgumentError, 'aws_s3_buckets does not accept resource parameters.' + end + resource_params + end + + private + + def fetch_from_api + backend = BackendFactory.create(inspec_runner) + @table = backend.list_buckets.buckets.map(&:to_h) + end + + class Backend + class AwsClientApi < AwsBackendBase + BackendFactory.set_default_backend self + self.aws_client_class = Aws::S3::Client + + def list_buckets + aws_service_client.list_buckets + end + end + end +end diff --git a/test/integration/aws/default/verify/controls/aws_s3_buckets.rb b/test/integration/aws/default/verify/controls/aws_s3_buckets.rb new file mode 100644 index 000000000..d40668690 --- /dev/null +++ b/test/integration/aws/default/verify/controls/aws_s3_buckets.rb @@ -0,0 +1,28 @@ +fixtures = {} +[ + 's3_bucket_public_name', + 's3_bucket_private_name', +].each do |fixture_name| + fixtures[fixture_name] = attribute( + fixture_name, + default: "default.#{fixture_name}", + description: 'See ../build/s3.tf', + ) +end + +control "aws_s3_buckets recall" do + + # You should be able to test if there are any buckets + describe aws_s3_buckets do + it { should exist } + end +end + +control "aws_s3_buckets properties" do + # you should be able to test the cidr_block of a subnet + describe aws_s3_buckets do + its('bucket_names') { should include fixtures['s3_bucket_public_name'], fixtures['s3_bucket_private_name'] } + its('bucket_names') { should_not include 'i_dont_exist' } + end +end + diff --git a/test/unit/resources/aws_s3_buckets_test.rb b/test/unit/resources/aws_s3_buckets_test.rb new file mode 100644 index 000000000..6dbc07ac8 --- /dev/null +++ b/test/unit/resources/aws_s3_buckets_test.rb @@ -0,0 +1,88 @@ +require 'helper' + +# MSBB = MockS3BucketsBackend +# Abbreviation not used outside this file + +#=============================================================================# +# Constructor Tests +#=============================================================================# +class AwsS3BucketsConstructor < Minitest::Test + def setup + AwsS3Buckets::BackendFactory.select(AwsMSBB::Basic) + end + + def test_constructor_no_args_ok + AwsS3Buckets.new + end + + def test_constructor_reject_unknown_resource_params + assert_raises(ArgumentError) { AwsS3Buckets.new(bla: 'blabla') } + end +end + +#=============================================================================# +# Search / Recall +#=============================================================================# +class AwsS3BucketsRecallEmptyTest < Minitest::Test + + def setup + AwsS3Buckets::BackendFactory.select(AwsMSBB::Empty) + end + + def test_search_miss_via_empty_vpcs + refute AwsS3Buckets.new.exists? + end +end + +class AwsS3BucketsRecallBasicTest < Minitest::Test + + def setup + AwsS3Buckets::BackendFactory.select(AwsMSBB::Basic) + end + + def test_search_hit_via_empty_filter + assert AwsS3Buckets.new.exists? + end +end + +#=============================================================================# +# Properties +#=============================================================================# +class AwsS3bucketsProperties < Minitest::Test + def setup + AwsS3Buckets::BackendFactory.select(AwsMSBB::Basic) + end + + def test_property_bucket_names + basic = AwsS3Buckets.new + assert_kind_of(Array, basic.bucket_names) + assert(basic.bucket_names.include?('bucket-01')) + assert(!basic.bucket_names.include?('NonExistentBucket')) + refute(basic.bucket_names.include?(nil)) + end +end + +#=============================================================================# +# Test Fixtures +#=============================================================================# +module AwsMSBB + class Empty < AwsBackendBase + def list_buckets + OpenStruct.new({ buckets: [] }) + end + end + + class Basic < AwsBackendBase + def list_buckets + fixtures = [ + OpenStruct.new({ + name: "bucket-01", + }), + OpenStruct.new({ + name: "bucket-02", + }), + ] + OpenStruct.new({ buckets: fixtures }) + end + end +end