From bd51c245d3dbeae4baa4c18a6a64d4be4e8838f7 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Fri, 3 Jul 2026 03:55:12 +0200 Subject: [PATCH] test(security): lock tenant isolation on match-score and status-suggestion Cross-user access to the new endpoints returns NotFound (carried by the JobTrackerContext global query filters). Regression guard for the class of tenant-leak bugs found in the M013-M015 assessments. Co-Authored-By: Claude Fable 5 --- .../JobApplicationsAuthorizationTests.cs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs b/JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs index fb29a95..61ef49d 100644 --- a/JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs +++ b/JobTrackerApi.Tests/JobApplicationsAuthorizationTests.cs @@ -38,6 +38,42 @@ public sealed class JobApplicationsAuthorizationTests Assert.IsType(result.Result); } + [Fact] + public async Task GetMatchScore_returns_not_found_for_other_users_job() + { + var dbName = Guid.NewGuid().ToString(); + await using var ownerDb = CreateDb(dbName, "owner-1"); + var company = new Company { Name = "Acme", OwnerUserId = "owner-1" }; + ownerDb.Companies.Add(company); + await ownerDb.SaveChangesAsync(); + ownerDb.JobApplications.Add(new JobApplication { JobTitle = "Secret", CompanyId = company.Id, OwnerUserId = "owner-1", Description = "C# .NET" }); + await ownerDb.SaveChangesAsync(); + var jobId = await ownerDb.JobApplications.Select(j => j.Id).FirstAsync(); + + await using var attackerDb = CreateDb(dbName, "other-user"); + var result = await CreateController(attackerDb).GetMatchScore(jobId, CancellationToken.None); + + Assert.IsType(result.Result); + } + + [Fact] + public async Task GetStatusSuggestion_returns_not_found_for_other_users_job() + { + var dbName = Guid.NewGuid().ToString(); + await using var ownerDb = CreateDb(dbName, "owner-1"); + var company = new Company { Name = "Acme", OwnerUserId = "owner-1" }; + ownerDb.Companies.Add(company); + await ownerDb.SaveChangesAsync(); + ownerDb.JobApplications.Add(new JobApplication { JobTitle = "Secret", CompanyId = company.Id, OwnerUserId = "owner-1" }); + await ownerDb.SaveChangesAsync(); + var jobId = await ownerDb.JobApplications.Select(j => j.Id).FirstAsync(); + + await using var attackerDb = CreateDb(dbName, "other-user"); + var result = await CreateController(attackerDb).GetStatusSuggestion(jobId, CancellationToken.None); + + Assert.IsType(result.Result); + } + private static JobTrackerContext CreateDb(string dbName, string? userId) { var options = new DbContextOptionsBuilder()