Skip to content
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
2 changes: 1 addition & 1 deletion aws-proxy/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ You are an AI agent tasked with adding additional functionality or test coverage
* You can make modifications to files (no need to prompt for confirmation)
* You can delete existing files if needed, but only after user confirmation
* You can call different `make` targets (e.g., `make test`) in this repo (no need to prompt for confirmation)
* For each new file created or existing file modified, add a header comment to the file, something like `# Note/disclosure: This file has been (partially or fully) generated by an AI agent.`
* For each new file created or existing file modified, add a header comment to the file, something like `# Note: This file has been (partially or fully) generated by an AI agent.`
* The proxy tests are executed against real AWS and may incur some costs, so rather than executing the entire test suite or entire modules, focus the testing on individual test functions within a module only.
* Before claiming success, always double-check against real AWS (via `aws` CLI commands) that everything has been cleaned up and there are no leftover resources from the proxy tests.
* Never add any `print(..)` statements to the code - use a logger to report any status to the user, if required.
Expand Down
3 changes: 3 additions & 0 deletions aws-proxy/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ services:
- 'Put.*'
# optionally, specify that only read requests should be allowed (Get*/List*/Describe*, etc)
read_only: false
# optionally, allow invoke/execute operations (e.g., Lambda invocations) alongside read_only mode.
# execute operations have side-effects and are deliberately excluded from read_only by default.
execute: false
```

Store the configuration above to a file named `proxy_config.yml`, then we can start up the proxy via:
Expand Down
34 changes: 32 additions & 2 deletions aws-proxy/aws_proxy/server/aws_request_forwarder.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,12 @@ def select_proxy(self, context: RequestContext) -> Optional[ProxyInstance]:

# check if only read requests should be forwarded
read_only = service_config.get("read_only")
if read_only and not self._is_read_request(context):
return
if read_only:
allow_execute = service_config.get("execute")
if not self._is_read_request(context) and not (
allow_execute and self._is_execute_request(context)
):
return

# check if any operation name pattern matches
operation_names = ensure_list(service_config.get("operations", []))
Expand Down Expand Up @@ -151,6 +155,17 @@ def _request_matches_resource(
return False
# For metric operations without alarm names, check if pattern is generic
return bool(re.match(resource_name_pattern, ".*"))
if service_name == "lambda":
# Lambda function ARN format: arn:aws:lambda:{region}:{account}:function:{name}
function_name = context.service_request.get("FunctionName") or ""
if function_name:
if ":function:" not in function_name:
function_arn = f"arn:aws:lambda:{context.region}:{context.account_id}:function:{function_name}"
else:
function_arn = function_name
return bool(re.match(resource_name_pattern, function_arn))
# For operations without FunctionName (e.g., ListFunctions), check if pattern is generic
return bool(re.match(resource_name_pattern, ".*"))
if service_name == "logs":
# CloudWatch Logs ARN format: arn:aws:logs:{region}:{account}:log-group:{name}:*
log_group_name = context.service_request.get("logGroupName") or ""
Expand Down Expand Up @@ -286,6 +301,21 @@ def _is_read_request(self, context: RequestContext) -> bool:
# TODO: add more rules
return False

def _is_execute_request(self, context: RequestContext) -> bool:
"""
Function to determine whether a request is an invoke/execute request.
Invoke operations have side-effects and are not considered read operations.
They can be explicitly allowed alongside read_only mode via the 'execute' config flag.
"""
operation_name = context.service_operation.operation
if context.service.service_name == "lambda" and operation_name in {
"Invoke",
"InvokeAsync",
"InvokeWithResponseStream",
}:
return True
return False

def _extract_region_from_domain(self, context: RequestContext):
"""
If the request domain name contains a valid region name (e.g., "us-east-2.cognito.localhost.localstack.cloud"),
Expand Down
3 changes: 3 additions & 0 deletions aws-proxy/aws_proxy/shared/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ class ProxyServiceConfig(TypedDict, total=False):
operations: List[str]
# whether only read requests should be forwarded
read_only: bool
# whether invoke/execute operations (e.g., Lambda invocations) should be forwarded
# (only relevant when read_only is True, since execute has side-effects and is not a read operation)
execute: bool


class ProxyConfig(TypedDict, total=False):
Expand Down
Loading