fix(security): close guest-surface findings on agent run entry paths - #411
Closed
esafwan wants to merge 5 commits into
Closed
fix(security): close guest-surface findings on agent run entry paths#411esafwan wants to merge 5 commits into
esafwan wants to merge 5 commits into
Conversation
- run_agent_sync / run_agent_stream / run_agent_sync_chat: resolve allow_guest via frappe.db.get_value before loading the Agent doc and return one generic 'Agent not found or access denied' PermissionError for both missing and guest-denied agents (closes the enumeration oracle) - ConversationManager.get_or_create_conversation: a conversation loaded by id must belong to the requested agent and be owned by the session user, match the session_id, or the caller must hold chat.view_all (closes cross-user conversation hijack for every caller) - ignore caller-supplied provider/model for Guest sessions - enforce has_capability(user, 'agent.use') for non-guest callers on all three entry paths
…stence oracle) Cross-family review (codex) found that a supplied-but-nonexistent conversation_id fell through to the latest-or-create path, distinguishing 'missing' from 'exists but forbidden'. Both now fail with the same generic PermissionError.
Sanjusha-tridz
marked this pull request as ready for review
August 12, 2026 07:44
esafwan
added a commit
that referenced
this pull request
Aug 12, 2026
… fixes onto agent_access.py PR #597's assert_agent_access/check_agent_access unify allow_guest/allowed_users/ allowed_roles enforcement, but branched before #411 landed three narrower fixes that #597 doesn't otherwise cover: - Agent-name enumeration oracle: run_agent_sync/run_agent_stream/run_agent_sync_chat loaded the Agent doc before checking allow_guest, so a Guest caller could tell "no such agent" (DoesNotExistError) from "exists but guests aren't allowed" (PermissionError) apart by exception type. Now both collapse to one generic PermissionError for Guest callers. Implemented inline per call site (not via a new agent_access.py helper) because agent_access.py has its own `frappe` import distinct from each caller module's — a shared helper doing frappe.db.get_value there would bypass the @patch("huf.ai.agent_integration.frappe")-style mocks these call sites' existing tests rely on (the same class of bug #597's own 7646fe7 fixed for assert_agent_access). - agent.use capability (RBAC) was never consulted on these three entry points; now enforced for all non-Guest callers. - Guest callers could pass provider/model overrides that were resolved (and thus billed against site API keys) before the guest check ran; now nulled for Guest before resolution. - ConversationManager.get_or_create_conversation trusted a caller-supplied conversation_id with no ownership check, letting any authenticated user (or Guest on a guest agent) read/append to another user's conversation. Now requires conversation.agent match plus owner/session_id/chat.view_all, and a missing id and an inaccessible id fail identically (no existence oracle). Adds ai/tests/test_conversation_manager_access.py (pure-mock unit tests, no live bench required) covering the new ownership branch, mirroring test_agent_access.py's style since no test previously existed for this method. Supersedes #411 (SEC-gap1-fix), which will be closed in favor of this branch.
Contributor
Author
|
Closing in favor of #597, which supersedes this PR. #597 replaces the duplicated
Rollback/history for this PR's original approach is preserved on |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Follow-up to the security gaps salvaged from closed #118 (full audit on file, every claim file:line-verified against develop @ 95daa90):
run_agent_syncpassed caller-suppliedconversation_idstraight toConversationManager.get_or_create_conversation, which loaded ANYAgent Conversationby name with no agent/owner/session check — any authenticated user (or Guest on a guest agent) could read and append to another user's conversation.DoesNotExistErrorvsPermissionError).run_agent_sync/run_agent_stream/chat API entry paths.Scope (3 files)
agent_integration.py(run_agent_sync,run_agent_stream),chat_api.py: resolveallow_guestviafrappe.db.get_valueBEFORE any Agent doc load; one generic 'Agent not found or access denied' for missing and not-permitted; provider/model override ignored for Guest;has_capability(user, 'agent.use')enforced for non-Guest callers.conversation_manager.py: explicitconversation_idmust match agent AND (owner == session user OR session_id matches ORchat.view_allcapability) — and a missing id and an inaccessible id now fail identically (no existence oracle).Key decisions
get_or_create_conversationso every caller (sync, stream, chat, hooks, flows) is protected at one point.agent.usewill now be denied — flagged for release notes. Scheduler/Administrator paths unaffected (Administrator has all capabilities).Agent contributions
Audit + fix by the release-orchestration pipeline; an independent second review REJECTED the first cut (found a remaining existence oracle for missing explicit IDs); oracle closed by the pipeline; second commit.
Tests executed
py_compileon all 3 files; rg-verified guest check precedes every doc load on the 3 entry paths.conversation_idraises the genericPermissionError(oracle closed); no-id create path unaffected.Risks and limitations
agent.uselose doc-event/flow agent execution (intended; needs release note).Rollback
Revert the 2 commits; entry paths return to entry-point-only enforcement.
Remaining work