Skip to content

Commit

Permalink
Add size limits for inputs and outputs (#3)
Browse files Browse the repository at this point in the history
* Add size limits for inputs and outputs

* Refactor
  • Loading branch information
KubaZ2 authored Oct 8, 2024
1 parent 1474af3 commit 5867f96
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 11 deletions.
9 changes: 5 additions & 4 deletions Backend/Manager/JailSandboxProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
using System.Diagnostics.CodeAnalysis;
using System.Net.Sockets;

using Microsoft.Extensions.Options;

namespace Sharp.Backend.Manager;

public class JailSandboxProvider : ISandboxProvider
public class JailSandboxProvider(IOptions<Options> options) : ISandboxProvider
{
private const int MaxOutputSize = 1024 * 1024; // 1MiB
private const int CopyBufferSize = 81920;

public async Task ExecuteAsync(ContainerFunction function, Stream assembly, Stream output)
Expand Down Expand Up @@ -53,11 +54,11 @@ public async Task ExecuteAsync(ContainerFunction function, Stream assembly, Stre
static (string, int) ThrowOutOfRange() => throw new ArgumentOutOfRangeException(nameof(function));
}

private static async Task ReadOutputAsync(NetworkStream input, Stream output, byte[] buffer)
private async Task ReadOutputAsync(NetworkStream input, Stream output, byte[] buffer)
{
int bufferLength = buffer.Length;
int read;
int remaining = MaxOutputSize;
int remaining = options.Value.MaxOutputSize;
while (true)
{
read = await input.ReadAsync(buffer.AsMemory(0, Math.Min(bufferLength, remaining)));
Expand Down
2 changes: 2 additions & 0 deletions Backend/Manager/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public class Options

[Required]
public required RateLimitOptions RateLimits { get; set; }

public int MaxOutputSize { get; set; } = 1024 * 1024;
}

public class RateLimitOptions
Expand Down
10 changes: 7 additions & 3 deletions Bot/Sharp/Attachments/AttachmentCodeProvider.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using Microsoft.Extensions.Options;

using NetCord;

namespace Sharp.Attachments;

public class AttachmentCodeProvider(IHttpClientFactory httpClientFactory) : IAttachmentCodeProvider
public class AttachmentCodeProvider(IHttpClientFactory httpClientFactory, IOptions<Options> options) : IAttachmentCodeProvider
{
public async ValueTask<AttachmentCodeResult> GetCodeAsync(IEnumerable<Attachment> attachments)
{
Expand All @@ -11,6 +13,9 @@ public async ValueTask<AttachmentCodeResult> GetCodeAsync(IEnumerable<Attachment
if (attachment is null)
return new AttachmentCodeResult.CodeNotFound();

if (attachment.Size > options.Value.MaxFileSize)
return new AttachmentCodeResult.FileTooLarge();

var extension = Path.GetExtension(attachment.FileName);

int extensionLength = extension.Length;
Expand All @@ -21,8 +26,7 @@ public async ValueTask<AttachmentCodeResult> GetCodeAsync(IEnumerable<Attachment

string code;
using (var client = httpClientFactory.CreateClient())
using (StreamReader reader = new(await client.GetStreamAsync(attachment.Url)))
code = await reader.ReadToEndAsync();
code = await client.GetStringAsync(attachment.Url);

return new AttachmentCodeResult.Success(extension, code);
}
Expand Down
2 changes: 2 additions & 0 deletions Bot/Sharp/Attachments/IAttachmentCodeProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ public abstract record AttachmentCodeResult
public record Success(string? Language, string Code) : AttachmentCodeResult;

public record CodeNotFound : AttachmentCodeResult;

public record FileTooLarge : AttachmentCodeResult;
}

public interface IAttachmentCodeProvider
Expand Down
43 changes: 41 additions & 2 deletions Bot/Sharp/Backend/BackendProvider.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
using System.Buffers;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;

using Microsoft.Extensions.Options;

namespace Sharp.Backend;

public class BackendProvider(IBackendUriProvider uriProvider, IHttpClientFactory clientFactory) : IBackendProvider
public class BackendProvider(IBackendUriProvider uriProvider, IHttpClientFactory clientFactory, IOptions<Options> options) : IBackendProvider
{
private const int CopyBufferSize = 81920 / sizeof(char);

public async Task<string> CommonAsync(Architecture platform, Stream assembly, string endpoint)
{
for (int i = 0; i < 3; i++)
Expand All @@ -30,12 +35,46 @@ public async Task<string> CommonAsync(Architecture platform, Stream assembly, st
throw new InvalidOperationException(message);
}

return await response.Content.ReadAsStringAsync();
return await ReadAsStringAsync(response.Content);
}

throw new InvalidOperationException("The backend is currently overloaded. Please try again later.");
}

private async ValueTask<string> ReadAsStringAsync(HttpContent content)
{
using var stream = await content.ReadAsStreamAsync();
using StreamReader reader = new(stream);

StringBuilder builder = new();
int remaining = options.Value.MaxFileSize;
var buffer = ArrayPool<char>.Shared.Rent(CopyBufferSize);
int bufferLength = buffer.Length;
int read;

try
{
while (true)
{
read = await reader.ReadAsync(buffer.AsMemory(0, Math.Min(bufferLength, remaining)));

if (read is 0)
break;

builder.Append(buffer.AsMemory(0, read));

if ((remaining -= read) is 0)
break;
}
}
finally
{
ArrayPool<char>.Shared.Return(buffer);
}

return builder.ToString();
}

public Task<string> AsmAsync(Architecture platform, Stream assembly)
{
return CommonAsync(platform, assembly, "asm");
Expand Down
10 changes: 8 additions & 2 deletions Bot/Sharp/Decompilation/DecompilationProvider.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Microsoft.Extensions.Options;

namespace Sharp.Decompilation;

public class DecompilationProvider(IDecompilerProvider decompilerProvider) : IDecompilationProvider
public class DecompilationProvider(IDecompilerProvider decompilerProvider, IOptions<Options> options) : IDecompilationProvider
{
public async Task<DecompilationResult> DecompileAsync(ulong operationId, Stream assembly, Language outputLanguage)
{
Expand All @@ -14,6 +16,10 @@ public async Task<DecompilationResult> DecompileAsync(ulong operationId, Stream
if (!success)
return new DecompilationResult.Fail(outputLanguage);

return new DecompilationResult.Success(writer.ToString());
var stringBuilder = writer.GetStringBuilder();

var code = stringBuilder.ToString(0, Math.Min(stringBuilder.Length, options.Value.MaxFileSize));

return new DecompilationResult.Success(code);
}
}
2 changes: 2 additions & 0 deletions Bot/Sharp/Options.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public class Options
public required InformationOptions Information { get; set; }

public FormattingOptions Formatting { get; set; } = new();

public int MaxFileSize { get; set; } = 1024 * 1024;
}

public class EmojiOptions
Expand Down
6 changes: 6 additions & 0 deletions Bot/Sharp/Responding/ResponseProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ public class ResponseProvider(IOptions<Options> options, IOptions<CommandService
{
AttachmentCodeResult.Success => AttachmentCodeSuccessResponse<T>(),
AttachmentCodeResult.CodeNotFound => AttachmentCodeNotFoundResponse<T>(),
AttachmentCodeResult.FileTooLarge => AttachmentTooLargeResponse<T>(),
_ => throw new ArgumentOutOfRangeException(nameof(result)),
};
}
Expand All @@ -161,6 +162,11 @@ public class ResponseProvider(IOptions<Options> options, IOptions<CommandService
return Error<T>("Code not found", "No code was provided.");
}

private T AttachmentTooLargeResponse<T>() where T : IMessageProperties, new()
{
return Error<T>("File too large", $"The file is too large. The maximum size is {options.Value.MaxFileSize} bytes.");
}

public async ValueTask<T> HelpResponseAsync<T>(ulong operationId) where T : IMessageProperties, new()
{
T message = new();
Expand Down

0 comments on commit 5867f96

Please sign in to comment.