[Security] Broken access control: Mark Wilson customer-PII CRM is authenticated-only (no staff/admin role gate) #688

Closed
opened 2026-07-17 22:36:15 +00:00 by spikerj · 1 comment
Owner

Vulnerability (broken access control / OWASP A01)

MarkWilsonsCustomerController (Domain/Customers) lists/creates/reads/updates customer PII — names, addresses, phone numbers, service history — and is gated only by a bare [Authorize]. The API's DefaultPolicy is RequireAuthenticatedUser() (Infrastructure/Authentication.cs:33), so bare [Authorize] = any authenticated user, and the handlers filter by _id only (no owner/tenant/role check).

This controller lives in the shared SpikerSoft.Api — the same instance and Keycloak realm that authenticates students, game players, art-studio and code-lesson users. So any platform account (e.g. a student) can call GET /…/customers, GET /…/customers/{id}, or PUT /…/customers/{id} and read or modify Mark Wilson's customer PII. spikersoft-issues#415 fixed the earlier anonymous-by-omission gap but only required authentication, not the staff/admin role this data warrants.

Fix (this PR)

Gate the controller with the codebase's established staff/admin pattern — [Authorize(Roles = "Admin,admin,Staff,staff")] — already used by 27 peer controllers (e.g. CacheController) and equivalent to the AdminOrStaff policy (SpikerSoftRoles.IsAdminOrStaff). Regular user-facing controllers keep bare [Authorize] (correct for them); only the staff CRM is tightened. Added MarkWilsonCustomerControllerAuthorizationTests cases asserting the role gate.

Related (same gap, NOT in this PR — needs per-controller confirmation)

The sibling generator-service CRM controllers share the identical bare-[Authorize] gap and should get the same gate after verifying none has a legitimate non-staff endpoint: ServiceCallsController, LaborsController, PartsController, MilagesController, GeneratorBrandsController, ServiceCallReasonsController.

## Vulnerability (broken access control / OWASP A01) `MarkWilsonsCustomerController` (Domain/Customers) lists/creates/reads/updates customer PII — names, addresses, phone numbers, service history — and is gated only by a bare `[Authorize]`. The API's `DefaultPolicy` is `RequireAuthenticatedUser()` (Infrastructure/Authentication.cs:33), so bare `[Authorize]` = **any authenticated user**, and the handlers filter by `_id` only (no owner/tenant/role check). This controller lives in the shared `SpikerSoft.Api` — the same instance and Keycloak realm that authenticates students, game players, art-studio and code-lesson users. So **any** platform account (e.g. a student) can call `GET /…/customers`, `GET /…/customers/{id}`, or `PUT /…/customers/{id}` and read or modify Mark Wilson's customer PII. spikersoft-issues#415 fixed the earlier anonymous-by-omission gap but only required *authentication*, not the staff/admin role this data warrants. ## Fix (this PR) Gate the controller with the codebase's established staff/admin pattern — `[Authorize(Roles = "Admin,admin,Staff,staff")]` — already used by 27 peer controllers (e.g. CacheController) and equivalent to the `AdminOrStaff` policy (`SpikerSoftRoles.IsAdminOrStaff`). Regular user-facing controllers keep bare `[Authorize]` (correct for them); only the staff CRM is tightened. Added `MarkWilsonCustomerControllerAuthorizationTests` cases asserting the role gate. ## Related (same gap, NOT in this PR — needs per-controller confirmation) The sibling generator-service CRM controllers share the identical bare-`[Authorize]` gap and should get the same gate after verifying none has a legitimate non-staff endpoint: `ServiceCallsController`, `LaborsController`, `PartsController`, `MilagesController`, `GeneratorBrandsController`, `ServiceCallReasonsController`.
Author
Owner

Resolved in spikersoft-backend PR #385 (merged to master). Gated MarkWilsonsCustomerController to [Authorize(Roles = "Admin,admin,Staff,staff")] (the pattern 27 peer controllers use), so customer PII is no longer reachable by any authenticated non-staff account. Added a role-gate assertion test (7 pass). Sibling CRM controllers noted here for a verified follow-up. Closing.

Resolved in spikersoft-backend PR #385 (merged to `master`). Gated `MarkWilsonsCustomerController` to `[Authorize(Roles = "Admin,admin,Staff,staff")]` (the pattern 27 peer controllers use), so customer PII is no longer reachable by any authenticated non-staff account. Added a role-gate assertion test (7 pass). Sibling CRM controllers noted here for a verified follow-up. Closing.
Sign in to join this conversation.