✓ Human-authored analysis; AI used for formatting and proofreading.
A platform engineer at a healthcare company reviews their Trusted Advisor dashboard. Everything is green. One bucket shows red:
Trusted Advisor does not have permission to check the policy, or the policy could not be evaluated for other reasons.
They've seen this before. The Deny statement is intentional. Their security team restricts AWS service access to sensitive buckets. They move on. The bucket is publicly readable.
What Trusted Advisor checks
The check is named Amazon S3 Bucket Permissions, check ID Pfx0RwqBli. The official documentation lists five alert criteria. Three red, one yellow, one green. From the AWS docs:
- Red: The bucket ACL allows List access or Upload/Delete access for Everyone or Any Authenticated AWS User and Block Public Access settings are not enabled.
- Red: A bucket policy allows public access and Block Public Access settings are not enabled.
- Red: Trusted Advisor does not have permission to check the policy, or the policy could not be evaluated for other reasons.
- Yellow: A bucket policy allows public access, but the Restrict Public Buckets setting is turned on and restricts access to only authorized users of that account.
- Green: The bucket is compliant and has full Block Public Access protection enabled.
The evaluation mechanism is API-based: Trusted Advisor uses IAM service roles to read each bucket's effective permissions via s3:GetBucketPolicy, s3:GetBucketAcl, and related calls. Standard scanner architecture assume a role, read the configuration, evaluate it.
The third red criterion
Zoom in on the criterion that's documented but rarely highlighted: "Trusted Advisor does not have permission to check the policy, or the policy could not be evaluated for other reasons."
A bucket policy can contain a Deny statement that explicitly blocks the Trusted Advisor service role from reading the bucket's effective permissions. This is a legitimate AWS feature. Customers have valid reasons to restrict service access. But the side effect is that Trusted Advisor cannot distinguish between:
- "I was denied access to a secure bucket" (intentional restriction; bucket is private)
- "I was denied access to a publicly exposed bucket" (the bucket happens to also be public)
Both produce the same red flag with the same generic message.
The compound misconfiguration
Two statements coexisting in the same bucket policy (sanitized with no real account IDs):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyAWSScanners",
"Effect": "Deny",
"Principal": {"AWS": "arn:aws:iam::000000000000:role/aws-service-role/trustedadvisor.amazonaws.com/AWSServiceRoleForTrustedAdvisor"},
"Action": ["s3:GetBucketPolicy", "s3:GetBucketAcl"],
"Resource": "arn:aws:s3:::patient-claims-data/*"
},
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::patient-claims-data", "arn:aws:s3:::patient-claims-data/*"]
}
]
}
This happens in practice through incremental configuration drift. The Deny is added by a security team to restrict AWS service scanning on a sensitive bucket. Months later, a different engineer adds a public read policy for a CDN migration. Or the bucket was already public when the Deny was layered on. Nobody catches the contradiction because the tool that should catch it has been blinded.
Why API-based scanners all share this blind spot
This isn't unique to Trusted Advisor. Prowler, Trivy, Checkov, ScoutSuite. Any tool that assumes an IAM role to evaluate bucket configuration is vulnerable to the same Deny statement. If the scanner's principal can be identified and denied, the scanner can be blinded. This is a structural limitation of runtime API-based analysis, not a bug in any particular tool. The fix is to evaluate the policy without assuming a role at all.
A different approach: static policy analysis
Export the bucket's configuration (policy JSON, ACLs, PAB settings) and evaluate it offline. The analyzer reads the policy the same way an attacker would by parsing the statements and evaluating effective permissions for any principal, including anonymous users. A Deny targeting specific service roles doesn't affect the analysis because the analyzer never assumes those roles.
Run the same bucket configuration through Stave's apply command. The Allow statement granting Principal: * is detected regardless of the Deny statement. Clear finding, no ambiguity, no "could not be evaluated."
stave apply --observations ./snapshot/ --controls ./controls/s3 --format json \
| jq '.findings[] | select(.control_id == "CTL.S3.PUBLIC.001")'
Output names the public-read finding on patient-claims-data directly. The Deny statement appears in the evidence chain but doesn't suppress the finding.
Reproducing it
The fact base and worked configuration are in the Stave repo. Two paths:
- 60-second walkthrough:
bash examples/demo-s3-public-read/run.sh. Runs the analysis end-to-end against a fixture pair (writeup with the bypass, remediated without it) and prints the verdict. - Full scenario inspection: open
examples/demo-s3-public-read/fixtures/before/for the unsafe configuration,after/for the remediation. The observation JSONs are checked in; readers can swap in their own bucket policies and re-run.
Why this matters
Security dashboards showing green doesn't mean your buckets are secure. The gap is documented by AWS as check Pfx0RwqBli. The criterion is correct as written; the message is accurate. The interpretive failure happens at the dashboard layer, where could not evaluate reads as not my problem to operators who've seen it a many times before.
Static policy analysis closes the gap by removing the dependency on the scanner's IAM identity. Whether a future Trusted Advisor patch addresses this specific case or not, the structural limitation of API-based scanners are blindable by Deny statements applies to every tool in the category. Independent evaluation is the layer that doesn't share the blind spot.
- AWS docs: Trusted Advisor S3 Bucket Permissions check (
Pfx0RwqBli) - Stave repo + demo: stave
Top comments (0)