The productSupplier controller's edit action lacked any server-side authorization check. While the create and delete actions were protected by a runtime.groovy RBAC rule requiring ROLE_ADMIN with supplemental ROLE_PRODUCT_MANAGER, the edit action was completely omitted from both the runtime.groovy rules and the RoleInterceptor hardcoded action maps.
This allowed any authenticated user with the lowest privilege level (ROLE_BROWSER) to modify existing product supplier records, including changing supplier names, codes, and manufacturer associations.
| Title | CWE | Affected Versions | Fixed In | Patch Date |
|---|---|---|---|---|
Missing Authorization on productSupplier/edit Controller Action |
CWE-862: Missing Authorization | < v0.9.3 | v0.9.3 (PR #4905, commit f767ac1a5) |
2024-10-22 |
Vector String: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L
| Metric | Value | Justification |
|---|---|---|
| Attack Vector (AV) | Network | Exploitable over HTTP |
| Attack Complexity (AC) | Low | No special conditions required |
| Privileges Required (PR) | Low | Any authenticated user (ROLE_BROWSER) |
| User Interaction (UI) | None | No user interaction needed |
| Scope (S) | Unchanged | Impact limited to the application |
| Confidentiality (C) | Low | Can read supplier data via edit page |
| Integrity (I) | High | Can modify any product supplier record |
| Availability (A) | Low | Corrupted supplier data could disrupt procurement |
- Files:
grails-app/controllers/org/pih/warehouse/RoleInterceptor.groovy,grails-app/conf/runtime.groovy - Endpoints:
POST /openboxes/productSupplier/edit/{id}
In runtime.groovy, the RBAC rule only covered create and delete:
// runtime.groovy line 109 (pre-patch)
[controller: 'productSupplier', actions: ['create', 'delete'],
accessRules: [minimumRequiredRole: RoleType.ROLE_ADMIN],
supplementalRoles: [RoleType.ROLE_PRODUCT_MANAGER]],In RoleInterceptor.groovy, there was no entry for productSupplier in adminActions, managerActions, or any other action map.
The edit action was reachable by any authenticated user.
The fix moved all authorization into RoleInterceptor.groovy:
// RoleInterceptor.groovy (post-patch)
def static adminActions = [
// ...
'productSupplier': ['create', 'delete', 'edit'] // edit added
]
def static productManagerActions = [
'productSupplier': ['create', 'delete', 'edit'] // new map
]A new needProductManager() method and hasRoleProductManager check were added to the interceptor's before() method. The runtime.groovy rule was removed entirely, consolidating all checks.
- OpenBoxes instance running a version before commit
f767ac1a5(pre-October 2024). - Two user accounts:
- Attacker: A low-privilege user with only
ROLE_BROWSER(the minimum authenticated role). - Admin: An admin account to verify the original supplier data before and after.
- Attacker: A low-privilege user with only
- At least one existing Product Supplier record in the system (note its ID).
Automated Testing: Run poc.py <url> <browser_user> <password> to automatically:
- Test if the Browser user can access the
/productSupplier/editendpoint - Verify authorization bypass
Manual Steps:
-
Log in as the Browser-level user. Navigate to the OpenBoxes login page and authenticate with the low-privilege account.
-
Identify a target Product Supplier ID. Product Supplier IDs are visible in the URL when an admin views a product's supplier tab, e.g.,
/openboxes/productSupplier/edit/ff80818190c3c1e10190c3c2a0e70001. If direct enumeration is not possible, IDs may also appear in the product detail page source or API responses. -
Navigate directly to the edit endpoint. In the browser address bar, enter:
https://<host>/openboxes/productSupplier/edit/<SUPPLIER_ID>Expected (pre-patch): The edit form loads successfully despite the user only having
ROLE_BROWSER. Expected (post-patch): The server returns a 403 Forbidden or redirects to an error page. -
Modify a supplier field and submit. On the edit form, change the supplier name or supplier code to a distinctive value (e.g.,
MODIFIED-BY-BROWSER). Click Save. -
Verify the change persisted. Log in as the Admin account. Navigate to the same Product Supplier record. Confirm the field was changed by the browser-level user.
Repeat step 3 on a version at or after commit f767ac1a5. The interceptor should block access and redirect to the forbidden error page. The edit form should never render for a user without ROLE_ADMIN + ROLE_PRODUCT_MANAGER.
Upgrade to version >= v0.9.3
| Date | Event |
|---|---|
| 2024-10-22 | Patch merged as PR #4905 (no security advisory) |
- CWE: CWE-862: Missing Authorization
- PR: openboxes/openboxes#4905
- Commit:
f767ac1a5987d4865d9f158c6a967680f8e45468 - Release: v0.9.3
- Files Changed:
RoleInterceptor.groovy,runtime.groovy,UserService.groovy,AuthTagLib.groovy,_productSuppliers.gsp