@@ -32,9 +32,34 @@ module DynamoDBSupport {
3232 import SET = AwsCryptographyDbEncryptionSdkStructuredEncryptionTypes
3333 import NN = DynamoDbNormalizeNumber
3434
35+ // Maximum length, in characters, of any single DynamoDB expression string
36+ // (KeyConditionExpression, FilterExpression, ConditionExpression, etc.) that
37+ // this library will parse. Matches DynamoDB's documented 4 KB server-side
38+ // expression-string limit and bounds the work done by the client-side
39+ // expression parser, preventing unbounded resource consumption from
40+ // pathologically long inputs.
41+ const MAX_EXPRESSION_LENGTH : nat := 4096
42+
43+ // Reject expression strings longer than MAX_EXPRESSION_LENGTH before they
44+ // reach the parser. `name` is the human-readable field name used in the
45+ // error message (e.g. "KeyConditionExpression").
46+ function method ValidateExpressionLength (expr : Option <string >, name : string )
47+ : Result< bool , Error>
48+ {
49+ if expr. None? then
50+ Success (true)
51+ else if |expr. value| <= MAX_EXPRESSION_LENGTH then
52+ Success (true)
53+ else
54+ Failure (E(name + " exceeds maximum length of "
55+ + String.Base10Int2String(MAX_EXPRESSION_LENGTH as int)
56+ + " characters. "))
57+ }
58+
3559 method GetNumberOfQueries (search : SearchableEncryptionInfo .BeaconVersion, query : DDB .QueryInput)
3660 returns (output : Result< PartitionCount, Error> )
3761 {
62+ var _ :- ValidateExpressionLength (query.KeyConditionExpression, "KeyConditionExpression");
3863 var numberOfQueries :- Filter. GetNumQueries (
3964 search,
4065 query.KeyConditionExpression,
@@ -281,6 +306,8 @@ module DynamoDBSupport {
281306 returns (output : Result< (Option< DDB. ExpressionAttributeValueMap> , PartitionNumber), Error> )
282307 ensures output. Success? ==> output. value. 1 < search. numPartitions
283308 {
309+ var _ :- ValidateExpressionLength (keyExpr, "KeyConditionExpression");
310+ var _ :- ValidateExpressionLength (filterExpr, "FilterExpression");
284311 if search. numPartitions <= 1 {
285312 :- Need (values.None? || PartitionName !in values.value, E("If no partitions are configured, do not specify " + PartitionName));
286313 return Success ((values, 0));
@@ -309,6 +336,8 @@ module DynamoDBSupport {
309336 returns (output : Result< DDB. QueryInput, Error> )
310337 modifies if search. Some? then search. value. Modifies () else {}
311338 {
339+ var _ :- ValidateExpressionLength (req.KeyConditionExpression, "KeyConditionExpression");
340+ var _ :- ValidateExpressionLength (req.FilterExpression, "FilterExpression");
312341 if search. None? {
313342 var _ :- Filter. TestBeaconize (
314343 actions,
@@ -343,6 +372,8 @@ module DynamoDBSupport {
343372 ensures output. Success? ==> output. value. Items. Some?
344373 modifies if search. Some? then search. value. Modifies () else {}
345374 {
375+ var _ :- ValidateExpressionLength (req.KeyConditionExpression, "KeyConditionExpression");
376+ var _ :- ValidateExpressionLength (req.FilterExpression, "FilterExpression");
346377 if search. None? {
347378 var trimmedItems := Seq. Map (i => DoRemoveBeacons(i), resp. Items. value);
348379 return Success (resp.(Items := Some(trimmedItems)));
@@ -386,6 +417,7 @@ module DynamoDBSupport {
386417 returns (output : Result< DDB. ScanInput, Error> )
387418 modifies if search. Some? then search. value. Modifies () else {}
388419 {
420+ var _ :- ValidateExpressionLength (req.FilterExpression, "FilterExpression");
389421 if search. None? {
390422 var _ :- Filter. TestBeaconize (
391423 actions,
@@ -414,6 +446,7 @@ module DynamoDBSupport {
414446 ensures ret. Success? ==> ret. value. Items. Some?
415447 modifies if search. Some? then search. value. Modifies () else {}
416448 {
449+ var _ :- ValidateExpressionLength (req.FilterExpression, "FilterExpression");
417450 if search. None? {
418451 var trimmedItems := Seq. Map (i => DoRemoveBeacons(i), resp. Items. value);
419452 return Success (resp.(Items := Some(trimmedItems)));
0 commit comments