Skip to content

Commit 9cd25d9

Browse files
committed
Block disallowed asset uploads
1 parent b6b437f commit 9cd25d9

4 files changed

Lines changed: 63 additions & 6 deletions

File tree

Refresh.Interfaces.APIv3/Endpoints/ApiTypes/Errors/ApiModerationError.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ namespace Refresh.Interfaces.APIv3.Endpoints.ApiTypes.Errors;
22

33
public class ApiModerationError : ApiError
44
{
5-
public static readonly ApiModerationError Instance = new();
6-
7-
public ApiModerationError() : base("This content was flagged as potentially unsafe, and administrators have been alerted. If you believe this is an error, please contact an administrator.", UnprocessableContent)
8-
{
9-
}
5+
public ApiModerationError(string message) : base(message, UnprocessableContent) {}
6+
7+
public const string AssetAutoFlaggedErrorWhen = "This content was flagged as potentially unsafe, and administrators have been alerted. If you believe this is an error, please contact an administrator.";
8+
public static readonly ApiModerationError AssetAutoFlaggedError = new(AssetAutoFlaggedErrorWhen);
9+
10+
public const string AssetDisallowedErrorWhen = "The asset you tried to upload is disallowed.";
11+
public static readonly ApiModerationError AssetDisallowedError = new(AssetDisallowedErrorWhen);
1012
}

Refresh.Interfaces.APIv3/Endpoints/ResourceApiEndpoints.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ IntegrationConfig integration
200200
return new ApiValidationError($"You have exceeded your filesize quota.");
201201
}
202202

203+
if (database.GetDisallowedAssetInfo(hash) != null)
204+
{
205+
context.Logger.LogWarning(BunkumCategory.UserContent, "User {0} has tried to upload a disallowed asset, rejecting.", user);
206+
return ApiModerationError.AssetDisallowedError;
207+
}
208+
203209
GameAsset? gameAsset = importer.ReadAndVerifyAsset(hash, body, TokenPlatform.Website, database);
204210
if (gameAsset == null)
205211
return ApiValidationError.CannotReadAssetError;
@@ -214,7 +220,7 @@ IntegrationConfig integration
214220

215221
if (aipi != null && aipi.ScanAndHandleAsset(dataContext, gameAsset))
216222
{
217-
return ApiModerationError.Instance;
223+
return ApiModerationError.AssetAutoFlaggedError;
218224
}
219225

220226
database.AddAssetToDatabase(gameAsset);

Refresh.Interfaces.Game/Endpoints/ResourceEndpoints.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ public Response UploadAsset(RequestContext context, string hash, string type, by
6161
context.Logger.LogWarning(BunkumCategory.UserContent, "{0} is above 2MB ({1} bytes), rejecting.", hash, body.Length);
6262
return RequestEntityTooLarge;
6363
}
64+
65+
if (database.GetDisallowedAssetInfo(hash) != null)
66+
{
67+
context.Logger.LogWarning(BunkumCategory.UserContent, "User {0} has tried to upload a disallowed asset, rejecting.", user);
68+
return Unauthorized;
69+
}
6470

6571
GameAsset? gameAsset = importer.ReadAndVerifyAsset(hash, body, token.TokenPlatform, database);
6672
if (gameAsset == null)

RefreshTests.GameServer/Tests/Assets/AssetDisallowanceTests.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1+
using System.Security.Cryptography;
12
using Refresh.Database.Models.Assets;
23
using Refresh.Database.Models.Authentication;
34
using Refresh.Database.Models.Users;
5+
using Refresh.Interfaces.APIv3.Endpoints.ApiTypes;
6+
using Refresh.Interfaces.APIv3.Endpoints.ApiTypes.Errors;
7+
using Refresh.Interfaces.APIv3.Endpoints.DataTypes.Response.Data;
48
using Refresh.Interfaces.Game.Types.UserData;
59
using RefreshTests.GameServer.Extensions;
610

@@ -115,4 +119,43 @@ public void ShowModeratedReturnsDisallowedAssetHashes()
115119
Assert.That(response.Resources.Contains("3"), Is.True);
116120
Assert.That(response.Resources.Contains("7"), Is.True);
117121
}
122+
123+
[Test]
124+
public void CannotUploadDisallowedAssetFromGame()
125+
{
126+
using TestContext context = this.GetServer();
127+
GameUser user = context.CreateUser();
128+
HttpClient client = context.GetAuthenticatedClient(TokenType.Game, user);
129+
130+
ReadOnlySpan<byte> data = "TEX a"u8;
131+
132+
string hash = BitConverter.ToString(SHA1.HashData(data))
133+
.Replace("-", "")
134+
.ToLower();
135+
136+
context.Database.DisallowAsset(hash, GameAssetType.Texture, "Weegee");
137+
138+
HttpResponseMessage message = client.PostAsync($"/lbp/upload/{hash}", new ByteArrayContent(data.ToArray())).Result;
139+
Assert.That(message.StatusCode, Is.EqualTo(Unauthorized));
140+
}
141+
142+
[Test]
143+
public void CannotUploadDisallowedAssetFromApi()
144+
{
145+
using TestContext context = this.GetServer();
146+
GameUser user = context.CreateUser();
147+
HttpClient client = context.GetAuthenticatedClient(TokenType.Api, user);
148+
149+
ReadOnlySpan<byte> data = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A];
150+
151+
string hash = BitConverter.ToString(SHA1.HashData(data))
152+
.Replace("-", "")
153+
.ToLower();
154+
155+
context.Database.DisallowAsset(hash, GameAssetType.Png, "Weegee");
156+
157+
ApiResponse<ApiGameAssetResponse>? response = client.PostData<ApiGameAssetResponse>($"/api/v3/assets/{hash}", new ByteArrayContent(data.ToArray()), false, true);
158+
Assert.That(response?.Error, Is.Not.Null);
159+
Assert.That(response!.Error!.Name, Is.EqualTo(nameof(ApiModerationError)));
160+
}
118161
}

0 commit comments

Comments
 (0)