4.1 KiB
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.mddocs/security-assessments/M014-security-remediation-verification.mddocs/security-assessments/M015-hostile-fixture-setup.mddocs/security-assessments/M015-hostile-fixture-setup.jsondocs/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.combob.m015@example.com
Alice-owned fixture resources created through the real API:
company_id = 1job_id = 1correspondence_id = 1attachment_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/1GET /api/attachments/download/1PATCH /api/attachments/1DELETE /api/attachments/1GET /api/correspondence/1DELETE /api/correspondence/1GET /api/jobapplications/1PUT /api/jobapplications/1PATCH /api/jobapplications/1/followupGET /api/jobapplications/1/timelineGET /api/jobapplications/1/tailored-cv-draftGET /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/historyas Bob200 OK- payload included Alice-owned event data, including the
Createdevent for Alice’s job
Example exploit input
GET /api/jobapplications/1/history
Cookie: jobtracker_auth=<bob session cookie>
Root cause
Two issues combined:
GetHistory(...)queriedJobEventsdirectly byJobApplicationIdwithout verifying that the parent job belonged to the current user.JobEventhad no owner-scoped query filter inData/JobTrackerContext.cs.
Fix
GetHistory(...)now checks whether the requested job exists in the current user’s scopedJobApplicationsquery and returns404if it does not.JobEventnow has an owner-scoped query filter tied toJobApplication.OwnerUserId.- Added focused regression test:
JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs
Replay after fix
Observed post-fix response:
GET /api/jobapplications/1/historyas Bob404 Not Found
Verdict
Fixed.
Automated Evidence
Focused regression test
dotnet test JobTrackerApi.Tests/JobTrackerApi.Tests.csproj --filter JobApplicationsAuthorizationTests
Observed:
- passed
- verifies
GetHistoryreturnsNotFoundfor 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.