diff --git a/docs/security-assessments/M015-authorization-replay-report.md b/docs/security-assessments/M015-authorization-replay-report.md new file mode 100644 index 0000000..f9f9bd3 --- /dev/null +++ b/docs/security-assessments/M015-authorization-replay-report.md @@ -0,0 +1,130 @@ +# M015 Cross-User Authorization Replay Report + +This report covers the follow-up tenant-boundary work after `M013` and `M014`. + +Related artifacts: + +- `docs/security-assessments/M013-adversarial-security-assessment.md` +- `docs/security-assessments/M014-security-remediation-verification.md` +- `docs/security-assessments/M015-hostile-fixture-setup.md` +- `docs/security-assessments/M015-hostile-fixture-setup.json` +- `docs/security-assessments/M015-s02-probe-results.json` + +## Test Setup + +A dedicated hostile-test SQLite database was created from the current EF model because the default development DB was missing core domain tables needed for real authorization probes. + +Fixture runtime: + +- clean SQLite DB under `.tmp/m015-fixture` +- API started with `Data__Root=/home/pi/development/JobTracker/.tmp/m015-fixture` +- registration temporarily enabled for the fixture runtime +- two real local users created through the API: + - `alice.m015@example.com` + - `bob.m015@example.com` + +Alice-owned fixture resources created through the real API: + +- `company_id = 1` +- `job_id = 1` +- `correspondence_id = 1` +- `attachment_id = 1` + +All mutating requests used the real cookie + CSRF contract. + +## Cross-User Probe Summary + +Bob targeted Alice’s fixture ids with a real authenticated session. + +### Defended in this pass + +The following probes failed closed with `404` when Bob targeted Alice’s resources: + +- `GET /api/attachments/1` +- `GET /api/attachments/download/1` +- `PATCH /api/attachments/1` +- `DELETE /api/attachments/1` +- `GET /api/correspondence/1` +- `DELETE /api/correspondence/1` +- `GET /api/jobapplications/1` +- `PUT /api/jobapplications/1` +- `PATCH /api/jobapplications/1/followup` +- `GET /api/jobapplications/1/timeline` +- `GET /api/jobapplications/1/tailored-cv-draft` +- `GET /api/jobapplications/1/followup-draft` + +These routes did not expose or mutate Alice-owned data in this hostile fixture pass. + +## Confirmed Finding + +### Cross-user read leak on job history + +- **Category:** Authorization / data exposure +- **Endpoint:** `GET /api/jobapplications/{id}/history` +- **Risk:** **Medium** + +#### Vulnerability + +Before the fix, Bob could request Alice’s job history by raw job id and receive Alice’s `JobEvent` rows. + +Observed pre-fix response: + +- `GET /api/jobapplications/1/history` as Bob +- `200 OK` +- payload included Alice-owned event data, including the `Created` event for Alice’s job + +#### Example exploit input + +```http +GET /api/jobapplications/1/history +Cookie: jobtracker_auth= +``` + +#### Root cause + +Two issues combined: + +1. `GetHistory(...)` queried `JobEvents` directly by `JobApplicationId` without verifying that the parent job belonged to the current user. +2. `JobEvent` had no owner-scoped query filter in `Data/JobTrackerContext.cs`. + +#### Fix + +- `GetHistory(...)` now checks whether the requested job exists in the current user’s scoped `JobApplications` query and returns `404` if it does not. +- `JobEvent` now has an owner-scoped query filter tied to `JobApplication.OwnerUserId`. +- Added focused regression test: + - `JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs` + +#### Replay after fix + +Observed post-fix response: + +- `GET /api/jobapplications/1/history` as Bob +- `404 Not Found` + +#### Verdict + +**Fixed.** + +## Automated Evidence + +### Focused regression test + +```bash +dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsAuthorizationTests +``` + +Observed: + +- passed +- verifies `GetHistory` returns `NotFound` for another user’s job + +## Final Assessment + +For the prioritized raw-id authorization seams exercised in this milestone: + +- **confirmed and fixed:** `GET /api/jobapplications/{id}/history` +- **no finding in this fixture pass:** attachments, correspondence, primary job read/update, follow-up patch, timeline, tailored draft, follow-up draft + +## Remaining Boundary + +This report covers the endpoints actually exercised in the hostile fixture pass. It does **not** claim that every authorization-sensitive route in the application has been exhaustively proven safe; it closes the high-risk raw-id seams prioritized from the earlier assessment with a real two-user runtime and replay evidence.