Skip to content

APIGW: add validation for AWS ARN in PutIntegration #12324

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
from localstack.services.edge import ROUTER
from localstack.services.moto import call_moto, call_moto_with_request
from localstack.services.plugins import ServiceLifecycleHook
from localstack.utils.aws.arns import get_partition
from localstack.utils.aws.arns import InvalidArnException, get_partition, parse_arn
from localstack.utils.collections import (
DelSafeDict,
PaginatedList,
Expand Down Expand Up @@ -1937,13 +1937,32 @@ def put_integration(
f"Member must satisfy enum value set: [HTTP, MOCK, AWS_PROXY, HTTP_PROXY, AWS]",
)

elif integration_type == IntegrationType.AWS_PROXY:
integration_uri = request.get("uri") or ""
if ":lambda:" not in integration_uri and ":firehose:" not in integration_uri:
elif integration_type in (IntegrationType.AWS_PROXY, IntegrationType.AWS):
if not request.get("integrationHttpMethod"):
raise BadRequestException("Enumeration value for HttpMethod must be non-empty")
if not (integration_uri := request.get("uri") or "").startswith("arn:"):
raise BadRequestException("Invalid ARN specified in the request")

try:
parsed_arn = parse_arn(integration_uri)
except InvalidArnException:
raise BadRequestException("Invalid ARN specified in the request")

if not any(
parsed_arn["resource"].startswith(action_type) for action_type in ("path", "action")
):
raise BadRequestException("AWS ARN for integration must contain path or action")

if integration_type == IntegrationType.AWS_PROXY and (
parsed_arn["account"] != "lambda"
or not parsed_arn["resource"].startswith("path/2015-03-31/functions/")
):
# the Firehose message is misleading, this is not implemented in AWS
raise BadRequestException(
"Integrations of type 'AWS_PROXY' currently only supports "
"Lambda function and Firehose stream invocations."
)

moto_rest_api = get_moto_rest_api(context=context, rest_api_id=request.get("restApiId"))
resource = moto_rest_api.resources.get(request.get("resourceId"))
if not resource:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3509,7 +3509,7 @@
}
},
"tests/aws/services/apigateway/test_apigateway_api.py::TestApigatewayIntegration::test_put_integration_response_validation": {
"recorded-date": "21-08-2024, 15:09:28",
"recorded-date": "03-03-2025, 14:27:24",
"recorded-content": {
"put-integration-wrong-method": {
"Error": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@
"last_validated_date": "2024-12-12T10:46:41+00:00"
},
"tests/aws/services/apigateway/test_apigateway_api.py::TestApigatewayIntegration::test_put_integration_response_validation": {
"last_validated_date": "2024-08-21T15:09:28+00:00"
"last_validated_date": "2025-03-03T14:27:24+00:00"
},
"tests/aws/services/apigateway/test_apigateway_api.py::TestApigatewayIntegration::test_put_integration_wrong_type": {
"last_validated_date": "2024-04-15T20:48:47+00:00"
Expand Down
80 changes: 80 additions & 0 deletions tests/aws/services/apigateway/test_apigateway_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,86 @@ def invoke_api_with_multi_value_header(url):
snapshot.match("invocation-hardcoded", response_hardcoded.json())


@markers.aws.validated
def test_put_integration_aws_proxy_uri(
aws_client,
create_rest_apigw,
create_lambda_function,
create_role_with_policy,
snapshot,
region_name,
):
api_id, _, root_resource_id = create_rest_apigw(
name=f"test-api-{short_uid()}",
description="APIGW test PutIntegration AWS_PROXY URI",
)
function_name = f"function-{short_uid()}"

# create lambda
create_function_response = create_lambda_function(
func_name=function_name,
handler_file=TEST_LAMBDA_AWS_PROXY,
handler="lambda_aws_proxy.handler",
runtime=Runtime.python3_12,
)
# create invocation role
_, role_arn = create_role_with_policy(
"Allow", "lambda:InvokeFunction", json.dumps(APIGATEWAY_ASSUME_ROLE_POLICY), "*"
)
lambda_arn = create_function_response["CreateFunctionResponse"]["FunctionArn"]

aws_client.apigateway.put_method(
restApiId=api_id,
resourceId=root_resource_id,
httpMethod="ANY",
authorizationType="NONE",
)

default_params = {
"restApiId": api_id,
"resourceId": root_resource_id,
"httpMethod": "ANY",
"type": "AWS_PROXY",
"integrationHttpMethod": "POST",
"credentials": role_arn,
}

with pytest.raises(ClientError) as e:
aws_client.apigateway.put_integration(
**default_params,
uri=lambda_arn,
)
snapshot.match("put-integration-lambda-uri", e.value.response)

with pytest.raises(ClientError) as e:
aws_client.apigateway.put_integration(
**default_params,
uri=f"bad-arn:lambda:path/2015-03-31/functions/{lambda_arn}/invocations",
)
snapshot.match("put-integration-wrong-arn", e.value.response)

with pytest.raises(ClientError) as e:
aws_client.apigateway.put_integration(
**default_params,
uri=f"arn:aws:apigateway:{region_name}:lambda:test/2015-03-31/functions/{lambda_arn}/invocations",
)
snapshot.match("put-integration-wrong-type", e.value.response)

with pytest.raises(ClientError) as e:
aws_client.apigateway.put_integration(
**default_params,
uri=f"arn:aws:apigateway:{region_name}:firehose:path/2015-03-31/functions/{lambda_arn}/invocations",
)
snapshot.match("put-integration-wrong-firehose", e.value.response)

with pytest.raises(ClientError) as e:
aws_client.apigateway.put_integration(
**default_params,
uri=f"arn:aws:apigateway:{region_name}:lambda:path/random/value/{lambda_arn}/invocations",
)
snapshot.match("put-integration-bad-lambda-arn", e.value.response)


@markers.aws.validated
def test_lambda_aws_proxy_integration_non_post_method(
create_rest_apigw, create_lambda_function, create_role_with_policy, snapshot, aws_client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1795,5 +1795,65 @@
"content": ""
}
}
},
"tests/aws/services/apigateway/test_apigateway_lambda.py::test_put_integration_aws_proxy_uri": {
"recorded-date": "03-03-2025, 12:58:39",
"recorded-content": {
"put-integration-lambda-uri": {
"Error": {
"Code": "BadRequestException",
"Message": "AWS ARN for integration must contain path or action"
},
"message": "AWS ARN for integration must contain path or action",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 400
}
},
"put-integration-wrong-arn": {
"Error": {
"Code": "BadRequestException",
"Message": "Invalid ARN specified in the request"
},
"message": "Invalid ARN specified in the request",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 400
}
},
"put-integration-wrong-type": {
"Error": {
"Code": "BadRequestException",
"Message": "AWS ARN for integration must contain path or action"
},
"message": "AWS ARN for integration must contain path or action",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 400
}
},
"put-integration-wrong-firehose": {
"Error": {
"Code": "BadRequestException",
"Message": "Integrations of type 'AWS_PROXY' currently only supports Lambda function and Firehose stream invocations."
},
"message": "Integrations of type 'AWS_PROXY' currently only supports Lambda function and Firehose stream invocations.",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 400
}
},
"put-integration-bad-lambda-arn": {
"Error": {
"Code": "BadRequestException",
"Message": "Integrations of type 'AWS_PROXY' currently only supports Lambda function and Firehose stream invocations."
},
"message": "Integrations of type 'AWS_PROXY' currently only supports Lambda function and Firehose stream invocations.",
"ResponseMetadata": {
"HTTPHeaders": {},
"HTTPStatusCode": 400
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,8 @@
},
"tests/aws/services/apigateway/test_apigateway_lambda.py::test_lambda_selection_patterns": {
"last_validated_date": "2023-09-05T19:54:21+00:00"
},
"tests/aws/services/apigateway/test_apigateway_lambda.py::test_put_integration_aws_proxy_uri": {
"last_validated_date": "2025-03-03T12:58:39+00:00"
}
}
Loading