initial: bootstrap from BukidBountyApp base

This commit is contained in:
Jonathan Sykes
2026-06-06 18:43:00 +08:00
commit eb4a5731fb
5674 changed files with 160857 additions and 0 deletions

View 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 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)