Skip to content

Commit 9ca6ed5

Browse files
committed
fix(idempotency): pass by value on idem key to guard against inadvert mutations
This protects against functions that inadvertently change the incoming event data
1 parent f50ec86 commit 9ca6ed5

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

aws_lambda_powertools/utilities/idempotency/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import logging
22
from typing import Any, Callable, Dict, Optional, Tuple
3+
from copy import deepcopy
34

45
from aws_lambda_powertools.utilities.idempotency.config import IdempotencyConfig
56
from aws_lambda_powertools.utilities.idempotency.exceptions import (
@@ -69,7 +70,7 @@ def __init__(
6970
Function keyword arguments
7071
"""
7172
self.function = function
72-
self.data = _prepare_data(function_payload)
73+
self.data = deepcopy(_prepare_data(function_payload))
7374
self.fn_args = function_args
7475
self.fn_kwargs = function_kwargs
7576

tests/functional/idempotency/test_idempotency.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,40 @@ def lambda_handler(event, context):
274274
stubber.assert_no_pending_responses()
275275
stubber.deactivate()
276276

277+
@pytest.mark.parametrize("idempotency_config", [{"use_local_cache": False}, {"use_local_cache": True}], indirect=True)
278+
def test_idempotent_lambda_first_execution_event_mutation(
279+
idempotency_config: IdempotencyConfig,
280+
persistence_store: DynamoDBPersistenceLayer,
281+
lambda_apigw_event,
282+
expected_params_update_item,
283+
expected_params_put_item,
284+
lambda_response,
285+
serialized_lambda_response,
286+
deserialized_lambda_response,
287+
hashed_idempotency_key,
288+
lambda_context,
289+
):
290+
"""
291+
Test idempotent decorator where lambda_handler is mutates the event
292+
"""
293+
294+
stubber = stub.Stubber(persistence_store.table.meta.client)
295+
ddb_response = {}
296+
297+
stubber.add_response("put_item", ddb_response, expected_params_put_item)
298+
stubber.add_response("update_item", ddb_response, expected_params_update_item)
299+
stubber.activate()
300+
301+
@idempotent(config=idempotency_config, persistence_store=persistence_store)
302+
def lambda_handler(event, context):
303+
event.popitem()
304+
return lambda_response
305+
306+
lambda_handler(lambda_apigw_event, lambda_context)
307+
308+
stubber.assert_no_pending_responses()
309+
stubber.deactivate()
310+
277311

278312
@pytest.mark.parametrize("idempotency_config", [{"use_local_cache": False}, {"use_local_cache": True}], indirect=True)
279313
def test_idempotent_lambda_expired(

0 commit comments

Comments
 (0)