Repository: getmoto/moto
Pull request #7491 (2024-03-19 13:51:16)

Lambda: throw error on empty resource policy
Closes https://github.com/getmoto/moto/issues/7475


Patch:
diff --git a/moto/awslambda/exceptions.py b/moto/awslambda/exceptions.py
index 85c91ecf6..fef642838 100644
--- a/moto/awslambda/exceptions.py
+++ b/moto/awslambda/exceptions.py
@@ -64,7 +64,7 @@ class UnknownFunctionException(LambdaClientError):
         super().__init__("ResourceNotFoundException", f"Function not found: {arn}")
 
 
-class FunctionUrlConfigNotFound(LambdaClientError):
+class GenericResourcNotFound(LambdaClientError):
     code = 404
 
     def __init__(self) -> None:
diff --git a/moto/awslambda/models.py b/moto/awslambda/models.py
index 46ac998f4..5df47ebd5 100644
--- a/moto/awslambda/models.py
+++ b/moto/awslambda/models.py
@@ -45,7 +45,7 @@ from moto.utilities.utils import load_resource_as_bytes
 from .exceptions import (
     ConflictException,
     CrossAccountNotAllowed,
-    FunctionUrlConfigNotFound,
+    GenericResourcNotFound,
     InvalidParameterValueException,
     InvalidRoleFormat,
     UnknownAliasException,
@@ -1216,7 +1216,7 @@ class LambdaFunction(CloudFormationModel, DockerModel):
 
     def get_url_config(self) -> "FunctionUrlConfig":
         if not self.url_config:
-            raise FunctionUrlConfigNotFound()
+            raise GenericResourcNotFound()
         return self.url_config
 
     def update_url_config(self, config: Dict[str, Any]) -> "FunctionUrlConfig":
diff --git a/moto/awslambda/policy.py b/moto/awslambda/policy.py
index 20f56fedb..6facc06ac 100644
--- a/moto/awslambda/policy.py
+++ b/moto/awslambda/policy.py
@@ -2,6 +2,7 @@ import json
 from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, TypeVar
 
 from moto.awslambda.exceptions import (
+    GenericResourcNotFound,
     PreconditionFailedException,
     UnknownPolicyException,
 )
@@ -26,6 +27,8 @@ class Policy:
         return json.dumps(p)
 
     def get_policy(self) -> Dict[str, Any]:
+        if not self.statements:
+            raise GenericResourcNotFound()
         return {
             "Policy": {
                 "Version": "2012-10-17",
diff --git a/moto/kinesis/responses.py b/moto/kinesis/responses.py
index 994583437..ba873a7ff 100644
--- a/moto/kinesis/responses.py
+++ b/moto/kinesis/responses.py
@@ -53,7 +53,14 @@ class KinesisResponse(BaseResponse):
             has_more_streams = True
 
         return json.dumps(
-            {"HasMoreStreams": has_more_streams, "StreamNames": streams_resp}
+            {
+                "HasMoreStreams": has_more_streams,
+                "StreamNames": streams_resp,
+                "StreamSummaries": [
+                    stream.to_json_summary()["StreamDescriptionSummary"]
+                    for stream in streams
+                ],
+            }
         )
 
     def delete_stream(self) -> str:


Test patch:
diff --git a/tests/test_awslambda/test_lambda_policy.py b/tests/test_awslambda/test_lambda_policy.py
index 524f28254..8f477fcb5 100644
--- a/tests/test_awslambda/test_lambda_policy.py
+++ b/tests/test_awslambda/test_lambda_policy.py
@@ -230,9 +230,13 @@ def test_remove_function_permission(key):
 
     remove = conn.remove_permission(FunctionName=name_or_arn, StatementId="1")
     assert remove["ResponseMetadata"]["HTTPStatusCode"] == 204
-    policy = conn.get_policy(FunctionName=name_or_arn)["Policy"]
-    policy = json.loads(policy)
-    assert policy["Statement"] == []
+
+    with pytest.raises(ClientError) as exc:
+        conn.get_policy(FunctionName=name_or_arn)["Policy"]
+
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ResourceNotFoundException"
+    assert err["Message"] == "The resource you requested does not exist."
 
 
 @pytest.mark.parametrize("key", ["FunctionName", "FunctionArn"])
@@ -275,9 +279,12 @@ def test_remove_function_permission__with_qualifier(key):
         FunctionName=name_or_arn, StatementId="1", Qualifier="2"
     )
     assert remove["ResponseMetadata"]["HTTPStatusCode"] == 204
-    policy = conn.get_policy(FunctionName=name_or_arn, Qualifier="2")["Policy"]
-    policy = json.loads(policy)
-    assert policy["Statement"] == []
+    with pytest.raises(ClientError) as exc:
+        conn.get_policy(FunctionName=name_or_arn, Qualifier="2")
+
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ResourceNotFoundException"
+    assert err["Message"] == "The resource you requested does not exist."
 
 
 @mock_aws
@@ -292,3 +299,31 @@ def test_get_unknown_policy():
         err["Message"]
         == "Function not found: arn:aws:lambda:us-west-2:123456789012:function:unknown"
     )
+
+
+@mock_aws
+def test_policy_error_if_blank_resource_policy():
+    # Setup
+    conn = boto3.client("lambda", _lambda_region)
+    zip_content = get_test_zip_file1()
+    function_name = str(uuid4())[0:6]
+    conn.create_function(
+        FunctionName=function_name,
+        Runtime=PYTHON_VERSION,
+        Role=(get_role_name()),
+        Handler="lambda_function.handler",
+        Code={"ZipFile": zip_content},
+        Description="test lambda function",
+        Timeout=3,
+        MemorySize=128,
+        Publish=True,
+    )
+
+    # Execute
+    with pytest.raises(ClientError) as exc:
+        conn.get_policy(FunctionName=function_name)
+
+    # Verify
+    err = exc.value.response["Error"]
+    assert err["Code"] == "ResourceNotFoundException"
+    assert err["Message"] == "The resource you requested does not exist."
diff --git a/tests/test_kinesis/test_kinesis.py b/tests/test_kinesis/test_kinesis.py
index 954e2baad..b4e4755cf 100644
--- a/tests/test_kinesis/test_kinesis.py
+++ b/tests/test_kinesis/test_kinesis.py
@@ -137,6 +137,26 @@ def test_describe_stream_summary():
     assert stream["StreamName"] == stream_name
 
 
+@mock_aws
+def test_list_streams_stream_discription():
+    conn = boto3.client("kinesis", region_name="us-west-2")
+
+    for i in range(3):
+        conn.create_stream(StreamName=f"stream{i}", ShardCount=i+1)
+
+    resp = conn.list_streams()
+    assert len(resp["StreamSummaries"]) == 3
+    for i, stream in enumerate(resp["StreamSummaries"]):
+        stream_name = f"stream{i}"
+        assert stream["StreamName"] == stream_name
+        assert (
+            stream["StreamARN"]
+            == f"arn:aws:kinesis:us-west-2:{ACCOUNT_ID}:stream/{stream_name}"
+        )
+        assert stream["StreamStatus"] == "ACTIVE"
+        assert stream.get("StreamCreationTimestamp")
+
+
 @mock_aws
 def test_basic_shard_iterator():
     client = boto3.client("kinesis", region_name="us-west-1")
diff --git a/tests/test_kinesis/test_server.py b/tests/test_kinesis/test_server.py
index 8eb20016e..1ac1beb7d 100644
--- a/tests/test_kinesis/test_server.py
+++ b/tests/test_kinesis/test_server.py
@@ -12,4 +12,8 @@ def test_list_streams():
     res = test_client.get("/?Action=ListStreams")
 
     json_data = json.loads(res.data.decode("utf-8"))
-    assert json_data == {"HasMoreStreams": False, "StreamNames": []}
+    assert json_data == {
+        "HasMoreStreams": False,
+        "StreamNames": [],
+        "StreamSummaries": []
+    }