Skip to content

fix(chat): prevent private conversation visibility leak#2018

Open
sriaradhyula wants to merge 12 commits into
mainfrom
prebuild/fix/private-chat-visibility
Open

fix(chat): prevent private conversation visibility leak#2018
sriaradhyula wants to merge 12 commits into
mainfrom
prebuild/fix/private-chat-visibility

Conversation

@sriaradhyula

@sriaradhyula sriaradhyula commented Jun 25, 2026

Copy link
Copy Markdown
Member

Description

Fixes a 0.5.x regression where unshared private chat conversations could be included in global/shared visibility paths. The list, search, and trash APIs now build a bounded Mongo candidate set before ReBAC checks:

  • owned by the caller
  • public conversations
  • directly shared with the caller
  • team-shared candidates, with team authorization still enforced by ReBAC

This also removes conversation:discover from the public everyone grant allowlist so private conversations cannot be made globally discoverable through the generic grant API.

Follow-up UI and sharing fixes in this PR:

  • Direct-open shared chat URLs preserve owner_id, accessLevel, and sharing metadata in the client store, so recipients see the shared/public badge.
  • Conversation list rows now carry viewer-specific viewer_has_shared_access and access_level metadata after ReBAC filtering; the chat store maps them to the sidebar so recipients see the right shared affordance.
  • Sidebar shared badges are viewer-aware: recipients see the shared icon, while the owner does not get a recipient badge just because they shared the chat.
  • Shared conversation recipients now see the sidebar share affordance again; hovering shows who shared the conversation. Read-only shares copy the conversation link, while edit-mode shares open a read-only sharing/details modal.
  • Chat sharing can target teams from the member-visible team endpoint, stores canonical team slugs for new team shares, resolves recipient access through canonical team_membership_sources, and writes best-effort conversation grants for team recipients.
  • Dynamic-agent execution details stay expanded when the completed turn includes warnings or errors; users can still collapse them manually.

Added regression coverage for the candidate query helper, affected API routes, public/team sharing behavior, service-account grants, direct-open shared chat metadata, recipient-only sidebar shared badges, recipient share affordance behavior, the mocked RBAC Playwright shared-recipient sidebar affordance flows for read-only and edit shares, team share dialog behavior, canonical team recipient access, warning timeline expansion, and the RBAC Playwright flow for recent chats, search/trash, and rejected everyone discover grants.

Authorization Boundary: CAS vs List Metadata

This PR keeps the access decision in the centralized ReBAC path. The conversation list first builds a bounded Mongo candidate set and then calls filterConversationsByImplicitOrExplicitPermission(...) before returning rows. Detail, message, turn, and share mutation routes continue to use requireConversationAccess(...) for authoritative enforcement.

The final access_level on GET /api/chat/conversations is display metadata for sidebar/share-button affordances. It distinguishes owner, shared edit, shared read-only, and admin audit for rows the caller is already allowed to see. Today that effective tier depends on Mongo share metadata (owner_id, owner_subject, sharing_access.permission, sharing.public_permission, and sharing.team_permissions) as well as ReBAC visibility. Calling CAS/OpenFGA per row would reintroduce the N+1 behavior and would still not answer the full "what affordance should the sidebar show?" question without another metadata lookup.

A future CAS bulk effective-access API would be the right place to centralize this display tier. Until then, this patch keeps enforcement centralized and derives only already-authorized list metadata locally, so a sidebar badge cannot break listing or weaken read/write/share gates.

Type of Change

  • Bugfix
  • New Feature
  • Breaking Change
  • Refactor
  • Documentation
  • Other (please describe)

Pre-release Helm Charts (Optional)

No chart changes.

Checklist

  • I have read the contributing guidelines
  • Existing issues have been referenced (where applicable)
  • I have verified this change is not present in other open pull requests
  • Functionality is documented
  • All code style checks pass
  • New code contribution is covered by automated tests
  • All new and existing tests pass

Validation

  • npm test -- --runTestsByPath src/lib/rbac/__tests__/conversation-implicit-authz.test.ts src/lib/authz/__tests__/http.test.ts src/app/api/__tests__/chat-conversations-remaining-rbac.test.ts src/app/api/__tests__/chat-sharing-public.test.ts src/app/api/__tests__/chat-sharing-teams.test.ts src/app/api/__tests__/chat-conversations-agent-auth.test.ts src/app/api/__tests__/chat-conversations-sa-grant.test.ts
  • npm test -- --runTestsByPath src/app/api/__tests__/chat-conversations-client-type.test.ts src/app/api/chat/shared/__tests__/route.test.ts
  • npm test -- --runTestsByPath src/lib/rbac/__tests__/conversation-implicit-authz.test.ts src/components/layout/__tests__/Sidebar.test.tsx src/store/__tests__/chat-store.test.ts src/app/api/__tests__/chat-conversations-client-type.test.ts
  • npm test -- --runTestsByPath src/components/layout/__tests__/Sidebar.test.tsx src/components/chat/__tests__/ShareButton.test.tsx
  • RUN_RBAC_REGRESSION=1 npx playwright test --config=playwright.rbac.config.ts e2e/rbac/chat-share-exposed.spec.ts
  • RUN_RBAC_REGRESSION=1 NEXTAUTH_SECRET=rbac-e2e-secret CAIPE_UI_BASE_URL=http://localhost:3100 npx playwright test --config=playwright.rbac.config.ts e2e/rbac/chat-navigation-regression.spec.ts --grep "read-only shared conversation recipients|edit-mode shared conversation recipients"
  • npm test -- --runTestsByPath 'src/app/(app)/chat/[uuid]/__tests__/page.test.tsx' 'src/components/layout/__tests__/Sidebar.test.tsx' 'src/components/chat/__tests__/DynamicAgentTimeline.test.tsx'
  • npm test -- --runTestsByPath src/app/api/__tests__/chat-sharing-teams.test.ts src/app/api/__tests__/chat-sharing-readonly.test.ts src/components/chat/__tests__/ShareDialogPublicToggle.test.tsx
  • npm test -- ShareButton.test.tsx --runInBand
  • npm run lint (0 errors; existing warnings remain)
  • npm run lint -- --quiet
  • npm run build
  • git diff --check

