Summary
The Ordering API exposes GET, cancel, and ship endpoints that accept an integer order ID but perform no ownership check against the authenticated user's identity. Any logged-in user can read another customer's full order (including shipping address and order contents), cancel it, or mark it as shipped by enumerating sequential integer IDs.
Details
In src/Ordering.API/Apis/OrdersApi.cs, three of the four order-mutation handlers do not verify that the order belongs to the caller.
GetOrderAsync (line 80-89) accepts orderId:int from the URL path and calls services.Queries.GetOrderAsync(orderId). The underlying query in src/Ordering.API/Application/Queries/OrderQueries.cs is:
var order = await context.Orders
.Include(o => o.OrderItems)
.FirstOrDefaultAsync(o => o.Id == id);
No Buyer.IdentityGuid == userId predicate is applied, unlike the sibling GetOrdersFromUserAsync method which correctly filters by the caller's subject claim (line 95-98 of OrdersApi.cs).
CancelOrderAsync (line 22-47) and ShipOrderAsync (line 51-78) accept an OrderNumber field in the request body and dispatch it directly to CancelOrderCommandHandler / ShipOrderCommandHandler. Both handlers retrieve the order by integer ID with no ownership guard:
// CancelOrderCommandHandler.cs
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
orderToUpdate.SetCancelledStatus();
// ShipOrderCommandHandler.cs
var orderToUpdate = await _orderRepository.GetAsync(command.OrderNumber);
orderToUpdate.SetShippedStatus();
The entire route group is protected by .RequireAuthorization() (Program.cs line 22), so a valid JWT is required, but no role or ownership check is enforced beyond that. Order IDs are auto-increment integers stored in a PostgreSQL sequence, making them trivially enumerable.
The ShipOrderAsync comment in the source even notes "administrator executes ship order from app," yet no admin role requirement exists on the route.
PoC
(available on request)
Impact
Any authenticated customer can enumerate and read the full shipping address and purchase details of all other customers' orders. They can also cancel any pending order (denial of service for other customers) or mark any order as shipped (order state corruption). The only prerequisite is a valid account on the platform, which is typically open to self-registration.
Summary
The Ordering API exposes GET, cancel, and ship endpoints that accept an integer order ID but perform no ownership check against the authenticated user's identity. Any logged-in user can read another customer's full order (including shipping address and order contents), cancel it, or mark it as shipped by enumerating sequential integer IDs.
Details
In
src/Ordering.API/Apis/OrdersApi.cs, three of the four order-mutation handlers do not verify that the order belongs to the caller.GetOrderAsync(line 80-89) acceptsorderId:intfrom the URL path and callsservices.Queries.GetOrderAsync(orderId). The underlying query insrc/Ordering.API/Application/Queries/OrderQueries.csis:No
Buyer.IdentityGuid == userIdpredicate is applied, unlike the siblingGetOrdersFromUserAsyncmethod which correctly filters by the caller's subject claim (line 95-98 ofOrdersApi.cs).CancelOrderAsync(line 22-47) andShipOrderAsync(line 51-78) accept anOrderNumberfield in the request body and dispatch it directly toCancelOrderCommandHandler/ShipOrderCommandHandler. Both handlers retrieve the order by integer ID with no ownership guard:The entire route group is protected by
.RequireAuthorization()(Program.csline 22), so a valid JWT is required, but no role or ownership check is enforced beyond that. Order IDs are auto-increment integers stored in a PostgreSQL sequence, making them trivially enumerable.The
ShipOrderAsynccomment in the source even notes "administrator executes ship order from app," yet no admin role requirement exists on the route.PoC
(available on request)
Impact
Any authenticated customer can enumerate and read the full shipping address and purchase details of all other customers' orders. They can also cancel any pending order (denial of service for other customers) or mark any order as shipped (order state corruption). The only prerequisite is a valid account on the platform, which is typically open to self-registration.