fix: enforce memory REST user authorization#1317
Conversation
🦋 Changeset detectedLatest commit: 8bb0ed9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 4 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughThis PR adds object-level authorization to the memory REST API handlers, binding all conversation, message, and working memory operations to the authenticated user. Cross-user access via direct ChangesMemory API Cross-User Authorization Fix
Sequence DiagramsequenceDiagram
participant Client as Authenticated Client
participant Route as Route Handler
participant Handler as Memory Handler
participant AuthHelper as normalizeAuthId
participant AuthCheck as validateRequestedUserId
participant AuthGate as authorizeConversation
participant Memory as Memory Service
Client->>Route: GET /api/memory/conversations/{id}<br/>with Bearer token
Route->>Route: Extract authenticatedUser from context
Route->>Handler: call handler with query + authenticatedUser
Handler->>AuthHelper: normalize user id from token
Handler->>AuthCheck: validate query.userId matches caller
AuthCheck-->>Handler: 403 Forbidden if mismatch
Handler->>Memory: lookup conversation by id
Memory-->>Handler: return conversation with userId field
Handler->>AuthGate: verify conversation.userId == caller id
AuthGate-->>Handler: 403 Forbidden if unauthorized
Handler-->>Route: return authorized conversation
Route-->>Client: 200 OK with conversation data
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint skipped: no ESLint configuration detected in root package.json. To enable, add Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
packages/server-core/src/handlers/memory.handlers.spec.ts (2)
140-210: ⚖️ Poor tradeoffTest coverage could be expanded to include mutation and search operations.
The current tests cover read operations and delete well. Consider adding tests for:
handleSaveMemoryMessages(cross-user message saving)handleUpdateMemoryConversation(cross-user updates)handleCloneMemoryConversation(cross-user cloning)handleSearchMemory(cross-user search filtering)This would provide complete regression coverage as mentioned in the PR objectives.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/server-core/src/handlers/memory.handlers.spec.ts` around lines 140 - 210, Add tests covering cross-user mutation and search paths: create new it() cases that call handleSaveMemoryMessages, handleUpdateMemoryConversation, handleCloneMemoryConversation with deps, victimConversationId and an authenticatedUser { id: attackerUserId } and assert expectForbidden on the result and that the original conversation remains unchanged when applicable (similar to the delete test pattern using handleGetMemoryConversation to verify owner still can read); also add a test for handleSearchMemory that runs a search as attackerUserId and verifies results are either forbidden or do not include victimConversationId (and run the same search as victimUserId to confirm expected hits), reusing agentId, attackerConversationId, victimConversationId, attackerUserId, victimUserId variables from the current spec for consistency.
4-4: 💤 Low valueImport
InMemoryStorageAdapterfrom@voltagent/coreinstead of a relative path
packages/server-core/src/handlers/memory.handlers.spec.tsimportsInMemoryStorageAdapterfrom../../../core/src/..., but@voltagent/corealready re-exportsInMemoryStorageAdapter, so the test should use:import { InMemoryStorageAdapter } from "`@voltagent/core`";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/server-core/src/handlers/memory.handlers.spec.ts` at line 4, The test imports InMemoryStorageAdapter via a relative path; update the import in memory.handlers.spec.ts to import { InMemoryStorageAdapter } from "`@voltagent/core`" instead of "../../../core/src/memory/adapters/storage/in-memory" so the spec uses the package re-export; ensure any other tests in the same file referencing that relative import are updated to the `@voltagent/core` import as well.packages/serverless-hono/src/routes.ts (1)
132-137: 💤 Low valueConsider extracting shared helper to avoid duplication.
This
getAuthenticatedUserhelper is identical to the one inpackages/server-hono/src/routes/memory.routes.ts(lines 45-50). If both packages share a common dependency, this could be extracted to reduce duplication.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/serverless-hono/src/routes.ts` around lines 132 - 137, The getAuthenticatedUser helper in packages/serverless-hono/src/routes.ts duplicates logic from the one in packages/server-hono/src/routes/memory.routes.ts; extract this function into a shared utility module (e.g., a new helper file in a shared package or common utilities) and have both modules import it instead of defining it locally. Move the logic that reads c.get?.("authenticatedUser") and type-guards to a single exported function (preserving the MemoryAuthenticatedUser type or exporting that type alongside it), update both call sites to import and use the shared getAuthenticatedUser, and remove the duplicate definitions to keep behavior identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/server-core/src/handlers/memory.handlers.spec.ts`:
- Around line 140-210: Add tests covering cross-user mutation and search paths:
create new it() cases that call handleSaveMemoryMessages,
handleUpdateMemoryConversation, handleCloneMemoryConversation with deps,
victimConversationId and an authenticatedUser { id: attackerUserId } and assert
expectForbidden on the result and that the original conversation remains
unchanged when applicable (similar to the delete test pattern using
handleGetMemoryConversation to verify owner still can read); also add a test for
handleSearchMemory that runs a search as attackerUserId and verifies results are
either forbidden or do not include victimConversationId (and run the same search
as victimUserId to confirm expected hits), reusing agentId,
attackerConversationId, victimConversationId, attackerUserId, victimUserId
variables from the current spec for consistency.
- Line 4: The test imports InMemoryStorageAdapter via a relative path; update
the import in memory.handlers.spec.ts to import { InMemoryStorageAdapter } from
"`@voltagent/core`" instead of
"../../../core/src/memory/adapters/storage/in-memory" so the spec uses the
package re-export; ensure any other tests in the same file referencing that
relative import are updated to the `@voltagent/core` import as well.
In `@packages/serverless-hono/src/routes.ts`:
- Around line 132-137: The getAuthenticatedUser helper in
packages/serverless-hono/src/routes.ts duplicates logic from the one in
packages/server-hono/src/routes/memory.routes.ts; extract this function into a
shared utility module (e.g., a new helper file in a shared package or common
utilities) and have both modules import it instead of defining it locally. Move
the logic that reads c.get?.("authenticatedUser") and type-guards to a single
exported function (preserving the MemoryAuthenticatedUser type or exporting that
type alongside it), update both call sites to import and use the shared
getAuthenticatedUser, and remove the duplicate definitions to keep behavior
identical.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0c52fc5b-439b-49d1-8fe5-2778651a1855
📒 Files selected for processing (6)
.changeset/quiet-memory-guards.mdpackages/server-core/src/handlers/memory.handlers.spec.tspackages/server-core/src/handlers/memory.handlers.tspackages/server-elysia/src/routes/memory.routes.tspackages/server-hono/src/routes/memory.routes.tspackages/serverless-hono/src/routes.ts
Summary
Fixes #1315
Verification
Notes
Summary by cubic
Enforces authorization for memory REST APIs by binding all reads/writes/search/list operations to the authenticated user and rejecting cross-user access. Fixes #1315.
@voltagent/server-hono,@voltagent/server-elysia, and@voltagent/serverless-honointo@voltagent/server-corehandlers.id/sub/userIdand enforce owner checks on conversations, messages, working memory, deletes, and search; return 403 on mismatch.userId; scope queries and counts to the authenticated user.Written for commit 8bb0ed9. Summary will update on new commits. Review in cubic
Summary by CodeRabbit
Bug Fixes
Tests