Skip to content

🛡️ refactor: Scope Action Mutations by Parent Resource Ownership#12237

Merged
danny-avila merged 7 commits into
devfrom
fix/action-ownership-scope
Mar 15, 2026
Merged

🛡️ refactor: Scope Action Mutations by Parent Resource Ownership#12237
danny-avila merged 7 commits into
devfrom
fix/action-ownership-scope

Conversation

@danny-avila

@danny-avila danny-avila commented Mar 15, 2026

Copy link
Copy Markdown
Owner

Summary

Fixed a cross-tenant authorization vulnerability where a user with EDIT access on their own agent or assistant could reference a foreign action_id to overwrite or hijack another resource's action record.

  • Removed the && short-circuit from the ownership guard on the POST (update) path in both the agents and assistants action routes, which silently bypassed the check for any action whose agent_id or assistant_id was falsy — including all cross-type records (e.g., an assistant-owned action has no agent_id, so the guard on the agents route never fired).
  • Pushed agent_id and assistant_id into the updateAction and deleteAction query filters so DB-level ownership is enforced atomically with the write, eliminating the check-then-act race between the pre-check fetch and the write operation.
  • Added a logger.warn when deleteAction returns null in both DELETE routes, surfacing silent partial failures caused by data-integrity skew between the agent/assistant document's actions array and the Action collection.
  • Updated the updateAction and deleteAction JSDoc in api/models/Action.js to accurately reflect the scoped query parameter shapes (agent_id?, assistant_id?) instead of the now-stale user field.
  • Added 12 integration tests in api/models/Action.spec.js using MongoMemoryServer and real Mongoose models, covering matching and mismatched agent_id/assistant_id for both update and delete operations, getActions scoping, and cross-type protection (agent filter cannot overwrite or delete an assistant-owned action and vice versa).

Change Type

  • Bug fix (non-breaking change which fixes an issue)

Testing

Tests are written as model-layer integration tests in api/models/Action.spec.js using mongodb-memory-server with real Mongoose models and no mocking of DB logic, per project testing philosophy.

cd api && npx jest Action.spec

Test Configuration:

12 tests across 4 describe blocks:

  • updateAction — matching and mismatched agent_id/assistant_id
  • deleteAction — matching and mismatched agent_id/assistant_id
  • getActions — unscoped and scoped baselines
  • Cross-type protection — agent filter cannot touch assistant-owned actions

Checklist

  • My code adheres to this project's style guidelines
  • I have performed a self-review of my own code
  • My changes do not introduce new warnings
  • I have written tests demonstrating that my changes are effective or that my feature works
  • Local unit tests pass with my changes

Prevent cross-tenant action overwrites by validating that an existing
action's agent_id/assistant_id matches the URL parameter before allowing
updates or deletes. Without this, a user with EDIT access on their own
agent could reference a foreign action_id to hijack another agent's
action record.
Copilot AI review requested due to automatic review settings March 15, 2026 01:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the Agent/Assistant action mutation routes to prevent cross-tenant overwrites by ensuring an existing action_id is scoped to the parent resource (agent_id / assistant_id) before allowing updates or deletes.

Changes:

  • Add parent-ownership checks when updating an existing action via POST /actions/:{agent|assistant}_id.
  • Scope action deletions by passing { action_id, agent_id } / { action_id, assistant_id } into deleteAction.
  • Return a 403 when an action is detected as belonging to a different parent resource.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
api/server/routes/assistants/actions.js Adds assistant ownership validation for action updates; scopes action deletion by assistant_id.
api/server/routes/agents/actions.js Adds agent ownership validation for action updates; scopes action deletion by agent_id.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

Comment thread api/server/routes/assistants/actions.js Outdated

