@@ -19,8 +19,11 @@ import (
1919 "bytes"
2020 "context"
2121 "testing"
22+ "time"
2223
2324 "github.com/aws/aws-sdk-go-v2/aws"
25+ "github.com/aws/aws-sdk-go-v2/service/sts"
26+ ststypes "github.com/aws/aws-sdk-go-v2/service/sts/types"
2427 pb "github.com/chainloop-dev/chainloop/app/artifact-cas/api/cas/v1"
2528 robotaccount "github.com/chainloop-dev/chainloop/internal/robotaccount/cas"
2629 jwtmiddleware "github.com/go-kratos/kratos/v2/middleware/auth/jwt"
@@ -174,6 +177,83 @@ func (c *countingCredsProvider) Retrieve(_ context.Context) (aws.Credentials, er
174177 return c .creds , nil
175178}
176179
180+ // fakeSTSAssumer captures the AssumeRoleInput passed by the credentials
181+ // provider so tests can lock down the AssumeRole call shape (inline
182+ // policy vs PolicyArns) without making a real AWS call.
183+ type fakeSTSAssumer struct {
184+ lastInput * sts.AssumeRoleInput
185+ }
186+
187+ func (f * fakeSTSAssumer ) AssumeRole (_ context.Context , in * sts.AssumeRoleInput , _ ... func (* sts.Options )) (* sts.AssumeRoleOutput , error ) {
188+ f .lastInput = in
189+ return & sts.AssumeRoleOutput {
190+ Credentials : & ststypes.Credentials {
191+ AccessKeyId : aws .String ("AKFAKE" ),
192+ SecretAccessKey : aws .String ("secret" ),
193+ SessionToken : aws .String ("token" ),
194+ Expiration : aws .Time (time .Now ().Add (time .Hour )),
195+ },
196+ }, nil
197+ }
198+
199+ // TestAssumeRoleInput_DefaultsToInlinePolicy locks down the fallback
200+ // path: with an empty SessionPolicyARN the AssumeRole call must carry an
201+ // inline Policy and must NOT pass PolicyArns — otherwise an upgrade
202+ // that forgets to set the new field would silently degrade to a session
203+ // scoped only by the BaseRoleARN's identity policies.
204+ func TestAssumeRoleInput_DefaultsToInlinePolicy (t * testing.T ) {
205+ t .Parallel ()
206+
207+ fake := & fakeSTSAssumer {}
208+ p := & sessionCredentialsProvider {
209+ stsClient : fake ,
210+ creds : & Credentials {
211+ AccessPointARN : "arn:aws:s3:us-east-1:111:accesspoint/ap-a" ,
212+ BaseRoleARN : "arn:aws:iam::111:role/r" ,
213+ },
214+ }
215+ ctx := jwtmiddleware .NewContext (context .Background (),
216+ & robotaccount.Claims {OrgID : "org-A" , StoredSecretID : "foo" , BackendType : "BT" , Role : robotaccount .Uploader })
217+
218+ _ , err := p .Retrieve (ctx )
219+ require .NoError (t , err )
220+ require .NotNil (t , fake .lastInput , "stub STS must have been invoked" )
221+
222+ require .NotNil (t , fake .lastInput .Policy , "empty SessionPolicyARN must produce an inline Policy" )
223+ assert .Contains (t , * fake .lastInput .Policy , "arn:aws:s3:us-east-1:111:accesspoint/ap-a/object/*" )
224+ assert .Empty (t , fake .lastInput .PolicyArns , "inline path must not also pass PolicyArns" )
225+ }
226+
227+ // TestAssumeRoleInput_UsesPolicyArnsWhenSet locks down the opt-in path:
228+ // a configured SessionPolicyARN must reach STS via PolicyArns, and the
229+ // inline Policy must be omitted so we don't double-count against the
230+ // packed-policy budget.
231+ func TestAssumeRoleInput_UsesPolicyArnsWhenSet (t * testing.T ) {
232+ t .Parallel ()
233+
234+ const managedARN = "arn:aws:iam::111:policy/chainloop-cas-session"
235+ fake := & fakeSTSAssumer {}
236+ p := & sessionCredentialsProvider {
237+ stsClient : fake ,
238+ creds : & Credentials {
239+ AccessPointARN : "arn:aws:s3:us-east-1:111:accesspoint/ap-a" ,
240+ BaseRoleARN : "arn:aws:iam::111:role/r" ,
241+ SessionPolicyARN : managedARN ,
242+ },
243+ }
244+ ctx := jwtmiddleware .NewContext (context .Background (),
245+ & robotaccount.Claims {OrgID : "org-A" , StoredSecretID : "foo" , BackendType : "BT" , Role : robotaccount .Uploader })
246+
247+ _ , err := p .Retrieve (ctx )
248+ require .NoError (t , err )
249+ require .NotNil (t , fake .lastInput , "stub STS must have been invoked" )
250+
251+ assert .Nil (t , fake .lastInput .Policy , "PolicyArns path must NOT also send an inline Policy" )
252+ require .Len (t , fake .lastInput .PolicyArns , 1 , "expected exactly one PolicyArn descriptor" )
253+ require .NotNil (t , fake .lastInput .PolicyArns [0 ].Arn )
254+ assert .Equal (t , managedARN , * fake .lastInput .PolicyArns [0 ].Arn )
255+ }
256+
177257// --- helpers -----------------------------------------------------------
178258
179259// newTestBackend constructs a fully wired *Backend that uses static dummy
0 commit comments