What happened?
reported via https://github.com/QuivrHQ/quivr/security/advisories/GHSA-3968-h7hg-28mx on 2 June 2026 - no response:
Summary
The prompt endpoints in the quivr backend accept any prompt UUID and read or update it without any ownership or affiliation check. The Prompt entity has no owner column and no row-level security. Any authenticated user who learns a prompt UUID can overwrite that prompt's content. Because prompt IDs are exposed in brain-details responses (BrainEntity.prompt_id), a Viewer-role member of a shared brain can retrieve the brain's prompt_id and inject a malicious system prompt that then affects every user of that brain.
Details
File: backend/api/quivr_api/modules/prompt/controller/prompt_routes.py
Vulnerable endpoints:
- GET /prompts/{prompt_id} - returns the prompt for any UUID.
- PUT /prompts/{prompt_id} - updates the content/title of the prompt for any UUID.
Neither handler resolves or checks an owner. The Prompt entity (prompt model) has no user_id field, and the repository update is keyed solely on prompt_id, so there is no authorization layer at the route, service, or data level.
Discovery / attack chain:
- Brain details (BrainEntity) expose a prompt_id field. Any member of a shared brain, including a read-only Viewer, can read the brain details and obtain the brain's prompt_id.
- The attacker calls PUT /prompts/{prompt_id} to overwrite the prompt body with an injected system prompt (for example a jailbreak or data-exfiltration instruction, or content that manipulates answers).
- Every subsequent chat against that brain uses the attacker-controlled system prompt, so the injection affects all users of the brain, not just the attacker.
This converts a read-only collaboration role into the ability to tamper with the core behavior of a shared brain, and more generally lets any authenticated user mutate any prompt in the system given its UUID.
PoC
Prerequisites: a running quivr backend with a Supabase/PostgreSQL instance, two users A (owner) and B (attacker, e.g. a Viewer on one of A's shared brains).
-
As B, read a shared brain's details and extract prompt_id:
GET /brains/{shared_brain_id}/ (returns BrainEntity including prompt_id)
-
As B, read the target prompt:
GET /prompts/{prompt_id}
Authorization: Bearer <token_B>
Returns the prompt content despite B not owning it.
-
As B, overwrite the prompt:
PUT /prompts/{prompt_id}
Authorization: Bearer <token_B>
{ "content": "", "title": "..." }
Returns success; the prompt body is replaced. Subsequent chats on any brain referencing this prompt_id now use the injected content.
Validation level: code-level. The Prompt service uses the Supabase SDK client (requires a live Supabase backend to exercise the HTTP path end-to-end). The route, service, and repository were traced and contain no ownership check at any layer, and the Prompt model has no owner column; the discovery vector via BrainEntity.prompt_id was confirmed by reading brain_entity.py. This finding is reported at code-level validation, distinct from finding 001 which was reproduced live.
Impact
Authenticated regular user (PR:L), including a read-only Viewer of a shared brain. Integrity: high - an attacker can silently rewrite the system prompt that governs a shared brain's responses, affecting all of that brain's users (prompt-injection at the configuration layer), and can tamper with any prompt in the deployment given its UUID. Confidentiality: low - prompt contents are readable cross-user. No availability impact.
Relevant log output
Twitter / LinkedIn details
No response
What happened?
reported via https://github.com/QuivrHQ/quivr/security/advisories/GHSA-3968-h7hg-28mx on 2 June 2026 - no response:
Summary
The prompt endpoints in the quivr backend accept any prompt UUID and read or update it without any ownership or affiliation check. The Prompt entity has no owner column and no row-level security. Any authenticated user who learns a prompt UUID can overwrite that prompt's content. Because prompt IDs are exposed in brain-details responses (BrainEntity.prompt_id), a Viewer-role member of a shared brain can retrieve the brain's prompt_id and inject a malicious system prompt that then affects every user of that brain.
Details
File: backend/api/quivr_api/modules/prompt/controller/prompt_routes.py
Vulnerable endpoints:
Neither handler resolves or checks an owner. The Prompt entity (prompt model) has no user_id field, and the repository update is keyed solely on prompt_id, so there is no authorization layer at the route, service, or data level.
Discovery / attack chain:
This converts a read-only collaboration role into the ability to tamper with the core behavior of a shared brain, and more generally lets any authenticated user mutate any prompt in the system given its UUID.
PoC
Prerequisites: a running quivr backend with a Supabase/PostgreSQL instance, two users A (owner) and B (attacker, e.g. a Viewer on one of A's shared brains).
As B, read a shared brain's details and extract prompt_id:
GET /brains/{shared_brain_id}/ (returns BrainEntity including prompt_id)
As B, read the target prompt:
GET /prompts/{prompt_id}
Authorization: Bearer <token_B>
Returns the prompt content despite B not owning it.
As B, overwrite the prompt:
PUT /prompts/{prompt_id}
Authorization: Bearer <token_B>
{ "content": "", "title": "..." }
Returns success; the prompt body is replaced. Subsequent chats on any brain referencing this prompt_id now use the injected content.
Validation level: code-level. The Prompt service uses the Supabase SDK client (requires a live Supabase backend to exercise the HTTP path end-to-end). The route, service, and repository were traced and contain no ownership check at any layer, and the Prompt model has no owner column; the discovery vector via BrainEntity.prompt_id was confirmed by reading brain_entity.py. This finding is reported at code-level validation, distinct from finding 001 which was reproduced live.
Impact
Authenticated regular user (PR:L), including a read-only Viewer of a shared brain. Integrity: high - an attacker can silently rewrite the system prompt that governs a shared brain's responses, affecting all of that brain's users (prompt-injection at the configuration layer), and can tamper with any prompt in the deployment given its UUID. Confidentiality: low - prompt contents are readable cross-user. No availability impact.
Relevant log output
Twitter / LinkedIn details
No response