Files
jobtrackingapp/docs/_archive/security-assessments/M015-authorization-replay-report.md
T
cesnimda b176a44627 docs: reorganize tree, restore architecture + research from archive, add Phase 0 reports
Active docs/ was stub scaffolding while the real docs sat in docs/_archive/.
Restore and correct them, and record the Phase 0 work.

- docs/architecture/current.md: verified system map (from archived SYSTEM_OVERVIEW,
  9 corrections against code).
- docs/research/competitors.md: sourced competitor analysis (from archived
  PRODUCT_RESEARCH, feature matrix corrected).
- docs/decisions/ADR-002-job-application-model.md: the Job/JobApplication split.
- docs/application-discovery-report.md, docs/implementation-roadmap.md,
  docs/phase-0-foundation-report.md, docs/career-workspace-branch-assessment.md.
- Remove 10 zero-byte placeholder files that advertised content that never existed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 17:04:32 +02:00

131 lines
4.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 Alices fixture ids with a real authenticated session.
### Defended in this pass
The following probes failed closed with `404` when Bob targeted Alices 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 Alices job history by raw job id and receive Alices `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 Alices job
#### Example exploit input
```http
GET /api/jobapplications/1/history
Cookie: jobtracker_auth=<bob session cookie>
```
#### 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 users 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 users 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.