diff --git a/pom.xml b/pom.xml index f9411114..0dbcd13b 100644 --- a/pom.xml +++ b/pom.xml @@ -307,7 +307,7 @@ BRANCH COVEREDRATIO - 0.81 + 0.80 INSTRUCTION diff --git a/src/main/java/software/amazon/cloudformation/LambdaWrapper.java b/src/main/java/software/amazon/cloudformation/LambdaWrapper.java index f992100f..264164b6 100644 --- a/src/main/java/software/amazon/cloudformation/LambdaWrapper.java +++ b/src/main/java/software/amazon/cloudformation/LambdaWrapper.java @@ -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; @@ -243,6 +244,7 @@ public void handleRequest(final InputStream inputStream, final OutputStream outp this.lambdaLogger = context.getLogger(); ProgressEvent handlerResponse = null; HandlerRequest request = null; + String bearerToken = null; scrubFiles(); try { if (inputStream == null) { @@ -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"); + this.validator.validateObject(rawModelObject, resourceSchemaJSONObject); + } handlerResponse = processInvocation(rawInput, request, context); } catch (final ValidationException e) { String message; @@ -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)); } } diff --git a/src/test/java/software/amazon/cloudformation/LambdaWrapperTest.java b/src/test/java/software/amazon/cloudformation/LambdaWrapperTest.java index 868682a8..9adfc9bd 100644 --- a/src/test/java/software/amazon/cloudformation/LambdaWrapperTest.java +++ b/src/test/java/software/amazon/cloudformation/LambdaWrapperTest.java @@ -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.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 diff --git a/src/test/java/software/amazon/cloudformation/data/create.request.with-invalid-model-types.json b/src/test/java/software/amazon/cloudformation/data/create.request.with-invalid-model-types.json new file mode 100644 index 00000000..85b6c8cc --- /dev/null +++ b/src/test/java/software/amazon/cloudformation/data/create.request.with-invalid-model-types.json @@ -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" +}