fix(runtime): bound tenant resource amplification

This commit is contained in:
Junyan Qin
2026-07-29 18:27:44 +08:00
parent e8d90c4259
commit 610915b9c5
32 changed files with 857 additions and 185 deletions
@@ -168,6 +168,15 @@ class TestPathTraversalPrevention:
assert loaded == content
await provider.delete(key)
@pytest.mark.asyncio
async def test_bounded_load_stops_after_limit(self, storage_provider):
provider, storage_path = storage_provider
with patch('langbot.pkg.storage.providers.localstorage.LOCAL_STORAGE_PATH', storage_path):
await provider.save('oversized.bin', b'12345')
with pytest.raises(ValueError, match='4-byte read limit'):
await provider.load_bounded('oversized.bin', max_bytes=4)
@pytest.mark.asyncio
async def test_delete_dir_recursive_non_existing_dir(self, storage_provider):
"""delete_dir_recursive should handle non-existing directories gracefully."""
@@ -134,6 +134,16 @@ class TestS3StorageProviderWithMoto:
loaded_data = await provider.load('test/file.txt')
assert loaded_data == test_data
@pytest.mark.asyncio
async def test_bounded_load_rejects_oversized_object(self, mock_app_with_s3_config, s3_mock):
s3storage = get_s3storage_module()
provider = s3storage.S3StorageProvider(mock_app_with_s3_config)
await provider.initialize()
await provider.save('test/oversized.bin', b'12345')
with pytest.raises(ValueError, match='4-byte read limit'):
await provider.load_bounded('test/oversized.bin', max_bytes=4)
@pytest.mark.asyncio
async def test_exists_returns_true_for_existing_object(self, mock_app_with_s3_config, s3_mock):
"""Test that exists returns True for existing object."""
@@ -8,6 +8,7 @@ import pytest
from langbot.pkg.api.http.authz import WorkspaceRequiredError
from langbot.pkg.api.http.context import ExecutionContext
from langbot.pkg.storage.mgr import StorageMgr
from langbot.pkg.storage.provider import HARD_MAX_STORAGE_OBJECT_BYTES
from langbot.pkg.utils.bounded_executor import current_blocking_work_scope
@@ -202,6 +203,39 @@ async def test_opaque_object_operations_reject_cross_scope_and_owner_type(manage
assert object_key in manager.storage_provider.values
@pytest.mark.asyncio
async def test_scoped_object_reads_and_writes_have_configured_and_hard_byte_limits(manager):
manager.ap.instance_config = SimpleNamespace(data={'storage': {'max_object_read_bytes': 4}})
with pytest.raises(ValueError, match='4-byte write limit'):
await manager.save_scoped(
_context(WORKSPACE_A),
owner_type='upload',
owner='account:a',
key='oversized.bin',
value=b'12345',
)
object_key = manager.scoped_object_key(
_context(WORKSPACE_A),
owner_type='upload',
owner='account:a',
key='external.bin',
)
manager.storage_provider.values[object_key] = b'12345'
manager.storage_provider.load = AsyncMock(side_effect=AssertionError('oversized object must not be loaded'))
with pytest.raises(ValueError, match='4-byte read limit'):
await manager.load_scoped_object_key(
_context(WORKSPACE_A),
object_key,
expected_owner_type='upload',
)
manager.storage_provider.load.assert_not_awaited()
manager.ap.instance_config.data['storage']['max_object_read_bytes'] = HARD_MAX_STORAGE_OBJECT_BYTES + 1
assert manager._object_read_limit() == HARD_MAX_STORAGE_OBJECT_BYTES
@pytest.mark.asyncio
async def test_scoped_provider_is_not_touched_after_generation_is_fenced(manager):
object_key = await manager.save_scoped(