if (actions_result && actions_result.length) {
const action = actions_result[0];
if (action.assistant_id && action.assistant_id !== assistant_id) {
Comment on lines 201 to 203
promises.push(updateAssistantDoc({ assistant_id }, assistantUpdateData));
promises.push(deleteAction({ action_id }));
promises.push(deleteAction({ action_id, assistant_id }));

Comment thread api/server/routes/agents/actions.js Outdated

if (actions_result && actions_result.length) {
const action = actions_result[0];
if (action.agent_id && action.agent_id !== agent_id) {
Comment on lines 256 to 258
);
await deleteAction({ action_id });
await deleteAction({ action_id, agent_id });
res.status(200).json({ message: 'Action deleted successfully' });
- Remove && short-circuit that bypassed the guard when agent_id or
  assistant_id was falsy (e.g. assistant-owned actions have no agent_id,
  so the check was skipped entirely on the agents route).
- Include agent_id / assistant_id in the updateAction and deleteAction
  query filters so the DB write itself enforces ownership atomically.
- Log a warning when deleteAction returns null (silent no-op from
  data-integrity mismatch).
Cover update, delete, and cross-type protection scenarios using
MongoMemoryServer to verify that scoped query filters (agent_id,
assistant_id) prevent cross-tenant overwrites and deletions at the
database level.
@danny-avila danny-avila changed the title 🛡️ fix: Scope action mutations by parent resource ownership 🛡️ fix: Scope Action Mutations by Parent Resource Ownership Mar 15, 2026
…ng agent actions

The duplicate handler was splitting `action.action_id` by `actionDelimiter`
to extract the domain, but `action_id` is a bare nanoid that doesn't
contain the delimiter. This produced malformed entries in the duplicated
agent's actions array (nanoid_action_newNanoid instead of
domain_action_newNanoid). The domain is available on `action.metadata.domain`.
Uses MongoMemoryServer with real Agent and Action models to verify:
- Duplicated actions use metadata.domain (not action_id) for the
  agent actions array entries
- Sensitive metadata fields are stripped from duplicated actions
- Original action documents are not modified
@danny-avila danny-avila changed the title 🛡️ fix: Scope Action Mutations by Parent Resource Ownership 🛡️ refactor: Scope Action Mutations by Parent Resource Ownership Mar 15, 2026
@danny-avila danny-avila merged commit 0c27ad2 into dev Mar 15, 2026
9 checks passed
@danny-avila danny-avila deleted the fix/action-ownership-scope branch March 15, 2026 14:19
jcbartle pushed a commit to jcbartle/LibreChat that referenced this pull request May 11, 2026
…ny-avila#12237)

* 🛡️ fix: Scope action mutations by parent resource ownership

Prevent cross-tenant action overwrites by validating that an existing
action's agent_id/assistant_id matches the URL parameter before allowing
updates or deletes. Without this, a user with EDIT access on their own
agent could reference a foreign action_id to hijack another agent's
action record.

* 🛡️ fix: Harden action ownership checks and scope write filters

- Remove && short-circuit that bypassed the guard when agent_id or
  assistant_id was falsy (e.g. assistant-owned actions have no agent_id,
  so the check was skipped entirely on the agents route).
- Include agent_id / assistant_id in the updateAction and deleteAction
  query filters so the DB write itself enforces ownership atomically.
- Log a warning when deleteAction returns null (silent no-op from
  data-integrity mismatch).

* 📝 docs: Update Action model JSDoc to reflect scoped query params

* ✅ test: Add Action ownership scoping tests

Cover update, delete, and cross-type protection scenarios using
MongoMemoryServer to verify that scoped query filters (agent_id,
assistant_id) prevent cross-tenant overwrites and deletions at the
database level.

* 🛡️ fix: Scope updateAction filter in agent duplication handler

* 🐛 fix: Use action metadata domain instead of action_id when duplicating agent actions

The duplicate handler was splitting `action.action_id` by `actionDelimiter`
to extract the domain, but `action_id` is a bare nanoid that doesn't
contain the delimiter. This produced malformed entries in the duplicated
agent's actions array (nanoid_action_newNanoid instead of
domain_action_newNanoid). The domain is available on `action.metadata.domain`.

* ✅ test: Add integration tests for agent duplication action handling

Uses MongoMemoryServer with real Agent and Action models to verify:
- Duplicated actions use metadata.domain (not action_id) for the
  agent actions array entries
- Sensitive metadata fields are stripped from duplicated actions
- Original action documents are not modified
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.

2 participants