Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/354 for stream #356

Merged
merged 4 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions dotnet/Vaas/src/Vaas/Messages/VerdictRequestForStream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Vaas.Messages;

public class VerdictRequestForStream
{
[JsonPropertyName("kind")]
public string Kind => "VerdictRequestForStream";

[JsonPropertyName("guid")]
public string Guid { get; }

[JsonPropertyName("session_id")]
public string SessionId { get; }

[JsonPropertyName("verdict_request_attributes")]
public Dictionary<string, string>? VerdictRequestAttributes { get; set; }

[JsonPropertyName("use_cache")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public bool? UseCache { get; init; }

[JsonPropertyName("use_hash_lookup")]
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public bool? UseHashLookup { get; init; }

public VerdictRequestForStream(string sessionId)
{
SessionId = sessionId;
Guid = System.Guid.NewGuid().ToString();
}
}
70 changes: 70 additions & 0 deletions dotnet/Vaas/src/Vaas/Vaas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@
private void HandleResponseMessage(ResponseMessage msg)
{
if (msg.MessageType != WebSocketMessageType.Text) return;
var message = JsonSerializer.Deserialize<Message>(msg.Text);

Check warning on line 112 in dotnet/Vaas/src/Vaas/Vaas.cs

View workflow job for this annotation

GitHub Actions / Build & Test C# SDK (6.0.x)

Possible null reference argument for parameter 'json' in 'Message? JsonSerializer.Deserialize<Message>(string json, JsonSerializerOptions? options = null)'.

Check warning on line 112 in dotnet/Vaas/src/Vaas/Vaas.cs

View workflow job for this annotation

GitHub Actions / Build & Test C# SDK (6.0.x)

Possible null reference argument for parameter 'json' in 'Message? JsonSerializer.Deserialize<Message>(string json, JsonSerializerOptions? options = null)'.
TaskCompletionSource<VerdictResponse>? tcs;
switch (message?.Kind)
{
Expand Down Expand Up @@ -183,6 +183,68 @@
return new VaasVerdict(verdictResponse);
}

public async Task<VaasVerdict> ForStreamAsync(
Stream stream,
CancellationToken cancellationToken,
Dictionary<string, string>? verdictRequestAttributes = null
)
{
if (stream == null)
throw new VaasClientException("Stream was null.");

var verdictResponse = await ForStreamRequestAsync(
new VerdictRequestForStream(SessionId ?? throw new InvalidOperationException())
{
UseCache = _options.UseCache,
UseHashLookup = _options.UseHashLookup,
VerdictRequestAttributes = verdictRequestAttributes
});
if (!verdictResponse.IsValid)
throw new JsonException("VerdictResponse is not valid");
if (verdictResponse.Verdict != Verdict.Unknown)
throw new VaasServerException("Server returned verdict without receiving content.");

if (
string.IsNullOrWhiteSpace(verdictResponse.Url)
|| string.IsNullOrWhiteSpace(verdictResponse.UploadToken)
)
{
throw new JsonException(
"VerdictResponse missing URL or UploadToken for stream upload."
);
}

var response = WaitForResponseAsync(verdictResponse.Guid);
await UploadStream(stream, verdictResponse.Url, verdictResponse.UploadToken, cancellationToken);

return new VaasVerdict(await response);
}

private async Task UploadStream(Stream stream, string url, string token, CancellationToken cancellationToken)
lennartdohmann marked this conversation as resolved.
Show resolved Hide resolved
{
using var requestContent = new StreamContent(stream);
using var requestMessage = new HttpRequestMessage(HttpMethod.Put, url);
requestMessage.Content = requestContent;
requestMessage.Headers.Authorization = new AuthenticationHeaderValue(token);

var response = await _uploadHttpClient.SendAsync(requestMessage, cancellationToken);
if (!response.IsSuccessStatusCode)
{
var responseBody = await response.Content.ReadAsStringAsync(cancellationToken);
ProblemDetails? problemDetails;
try
{
problemDetails = JsonSerializer.Deserialize<ProblemDetails>(responseBody);
}
catch (JsonException)
{
throw new VaasServerException("Server did not return proper ProblemDetails");
}

throw ProblemDetailsToException(problemDetails);
}
}

public async Task<VaasVerdict> ForSha256Async(ChecksumSha256 sha256, CancellationToken cancellationToken, ForSha256Options? options = null)
{
var url = _options.Url;
Expand Down Expand Up @@ -312,6 +374,14 @@

return await WaitForResponseAsync(verdictRequest.Guid);
}

private async Task<VerdictResponse> ForStreamRequestAsync(VerdictRequestForStream verdictRequest)
{
var jsonString = JsonSerializer.Serialize(verdictRequest);
AuthenticatedClient.Send(jsonString);

return await WaitForResponseAsync(verdictRequest.Guid);
}

private async Task<VerdictResponse> ForUrlRequestAsync(VerdictRequestForUrl verdictRequestForUrl)
{
Expand Down
68 changes: 68 additions & 0 deletions dotnet/Vaas/test/Vaas.Test/IntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
Expand All @@ -19,6 +20,7 @@ public class IntegrationTests
"wss://gateway.production.vaas.gdatasecurity.de"));

private readonly ITestOutputHelper _output;
private readonly HttpClient _httpClient = new();

public IntegrationTests(ITestOutputHelper output)
{
Expand Down Expand Up @@ -178,6 +180,72 @@ public async Task ForUrl_WithUrlWithStatusCode4xx_ThrowsVaasClientException()
"Call failed with status code 404 (Not Found): GET https://upload.production.vaas.gdatasecurity.de/nocontenthere",
e.Message);
}

