@@ -296,13 +296,14 @@ func (p *sessionCredentialsProvider) Retrieve(ctx context.Context) (aws.Credenti
296296 return p .ambientCreds .Retrieve (ctx )
297297 }
298298
299- // Session policy intersects with the base role's permissions; even
300- // if the role grants accesspoint/*, this session can only touch the
301- // caller's AP and prefix. The prefix is the requesting-org UUID
302- // straight from ctx — same source as the session name — so a
303- // tampered AccessPointARN in the secret blob can't widen the prefix
304- // scope to escape into another tenant's namespace.
305- sessionPolicy := buildSessionPolicy (p .creds .AccessPointARN , info .OrgID )
299+ // Session policy intersects with the base role's permissions and
300+ // pins this session to the caller's AP ARN. Cross-tenant defense
301+ // against a tampered AccessPointARN in the secret blob lives in the
302+ // AP's resource policy (aws:userid StringEquals on the role session
303+ // name minted from the request-context org UUID), not here — keeping
304+ // the inline policy small leaves headroom in STS's packed-policy
305+ // budget for tags inherited from the caller principal.
306+ sessionPolicy := buildSessionPolicy (p .creds .AccessPointARN )
306307
307308 out , err := p .stsClient .AssumeRole (ctx , & sts.AssumeRoleInput {
308309 RoleArn : aws .String (p .creds .BaseRoleARN ),
@@ -334,15 +335,15 @@ func roleSessionName(orgUUID string) string {
334335 return "cas-" + orgUUID
335336}
336337
337- // buildSessionPolicy returns an IAM policy document that allows only the
338- // operations the backend actually performs, and only against this
339- // tenant's AP + key prefix. The Resource ARNs use the AP form
340- // "${apARN}/object/${keyPrefix}/*".
341- func buildSessionPolicy ( apARN , keyPrefix string ) string {
342- // Minimal, hand-written JSON — keeping it small reduces request
343- // payload (STS limits session policies to 2048 chars by default) .
344- return fmt . Sprintf ( `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:GetObject","s3:PutObject","s3:DeleteObject","s3:GetObjectAttributes"],"Resource":"%s/object/%s/*"}]}` ,
345- apARN , keyPrefix )
338+ // buildSessionPolicy returns an IAM policy document allowing only the
339+ // S3 actions the backend actually performs (Get/Head/Put — HeadObject
340+ // is authorized as s3:GetObject) and scoped to this tenant's AP ARN.
341+ // Cross-tenant isolation is enforced by the AP resource policy's
342+ // aws:userid check against the role session name, not by this policy;
343+ // keeping the inline document minimal preserves headroom in the STS
344+ // packed-policy budget against tags inherited from the caller .
345+ func buildSessionPolicy ( apARN string ) string {
346+ return fmt . Sprintf ( `{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Action":["s3:GetObject","s3:PutObject"],"Resource":"%s/object/*"}]}` , apARN )
346347}
347348
348349// hexSha256ToBinaryB64 decodes the hex sha and re-encodes as base64. S3
0 commit comments