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