mirror of
https://github.com/open-webui/open-webui.git
synced 2026-05-18 05:05:09 +02:00
fix: strip model params for read-only callers on per-id endpoint (#24525)
GET /api/v1/models/model?id=<model_id> at routers/models.py:412 returned the full model.model_dump() to any caller with read access, including the params dict that holds the admin-curated system prompt and other behavior config. The user-facing /api/models endpoint already strips this via utils/models.py:170,210 with the comment "Remove params to avoid exposing sensitive info", and /api/v1/models/list gates by write permission so non-curators don't see the model in their workspace listing at all. The per-id endpoint missed the same gate, so a user with read-only access (e.g. granted access to use the model in chat) could open /workspace/models/edit?id=<not-mine> in the browser and read the system prompt verbatim from the network response, even though saving was correctly blocked. Compute write_access once at the top of the handler so it can serve both the response-shape decision and the response field. When the caller lacks write access, replace params with an empty dict in the serialised response. Owners, admins under BYPASS_ADMIN_ACCESS_CONTROL, and explicit write-grant holders still get the full payload so the workspace edit UI keeps working for users who legitimately curate the model. Read-permission users continue to receive everything else they need to chat with the model — the chat path resolves prompt/params server-side from the stored ModelModel and never echoes them back through this endpoint. Reported by destination-one in GHSA-h2cw-7qw9-56xr. Co-authored-by: destination-one <destination-one@users.noreply.github.com>
This commit is contained in:
@@ -413,9 +413,20 @@ class ModelIdForm(BaseModel):
|
||||
async def get_model_by_id(id: str, user=Depends(get_verified_user), db: AsyncSession = Depends(get_async_session)):
|
||||
model = await Models.get_model_by_id(id, db=db)
|
||||
if model:
|
||||
if (
|
||||
write_access = (
|
||||
(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||
or model.user_id == user.id
|
||||
or user.id == model.user_id
|
||||
or await AccessGrants.has_access(
|
||||
user_id=user.id,
|
||||
resource_type='model',
|
||||
resource_id=model.id,
|
||||
permission='write',
|
||||
db=db,
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
write_access
|
||||
or await AccessGrants.has_access(
|
||||
user_id=user.id,
|
||||
resource_type='model',
|
||||
@@ -424,19 +435,18 @@ async def get_model_by_id(id: str, user=Depends(get_verified_user), db: AsyncSes
|
||||
db=db,
|
||||
)
|
||||
):
|
||||
model_dict = model.model_dump()
|
||||
# Strip params (system prompt and other admin-curated config)
|
||||
# for read-only callers — matches the params strip already
|
||||
# enforced on /api/models in utils/models.py. Owners, admins
|
||||
# under BYPASS_ADMIN_ACCESS_CONTROL, and write-grant holders
|
||||
# still receive the full object so the workspace edit UI keeps
|
||||
# working for users who legitimately curate the model.
|
||||
if not write_access:
|
||||
model_dict['params'] = {}
|
||||
return ModelAccessResponse(
|
||||
**model.model_dump(),
|
||||
write_access=(
|
||||
(user.role == 'admin' and BYPASS_ADMIN_ACCESS_CONTROL)
|
||||
or user.id == model.user_id
|
||||
or await AccessGrants.has_access(
|
||||
user_id=user.id,
|
||||
resource_type='model',
|
||||
resource_id=model.id,
|
||||
permission='write',
|
||||
db=db,
|
||||
)
|
||||
),
|
||||
**model_dict,
|
||||
write_access=write_access,
|
||||
)
|
||||
else:
|
||||
raise HTTPException(
|
||||
|
||||
Reference in New Issue
Block a user