ref #10: [fix] enforce UUID chat ids
Normalize chat ids to a single UUID form so locks, repository keys, and mount paths cannot diverge through path-like aliases.
This commit is contained in:
parent
44f1549d80
commit
e629e34c4d
7 changed files with 192 additions and 80 deletions
|
|
@ -28,6 +28,9 @@ from repository.sandbox_session import InMemorySandboxSessionRepository
|
|||
from usecase.interface import Attrs
|
||||
from usecase.sandbox import CleanupExpiredSandboxes, CreateSandbox, CreateSandboxCommand
|
||||
|
||||
CHAT_ID = '123e4567-e89b-12d3-a456-426614174000'
|
||||
NON_CANONICAL_CHAT_ID = '123E4567E89B12D3A456426614174000'
|
||||
|
||||
|
||||
class FakeLogger:
|
||||
def __init__(self) -> None:
|
||||
|
|
@ -246,12 +249,12 @@ async def exercise_get_request(
|
|||
await app.router.shutdown()
|
||||
|
||||
|
||||
def test_post_create_returns_session(monkeypatch) -> None:
|
||||
def test_post_create_returns_session_with_canonical_chat_id(monkeypatch) -> None:
|
||||
config = build_config()
|
||||
expires_at = datetime(2026, 4, 2, 12, 5, tzinfo=UTC)
|
||||
session = SandboxSession(
|
||||
session_id='session-123',
|
||||
chat_id='chat-123',
|
||||
chat_id=CHAT_ID,
|
||||
container_id='container-123',
|
||||
status=SandboxStatus.RUNNING,
|
||||
created_at=expires_at - timedelta(minutes=5),
|
||||
|
|
@ -276,19 +279,19 @@ def test_post_create_returns_session(monkeypatch) -> None:
|
|||
app = app_module.create_app(config=config)
|
||||
|
||||
status_code, response = asyncio.run(
|
||||
exercise_create_request(app, {'chat_id': 'chat-123'})
|
||||
exercise_create_request(app, {'chat_id': NON_CANONICAL_CHAT_ID})
|
||||
)
|
||||
|
||||
assert status_code == 200
|
||||
assert response == {
|
||||
'session_id': 'session-123',
|
||||
'chat_id': 'chat-123',
|
||||
'chat_id': CHAT_ID,
|
||||
'container_id': 'container-123',
|
||||
'status': 'running',
|
||||
'expires_at': '2026-04-02T12:05:00Z',
|
||||
}
|
||||
assert len(create_usecase.commands) == 1
|
||||
assert create_usecase.commands[0].chat_id == 'chat-123'
|
||||
assert create_usecase.commands[0].chat_id == CHAT_ID
|
||||
assert cleanup_usecase.calls >= 1
|
||||
assert any(
|
||||
message == 'http_request'
|
||||
|
|
@ -299,10 +302,19 @@ def test_post_create_returns_session(monkeypatch) -> None:
|
|||
assert docker_client.close_calls == 1
|
||||
|
||||
|
||||
def test_post_create_maps_start_errors_to_service_unavailable(monkeypatch) -> None:
|
||||
def test_post_create_rejects_non_uuid_chat_id(monkeypatch) -> None:
|
||||
config = build_config()
|
||||
expires_at = datetime(2026, 4, 2, 12, 5, tzinfo=UTC)
|
||||
session = SandboxSession(
|
||||
session_id='session-123',
|
||||
chat_id=CHAT_ID,
|
||||
container_id='container-123',
|
||||
status=SandboxStatus.RUNNING,
|
||||
created_at=expires_at - timedelta(minutes=5),
|
||||
expires_at=expires_at,
|
||||
)
|
||||
logger = FakeLogger()
|
||||
create_usecase = FakeCreateSandboxUsecase(error=SandboxStartError('chat-123'))
|
||||
create_usecase = FakeCreateSandboxUsecase(session=session)
|
||||
cleanup_usecase = FakeCleanupExpiredSandboxes()
|
||||
docker_client = FakeDockerClient()
|
||||
container = build_container(
|
||||
|
|
@ -320,7 +332,37 @@ def test_post_create_maps_start_errors_to_service_unavailable(monkeypatch) -> No
|
|||
app = app_module.create_app(config=config)
|
||||
|
||||
status_code, response = asyncio.run(
|
||||
exercise_create_request(app, {'chat_id': 'chat-123'})
|
||||
exercise_create_request(app, {'chat_id': 'x/../y'})
|
||||
)
|
||||
|
||||
assert status_code == 422
|
||||
assert 'detail' in response
|
||||
assert create_usecase.commands == []
|
||||
assert docker_client.close_calls == 1
|
||||
|
||||
|
||||
def test_post_create_maps_start_errors_to_service_unavailable(monkeypatch) -> None:
|
||||
config = build_config()
|
||||
logger = FakeLogger()
|
||||
create_usecase = FakeCreateSandboxUsecase(error=SandboxStartError(CHAT_ID))
|
||||
cleanup_usecase = FakeCleanupExpiredSandboxes()
|
||||
docker_client = FakeDockerClient()
|
||||
container = build_container(
|
||||
config,
|
||||
create_usecase,
|
||||
cleanup_usecase,
|
||||
logger,
|
||||
docker_client,
|
||||
)
|
||||
monkeypatch.setattr(app_module, 'build_container', lambda **kwargs: container)
|
||||
monkeypatch.setattr(
|
||||
app_module.FastAPIInstrumentor, 'instrument_app', lambda *args, **kwargs: None
|
||||
)
|
||||
|
||||
app = app_module.create_app(config=config)
|
||||
|
||||
status_code, response = asyncio.run(
|
||||
exercise_create_request(app, {'chat_id': CHAT_ID})
|
||||
)
|
||||
|
||||
assert status_code == 503
|
||||
|
|
@ -349,7 +391,7 @@ def test_post_create_maps_generic_sandbox_errors_to_internal_error(monkeypatch)
|
|||
app = app_module.create_app(config=config)
|
||||
|
||||
status_code, response = asyncio.run(
|
||||
exercise_create_request(app, {'chat_id': 'chat-123'})
|
||||
exercise_create_request(app, {'chat_id': CHAT_ID})
|
||||
)
|
||||
|
||||
assert status_code == 500
|
||||
|
|
@ -362,7 +404,7 @@ def test_removed_user_endpoint_returns_not_found(monkeypatch) -> None:
|
|||
expires_at = datetime(2026, 4, 2, 12, 5, tzinfo=UTC)
|
||||
session = SandboxSession(
|
||||
session_id='session-123',
|
||||
chat_id='chat-123',
|
||||
chat_id=CHAT_ID,
|
||||
container_id='container-123',
|
||||
status=SandboxStatus.RUNNING,
|
||||
created_at=expires_at - timedelta(minutes=5),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue