Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: a couple more places where ResponseURL is logged #20977

Merged
merged 4 commits into from
Jul 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export type HandlerResponse = undefined | {
};

export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent, context: AWSLambda.Context) {
external.log(JSON.stringify(event, undefined, 2));
const sanitizedEvent = { ...event, ResponseURL: '...' };
Naumel marked this conversation as resolved.
Show resolved Hide resolved
external.log(JSON.stringify(sanitizedEvent, undefined, 2));

// ignore DELETE event when the physical resource ID is the marker that
// indicates that this DELETE is a subsequent DELETE to a failed CREATE
Expand All @@ -39,7 +40,7 @@ export async function handler(event: AWSLambda.CloudFormationCustomResourceEvent
// cloudformation (otherwise cfn waits).
// eslint-disable-next-line @typescript-eslint/no-require-imports
const userHandler: Handler = require(external.userHandlerIndex).handler;
const result = await userHandler(event, context);
const result = await userHandler(sanitizedEvent, context);

// validate user response and create the combined event
const responseEvent = renderResponse(event, result);
Expand Down
10 changes: 10 additions & 0 deletions packages/@aws-cdk/custom-resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ def is_complete(event, context):
return { 'IsComplete': is_ready }
```

> **Security Note**: the Custom Resource Provider Framework will write the value of `ResponseURL`,
> which is a pre-signed S3 URL used to report the success or failure of the Custom Resource execution
> back to CloudFormation, in a readable form to the AWS Step Functions execution history.
>
> Anybody who can list and read AWS StepFunction executions in your account will be able to write
> a fake response to this URL and make your CloudFormation deployments fail.
>
> Do not use this library if your threat model requires that you cannot trust actors who are able
> to list StepFunction executions in your account.

### Handling Lifecycle Events: onEvent

The user-defined `onEvent` AWS Lambda function is invoked whenever a resource
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ export = {
* @param cfnRequest The cloudformation custom resource event.
*/
async function onEvent(cfnRequest: AWSLambda.CloudFormationCustomResourceEvent) {
log('onEventHandler', { ...cfnRequest, ResponseURL: '...' });
const sanitizedRequest = { ...cfnRequest, ResponseURL: '...' } as const;
log('onEventHandler', sanitizedRequest);

cfnRequest.ResourceProperties = cfnRequest.ResourceProperties || { };

const onEventResult = await invokeUserFunction(consts.USER_ON_EVENT_FUNCTION_ARN_ENV, cfnRequest) as OnEventResponse;
const onEventResult = await invokeUserFunction(consts.USER_ON_EVENT_FUNCTION_ARN_ENV, sanitizedRequest) as OnEventResponse;
log('onEvent returned:', onEventResult);

// merge the request and the result from onEvent to form the complete resource event
Expand Down Expand Up @@ -57,9 +58,10 @@ async function onEvent(cfnRequest: AWSLambda.CloudFormationCustomResourceEvent)

// invoked a few times until `complete` is true or until it times out.
async function isComplete(event: AWSCDKAsyncCustomResource.IsCompleteRequest) {
log('isComplete', event);
const sanitizedRequest = { ...event, ResponseURL: '...' } as const;
log('isComplete', sanitizedRequest);

const isCompleteResult = await invokeUserFunction(consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV, event) as IsCompleteResponse;
const isCompleteResult = await invokeUserFunction(consts.USER_IS_COMPLETE_FUNCTION_ARN_ENV, sanitizedRequest) as IsCompleteResponse;
log('user isComplete returned:', isCompleteResult);

// if we are not complete, return false, and don't send a response back.
Expand All @@ -68,6 +70,7 @@ async function isComplete(event: AWSCDKAsyncCustomResource.IsCompleteRequest) {
throw new Error('"Data" is not allowed if "IsComplete" is "False"');
}

// This must be the full event, it will be deserialized in `onTimeout` to send the response to CloudFormation
throw new cfnResponse.Retry(JSON.stringify(event));
}

Expand All @@ -93,9 +96,9 @@ async function onTimeout(timeoutEvent: any) {
});
}

async function invokeUserFunction(functionArnEnv: string, payload: any) {
async function invokeUserFunction<A extends { ResponseURL: '...' }>(functionArnEnv: string, sanitizedPayload: A) {
const functionArn = getEnv(functionArnEnv);
log(`executing user function ${functionArn} with payload`, payload);
log(`executing user function ${functionArn} with payload`, sanitizedPayload);

// transient errors such as timeouts, throttling errors (429), and other
// errors that aren't caused by a bad request (500 series) are retried
Expand All @@ -104,7 +107,7 @@ async function invokeUserFunction(functionArnEnv: string, payload: any) {
FunctionName: functionArn,

// Strip 'ResponseURL' -- the downstream CR doesn't need it and can only log it by accident
Payload: JSON.stringify({ ...payload, ResponseURL: undefined }),
Payload: JSON.stringify({ ...sanitizedPayload, ResponseURL: undefined }),
});

log('user function response:', resp, typeof(resp));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"version": "20.0.0",
"files": {
"17cb4b37288c269a54418db6e9c7c3763b2d1a82bdc374be4653bd366345eccb": {
"60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26": {
"source": {
"path": "asset.17cb4b37288c269a54418db6e9c7c3763b2d1a82bdc374be4653bd366345eccb",
"path": "asset.60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26",
"packaging": "zip"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "17cb4b37288c269a54418db6e9c7c3763b2d1a82bdc374be4653bd366345eccb.zip",
"objectKey": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand All @@ -27,15 +27,15 @@
}
}
},
"7f17b1fbdb3783f2f992a94602a37c674f58741617a65f348b43ba1a7637a115": {
"ff8909e2b3e01298b53c87d97e8e745b4f0b2e4b6d29d5680c44e5da87a207a4": {
"source": {
"path": "PipelineSecurityStack.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "7f17b1fbdb3783f2f992a94602a37c674f58741617a65f348b43ba1a7637a115.json",
"objectKey": "ff8909e2b3e01298b53c87d97e8e745b4f0b2e4b6d29d5680c44e5da87a207a4.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
"S3Bucket": {
"Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
},
"S3Key": "17cb4b37288c269a54418db6e9c7c3763b2d1a82bdc374be4653bd366345eccb.zip"
"S3Key": "60767da3831353fede3cfe92efef10580a600592dec8ccbb06c051e95b9c1b26.zip"
},
"Timeout": 900,
"MemorySize": 128,
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading