A focal point is two finite numbers
float.TryParse takes "NaN" and "Infinity", and Math.Clamp keeps a NaN, so `focus=NaN,NaN` on an upload or a media PUT was stored as given. Once the media was posted, every status and timeline response holding the post, and the Note and its delivery, failed to serialise, for every viewer. FocalPoint.Parse takes finite numbers only (anything else is ignored, as an unreadable focus was); the mapper and the renderer leave out a focus that isn't sound, and migration _016 removes the ones stored before, from the uploads and from the copy each post keeps. What PrivaPub sends is unchanged for any focus that could be sent before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
402f9e0d75
commit
52d201eee9
9 files changed
+100
-17
No files matched your search
@@ -136,6 +136,25 @@ namespace PrivaPub.Tests.Http
|
||||
Assert.Null((await alice.Client.Put($"/api/v1/media/{id}", ("description", ""))).Ok().Body.Text("description"));
|
||||
}
|
||||
|
||||
// a focal point is two finite numbers: NaN or Infinity is ignored, never stored (it broke every timeline holding the post)
|
||||
[Fact]
|
||||
public async Task A_focal_point_that_is_not_a_number_is_ignored()
|
||||
{
|
||||
var alice = await _host.Mastodon("alice");
|
||||
var uploaded = (await Upload(alice, "/api/v2/media", MastodonHelpers.JpegWithMetadata(32, 32), "image/jpeg", "a.jpg", ("focus", "NaN,NaN"))).Ok();
|
||||
Assert.Null(uploaded.Body["meta"]?["focus"]);
|
||||
var id = uploaded.Body.Text("id");
|
||||
|
||||
(await alice.Client.Put($"/api/v1/media/{id}", ("focus", "0.5,0.25"))).Ok();
|
||||
foreach (var unsound in new[] { "NaN,0", "Infinity,1", "0,-Infinity" })
|
||||
{
|
||||
var focus = (await alice.Client.Put($"/api/v1/media/{id}", ("focus", unsound))).Ok().Body["meta"]!["focus"]!;
|
||||
Assert.Equal((0.5, 0.25), (focus["x"]!.GetValue<double>(), focus["y"]!.GetValue<double>()));
|
||||
}
|
||||
var status = (await alice.Client.Post("/api/v1/statuses", ("status", "focused"), ("media_ids[]", id))).Ok();
|
||||
Assert.Equal(0.5, status.Body["media_attachments"]![0]!["meta"]!["focus"]!["x"]!.GetValue<double>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Unsupported_unreadable_and_missing_files_are_refused_with_422()
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using PrivaPub.Models.Media;
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Driver;
|
||||
using MongoDB.Entities;
|
||||
@@ -22,6 +23,31 @@ namespace PrivaPub.Tests.Infrastructure
|
||||
[Trait("Category", "Integration")]
|
||||
public class MigrationTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task A_focal_point_that_is_not_a_number_is_removed()
|
||||
{
|
||||
Assert.SkipUnless(MongoFixture.Enabled, MongoFixture.Skip);
|
||||
var token = TestContext.Current.CancellationToken;
|
||||
var broken = new MediaAttachment { OwnerAvatarId = "a1", Focus = [float.NaN, 0f] };
|
||||
var sound = new MediaAttachment { OwnerAvatarId = "a1", Focus = [0.5f, -0.5f] };
|
||||
var post = new Post
|
||||
{
|
||||
ObjectURI = $"https://privapub.test/{Guid.NewGuid():N}",
|
||||
Media = [new PostMedia { URL = "https://privapub.test/media/files/a.jpg", Focus = [float.PositiveInfinity, 0f] }, new PostMedia { URL = "https://privapub.test/media/files/b.jpg", Focus = [0.1f, 0.2f] }]
|
||||
};
|
||||
await DB.Default.SaveAsync(broken, token);
|
||||
await DB.Default.SaveAsync(sound, token);
|
||||
await DB.Default.SaveAsync(post, token);
|
||||
|
||||
await new _016_focal_points_are_finite().UpgradeAsync();
|
||||
|
||||
Assert.Null((await DB.Default.Find<MediaAttachment>().OneAsync(broken.ID, token)).Focus);
|
||||
Assert.Equal(new[] { 0.5f, -0.5f }, (await DB.Default.Find<MediaAttachment>().OneAsync(sound.ID, token)).Focus);
|
||||
var migrated = await DB.Default.Find<Post>().OneAsync(post.ID, token);
|
||||
Assert.Null(migrated.Media[0].Focus);
|
||||
Assert.Equal(new[] { 0.1f, 0.2f }, migrated.Media[1].Focus);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Stored_remote_content_is_sanitized_and_local_content_rendered()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user