initial: bootstrap from BukidBountyApp base
This commit is contained in:
116
ai-docs/audit-2026-05-14-190110.md
Normal file
116
ai-docs/audit-2026-05-14-190110.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# 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 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)
|
||||
Reference in New Issue
Block a user