Skip to content

fix(aws/s3): correlate SSL bucket policy to its bucket by identity [FIX-355] - #76

Open
drewblas wants to merge 1 commit into
mainfrom
fix/s3-ssl-bucket-coverage
Open

fix(aws/s3): correlate SSL bucket policy to its bucket by identity [FIX-355]#76
drewblas wants to merge 1 commit into
mainfrom
fix/s3-ssl-bucket-coverage

Conversation

@drewblas

@drewblas drewblas commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes a false positive in the S3.5 (require SSL) FinOps policy: a correct, textbook SSL-deny bucket policy is reported as non-compliant.

Root cause. The check confirmed that a bucket policy's non-SSL deny statement covers the attached bucket by string-matching the deny statement's Resource ARNs against the policy's bucket argument. That comparison breaks under mixed resolution — when one side resolves and the other doesn't:

  • the bucket name is set from a local/variable, so the policy's bucket = aws_s3_bucket.x.bucket resolves to the literal name, but
  • the deny statement lists aws_s3_bucket.x.arn, whose ARN isn't known until apply, so it renders as an unresolved synthetic token.

A real name never matches a synthetic token, so resourcesCoverBucket returned false and the compliant policy was flagged.

Fix

Move the resource-coverage decision out of the parser (which only had the policy's bucket string) and into s3.S3.PostProcess, where the policy is already linked to its bucket. Match each deny Resource ARN against both of the bucket's identities:

  • its resolved name (arn:aws:s3:::<name> or a bare <name>), and
  • its synthetic instance token — which equals bucket.ID, exactly what .arn/.id render to when unresolved.

This is independent of which side resolved, with no benefit-of-the-doubt fallback, so it still rejects policies that protect a different bucket or omit the bucket/objects ARNs.

The SSL-deny facts (whether a compliant deny statement exists and the resources it lists) are extracted at parse time into a new transient BucketPolicy.SSLDeny field. PostProcess runs in the parser plugin before serialization, so no proto/codegen change is required.

Changes

  • pkg/tree/aws/s3/bucket_policy.go — add transient SSLDeny *SSLDenyInfo.
  • pkg/tree/aws/s3/s3.go — finalize DeniesInsecureTransport in PostProcess; add denyResourcesCoverBucket / arnRefersToBucket (identity-based matching).

Verification

  • go-proto S3 unit tests pass.
  • Paired with the parser change (extracts SSLDenyInfo), an 8-case matrix covers every resolution combination; guards for wrong-bucket / objects-only coverage still fail correctly.
  • Confirmed end-to-end against the affected repo (all 4 workspaces): the deny policy now resolves to denies=true, so S3.5 passes.

Rollout

Cross-repo, release-ordered: merge and release go-proto first, then bump infracost/parser's go.mod to the new version and merge the paired parser PR.

Linear: FIX-355

…IX-355]

The S3.5 (require-SSL) check confirmed a bucket policy's non-SSL deny
statement covered the attached bucket by string-matching the deny
statement's Resource ARNs against the policy's `bucket` argument. That
comparison breaks when the two sides resolve differently: e.g. the
`bucket` argument resolves to a literal name (name set from a local/var)
while the deny statement references `aws_s3_bucket.x.arn`, which stays an
unresolved synthetic token. A correct, textbook SSL-deny policy was then
reported as non-compliant.

Move the resource-coverage decision into PostProcess, where the policy is
already linked to its bucket, and match the deny Resource ARNs against
both of the bucket's identities:

  - its resolved name (arn:aws:s3:::<name> or a bare <name>), and
  - its synthetic instance token, which equals bucket.ID and is exactly
    what .arn/.id render to when unresolved.

Matching on both is independent of which side resolved, with no
benefit-of-the-doubt fallback, so it still rejects policies that protect
a different bucket or omit the bucket/objects ARNs.

The SSL-deny facts (whether a compliant deny statement is present and the
resources it lists) are extracted at parse time into a new transient
BucketPolicy.SSLDeny field. PostProcess runs in the parser plugin before
serialization, so this needs no proto/codegen change.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant