Skip to content

Commit

Permalink
fix(custom-resources): assumedRole from AwsCustomResource invocation …
Browse files Browse the repository at this point in the history
…leaked to next execution (#15776)

Fixes #15425

Credit to @nicolai-shape for proposing the fix itself.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
theipster authored Dec 13, 2021
1 parent 5497525 commit e138188
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -161,26 +161,26 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent

if (call) {

let credentials;
if (call.assumedRoleArn) {

const timestamp = (new Date()).getTime();

const params = {
RoleArn: call.assumedRoleArn,
RoleSessionName: `${timestamp}-${physicalResourceId}`.substring(0, 64),
};

AWS.config.credentials = new AWS.ChainableTemporaryCredentials({
credentials = new AWS.ChainableTemporaryCredentials({
params: params,
});

}

if (!Object.prototype.hasOwnProperty.call(AWS, call.service)) {
throw Error(`Service ${call.service} does not exist in AWS SDK version ${AWS.VERSION}.`);
}
const awsService = new (AWS as any)[call.service]({
apiVersion: call.apiVersion,
credentials: credentials,
region: call.region,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import * as AWS from 'aws-sdk';
import { PhysicalResourceId } from '../../../lib';
import { handler } from '../../../lib/aws-custom-resource/runtime/index';

/* eslint-disable no-console */
console.log = jest.fn();

jest.mock('aws-sdk', () => {
return {
...jest.requireActual('aws-sdk'),
SSM: jest.fn(() => {
return {
config: {
apiVersion: 'apiVersion',
region: 'eu-west-1',
},
getParameter: () => {
return {
promise: async () => {},
};
},
};
}),
};
});

jest.mock('https', () => {
return {
request: (_: any, callback: () => void) => {
return {
on: () => undefined,
write: () => true,
end: callback,
};
},
};
});

afterEach(() => {
jest.clearAllMocks();
});

test('SDK global credentials are never set', async () => {
// WHEN
await handler({
LogicalResourceId: 'logicalResourceId',
RequestId: 'requestId',
RequestType: 'Create',
ResponseURL: 'responseUrl',
ResourceProperties: {
Create: JSON.stringify({
action: 'getParameter',
assumedRoleArn: 'arn:aws:iam::123456789012:role/CoolRole',
parameters: {
Name: 'foo',
},
physicalResourceId: PhysicalResourceId.of('id'),
service: 'SSM',
}),
ServiceToken: 'serviceToken',
},
ResourceType: 'resourceType',
ServiceToken: 'serviceToken',
StackId: 'stackId',
}, {} as AWSLambda.Context);

// THEN
expect(AWS.config).toBeInstanceOf(AWS.Config);
expect(AWS.config.credentials).toBeNull();
});

test('SDK credentials are not persisted across subsequent invocations', async () => {
// GIVEN
const mockCreds = new AWS.ChainableTemporaryCredentials();
jest.spyOn(AWS, 'ChainableTemporaryCredentials').mockReturnValue(mockCreds);

// WHEN
await handler({
LogicalResourceId: 'logicalResourceId',
RequestId: 'requestId',
RequestType: 'Create',
ResponseURL: 'responseUrl',
ResourceProperties: {
Create: JSON.stringify({
action: 'getParameter',
parameters: {
Name: 'foo',
},
physicalResourceId: PhysicalResourceId.of('id'),
service: 'SSM',
}),
ServiceToken: 'serviceToken',
},
ResourceType: 'resourceType',
ServiceToken: 'serviceToken',
StackId: 'stackId',
}, {} as AWSLambda.Context);

await handler({
LogicalResourceId: 'logicalResourceId',
RequestId: 'requestId',
RequestType: 'Create',
ResponseURL: 'responseUrl',
ResourceProperties: {
Create: JSON.stringify({
action: 'getParameter',
assumedRoleArn: 'arn:aws:iam::123456789012:role/CoolRole',
parameters: {
Name: 'foo',
},
physicalResourceId: PhysicalResourceId.of('id'),
service: 'SSM',
}),
ServiceToken: 'serviceToken',
},
ResourceType: 'resourceType',
ServiceToken: 'serviceToken',
StackId: 'stackId',
}, {} as AWSLambda.Context);

await handler({
LogicalResourceId: 'logicalResourceId',
RequestId: 'requestId',
RequestType: 'Create',
ResponseURL: 'responseUrl',
ResourceProperties: {
Create: JSON.stringify({
action: 'getParameter',
parameters: {
Name: 'foo',
},
physicalResourceId: PhysicalResourceId.of('id'),
service: 'SSM',
}),
ServiceToken: 'serviceToken',
},
ResourceType: 'resourceType',
ServiceToken: 'serviceToken',
StackId: 'stackId',
}, {} as AWSLambda.Context);

// THEN
expect(AWS.SSM).toHaveBeenNthCalledWith(1, {
apiVersion: undefined,
credentials: undefined,
region: undefined,
});
expect(AWS.SSM).toHaveBeenNthCalledWith(2, {
apiVersion: undefined,
credentials: mockCreds,
region: undefined,
});
expect(AWS.SSM).toHaveBeenNthCalledWith(3, {
apiVersion: undefined,
credentials: undefined,
region: undefined,
});
});

0 comments on commit e138188

Please sign in to comment.