aws-s3-static-website-public
S3 static website hosting publishes a public HTTP endpoint serving bucket objects, independent of the bucket policy's API access controls.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?bucket) ==
ObjectStorage
?bucket.provider_type == 'AWS::S3::Bucket'
?bucket.properties.WebsiteConfiguration.IndexDocument is set (website hosting enabled)
?public_grant represents a bucket policy or bucket ACL granting s3:GetObject to Principal:* (or allUsers grantee in ACL)
?bucket.properties.PublicAccessBlockConfiguration.BlockPublicAcls == false AND ?bucket.properties.ObjectOwnership != 'BucketOwnerEnforced' OR access is via bucket policy (not ACL)
emit
| source type | ObjectStorage |
|---|---|
| target type | AnonymousIdentity |
| source | ?bucket |
| target | <AnonymousIdentity> |
| conditions | resource_policy |
| state logic | ACTIVE when BOTH conditions hold: (1) website hosting is confirmed (GetBucketWebsite succeeds with IndexDocument set) AND (2) the bucket allows object reads to unauthenticated principals via bucket policy OR via ACL when ObjectOwnership != BucketOwnerEnforced. CONDITIONAL(resource_policy) when website hosting is enabled but the access control configuration is not yet resolved. BLOCKED when BlockPublicAcls=true AND the only public grant is via ACL (policy-based grants are separate). BLOCKED when ObjectOwnership=BucketOwnerEnforced AND no bucket-policy public allow exists. |
Narrative
{bucket.name} has S3 static website hosting enabled and allows unauthenticated public read - the bucket's contents are served publicly via the S3 website endpoint URL.
Raw rule rules/derived/aws/s3.yaml
id: aws-s3-static-website-public
emits: ExposedToInternet
description: S3 static website hosting publishes a public HTTP endpoint serving bucket objects, independent
of the bucket policy's API access controls.
match:
- - bucket: null
- HasPolicy
- public_grant: null
where:
- node_type(?bucket) == ObjectStorage
- ?bucket.provider_type == 'AWS::S3::Bucket'
- ?bucket.properties.WebsiteConfiguration.IndexDocument is set (website hosting enabled)
- ?public_grant represents a bucket policy or bucket ACL granting s3:GetObject to Principal:* (or allUsers
grantee in ACL)
- ?bucket.properties.PublicAccessBlockConfiguration.BlockPublicAcls == false AND ?bucket.properties.ObjectOwnership
!= 'BucketOwnerEnforced' OR access is via bucket policy (not ACL)
emit:
source_type: ObjectStorage
target_type: AnonymousIdentity
source: ?bucket
target: <AnonymousIdentity>
permissions: []
conditions:
- resource_policy
state_logic: 'ACTIVE when BOTH conditions hold: (1) website hosting is confirmed (GetBucketWebsite succeeds
with IndexDocument set) AND (2) the bucket allows object reads to unauthenticated principals via bucket
policy OR via ACL when ObjectOwnership != BucketOwnerEnforced. CONDITIONAL(resource_policy) when website
hosting is enabled but the access control configuration is not yet resolved. BLOCKED when BlockPublicAcls=true
AND the only public grant is via ACL (policy-based grants are separate). BLOCKED when ObjectOwnership=BucketOwnerEnforced
AND no bucket-policy public allow exists.'
confidence: min(contributing_confidences) * 0.90
derived_from:
- ?bucket HasPolicy ?public_grant (GetBucketPolicy + bucket ACL evidence)
- ?bucket.properties.WebsiteConfiguration.IndexDocument (GetBucketWebsite)
false_positive_note: "Static website hosting alone does not guarantee objects are publicly readable;\
\ the bucket policy or ACL must also permit GetObject to Principal:* or allUsers. If ObjectOwnership=BucketOwnerEnforced,\
\ ACL grants are silently ignored; in that case only a bucket-policy public allow makes objects readable\
\ \u2014 do NOT emit based on ACL when ObjectOwnership is enforced. Block Public Access BlockPublicAcls=true\
\ blocks ACL-based grants (the engine should BLOCK the output when only ACL permits access). The website\
\ endpoint serves objects over HTTP (not HTTPS) unless a CloudFront distribution fronts it; the ExposedToInternet\
\ fact is still valid. Do NOT emit if the only public path is via an authenticated S3 API call (signed\
\ URL)."
narrative: "{bucket.name} has S3 static website hosting enabled and allows unauthenticated public read\
\ \u2014 the bucket's contents are served publicly via the S3 website endpoint URL."