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

4.1 KiB
Raw Blame History

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

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

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.