[Fact]
public async Task ForStream_WithEicarString_ReturnsMalicious()
{
// Arrange
var vaas = await AuthenticateWithCredentials();
var targetStream = new MemoryStream();
var eicarBytes = System.Text.Encoding.UTF8.GetBytes("X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*");
targetStream.Write(eicarBytes, 0, eicarBytes.Length);
targetStream.Position = 0;

// Act
var verdict = await vaas.ForStreamAsync(targetStream, CancellationToken.None);

// Assert
Assert.Equal(Verdict.Malicious, verdict.Verdict);
}

[Fact]
public async Task ForStream_WithCleanString_ReturnsClean()
{
// Arrange
var vaas = await AuthenticateWithCredentials();
var targetStream = new MemoryStream();
var cleanBytes = System.Text.Encoding.UTF8.GetBytes("This is a clean file");
targetStream.Write(cleanBytes, 0, cleanBytes.Length);
targetStream.Position = 0;

// Act
var verdict = await vaas.ForStreamAsync(targetStream, CancellationToken.None);

// Assert
Assert.Equal(Verdict.Clean, verdict.Verdict);
}

[Fact]
public async Task ForStream_WithCleanUrl_ReturnsClean()
{
// Arrange
var vaas = await AuthenticateWithCredentials();
var url = new Uri("https://raw.githubusercontent.com/GDATASoftwareAG/vaas/main/Readme.md");
var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, url), CancellationToken.None);
var targetStream = await response.Content.ReadAsStreamAsync();

// Act
var verdict = await vaas.ForStreamAsync(targetStream, CancellationToken.None);

// Assert
Assert.Equal(Verdict.Clean, verdict.Verdict);
}

[Fact]
public async Task ForStream_WithEicarUrl_ReturnsEicar()
{
// Arrange
var vaas = await AuthenticateWithCredentials();
var url = new Uri("https://secure.eicar.org/eicar.com.txt");
var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, url), CancellationToken.None);
var targetStream = await response.Content.ReadAsStreamAsync();

// Act
var verdict = await vaas.ForStreamAsync(targetStream, CancellationToken.None);

// Assert
Assert.Equal(Verdict.Malicious, verdict.Verdict);
}

private async Task<Vaas> AuthenticateWithCredentials()
{
Expand Down
Loading