From 96d8d1dc64192a355fd03cf02fb058b897a21cdb Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Thu, 12 Mar 2026 23:44:12 -0700 Subject: [PATCH] Be more strict with SQS region --- app/fanout/AGENTS_fanout.md | 2 +- app/fanout/sqs.py | 22 ++++++++++++++++++++++ tests/test_fanout.py | 32 +++++++++++++++++++++++++++++++- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/app/fanout/AGENTS_fanout.md b/app/fanout/AGENTS_fanout.md index 2a1df05..3c5d1b3 100644 --- a/app/fanout/AGENTS_fanout.md +++ b/app/fanout/AGENTS_fanout.md @@ -82,7 +82,7 @@ Push notifications via Apprise library. Config blob: ### sqs (sqs.py) Amazon SQS delivery. Config blob: - `queue_url` — target queue URL -- `region_name` (optional), `endpoint_url` (optional) +- `region_name` (optional; inferred from standard AWS SQS queue URLs when omitted), `endpoint_url` (optional) - `access_key_id`, `secret_access_key`, `session_token` (all optional; blank uses the normal AWS credential chain) - Publishes a JSON envelope of the form `{"event_type":"message"|"raw_packet","data":...}` - Supports both decoded messages and raw packets via normal scope selection diff --git a/app/fanout/sqs.py b/app/fanout/sqs.py index fbf8a65..79f3822 100644 --- a/app/fanout/sqs.py +++ b/app/fanout/sqs.py @@ -7,6 +7,7 @@ import hashlib import json import logging from functools import partial +from urllib.parse import urlparse import boto3 from botocore.exceptions import BotoCoreError, ClientError @@ -28,6 +29,24 @@ def _build_payload(data: dict, *, event_type: str) -> str: ) +def _infer_region_from_queue_url(queue_url: str) -> str | None: + """Infer AWS region from a standard SQS queue URL host when possible.""" + host = urlparse(queue_url).hostname or "" + if not host: + return None + + parts = host.split(".") + if len(parts) < 4 or parts[0] != "sqs": + return None + if parts[2] != "amazonaws": + return None + if parts[3] not in {"com", "com.cn"}: + return None + + region = parts[1].strip() + return region or None + + def _is_fifo_queue(queue_url: str) -> bool: """Return True when the configured queue URL points at an SQS FIFO queue.""" return queue_url.rstrip("/").endswith(".fifo") @@ -69,12 +88,15 @@ class SqsModule(FanoutModule): async def start(self) -> None: kwargs: dict[str, str] = {} + queue_url = str(self.config.get("queue_url", "")).strip() region_name = str(self.config.get("region_name", "")).strip() endpoint_url = str(self.config.get("endpoint_url", "")).strip() access_key_id = str(self.config.get("access_key_id", "")).strip() secret_access_key = str(self.config.get("secret_access_key", "")).strip() session_token = str(self.config.get("session_token", "")).strip() + if not region_name: + region_name = _infer_region_from_queue_url(queue_url) or "" if region_name: kwargs["region_name"] = region_name if endpoint_url: diff --git a/tests/test_fanout.py b/tests/test_fanout.py index 28f78e3..12a165c 100644 --- a/tests/test_fanout.py +++ b/tests/test_fanout.py @@ -590,13 +590,43 @@ class TestSqsModule: async def test_status_connected_with_queue_url(self): from app.fanout.sqs import SqsModule - with patch("app.fanout.sqs.boto3.client", return_value=MagicMock()): + with patch("app.fanout.sqs.boto3.client", return_value=MagicMock()) as mock_client: mod = SqsModule( "test", {"queue_url": "https://sqs.us-east-1.amazonaws.com/123456789012/mesh-events"}, ) await mod.start() assert mod.status == "connected" + mock_client.assert_called_once_with("sqs", region_name="us-east-1") + await mod.stop() + + @pytest.mark.asyncio + async def test_explicit_region_overrides_queue_url_inference(self): + from app.fanout.sqs import SqsModule + + with patch("app.fanout.sqs.boto3.client", return_value=MagicMock()) as mock_client: + mod = SqsModule( + "test", + { + "queue_url": "https://sqs.us-east-1.amazonaws.com/123456789012/mesh-events", + "region_name": "us-west-2", + }, + ) + await mod.start() + mock_client.assert_called_once_with("sqs", region_name="us-west-2") + await mod.stop() + + @pytest.mark.asyncio + async def test_custom_queue_url_does_not_infer_region(self): + from app.fanout.sqs import SqsModule + + with patch("app.fanout.sqs.boto3.client", return_value=MagicMock()) as mock_client: + mod = SqsModule( + "test", + {"queue_url": "https://localhost:4566/000000000000/mesh-events"}, + ) + await mod.start() + mock_client.assert_called_once_with("sqs") await mod.stop() @pytest.mark.asyncio