5.8 KiB
5.8 KiB
Store Owner Audit — 2026-05-14 19:01:10 UTC
Running notes from the Store Owner accessible-pages audit. Bugs we have already fixed are listed first for traceability; the open items below are not yet patched and need a follow-up decision before merge.
Fixed (commit b4defbe + this batch)
Erratic table names (Hypervel surfaces these as empty 400s)
app/Http/Controllers/Market/ProductController.php:766pluck('stores.hashkey')→pluck('str.hashkey'). Drove the Manage Listings modal inManageProductsAdmin.vueto fail on every open.app/Http/Controllers/Market/BatchController.php:91where('products.id', …)→where('prd_items.id', …). Broke the "already attached to this store" check in batch-import (existing mode).
Validation/permission walls
ProductController::editProductAdminrequiredModifyAllProducts(Big-3 only) even whendata.store_hashwas supplied. STORE_OWNER edits viaUpdateProductModal.vuereturned 401. Now per-store edits only needModifyOwnProduct/AddProducttoOwnStoreplus the existing ownership branch.ProductController::toggleProductStatus_Admin— same gate, same relaxation.ProductController::deleteProduct_AdminrequiredDeleteAllProducts(Big-3 only). Now acceptsDeleteOwnProductand the ancestor-of-creator path so owners can delete products they created.PosController::voidSessionhad no authorization check at all. Any session_hash could void any session. Now gated byUserPermissions::isUserAllowedAccessToStore.PosAccessKeyController::destroyandtoggleStatusonly checked the action permission. Now also verify the key belongs to a store the caller owns/manages.StoreController::autoCreatehad no RBAC. Now requires Big-3 or STORE_OWNER, and dedupes generated names against the globally uniquestr.nameindex by appending a short suffix.HomeStoreOwner.vuereferenced an undefinedcreatingStoreref (the actual ref iscreatingQuickStore) — the "Creating your store..." spinner never rendered.
BatchAddProducts opened to STORE_OWNER with guardrails
BatchController::batchCreateProductsnow allows STORE_OWNER, but only whentarget_store_hashis provided AND the target store is owned (or managed) by the caller. Without it the call is rejected up-front instead of silently 401-ing partway through the loop.- New-mode rows are rejected when a global product with the same name (case-insensitive, trimmed) already exists. Owners are expected to pick the existing one via the fuzzy-search modal.
BatchAddProducts.vuenow redirects STORE_OWNER to CreateStore via a yes/no modal when they have zero selectable stores, and refuses submit if no target store is picked.
Open — not yet patched, decide before merging
editStoreDetails data leak (medium)
- File:
app/Http/Controllers/Market/StoreController.php:455 - Route:
POST /Edit/Store/Details/data(routes/web.php:468-474, middlewareauth + module:stores). - Behavior: looks up a
strrow byhashkey, and returnsname,category,subcategory,description,address,remarks,status,is_active,photourl,owner.hashkey,manager.hashkey, allstore_managersuser hashkeys, all linked cooperative hashkeys, the parent-user selector list, and the dropzone payload — without any authorization check against the caller. Any authenticated user can issue this POST with any store's hashkey and get back the owner/manager/coop graph plus internal remarks. There is no modification path here, so this is information disclosure rather than escalation, butremarksis explicitly marked internal in the CreateStore flow and the manager/cooperative hashkeys leak the org graph. - Suggested fix: require Big-3 OR
(
store->owner_id/manager_idmatches caller) OR caller appears instore_managerspivot OR caller is ancestor of any of the above (the same predicate used bycanUserAccessPos/isUserAllowedAccessToStore). Stripremarksfrom the response for non-Big-3. - Status: NOT FIXED IN THIS COMMIT. Awaiting decision on
whether to (a) tighten the endpoint and accept the chance of
breaking any external/legacy caller, or (b) keep the
read-anybody behavior and only strip
remarks.
Other observations (not bugs strictly)
editProductAdminByStore(ProductController.php:494) has aTODO Check first if store_id is owned by current usercomment and modifies the global product directly. Currently nothing routes to it, but leaving it in the codebase is a footgun if someone wires it up later. Recommend deleting.listProducts_Adminscopes non-Big-3 bycreated_by, so a store owner who attached a Big-3-created global product into their store cannot see/manage that product from "My Products". Per dictionary L265 this is intentional, but it does mean store-listings management for owners is split between "My Products" (creator-scoped) and the per-store flows (AddProductsToStore,ManageStoresAdmin).- The Store Owner home's "Import Products" tile (
pagename: BatchAddProducts) now works for STORE_OWNER thanks to the BatchController relaxation above. Pre-fix this would have loaded the page and then 401-ed on submit.
Stores accessed during this audit
StoreController(full file)ProductController(full file)BatchController(lines 33–172)PosController(sample around startSession/voidSession/getTodayStats)PosAccessKeyController(full file)- Pages:
HomeStoreOwner.vue,CreateProductStoreOwner.vue,CreateStore.vue,AddProductsToStore.vue,BatchAddProducts.vue,ManageProductAdmin.vue,ManageProductsAdmin.vue,ManageStoresAdmin.vue,PosAccessKeys.vue,UpdateProductModal.vue - Routes:
routes/web.php(home-data + Store/Product/POS blocks)