55 Attributes ,
66 diag ,
77 Context as OtelContext ,
8+ context as otelContext ,
89 trace ,
910 context ,
1011 propagation ,
@@ -27,6 +28,7 @@ import {
2728 AWSXRAY_TRACE_ID_HEADER_CAPITALIZED ,
2829 customExtractor ,
2930 headerGetter ,
31+ SKIP_CREDENTIAL_CAPTURE_KEY ,
3032} from './../../src/patches/instrumentation-patch' ;
3133import * as sinon from 'sinon' ;
3234import { AWSXRAY_TRACE_ID_HEADER , AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray' ;
@@ -37,6 +39,7 @@ import * as nock from 'nock';
3739import { ReadableSpan , Span as SDKSpan } from '@opentelemetry/sdk-trace-base' ;
3840import { getTestSpans } from '@opentelemetry/contrib-test-utils' ;
3941import { instrumentationConfigs } from '../../src/register' ;
42+ import { STS } from '@aws-sdk/client-sts' ;
4043
4144// It is assumed that bedrock.test.ts has already registered the
4245// necessary instrumentations for testing by calling:
@@ -692,6 +695,54 @@ describe('InstrumentationPatchTest', () => {
692695 } ) ;
693696 } ) ;
694697
698+ it ( 'prevents recursion when credentials provider makes STS calls' , async ( ) => {
699+ let credentialsCallCount = 0 ;
700+ let skipCredentialCaptureValue = false ;
701+
702+ // Create recursive credentials provider
703+ const recursiveCredentialsProvider = async ( ) : Promise < any > => {
704+ credentialsCallCount ++ ;
705+ if ( otelContext . active ( ) . getValue ( SKIP_CREDENTIAL_CAPTURE_KEY ) ) {
706+ skipCredentialCaptureValue = true ;
707+ }
708+
709+ const credentialsStsClient = new STS ( {
710+ region : 'us-east-1' ,
711+ credentials : recursiveCredentialsProvider ,
712+ } ) ;
713+ await credentialsStsClient
714+ . assumeRoleWithWebIdentity ( {
715+ RoleArn : 'arn:aws:iam::123456789012:role/test-role' ,
716+ RoleSessionName : 'test-session' ,
717+ WebIdentityToken : 'mock-token' ,
718+ } )
719+ . catch ( ( err : any ) => { } ) ;
720+ return { accessKeyId : 'sts-access-key' , secretAccessKey : 'secret' } ;
721+ } ;
722+
723+ // Create main client with recursive credentials provider
724+ const mainClient = new STS ( {
725+ region : 'us-east-1' ,
726+ credentials : recursiveCredentialsProvider ,
727+ } ) ;
728+
729+ // Mock HTTP responses
730+ nock ( 'https://sts.us-east-1.amazonaws.com' )
731+ . post ( '/' )
732+ . reply ( 200 , '<GetCallerIdentityResponse></GetCallerIdentityResponse>' ) ;
733+
734+ // Make Lambda call - this triggers credential extraction which calls STS
735+ await mainClient . getCallerIdentity ( { } ) . catch ( ( err : any ) => { } ) ;
736+
737+ const testSpans = getTestSpans ( ) ;
738+ const stsSpans = testSpans . filter ( s => s . name . includes ( 'GetCallerIdentity' ) ) ;
739+
740+ expect ( stsSpans . length ) . toBe ( 1 ) ;
741+ expect ( credentialsCallCount ) . toBe ( 2 ) ; // 1. Main client needs credentials 2. Nested STS client needs credentials
742+ expect ( skipCredentialCaptureValue ) . toBe ( true ) ; // Should detect skip key in context
743+ expect ( stsSpans [ 0 ] . attributes [ AWS_ATTRIBUTE_KEYS . AWS_AUTH_ACCOUNT_ACCESS_KEY ] ) . toBe ( 'sts-access-key' ) ;
744+ } ) ;
745+
695746 it ( 'injects trace context header into request via propagator' , async ( ) => {
696747 lambda = new Lambda ( {
697748 region : region ,
0 commit comments