Repository: awslabs/aws-lambda-rust-runtime (language: rust)

Issue:
Consider removing `#[serde(deny_unknown_fields)]` from request types
#666 added `#[serde(deny_unknown_fields)]` to `ApiGatewayProxyRequest` and other request types:
https://github.com/awslabs/aws-lambda-rust-runtime/blob/d513b13b4c48122602c0690f55147607f3bcc0da/lambda-events/src/event/apigw/mod.rs#L16

Using this serde attribute causes deserialization to fail if any unexpected fields are included in the request payload. This has two negative consequences:
1. Due to aws/aws-sam-cli#6442, Lambda functions written using this library cannot be tested with versions of `sam local` from the past 3 years. This is a bug in `aws-sam-cli`, not the `aws_lambda_events` crate, but it illustrates a downside to strict request parsing.
2. If the Amazon API Gateway team were to add a new feature that involved a new request field, all deployed Rust Lambda functions using the Lambda proxy integration might immediately break in production. In most APIs, adding a new field is not considered a breaking change. Unless there's a guarantee from that team never to add additional fields, this could be a ticking time bomb.

Unless there's a strong reason to reject request payloads with unknown fields that outweighs these downsides, I'd recommend removing `deny_unknown_fields`.


Discussion / hints:
I don't really remember why I added those attributes, and I don't have a strong opinion about it. Feel free to open a PR.

Fix patch:
diff --git a/lambda-events/src/event/alb/mod.rs b/lambda-events/src/event/alb/mod.rs
index 7bb1eb7f..259dce23 100644
--- a/lambda-events/src/event/alb/mod.rs
+++ b/lambda-events/src/event/alb/mod.rs
@@ -9,7 +9,6 @@ use serde::{Deserialize, Serialize};
 /// `AlbTargetGroupRequest` contains data originating from the ALB Lambda target group integration
 #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
 #[serde(rename_all = "camelCase")]
-#[serde(deny_unknown_fields)]
 pub struct AlbTargetGroupRequest {
     #[serde(with = "http_method")]
     pub http_method: Method,
diff --git a/lambda-events/src/event/apigw/mod.rs b/lambda-events/src/event/apigw/mod.rs
index 9119bdc7..fcc5ed0c 100644
--- a/lambda-events/src/event/apigw/mod.rs
+++ b/lambda-events/src/event/apigw/mod.rs
@@ -13,7 +13,6 @@ use std::collections::HashMap;
 /// `ApiGatewayProxyRequest` contains data coming from the API Gateway proxy
 #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
 #[serde(rename_all = "camelCase")]
-#[serde(deny_unknown_fields)]
 pub struct ApiGatewayProxyRequest<T1 = Value>
 where
     T1: DeserializeOwned,
@@ -120,7 +119,6 @@ where
 /// `ApiGatewayV2httpRequest` contains data coming from the new HTTP API Gateway
 #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
 #[serde(rename_all = "camelCase")]
-#[serde(deny_unknown_fields)]
 pub struct ApiGatewayV2httpRequest {
     #[serde(default, rename = "type")]
     pub kind: Option<String>,
@@ -335,7 +333,6 @@ pub struct ApiGatewayRequestIdentity {
 /// `ApiGatewayWebsocketProxyRequest` contains data coming from the API Gateway proxy
 #[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
 #[serde(rename_all = "camelCase")]
-#[serde(deny_unknown_fields)]
 pub struct ApiGatewayWebsocketProxyRequest<T1 = Value, T2 = Value>
 where
     T1: DeserializeOwned,
diff --git a/lambda-events/src/event/cloudformation/mod.rs b/lambda-events/src/event/cloudformation/mod.rs
index e2d51745..0d3f816c 100644
--- a/lambda-events/src/event/cloudformation/mod.rs
+++ b/lambda-events/src/event/cloudformation/mod.rs
@@ -18,7 +18,6 @@ where
 
 #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
 #[serde(rename_all = "PascalCase")]
-#[serde(deny_unknown_fields)]
 pub struct CreateRequest<P2 = Value>
 where
     P2: DeserializeOwned + Serialize,
@@ -37,7 +36,6 @@ where
 
 #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
 #[serde(rename_all = "PascalCase")]
-#[serde(deny_unknown_fields)]
 pub struct UpdateRequest<P1 = Value, P2 = Value>
 where
     P1: DeserializeOwned + Serialize,
@@ -60,7 +58,6 @@ where
 
 #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
 #[serde(rename_all = "PascalCase")]
-#[serde(deny_unknown_fields)]
 pub struct DeleteRequest<P2 = Value>
 where
     P2: DeserializeOwned + Serialize,
@@ -87,7 +84,6 @@ mod test {
 
     #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
     #[serde(rename_all = "PascalCase")]
-    #[serde(deny_unknown_fields)]
     struct TestProperties {
         key_1: String,
         key_2: Vec<String>,
diff --git a/lambda-http/src/deserializer.rs b/lambda-http/src/deserializer.rs
index a77f68a5..7756629c 100644
--- a/lambda-http/src/deserializer.rs
+++ b/lambda-http/src/deserializer.rs
@@ -107,7 +107,7 @@ mod tests {
 
     #[test]
     fn test_deserialize_error() {
-        let err = serde_json::from_str::<LambdaRequest>("{\"command\": \"hi\"}").unwrap_err();
+        let err = serde_json::from_str::<LambdaRequest>("{\"body\": {}}").unwrap_err();
 
         assert_eq!(ERROR_CONTEXT, err.to_string());
     }


Test patch: