Post-shipment cancellation requires special handling or admin approval
Why it matters
An order that was shipped yesterday cannot be cancelled the same way as an order placed five minutes ago. If the same code path applies to both, a customer can trigger a 'cancelled' transition on an already-shipped order — potentially generating an automated refund while the package is still in transit and arrives at the customer's door. This is a CWE-285 failure: the system grants a cancellation action to a user for an order state where that action should be restricted to admin review. Post-shipment cases require a separate branch: queue the request, notify the admin, and communicate a different resolution timeline to the customer.
Severity rationale
Medium because a single undifferentiated cancellation path enables customers to cancel shipped orders with no admin oversight, creating refund and logistics conflicts.
Remediation
Add a second branch to your cancellation handler at app/api/orders/[id]/cancel/route.ts that routes post-shipment requests to an admin review queue rather than auto-cancelling.
const POST_SHIPMENT = ['shipped', 'delivered']
if (POST_SHIPMENT.includes(order.status)) {
await db.cancellationRequests.create({
data: {
orderId: order.id,
userId: session.user.id,
requestedAt: new Date(),
status: 'pending_review',
reason: body.reason,
},
})
await sendAdminNotification({ type: 'post_shipment_cancellation', orderId: order.id })
return Response.json({
message: 'Request received. Our team will contact you within 1–2 business days.',
})
}
Detection
-
ID:
postshipment-cancellation -
Severity:
medium -
What to look for: Check whether the cancellation logic explicitly distinguishes between pre-shipment and post-shipment orders. Count the number of distinct code branches handling cancellation for different order statuses. For orders that are already
shippedordelivered, look for at least 1 of these special handling patterns: a separate endpoint or flow for post-shipment cancellation requests, a flag or queue entry that routes the request to an admin for approval, or a clear user-facing error message (quote the exact message if found) explaining that standard cancellation is not available. The key question is: does the system have at least 2 distinct branches — one for pre-shipment and one for post-shipment? -
Pass criteria: The cancellation flow explicitly handles the post-shipment case differently from pre-shipment, with at least 2 distinct code branches. Shipped or delivered orders either cannot be cancelled through the standard customer path (with a helpful error message directing the customer to contact support or initiate a return) or enter a separate admin-approval queue. The system does not silently apply a standard cancellation to an already-shipped order. A single code path that treats all statuses identically does not count as pass.
-
Fail criteria: The cancellation handler treats all order statuses the same (only 1 code branch) — a shipped order can be cancelled through the exact same path as a pending order with no differentiation, no admin notification, and no flagging for special handling.
-
Skip (N/A) when: The project does not support post-shipment cancellations at all and clearly communicates this to customers at the time of purchase (e.g., "All sales are final once shipped"). Or the project delegates all post-shipment handling to an external returns management platform. The cancellation handler explicitly rejects all non-pre-shipment statuses.
-
Detail on fail:
"The cancellation endpoint at POST /api/orders/[id]/cancel applies the same logic regardless of order status (1 code branch, 0 post-shipment guards). A shipped order can be cancelled by the customer with no admin notification or special handling." -
Remediation: Add explicit post-shipment routing in your cancellation handler at
app/api/orders/[id]/cancel/route.ts:const POST_SHIPMENT_STATUSES = ['shipped', 'delivered'] if (POST_SHIPMENT_STATUSES.includes(order.status)) { // Route to admin queue instead of auto-cancelling await db.cancellationRequests.create({ data: { orderId: order.id, userId: session.user.id, requestedAt: new Date(), status: 'pending_review', reason: body.reason, }, }) // Notify admin await sendAdminNotification({ type: 'post_shipment_cancellation_request', orderId: order.id }) return Response.json({ success: true, message: 'Your cancellation request has been received and is under review. ' + 'Our team will contact you within 1-2 business days.', }) }
External references
- cwe · CWE-285 — Improper Authorization
- cwe · CWE-841 — Improper Enforcement of Behavioral Workflow
- owasp:2021 · A01 — Broken Access Control
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ecommerce-order-management·automated