s3-estate-calibration-auditor skill
Audit an estate of AWS S3 buckets for the one bucket that is genuinely publicly or cross-account exposed, without over-flagging the many buckets that READ as exposed but are neutralised. Resolves each bucket's EFFECTIVE verdict by composing four layers (Block Public Access x bucket policy x bucket ACL x access points), never one layer alone, then rolls the per-bucket verdicts up into an estate verdict. Its discipline is symmetric: BPA (RestrictPublicBuckets / BlockPublicPolicy) neutralises a Principal '*' policy but NOT a cross-account grant; IgnorePublicAcls kills a public-group ACL grant but NOT a cross-account canonical-user grant; a narrowing Condition (org id, ExternalId, SourceIp, access-point delegation) scopes a Principal '*' so it is not public. On a needle estate it names the ONE live bucket as the primary finding; on a clean estate it reports NO live exposure and does not manufacture findings. Then it states what the bucket configs alone cannot answer (per-object ACLs, CloudFront/CDN fronting, the trusted principals' identity policies, account-level BPA dependency, data sensitivity). Use when asked to review an S3 bucket fleet for public exposure, cross-account access, or whether the estate is clean. Vendor-neutral; runs offline against describe-bucket / get-bucket-policy / get-bucket-acl / list-access-points JSON with no Anyshift account.
Is the s3-estate-calibration-auditor skill safe?
Clean: nothing in its files matched our rules. We read 263 files in the folder on 2026-09-28.
- low
SKILL.md:1The description is over 1,024 characters, the limit agents read.
1372 characters
Install the s3-estate-calibration-auditor skill
A skill is a folder. Copy it into your agent's skills folder and the agent loads it when the task matches its description.
git clone --depth 1 https://github.com/anyshift-io/sre-skills.git /tmp/sre-skills mkdir -p ~/.claude/skills cp -r /tmp/sre-skills/skills/s3-estate-calibration-auditor ~/.claude/skills/s3-estate-calibration-auditor
In the Claude apps, zip the folder and upload it from the Skills settings. The folder on GitHub
The instructions your agent would load
SKILL.md as published, without the frontmatter. Read it on GitHub
s3-estate-calibration-auditor
Effective-exposure audit skill for an estate of AWS S3 buckets. Takes the config layers for every bucket in the estate (public-access-block.json, bucket-policy.json, bucket-acl.json, and access-points.json where present), resolves each bucket's effective verdict by composing all four layers, then answers one question a per-layer read cannot: across 8-12 buckets that mostly READ as exposed, which one is genuinely live, and is the estate otherwise clean. It returns the live bucket as the headline, ranked by severity, with a fix, then names exactly where the bucket configs stop being able to answer the question.
Effective S3 exposure is a join across four layers that each get read wrong one at a time. A public-looking bucket policy is inert under RestrictPublicBuckets; a cross-account grant survives BPA-all-on; a public-group ACL is dead under IgnorePublicAcls but a cross-account canonical user beside it is not; a clean bucket can still be public through an access point. In an estate, the trap doubles: several buckets carry a Principal '*' policy, an AllUsers ACL, or a wide-open look that is genuinely neutralised, and exactly one bucket carries a real live grant that reads just like its neutralised siblings. This skill composes the layers per bucket instead of clearing each layer in isolation, then calibrates the estate: it does not over-flag the neutralised baits, and it does not miss the buried needle.
When to invoke
access, or "is anything in this account public."
- An agent is asked to review an S3 bucket fleet for public exposure, cross-account
effectively exposed, not just whether a Principal '*' or an AllUsers grant appears somewhere in the configs.
- An estate is being shipped or changed and the question is whether any bucket is
"but BPA is on / it is scoped" needs to be confirmed against the effective verdict rather than taken on trust.
- An estate looks exposed (several Principal '*' policies, AllUsers ACLs) and the claim
closed" needs to be checked against the cross-account grants BPA does not touch.
- An estate looks locked down (BPA all on everywhere) and the claim "the account is
What this skill reads, and what it does not
It reads the static configuration of an estate of buckets: per bucket, any subset of the Block Public Access booleans, the bucket policy, the bucket ACL, and the access points. Those are S3 control-plane reads (get-public-access-block, get-bucket-policy, get-bucket-acl, list-access-points + get-access-point-policy). That is the entire input. The audit is correct and complete for what the bucket configs can tell you, and it is explicit about the rest. Reachability-on-paper is not exposure-in-fact, and every audit ends by naming the joins it cannot make:
public-read grant even when the bucket is private. Join: bucket config to its per-object ACLs.
- It does not see per-object ACLs. An individual object can carry its own
is served publicly through a CloudFront distribution with Origin Access Control. Join: bucket to its CDN distribution.
- It does not see CloudFront / CDN fronting. A bucket can be private while its data
conditional grant only matters in proportion to what the trusted account/role can do and whether it re-shares onward, which lives in that account. Join: this bucket to the identity policies of the principals it trusts.
- It does not contain the privileges of a trusted principal. A cross-account or
access this config Allows. Join: bucket to its VPC-endpoint policies and the org SCPs.
- It does not see VPC-endpoint policies or org SCPs, which can further restrict
which is not in the config.
- A flagged grant's exploitability depends on the data sensitivity of the objects,
A clean (deceptive-clean) estate still gets a boundary section, because a BPA-neutralised estate is not a proven-safe system: turning BPA off would expose the latent statements.
The model
For every bucket in the estate, build the effective verdict by composing four layers. Never read one layer alone. A finding is LIVE only when a real public or cross-account grant survives the layer that would neutralise it:
policy and ACL grants, but do NOT touch cross-account grants.
- Block Public Access (BPA) -- four booleans that neutralise otherwise-public
other account, or '*' narrowed by a Condition.
- The bucket policy -- a resource policy whose Principal can be public ('*'), a named
AuthenticatedUsers public groups.
- The bucket ACL -- legacy grants to canonical users, or to the AllUsers /
independent of (but not exceeding) the bucket.
- Access points -- each with its OWN BPA and policy, able to expose data
The estate is clean iff no bucket is live. The needle is whichever bucket carries a live finding among many neutralised/scoped lookalikes.
The methodology, in order
1. Parse all four layers for every bucket
Before any judgment, read each layer for each bucket. Process EVERY bucket in the estate, not the first couple:
all four are False (no BPA). The two that neutralise existing grants are RestrictPublicBuckets / BlockPublicPolicy (for a public policy) and IgnorePublicAcls (for a public-group ACL). BlockPublicAcls only blocks new public ACLs and does not disable an existing one.
- BPA: read the four booleans from public-access-block.json. An absent file means
cannot make a bucket public; classify only the Allow statements. For each Allow, read the Principal (public '', a named AWS account, or '' with a Condition) and the Condition.
- Policy: read each Statement in bucket-policy.json. A Deny grants nothing and
AuthenticatedUsers Group URI is a public-group grant; a CanonicalUser that is not the bucket owner is a cross-account grant; an owner-only ACL produces nothing.
- ACL: read each grant in bucket-acl.json. A Grantee that is the AllUsers or
PublicAccessBlock and Policy; resolve the AP policy exactly like a bucket policy, against the AP's own BPA.
- Access points: read each entry in access-points.json. Each AP has its OWN
Recognise the BPA switches by name, and recognise a narrowing Condition: aws:PrincipalOrgID, aws:PrincipalOrgPaths, aws:PrincipalAccount, aws:PrincipalArn, aws:SourceArn, aws:SourceAccount, aws:SourceVpc, aws:SourceVpce, aws:SourceIp, aws:VpcSourceIp, sts:ExternalId, and the access-point delegation keys s3:DataAccessPointAccount / s3:DataAccessPointArn / s3:AccessPointNetworkOrigin. Any of these on a Principal '*' scopes it to a bounded caller set.
Read each bucket's BPA booleans verbatim — do not let a bucket's name or its grants tell you what they are. Each boolean being true is the safe direction: IgnorePublicAcls: true means an existing public ACL is ignored (dead); RestrictPublicBuckets: true means a public policy is denied. Do not invert it. The dominant miscalibration is asserting a boolean value to fit an expectation: a bucket named exports, public, share, partner, or cdn, or any bucket carrying an AllUsers / AuthenticatedUsers grant, invites the assumption that it must be the exposed one — and that assumption makes you misread its IgnorePublicAcls as false. In these estates the common case is the opposite: a shareable-sounding bucket with an AllUsers grant and IgnorePublicAcls: true, which is neutralised, not live. The presence of a grant is not evidence about the boolean. To keep the transcription faithful: for any bucket carrying an AllUsers / AuthenticatedUsers ACL grant or a Principal '' policy, paste that bucket's entire public-access-block.json as a verbatim JSON block** before you classify it, and read the four booleans out of the pasted block. Pasting the raw object is harder to get wrong than filling a value in from memory, which is where the misread creeps in.
2. Resolve each bucket's effective verdict (the composition)
Compose the layers; do not condemn a bucket on "Principal '*' is present" alone, and do not clear it on "BPA is on" alone. The codes:
narrowing Condition, and BPA is NOT restricting (RestrictPublicBuckets and BlockPublicPolicy both off). Live public exposure: anyone on the internet can perform the granted actions.
- POLICY-PUBLIC (critical, LIVE) -- the bucket policy allows Principal '*' with NO
no Condition and the AP's own BPA is not restricting. Data is reachable publicly through the access point even when the bucket policy and bucket BPA are clean. Auditing only the bucket misses this.
- AP-PUBLIC (critical, LIVE) -- an access point's own policy allows Principal '*' with
NOT public, so BPA does not govern it: a cross-account grant stays fully live even with all four BPA switches on. The classic misread is seeing BPA-all-on and calling the bucket locked down.
- XACCT-POLICY (high, LIVE) -- the bucket policy grants a named other account. This is
bucket owner. IgnorePublicAcls neutralises the public GROUPS, not a named canonical user, so this grant survives BPA-all-on. A TLS-only Deny in the policy does not address it.
- XACCT-ACL (high, LIVE) -- the bucket ACL grants a canonical user that is not the
group and IgnorePublicAcls is OFF. Live public via ACL, independent of the bucket policy.
More skills from anyshift-io/sre-skills
- Aiam-deceptive-escalation-auditorAudit the union of every IAM policy attached to one principal for privilege-escalation paths that no single statement reveals, and for apparent escalations that are already neutralised. Resolves the effective permission set across all attached policies (Allow minus blanket Deny), then checks the cross-statement escalation combos (iam:PassRole + a compute-launch action, policy-rewrite-in-place, function-code hijack, self-attach admin, trust-policy rewrite + assume, credential minting for another identity), the wildcard grants (Action '*' on Resource '*', service-level wildcards, Allow+NotAction), and the trust-policy exposure. Its discipline is symmetric: it does NOT flag a PassRole combo killed by an explicit Deny, an Action '*' pinned to one bucket, an sts:AssumeRole whose target does not trust back, a mutation kit capped by a permissions-boundary Deny, or a cross-account assume sealed by an unsatisfiable Condition. Reports findings with severity and a fix, then names what a single principal's policies cannot answer (the privileges of a passed/assumed role, the permissions boundary, the org SCPs). Use when asked to audit an IAM policy, role, or user for escalation, over-broad grants, or "can this principal become admin." Vendor-neutral; runs offline against the policy JSON with no Anyshift account.
- Akubectl-investigatorInvestigate a live or recent incident in a Kubernetes cluster. Anchor the window, bisect the change surface (rollouts, ConfigMaps/Secrets, RBAC, HPA/cluster changes, CronJobs), classify against four reference failure paths (OOM, DNS, cascading-failure, deploy-correlator), confirm the hypothesis with three independent signals, quantify blast radius, and propose mitigation before root cause. Use whenever an agent is asked "what is breaking in the cluster right now", "why did this pod/Deployment just page", "did the rollout cause Z", or to triage an active Kubernetes incident. Vendor-neutral by default (works with kubectl, kube-state-metrics, and whatever telemetry you have); an opt-in Anyshift integration is documented separately.
- Asg-deceptive-reachability-auditorAudit a fleet of AWS security groups for the multi-hop lateral-movement path that no single ingress rule reveals. Builds a directed reachability graph from the SG-to-SG references (an ingress rule on SG B naming SG A means a host in A can reach B), adds an internet edge for every 0.0.0.0/0 rule, then composes those edges into the transitive closure from a named entry point (the internet, or a compromised host). Reports the shortest reachable path to the crown-jewel tier, the blast radius, and any pivot/hub SG that bridges otherwise-isolated regions, each ranked by severity with a fix. Its discipline is symmetric: on a segmented or orphaned fleet where the chain does NOT reach the crown jewel, it reports clean and names the boundary instead of fabricating a path. Then it states what the SG graph alone cannot answer (live host membership, route tables, NACLs, app-layer auth). Use when asked to review a security-group fleet for lateral movement, blast radius, or whether the internet can reach a sensitive tier. Vendor-neutral; runs offline against describe-security-groups + describe-instances JSON with no Anyshift account.
- Asqs-queue-auditorAudit a single AWS SQS queue's configuration for the misconfigurations that silently drop or re-deliver messages while every attribute reads as fine. Parses the GetQueueAttributes output (and the referenced dead-letter queue), checks the redrive path (DLQ present, maxReceiveCount band, DLQ-vs-source retention ordering), the message lifecycle (poison messages aging out before they reach the DLQ, default visibility timeout, short retention), and exposure (open resource policy, encryption at rest, FIFO dedup contract). Reports findings with severity and a recommendation, then names the boundary: the questions a single queue's config cannot answer (consumer processing time, live behaviour, the IAM union, the producers and consumers on either side). Use when asked to review, harden, or sanity-check an SQS queue, or to explain why messages are going missing. Vendor-neutral; runs offline against the queue attributes with no Anyshift account.