-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: RBAC bypass vulnerabilities in model access #4270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,11 +7,12 @@ | |
| import time | ||
| from typing import Any | ||
|
|
||
| from llama_stack.core.access_control.access_control import is_action_allowed | ||
| from llama_stack.core.datatypes import ( | ||
| ModelWithOwner, | ||
| RegistryEntrySource, | ||
| ) | ||
| from llama_stack.core.request_headers import PROVIDER_DATA_VAR, NeedsRequestProviderData | ||
| from llama_stack.core.request_headers import PROVIDER_DATA_VAR, NeedsRequestProviderData, get_authenticated_user | ||
| from llama_stack.core.utils.dynamic import instantiate_class_type | ||
| from llama_stack.log import get_logger | ||
| from llama_stack_api import ( | ||
|
|
@@ -66,6 +67,7 @@ async def _get_dynamic_models_from_provider_data(self) -> list[Model]: | |
| return [] | ||
|
|
||
| dynamic_models = [] | ||
| user = get_authenticated_user() | ||
|
|
||
| for provider_id, provider in self.impls_by_provider_id.items(): | ||
| # Check if this provider supports provider_data | ||
|
|
@@ -93,15 +95,32 @@ async def _get_dynamic_models_from_provider_data(self) -> list[Model]: | |
| if not models: | ||
| continue | ||
|
|
||
| # Ensure models have fully qualified identifiers with provider_id prefix | ||
| # Ensure models have fully qualified identifiers and apply RBAC filtering | ||
| for model in models: | ||
| # Only add prefix if model identifier doesn't already have it | ||
| if not model.identifier.startswith(f"{provider_id}/"): | ||
| model.identifier = f"{provider_id}/{model.provider_resource_id}" | ||
|
|
||
| dynamic_models.append(model) | ||
|
|
||
| logger.debug(f"Fetched {len(models)} models from provider {provider_id} using provider_data") | ||
| # Convert to ModelWithOwner for RBAC check | ||
| temp_model = ModelWithOwner( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @derekhiggins we ask providers to return Model, but we always need ModelWithOwner. can we move the ResourceWithOwner up a level in the class hierarcy?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We'd have to look into how difficult it would be but wouldn't be a bad idea, I'd like to add a few simple RBAC policies into the integration auth tests next. Then would be more comfortable with refactors like this. |
||
| identifier=model.identifier, | ||
| provider_id=provider_id, | ||
| provider_resource_id=model.provider_resource_id, | ||
| model_type=model.model_type, | ||
| metadata=model.metadata, | ||
| ) | ||
|
|
||
| # Apply RBAC check - only include models user has read permission for | ||
| if is_action_allowed(self.policy, "read", temp_model, user): | ||
| dynamic_models.append(model) | ||
| else: | ||
| logger.debug( | ||
| f"Access denied to dynamic model '{model.identifier}' for user {user.principal if user else 'anonymous'}" | ||
| ) | ||
|
|
||
| logger.debug( | ||
| f"Fetched {len(dynamic_models)} accessible models from provider {provider_id} using provider_data" | ||
| ) | ||
|
|
||
| except Exception as e: | ||
| logger.debug(f"Failed to list models from provider {provider_id} with provider_data: {e}") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this reads awkward, maybe we need to think about the RBAC API a bit so this "temp model" creation is not necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, creating that seemed a little excessive but I couldn't see any way around it