Scope conversation list/search/trash candidates before ReBAC checks so unshared private chats are not considered visible, and keep conversation discovery grants out of everyone public grants.

Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
@caipe-security

Copy link
Copy Markdown

✅ No proprietary content detected. This PR is clear for review!

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

🧪 CAIPE UI Test Results

All tests passed

🟠 Overall Coverage: 58%

Coverage
lines
statements
functions
branches

📊 Detailed Coverage

Metric Covered Total Percentage
Lines 29402 46521 63.20%
Statements 31401 51263 61.25%
Functions 5301 9386 56.47%
Branches 19025 36893 51.56%

✅ Test Suites

  • ✅ auth-guard.test.tsx - Route protection & authorization
  • ✅ token-expiry-guard.test.tsx - Token expiry handling
  • ✅ dynamic-agent-client.test.ts - Dynamic Agents streaming client
  • ✅ auth-utils.test.ts - Authentication utilities (100% coverage)
  • ✅ auth-config.test.ts - OIDC configuration
📈 Coverage Thresholds
Threshold Target Current Status
Minimum 40% 58% ✅ Pass
Good 60% 58% ⚠️ Below target
Excellent 80% 58% ⚠️ Below target
⚠️ Areas Needing Tests

High Priority:

  • lib/streaming/* - Core stream event handling
  • store/chat-store.ts - Chat state management
  • store/agent-skills-store.ts - Agent skills
  • lib/api-client.ts - API communication
  • lib/storage-mode.ts - MongoDB/localStorage switching

Medium Priority:

  • components/chat/ChatPanel.tsx - Main chat interface
  • components/agent-builder/* - Agent builder UI
  • lib/mongodb.ts - MongoDB integration

💡 Run locally: make caipe-ui-tests
📦 Full report: Check workflow artifacts

@caipe-ci-build

caipe-ci-build Bot commented Jun 25, 2026

Copy link
Copy Markdown
Prebuild Artifacts for `a2f64d8` (archived)

Prebuild Artifacts for a2f64d8

Branch: prebuild/fix/private-chat-visibility
Commit: a2f64d8

Docker Images

Artifact Image Tag Status CI
caipe-ui ghcr.io/cnoe-io/prebuild/caipe-ui fix-private-chat-visibility-2 Published CI
Docker pull commands
docker pull ghcr.io/cnoe-io/prebuild/caipe-ui:fix-private-chat-visibility-2

These prebuild artifacts will be automatically cleaned up when the PR is closed or merged.

Carry sharing metadata into direct-open chat store entries and keep timeline warning details expanded after a turn completes.

Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
Make sidebar shared badges viewer-aware and preserve conversation access levels from direct chat loads so recipients see shared chat indicators without showing recipient badges to owners.

Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
Allow chat shares to target teams from the member-visible team endpoint, persist canonical team slugs when available, resolve recipient access through canonical team membership, and write best-effort conversation grants for team recipients.

Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
@caipe-ci-build

caipe-ci-build Bot commented Jun 25, 2026

Copy link
Copy Markdown
Prebuild Artifacts for `af5e227` (archived)

Prebuild Artifacts for af5e227

Branch: prebuild/fix/private-chat-visibility
Commit: af5e227

Docker Images

Artifact Image Tag Status CI
caipe-ui ghcr.io/cnoe-io/prebuild/caipe-ui fix-private-chat-visibility-5 Published CI
Docker pull commands
docker pull ghcr.io/cnoe-io/prebuild/caipe-ui:fix-private-chat-visibility-5

These prebuild artifacts will be automatically cleaned up when the PR is closed or merged.

Annotate conversation list rows with a viewer-specific shared flag after RBAC filtering and carry it through the chat store.

Use that signal in the sidebar so recipients see the shared badge even when owner metadata is missing, while owners stay unbadged.

Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
@caipe-ci-build

Copy link
Copy Markdown

Prebuild Artifacts for b34544e

Branch: prebuild/fix/private-chat-visibility
Commit: b34544e

Docker Images

Artifact Image Tag Status CI
caipe-ui ghcr.io/cnoe-io/prebuild/caipe-ui fix-private-chat-visibility-6 Published CI
Docker pull commands
docker pull ghcr.io/cnoe-io/prebuild/caipe-ui:fix-private-chat-visibility-6

These prebuild artifacts will be automatically cleaned up when the PR is closed or merged.

Restore the sidebar share affordance for shared conversation recipients without opening owner-only share management.

Show shared-by details in the shared badge and recipient share tooltip, with recipient clicks copying the conversation link.

Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
@sriaradhyula sriaradhyula marked this pull request as ready for review June 25, 2026 18:26
Signed-off-by: Sri Aradhyula <sraradhy@cisco.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

1 participant