Skip to content
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
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>0.81</minimum>
<minimum>0.80</minimum>
</limit>
<limit>
<counter>INSTRUCTION</counter>
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/software/amazon/cloudformation/LambdaWrapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
Expand Down Expand Up @@ -243,6 +244,7 @@ public void handleRequest(final InputStream inputStream, final OutputStream outp
this.lambdaLogger = context.getLogger();
ProgressEvent<ResourceT, CallbackT> handlerResponse = null;
HandlerRequest<ResourceT, CallbackT> request = null;
String bearerToken = null;
scrubFiles();
try {
if (inputStream == null) {
Expand All @@ -252,9 +254,16 @@ public void handleRequest(final InputStream inputStream, final OutputStream outp
String input = this.serializer.decompress(IOUtils.toString(inputStream, StandardCharsets.UTF_8));

JSONObject rawInput = new JSONObject(new JSONTokener(input));
bearerToken = rawInput.getString("bearerToken");

// deserialize incoming payload to modelled request
request = this.serializer.deserialize(input, typeReference);
try {
request = this.serializer.deserialize(input, typeReference);
} catch (MismatchedInputException e) {
JSONObject resourceSchemaJSONObject = provideResourceSchemaJSONObject();
JSONObject rawModelObject = rawInput.getJSONObject("requestData").getJSONObject("resourceProperties");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens to exceptions thrown here, if the JSON payload is completely malformed and doesn't contain the fields you're expecting?

Copy link
Author

@wbingli wbingli Jun 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. The JSON payload completely malformed only if it invoked not by CloudFormation, which this case is really unlikely, or someone debug with local sam.

  2. The above this.serializer.deserialize(input, typeReference) will throw different exception if the payload is malformed, it won't be catched by MismatchedInputException. We have unit test to cover the malformed input (0 bytes or invalid json), those passed as expected.

  3. The only case I can think that rawInput.getJSONObject("requestData").getJSONObject("resourceProperties"); is null or malformed requestData, the whole payload HandlerRequest has MismatchedInputException in other fields. However, I don't think it's case we need to worry about.

this.validator.validateObject(rawModelObject, resourceSchemaJSONObject);
}
handlerResponse = processInvocation(rawInput, request, context);
} catch (final ValidationException e) {
String message;
Expand Down Expand Up @@ -283,8 +292,7 @@ public void handleRequest(final InputStream inputStream, final OutputStream outp
} finally {
// A response will be output on all paths, though CloudFormation will
// not block on invoking the handlers, but rather listen for callbacks
writeResponse(outputStream,
createProgressResponse(handlerResponse, request != null ? request.getBearerToken() : null));
writeResponse(outputStream, createProgressResponse(handlerResponse, bearerToken));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,31 @@ public void invokeHandler_SchemaValidationFailure(final String requestDataPath,
}
}

@Test
public void invokeHandler_invalidModelTypes_causesSchemaValidationFailure() throws IOException {
// use actual validator to verify behaviour
final WrapperOverride wrapper = new WrapperOverride(callbackAdapter, platformCredentialsProvider,
providerLoggingCredentialsProvider, platformEventsLogger,
providerEventsLogger, platformMetricsPublisher,
providerMetricsPublisher, scheduler, new Validator() {
}, httpClient);

wrapper.setTransformResponse(resourceHandlerRequest);

try (final InputStream in = loadRequestStream("create.request.with-invalid-model-types.json");
final OutputStream out = new ByteArrayOutputStream()) {
final Context context = getLambdaContext();

wrapper.handleRequest(in, out, context);

// verify output response
verifyHandlerResponse(out,
HandlerResponse.<TestModel>builder().bearerToken("123456").errorCode("InvalidRequest")
.operationStatus(OperationStatus.FAILED)
.message("Model validation failed (#/property1: expected type: String, found: JSONArray)").build());
}
}

@Test
public void invokeHandler_extraneousModelFields_causesSchemaValidationFailure() throws IOException {
// use actual validator to verify behaviour
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"awsAccountId": "123456789012",
"bearerToken": "123456",
"region": "us-east-1",
"action": "CREATE",
"responseEndpoint": "https://cloudformation.us-west-2.amazonaws.com",
"resourceType": "AWS::Test::TestModel",
"resourceTypeVersion": "1.0",
"requestContext": {},
"requestData": {
"callerCredentials": {
"accessKeyId": "IASAYK835GAIFHAHEI23",
"secretAccessKey": "66iOGPN5LnpZorcLr8Kh25u8AbjHVllv5/poh2O0",
"sessionToken": "lameHS2vQOknSHWhdFYTxm2eJc1JMn9YBNI4nV4mXue945KPL6DHfW8EsUQT5zwssYEC1NvYP9yD6Y5s5lKR3chflOHPFsIe6eqg"
},
"platformCredentials": {
"accessKeyId": "32IEHAHFIAG538KYASAI",
"secretAccessKey": "0O2hop/5vllVHjbA8u52hK8rLcroZpnL5NPGOi66",
"sessionToken": "gqe6eIsFPHOlfhc3RKl5s5Y6Dy9PYvN1CEYsswz5TQUsE8WfHD6LPK549euXm4Vn4INBY9nMJ1cJe2mxTYFdhWHSnkOQv2SHemal"
},
"providerCredentials": {
"accessKeyId": "HDI0745692Y45IUTYR78",
"secretAccessKey": "4976TUYVI234/5GW87ERYG823RF87GY9EIUH452I3",
"sessionToken": "842HYOFIQAEUDF78R8T7IU43HSADYGIFHBJSDHFA87SDF9PYvN1CEYASDUYFT5TQ97YASIHUDFAIUEYRISDKJHFAYSUDTFSDFADS"
},
"providerLogGroupName": "providerLoggingGroupName",
"logicalResourceId": "myBucket",
"resourceProperties": {
"property1": [
"list-instead-of-string"
],
"property2": 123
},
"systemTags": {
"aws:cloudformation:stack-id": "SampleStack"
},
"stackTags": {
"tag1": "abc"
},
"previousStackTags": {
"tag1": "def"
}
},
"stackId": "arn:aws:cloudformation:us-east-1:123456789012:stack/SampleStack/e722ae60-fe62-11e8-9a0e-0ae8cc519968"
}