Files
BarangaySystem/ai-docs/audit-2026-05-14-190110.md
2026-06-06 18:43:00 +08:00

5.8 KiB
Raw Blame History

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:766 pluck('stores.hashkey')pluck('str.hashkey'). Drove the Manage Listings modal in ManageProductsAdmin.vue to fail on every open.
  • app/Http/Controllers/Market/BatchController.php:91 where('products.id', …)where('prd_items.id', …). Broke the "already attached to this store" check in batch-import (existing mode).

Validation/permission walls

  • ProductController::editProductAdmin required ModifyAllProducts (Big-3 only) even when data.store_hash was supplied. STORE_OWNER edits via UpdateProductModal.vue returned 401. Now per-store edits only need ModifyOwnProduct / AddProducttoOwnStore plus the existing ownership branch.
  • ProductController::toggleProductStatus_Admin — same gate, same relaxation.
  • ProductController::deleteProduct_Admin required DeleteAllProducts (Big-3 only). Now accepts DeleteOwnProduct and the ancestor-of-creator path so owners can delete products they created.
  • PosController::voidSession had no authorization check at all. Any session_hash could void any session. Now gated by UserPermissions::isUserAllowedAccessToStore.
  • PosAccessKeyController::destroy and toggleStatus only checked the action permission. Now also verify the key belongs to a store the caller owns/manages.
  • StoreController::autoCreate had no RBAC. Now requires Big-3 or STORE_OWNER, and dedupes generated names against the globally unique str.name index by appending a short suffix.
  • HomeStoreOwner.vue referenced an undefined creatingStore ref (the actual ref is creatingQuickStore) — the "Creating your store..." spinner never rendered.

BatchAddProducts opened to STORE_OWNER with guardrails

  • BatchController::batchCreateProducts now allows STORE_OWNER, but only when target_store_hash is 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.vue now 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, middleware auth + module:stores).
  • Behavior: looks up a str row by hashkey, and returns name, category, subcategory, description, address, remarks, status, is_active, photourl, owner.hashkey, manager.hashkey, all store_managers user 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, but remarks is 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_id matches caller) OR caller appears in store_managers pivot OR caller is ancestor of any of the above (the same predicate used by canUserAccessPos / isUserAllowedAccessToStore). Strip remarks from 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 a TODO Check first if store_id is owned by current user comment 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_Admin scopes non-Big-3 by created_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 33172)
  • 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)