fix: close TOCTOU in CREATE path; add anti-forgery, loose-track, and case-sensitivity tests

FindOrCreateRelease now returns (ReleaseDto, bool WasCreated); the CREATE path in UploadAsync
rejects WasCreated=false as a duplicate rather than silently attaching on a lost race.
This commit is contained in:
daniel-c-harvey
2026-06-19 15:55:08 -04:00
parent bd85507308
commit 558ff4b4c6
5 changed files with 90 additions and 27 deletions
+17 -5
View File
@@ -170,11 +170,13 @@ public class UnifiedTrackService
CreatedByUserId = createdByUserId,
};
// FindOrCreateRelease's find branch still backstops a concurrent insert of the same
// (title, artist) between the duplicate peek and this call — it returns the winning row
// rather than throwing. Medium and every other field apply only on the create it performs.
// FindOrCreateRelease either creates a fresh release (WasCreated = true) or returns the
// row the concurrent winner just inserted (WasCreated = false). In the CREATE path the
// duplicate peek above already verified no pre-existing row exists — so WasCreated = false
// means we lost a concurrent-insert race. Treat that as the duplicate condition: reject
// rather than silently attaching, keeping the DB unique index as the final safety net.
var releaseResult = await _sqlTrackService.FindOrCreateRelease(album, artist, releaseData, ct);
if (!releaseResult.Success || releaseResult.Value is null)
if (!releaseResult.Success)
{
var error = releaseResult.Messages.FirstOrDefault()?.Message ?? "Unknown error";
_logger.LogError(
@@ -183,7 +185,17 @@ public class UnifiedTrackService
return ResultContainer<TrackDto>.CreateFailResult($"Track was uploaded but could not be saved: {error}");
}
resolvedReleaseId = releaseResult.Value.Id;
var (resolvedRelease, wasCreated) = releaseResult.Value;
if (!wasCreated)
{
// The winning concurrent upload created this release between our peek and our insert.
// Reject with the same marker the pre-flight peek uses so the controller maps it to 409.
return ResultContainer<TrackDto>.CreateFailResult(
$"{DuplicateReleaseMarker}A release titled '{resolvedRelease.Title}' by {resolvedRelease.Artist} already " +
"exists. The upload form creates new releases only — use the edit tools to change an existing one.");
}
resolvedReleaseId = resolvedRelease.Id;
}
var trackDto = TrackConverter.Convert(unpersisted);