Skip to content

fix: enforce memory REST user authorization#1317

Open
3em0 wants to merge 1 commit into
VoltAgent:mainfrom
3em0:fix/memory-rest-authorization
Open

fix: enforce memory REST user authorization#1317
3em0 wants to merge 1 commit into
VoltAgent:mainfrom
3em0:fix/memory-rest-authorization

Conversation

@3em0
Copy link
Copy Markdown

@3em0 3em0 commented May 27, 2026

Summary

  • pass authenticated user context from Hono, Elysia, and serverless Hono memory routes into server-core memory handlers
  • enforce conversation owner checks and authenticated userId binding across memory REST reads/writes
  • add regression coverage for cross-user conversation, message, working-memory, delete, and list access

Fixes #1315

Verification

  • npm exec --yes pnpm@8.10.5 -- biome check packages/server-core/src/handlers/memory.handlers.ts packages/server-core/src/handlers/memory.handlers.spec.ts packages/server-hono/src/routes/memory.routes.ts packages/server-elysia/src/routes/memory.routes.ts packages/serverless-hono/src/routes.ts
  • npm exec --yes pnpm@8.10.5 -- --filter @voltagent/internal --filter @voltagent/core run build
  • npm exec --yes pnpm@8.10.5 -- --filter @voltagent/server-core run build
  • npm exec --yes pnpm@8.10.5 -- --filter @voltagent/server-core exec vitest run src/handlers/memory.handlers.spec.ts
  • npm exec --yes pnpm@8.10.5 -- --filter @voltagent/server-core run typecheck

Notes

  • Full adapter package typechecks currently fail on existing unrelated issues, including missing built internal packages such as @voltagent/resumable-streams/@voltagent/mcp-server/@voltagent/a2a-server and pre-existing Hono/Elysia route typing errors around dynamic numeric status codes.

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.

  • Bug Fixes
    • Pass authenticated user from @voltagent/server-hono, @voltagent/server-elysia, and @voltagent/serverless-hono into @voltagent/server-core handlers.
    • Normalize id/sub/userId and enforce owner checks on conversations, messages, working memory, deletes, and search; return 403 on mismatch.
    • Prevent authenticated requests from overriding userId; scope queries and counts to the authenticated user.
    • Add regression tests for cross-user conversation, message, working-memory, delete, and list access.

Written for commit 8bb0ed9. Summary will update on new commits. Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • REST API memory operations are now authenticated, ensuring users can only access their own conversations and preventing unauthorized access to other users' data.
  • Tests

    • Added comprehensive authorization tests to validate that memory operations properly enforce user-scoped access controls.

Review Change Stack

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 27, 2026

🦋 Changeset detected

Latest commit: 8bb0ed9

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@voltagent/server-core Patch
@voltagent/server-hono Patch
@voltagent/server-elysia Patch
@voltagent/serverless-hono Patch

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

This 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 conversationId lookup is now blocked with HTTP 403, preventing IDOR vulnerabilities across all memory operations and all three server frameworks.

Changes

Memory API Cross-User Authorization Fix

Layer / File(s) Summary
Auth Context Types and Core Authorization Helpers
packages/server-core/src/handlers/memory.handlers.ts
Introduces MemoryAuthenticatedUser type and MemoryAuthContext shape. Adds internal helpers: normalizeAuthId() to extract user ID from multiple fields, forbidden403() for standard 403 responses, validateRequestedUserId() to check caller ID against query/body userId, and authorizeConversation() to enforce conversation ownership.
Authorization Test Coverage
packages/server-core/src/handlers/memory.handlers.spec.ts
Comprehensive test suite with two in-memory conversations owned by different users. Validates 403 denial for cross-user reads (conversations, messages, working memory), deletion attempts, conversation listing scope, and userId override rejection.
Handler Authorization Implementation
packages/server-core/src/handlers/memory.handlers.ts
All 12 handler function signatures extended with MemoryAuthContext. List conversations derive userId from authenticated user; get/delete/clone operations authorize conversation ownership; message operations authorize parent conversation; working memory operations enforce scope-specific authorization; save/create/update/search all validate caller against authenticated user with early 403 returns.
Elysia Route Integration
packages/server-elysia/src/routes/memory.routes.ts
Adds MemoryAuthenticatedUser import. Creates getAuthenticatedUser(store) helper. Wires authenticated user through all memory route handlers via store.authenticatedUser extraction.
Hono Route Integration
packages/server-hono/src/routes/memory.routes.ts
Adds MemoryAuthenticatedUser import. Creates getAuthenticatedUser(c) helper to extract from c.get?.("authenticatedUser"). Threads authenticated user into all memory handler calls across routes.
Serverless Hono Route Integration
packages/serverless-hono/src/routes.ts
Adds MemoryAuthenticatedUser import. Creates getAuthenticatedUser(c) helper for serverless context. Includes authenticated user in all memory endpoint options for conversation, message, working memory, and search operations.
Changeset Documentation
.changeset/quiet-memory-guards.md
Documents patch-level version bumps for all affected server packages (@voltagent/server-core, @voltagent/server-hono, @voltagent/server-elysia, @voltagent/serverless-hono). Records security fix: memory REST API operations now bound to authenticated user to prevent cross-user access.

Sequence Diagram

sequenceDiagram
  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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

A vault once stood with unlocked doors,
where conversations leaked across floors,
now guards patrol each memory gate,
and users see only their own fate.
Authentication binds, and boundaries hold—
your secrets safe, your access controlled. 🐰🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix: enforce memory REST user authorization' directly describes the primary change—adding authorization enforcement to prevent cross-user memory access.
Description check ✅ Passed The description covers all required template sections: summary of changes, issue linkage (#1315), comprehensive verification steps, notes on existing constraints, and changeset metadata.
Linked Issues check ✅ Passed The PR fully addresses issue #1315 objectives: passes authenticated user context through all route handlers, enforces conversation owner checks, prevents fallback to caller-supplied userId, and includes regression tests for cross-user access attempts.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the object-level authorization fix: auth context threading, handler authorization logic, route integration, and test coverage for IDOR scenarios.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
packages/server-core/src/handlers/memory.handlers.spec.ts (2)

140-210: ⚖️ Poor tradeoff

Test 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 value

Import InMemoryStorageAdapter from @voltagent/core instead of a relative path
packages/server-core/src/handlers/memory.handlers.spec.ts imports InMemoryStorageAdapter from ../../../core/src/..., but @voltagent/core already re-exports InMemoryStorageAdapter, 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 value

Consider extracting shared helper to avoid duplication.

This getAuthenticatedUser helper is identical to the one in packages/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

📥 Commits

Reviewing files that changed from the base of the PR and between 5be7626 and 8bb0ed9.

📒 Files selected for processing (6)
  • .changeset/quiet-memory-guards.md
  • packages/server-core/src/handlers/memory.handlers.spec.ts
  • packages/server-core/src/handlers/memory.handlers.ts
  • packages/server-elysia/src/routes/memory.routes.ts
  • packages/server-hono/src/routes/memory.routes.ts
  • packages/serverless-hono/src/routes.ts

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed

Re-trigger cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Memory REST API permits cross-user conversation access by conversationId

1 participant