mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-11-04 14:52:12 +08:00
fix(api-tools): forbid processors from widening workspace operation grants
A processor granted pipeline.extensions.update / pipeline.update / agent.update derives resource.manage, which let it add any resource.manage operation to its own grants. Granting is a human authorization act, so processor principals may only keep or remove grants, and the catalog marks nothing grantable for them. LANG-226 Co-Authored-By: Claude Opus 5.5 <[email protected]> Co-authored-by: multica-agent <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
multica-agent
parent
a0405a0b2c
commit
24038395dd
@@ -10,6 +10,11 @@ from .contracts import ApplicationAPIError, ApplicationAPIException, Operation
|
||||
from .operation_docs import DOMAINS, get_operation_doc
|
||||
|
||||
|
||||
def _is_processor(context: Any) -> bool:
|
||||
principal = getattr(context, 'principal', None)
|
||||
return getattr(principal, 'principal_type', None) == PrincipalType.PROCESSOR
|
||||
|
||||
|
||||
def validate_grant_change(
|
||||
ap: Any,
|
||||
editor_context: RequestContext,
|
||||
@@ -66,6 +71,18 @@ def validate_grant_change(
|
||||
if not added_ids:
|
||||
return normalized_new
|
||||
|
||||
# Granting is a human authorization act. A processor that can update processor
|
||||
# configuration must not widen any processor's grants, including its own.
|
||||
if _is_processor(editor_context):
|
||||
raise ApplicationAPIException(
|
||||
ApplicationAPIError(
|
||||
code='permission_denied',
|
||||
message=f'Processors cannot grant workspace operations: {added_ids}',
|
||||
status_code=403,
|
||||
details={'operations': added_ids, 'denied_operations': added_ids},
|
||||
)
|
||||
)
|
||||
|
||||
app_api = getattr(ap, 'application_api', None)
|
||||
if app_api is None:
|
||||
from .api import ApplicationAPI
|
||||
@@ -147,7 +164,7 @@ def get_workspace_operations_catalog(ap: Any, context: Any) -> dict[str, Any]:
|
||||
has_perm = (
|
||||
has_permission(context, spec.permission) if context is not None and hasattr(context, 'workspace') else False
|
||||
)
|
||||
grantable = bool(can_run and has_perm)
|
||||
grantable = bool(can_run and has_perm and not _is_processor(context))
|
||||
|
||||
perm = spec.permission
|
||||
perm_str = perm.value if hasattr(perm, 'value') else str(perm)
|
||||
|
||||
@@ -249,3 +249,34 @@ def test_workspace_operations_catalog_and_grantable_flags(test_host):
|
||||
]
|
||||
for op in viewer_view_ops:
|
||||
assert op['grantable'] is True
|
||||
|
||||
|
||||
# 8. A processor must not widen grants, even within its own derived permissions
|
||||
def test_processor_principal_cannot_add_grants(test_host):
|
||||
import asyncio
|
||||
|
||||
from langbot.pkg.api.application.processor_context import build_processor_request_context
|
||||
|
||||
processor_ctx = asyncio.run(
|
||||
build_processor_request_context(
|
||||
test_host,
|
||||
workspace_uuid='ws-1',
|
||||
processor_kind='pipeline',
|
||||
processor_uuid='pipe-1',
|
||||
run_id='run-1',
|
||||
operation_ids=['pipeline.extensions.update'],
|
||||
)
|
||||
)
|
||||
old = ['pipeline.extensions.update']
|
||||
|
||||
with pytest.raises(ApplicationAPIException) as exc_info:
|
||||
validate_grant_change(test_host, processor_ctx, old, [*old, 'bot.delete'])
|
||||
assert exc_info.value.error.code == 'permission_denied'
|
||||
assert exc_info.value.error.details['operations'] == ['bot.delete']
|
||||
|
||||
# Keeping or removing existing grants is still allowed.
|
||||
assert validate_grant_change(test_host, processor_ctx, old, old) == old
|
||||
assert validate_grant_change(test_host, processor_ctx, old, []) == []
|
||||
|
||||
catalog = get_workspace_operations_catalog(test_host, processor_ctx)
|
||||
assert not any(op['grantable'] for op in catalog['workspace_operations'])
|
||||
|
||||
Reference in New Issue
Block a user