Skip to content

fix(security): prevent cross-vendor order status change via orders bulk-actions endpoint (IDOR)#3291

Open
MdAsifHossainNadim wants to merge 1 commit into
developfrom
fix/order-bulk-action-ownership-idor
Open

fix(security): prevent cross-vendor order status change via orders bulk-actions endpoint (IDOR)#3291
MdAsifHossainNadim wants to merge 1 commit into
developfrom
fix/order-bulk-action-ownership-idor

Conversation

@MdAsifHossainNadim

@MdAsifHossainNadim MdAsifHossainNadim commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

All Submissions:

  • My code follows the WordPress coding standards
  • My code is tested
  • My code has proper inline documentation

Changes proposed in this Pull Request:

Fixes a cross-vendor IDOR on the vendor orders bulk-actions REST endpoint: any authenticated vendor could change the status of any other vendor's order.

What's the actual issue?

POST /wp-json/dokan/v2/orders/bulk-actions (and the inherited dokan/v3 route) is guarded only by update_order_permissions_check(), which checks the generic dokan_manage_order capability and the global "order status change" setting — it never verifies that the submitted order ids belong to the requesting vendor. The callback then forwards the raw order_ids straight into dokan_apply_bulk_order_status_change(), which calls $order->update_status() on each id.

So a vendor could send another vendor's order ids in the order_ids array and flip their statuses (e.g. mark a competitor's pending orders as completed/processing), bypassing the per-order ownership check that the single-order endpoints already enforce.

How we fixed itincludes/REST/OrderControllerV2.php

Before dispatching the status change, drop any order the current vendor does not own. Admins / shop managers (manage_woocommerce) are exempt and keep acting on every order, exactly like the single-order endpoints:

$order_ids = $requests->get_param( 'order_ids' );

// A vendor may only bulk-update their own orders; foreign order ids are dropped (admins/shop managers are exempt).
if ( ! current_user_can( 'manage_woocommerce' ) ) {
    $vendor_id = dokan_get_current_user_id();
    $order_ids = array_filter(
        $order_ids,
        function ( $order_id ) use ( $vendor_id ) {
            return dokan_is_seller_has_order( $vendor_id, $order_id );
        }
    );
}

This reuses Dokan's canonical ownership helper (dokan_is_seller_has_order()), and OrderControllerV3 inherits the method so the dokan/v3 route is covered by the same change.

How to test

  1. As Vendor A, open Dashboard → Orders, select a few of your own orders, pick a Bulk Action (e.g. Change status to Processing) and Apply → it still works (no regression).
  2. As Vendor A, send a crafted request with Vendor B's order id:
    fetch('/wp-json/dokan/v2/orders/bulk-actions', {
      method:'POST',
      headers:{'Content-Type':'application/json','X-WP-Nonce':window.dokan.rest.nonce},
      credentials:'same-origin',
      body: JSON.stringify({ order_ids:[<VENDOR_B_ORDER_ID>], status:'completed' })
    })
    • Before: Vendor B's order is changed to completed.
    • After: Vendor B's order status is unchanged.

Test result: ✅ Verified on a live install. As a non-admin vendor: the vendor's own order changed status normally, while a foreign order (owned by another seller) stayed in its original status after the crafted request. Admins/shop managers remain unaffected.

Related Pull Request(s)

  • Security audit tracking (finding L1): getdokan/plugin-internal-tasks#1994

Closes

  • Closes getdokan/plugin-internal-tasks#1999

Changelog entry

Fix — Cross-vendor order status change via the orders bulk-actions REST endpoint

The vendor orders bulk-actions endpoint changed order statuses from a vendor-supplied list of ids without checking ownership, so a vendor could change another vendor's order statuses. Order ids are now filtered to the requesting vendor's own orders (admins and shop managers are exempt).

…point (IDOR)

The dokan/v2 (and inherited dokan/v3) orders bulk-actions REST endpoint changed
order statuses from a vendor-supplied list of ids without verifying ownership,
so any authenticated vendor could change another vendor's order statuses. Filter
the submitted order ids to the requesting vendor's own orders before dispatching
the status change; admins and shop managers (manage_woocommerce) are exempt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

process_orders_bulk_action() in OrderControllerV2 now filters the requested order_ids to only those belonging to the current vendor when the user lacks manage_woocommerce. The bulk_orders response payload is built from this filtered list.

Changes

Vendor-scoped bulk order status update

Layer / File(s) Summary
Vendor ownership filter for bulk action
includes/REST/OrderControllerV2.php
For non-manage_woocommerce users, order_ids are filtered to only orders owned by the current vendor before building the bulk_orders response and applying the bulk status change.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • getdokan/dokan#3246: Adds per-handler ownership validation in includes/Ajax.php to prevent unauthorized order note/status updates, same vendor scoping pattern.

Suggested labels

:tada: QA Approved, Dev Review Done

Suggested reviewers

  • mrabbani

Poem

🐇 Hoppity-hop, the orders align,
Only yours, dear vendor, stay in line!
No sneaky bulk edits from outside your den,
The filter keeps strangers from meddling again.
Safe orders for all — now that feels fine! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the security fix for cross-vendor order status changes in the bulk-actions endpoint.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description covers the required checklist items, change summary, testing, related issue, and changelog, with only optional sections left out.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/order-bulk-action-ownership-idor

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.

@MdAsifHossainNadim MdAsifHossainNadim self-assigned this Jul 2, 2026
@MdAsifHossainNadim MdAsifHossainNadim added Needs: Testing This requires further testing Needs: Dev Review It requires a developer review and approval labels Jul 2, 2026
@MdAsifHossainNadim MdAsifHossainNadim added Dev Review Done and removed Needs: Dev Review It requires a developer review and approval labels Jul 9, 2026
@dev-shahed dev-shahed added 🎉 QA Approved This PR is approved by the QA team and removed Needs: Testing This requires further testing QA In Progress labels Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Dev Review Done 🎉 QA Approved This PR is approved by the QA team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants