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
|
|
@ -12,6 +12,9 @@ from adapter.docker.runtime import DockerSandboxRuntime
|
|||
from domain.error import SandboxError, SandboxStartError
|
||||
from domain.sandbox import SandboxStatus
|
||||
|
||||
CHAT_ID = '123e4567-e89b-12d3-a456-426614174000'
|
||||
NON_CANONICAL_CHAT_ID = '123E4567E89B12D3A456426614174000'
|
||||
|
||||
|
||||
class FakeContainer:
|
||||
def __init__(self, container_id: str) -> None:
|
||||
|
|
@ -92,7 +95,9 @@ def build_config(tmp_path: Path) -> SandboxConfig:
|
|||
)
|
||||
|
||||
|
||||
def test_runtime_create_applies_mount_policy_and_labels(tmp_path: Path) -> None:
|
||||
def test_runtime_create_applies_mount_policy_and_labels_with_canonical_chat_id(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
config = build_config(tmp_path)
|
||||
(tmp_path / 'dependencies').mkdir()
|
||||
(tmp_path / 'lambda-tools').mkdir()
|
||||
|
|
@ -103,25 +108,25 @@ def test_runtime_create_applies_mount_policy_and_labels(tmp_path: Path) -> None:
|
|||
|
||||
session = runtime.create(
|
||||
session_id='session-123',
|
||||
chat_id='chat-123',
|
||||
chat_id=NON_CANONICAL_CHAT_ID,
|
||||
created_at=created_at,
|
||||
expires_at=expires_at,
|
||||
)
|
||||
|
||||
assert session.session_id == 'session-123'
|
||||
assert session.chat_id == 'chat-123'
|
||||
assert session.chat_id == CHAT_ID
|
||||
assert session.container_id == 'container-123'
|
||||
assert session.status is SandboxStatus.RUNNING
|
||||
assert session.created_at == created_at
|
||||
assert session.expires_at == expires_at
|
||||
assert (tmp_path / 'chats' / 'chat-123').is_dir()
|
||||
assert (tmp_path / 'chats' / CHAT_ID).is_dir()
|
||||
|
||||
call = containers.run_calls[0]
|
||||
assert call['args'] == ('sandbox:latest',)
|
||||
assert call['kwargs']['detach'] is True
|
||||
assert call['kwargs']['labels'] == {
|
||||
'session_id': 'session-123',
|
||||
'chat_id': 'chat-123',
|
||||
'chat_id': CHAT_ID,
|
||||
'expires_at': expires_at.isoformat(),
|
||||
}
|
||||
|
||||
|
|
@ -129,7 +134,7 @@ def test_runtime_create_applies_mount_policy_and_labels(tmp_path: Path) -> None:
|
|||
assert [dict(mount) for mount in mounts] == [
|
||||
{
|
||||
'Target': '/workspace/chat',
|
||||
'Source': str((tmp_path / 'chats' / 'chat-123').resolve(strict=False)),
|
||||
'Source': str((tmp_path / 'chats' / CHAT_ID).resolve(strict=False)),
|
||||
'Type': 'bind',
|
||||
'ReadOnly': False,
|
||||
},
|
||||
|
|
@ -160,13 +165,13 @@ def test_runtime_create_raises_start_error_when_container_id_is_missing(
|
|||
with pytest.raises(SandboxStartError) as excinfo:
|
||||
runtime.create(
|
||||
session_id='session-123',
|
||||
chat_id='chat-123',
|
||||
chat_id=CHAT_ID,
|
||||
created_at=datetime(2026, 4, 2, 12, 0, tzinfo=UTC),
|
||||
expires_at=datetime(2026, 4, 2, 12, 5, tzinfo=UTC),
|
||||
)
|
||||
|
||||
assert str(excinfo.value) == 'sandbox_start_failed'
|
||||
assert excinfo.value.chat_id == 'chat-123'
|
||||
assert excinfo.value.chat_id == CHAT_ID
|
||||
|
||||
|
||||
def test_runtime_stop_ignores_missing_container(tmp_path: Path) -> None:
|
||||
|
|
@ -192,7 +197,8 @@ def test_runtime_stop_wraps_docker_errors(tmp_path: Path) -> None:
|
|||
assert str(excinfo.value) == 'sandbox_stop_failed'
|
||||
|
||||
|
||||
def test_runtime_create_rejects_chat_path_traversal(tmp_path: Path) -> None:
|
||||
@pytest.mark.parametrize('chat_id', ['.', 'a/..', 'x/../y'])
|
||||
def test_runtime_create_rejects_non_uuid_chat_id(tmp_path: Path, chat_id: str) -> None:
|
||||
config = build_config(tmp_path)
|
||||
(tmp_path / 'dependencies').mkdir()
|
||||
(tmp_path / 'lambda-tools').mkdir()
|
||||
|
|
@ -202,11 +208,11 @@ def test_runtime_create_rejects_chat_path_traversal(tmp_path: Path) -> None:
|
|||
with pytest.raises(SandboxStartError) as excinfo:
|
||||
runtime.create(
|
||||
session_id='session-123',
|
||||
chat_id='../escape',
|
||||
chat_id=chat_id,
|
||||
created_at=datetime(2026, 4, 2, 12, 0, tzinfo=UTC),
|
||||
expires_at=datetime(2026, 4, 2, 12, 5, tzinfo=UTC),
|
||||
)
|
||||
|
||||
assert str(excinfo.value) == 'sandbox_start_failed'
|
||||
assert excinfo.value.chat_id == '../escape'
|
||||
assert excinfo.value.chat_id == chat_id
|
||||
assert containers.run_calls == []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue