From 4fb144e3228480b6769af1089dc770b16de60ca4 Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Mon, 2 Oct 2023 22:04:40 +0400 Subject: [PATCH 1/8] feat(core): add ipv6 to loopback addresses --- src/SecTester.Core/Configuration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SecTester.Core/Configuration.cs b/src/SecTester.Core/Configuration.cs index 6843730..293a636 100644 --- a/src/SecTester.Core/Configuration.cs +++ b/src/SecTester.Core/Configuration.cs @@ -14,7 +14,7 @@ public class Configuration { private readonly Regex _schemaRegex = new(@"^.+:\/\/"); private readonly Regex _hostnameNormalizationRegex = new(@"^(?!(?:\w+:)?\/\/)|^\/\/"); - private readonly string[] _loopbackAddresses = { "localhost", "127.0.0.1" }; + private readonly string[] _loopbackAddresses = { "localhost", "127.0.0.1", "::1" }; private readonly List _credentialProviders; public string Bus { get; private set; } From bc4303c5cd2b6c22ec6ea2e78873f8656ba2941e Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Mon, 2 Oct 2023 22:16:23 +0400 Subject: [PATCH 2/8] refactor(repeater): get rid of redundant commands --- .../Api/CreateRepeaterRequest.cs | 6 +++--- .../Api/DefaultRepeaters.cs | 14 ++----------- .../Api/ListRepeatersRequest.cs | 6 ------ .../Api/DefaultRepeatersTests.cs | 21 +++++++------------ 4 files changed, 12 insertions(+), 35 deletions(-) delete mode 100644 src/SecTester.Repeater/Api/ListRepeatersRequest.cs diff --git a/src/SecTester.Repeater/Api/CreateRepeaterRequest.cs b/src/SecTester.Repeater/Api/CreateRepeaterRequest.cs index 4cf42f0..eeabaf4 100644 --- a/src/SecTester.Repeater/Api/CreateRepeaterRequest.cs +++ b/src/SecTester.Repeater/Api/CreateRepeaterRequest.cs @@ -2,11 +2,10 @@ using System.Text; using SecTester.Bus.Commands; using SecTester.Bus.Dispatchers; -using SecTester.Core; namespace SecTester.Repeater.Api; -internal record CreateRepeaterRequest : HttpRequest +internal record CreateRepeaterRequest : HttpRequest { public CreateRepeaterRequest(string name, string? description) : base("/api/v1/repeaters", HttpMethod.Post, expectReply: false) @@ -14,7 +13,8 @@ public CreateRepeaterRequest(string name, string? description) : var data = new { Name = name, - Description = description + Description = description, + ClientRole = "dev-centric" }; var content = MessageSerializer.Serialize(data); Body = new StringContent(content, Encoding.UTF8, "application/json"); diff --git a/src/SecTester.Repeater/Api/DefaultRepeaters.cs b/src/SecTester.Repeater/Api/DefaultRepeaters.cs index c700b52..d3cf91f 100644 --- a/src/SecTester.Repeater/Api/DefaultRepeaters.cs +++ b/src/SecTester.Repeater/Api/DefaultRepeaters.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using System.Threading.Tasks; using SecTester.Core.Bus; using SecTester.Core.Exceptions; @@ -17,13 +15,11 @@ public DefaultRepeaters(ICommandDispatcher commandDispatcher) public async Task CreateRepeater(string name, string? description = default) { - await _commandDispatcher.Execute(new CreateRepeaterRequest(name, description)).ConfigureAwait(false); - - var repeaterId = (await FindRepeaterByName(name).ConfigureAwait(false))?.Id; + var repeaterId = (await _commandDispatcher.Execute(new CreateRepeaterRequest(name, description)).ConfigureAwait(false))?.Id; if (string.IsNullOrEmpty(repeaterId)) { - throw new SecTesterException("Cannot find created repeater id"); + throw new SecTesterException("Cannot create repeater"); } return repeaterId!; @@ -35,10 +31,4 @@ await _commandDispatcher.Execute( new DeleteRepeaterRequest(repeaterId) ).ConfigureAwait(false); } - - private async Task FindRepeaterByName(string name) - { - var repeaters = await _commandDispatcher.Execute(new ListRepeatersRequest()).ConfigureAwait(false); - return repeaters?.FirstOrDefault(repeater => repeater.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); - } } diff --git a/src/SecTester.Repeater/Api/ListRepeatersRequest.cs b/src/SecTester.Repeater/Api/ListRepeatersRequest.cs deleted file mode 100644 index fafdb18..0000000 --- a/src/SecTester.Repeater/Api/ListRepeatersRequest.cs +++ /dev/null @@ -1,6 +0,0 @@ -using System.Collections.Generic; -using SecTester.Bus.Commands; - -namespace SecTester.Repeater.Api; - -internal record ListRepeatersRequest() : HttpRequest>("/api/v1/repeaters/"); diff --git a/test/SecTester.Repeater.Tests/Api/DefaultRepeatersTests.cs b/test/SecTester.Repeater.Tests/Api/DefaultRepeatersTests.cs index 647a053..3800dd6 100644 --- a/test/SecTester.Repeater.Tests/Api/DefaultRepeatersTests.cs +++ b/test/SecTester.Repeater.Tests/Api/DefaultRepeatersTests.cs @@ -2,8 +2,7 @@ namespace SecTester.Repeater.Tests.Api; public class DefaultRepeatersTests : IDisposable { - const string Id = "99138d92-69db-44cb-952a-1cd9ec031e20"; - const string AnotherId = "220baaac-b7ec-46a7-ab5e-ff1e96b0785e"; + private const string Id = "99138d92-69db-44cb-952a-1cd9ec031e20"; private readonly ICommandDispatcher _commandDispatcher; private readonly DefaultRepeaters _sut; @@ -24,33 +23,27 @@ public void Dispose() public async Task CreateRepeater_CreatesRepeater() { // arrange - _commandDispatcher.Execute(Arg.Any()).Returns(new List - { - new(AnotherId, "bar"), new(Id, "foo") - }); + _commandDispatcher.Execute(Arg.Any()).Returns(new RepeaterIdentity(Id, "foo")); // act var result = await _sut.CreateRepeater("foo"); - // + // assert result.Should().Be(Id); } [Fact] - public async Task CreateRepeater_CreatedRepeaterNotFound_ThrowsError() + public async Task CreateRepeater_ThrowsError() { // arrange - _commandDispatcher.Execute(Arg.Any()).Returns(new List - { - new(AnotherId, "bar") - }); + _commandDispatcher.Execute(Arg.Any()).Returns(new RepeaterIdentity("", "foo")); // act var act = () => _sut.CreateRepeater("foo"); - // + // assert await act.Should().ThrowAsync() - .WithMessage("Cannot find created repeater id"); + .WithMessage("Cannot create repeater"); } [Fact] From 8573f3a7cadc447fe83b6b6530273a3155dda5b9 Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Mon, 2 Oct 2023 22:26:36 +0400 Subject: [PATCH 3/8] refactor(repeater): refrain from supporting websockets --- .../Extensions/ServiceCollectionExtensions.cs | 2 - src/SecTester.Repeater/Protocol.cs | 3 +- .../Runners/DefaultWebSocketFactory.cs | 35 --- src/SecTester.Repeater/Runners/IRequest.cs | 2 - .../Runners/IWebSocketFactory.cs | 12 - .../Runners/WebSocketResponseBody.cs | 17 -- .../Runners/WsRequestRunner.cs | 164 ------------- .../ServiceCollectionExtensionsTests.cs | 1 - .../Fixtures/Startup.cs | 131 ----------- .../Fixtures/TestServerApplicationFactory.cs | 16 -- .../Fixtures/TestServerApplicationFixture.cs | 56 ----- .../Mocks/MockWebSocketFactory.cs | 24 -- .../Runners/WsRequestRunnerTests.cs | 215 ------------------ .../SecTester.Repeater.Tests.csproj | 1 - 14 files changed, 1 insertion(+), 678 deletions(-) delete mode 100644 src/SecTester.Repeater/Runners/DefaultWebSocketFactory.cs delete mode 100644 src/SecTester.Repeater/Runners/IWebSocketFactory.cs delete mode 100644 src/SecTester.Repeater/Runners/WebSocketResponseBody.cs delete mode 100644 src/SecTester.Repeater/Runners/WsRequestRunner.cs delete mode 100644 test/SecTester.Repeater.Tests/Fixtures/Startup.cs delete mode 100644 test/SecTester.Repeater.Tests/Fixtures/TestServerApplicationFactory.cs delete mode 100644 test/SecTester.Repeater.Tests/Fixtures/TestServerApplicationFixture.cs delete mode 100644 test/SecTester.Repeater.Tests/Mocks/MockWebSocketFactory.cs delete mode 100644 test/SecTester.Repeater.Tests/Runners/WsRequestRunnerTests.cs diff --git a/src/SecTester.Repeater/Extensions/ServiceCollectionExtensions.cs b/src/SecTester.Repeater/Extensions/ServiceCollectionExtensions.cs index c90a88d..da2383e 100644 --- a/src/SecTester.Repeater/Extensions/ServiceCollectionExtensions.cs +++ b/src/SecTester.Repeater/Extensions/ServiceCollectionExtensions.cs @@ -26,9 +26,7 @@ public static IServiceCollection AddSecTesterRepeater(this IServiceCollection co .AddScoped() .AddScoped() .AddScoped() - .AddScoped() .AddScoped() - .AddScoped() .AddScoped(sp => protocol => sp.GetServices().FirstOrDefault(x => x.Protocol == protocol) ) diff --git a/src/SecTester.Repeater/Protocol.cs b/src/SecTester.Repeater/Protocol.cs index 7f01d3e..4221d78 100644 --- a/src/SecTester.Repeater/Protocol.cs +++ b/src/SecTester.Repeater/Protocol.cs @@ -2,6 +2,5 @@ namespace SecTester.Repeater; public enum Protocol { - Http, - Ws + Http } diff --git a/src/SecTester.Repeater/Runners/DefaultWebSocketFactory.cs b/src/SecTester.Repeater/Runners/DefaultWebSocketFactory.cs deleted file mode 100644 index 1bbba20..0000000 --- a/src/SecTester.Repeater/Runners/DefaultWebSocketFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Net; -using System.Net.WebSockets; -using System.Threading; -using System.Threading.Tasks; - -namespace SecTester.Repeater.Runners; - -public class DefaultWebSocketFactory : IWebSocketFactory -{ - private readonly RequestRunnerOptions _options; - - public DefaultWebSocketFactory(RequestRunnerOptions options) - { - _options = options ?? throw new ArgumentNullException(nameof(options)); - } - - public async Task CreateWebSocket(Uri uri, CancellationToken cancellationToken = default) - { - var proxy = _options.ProxyUrl is not null ? new WebProxy(_options.ProxyUrl) : null; - // TODO: disable certs validation. For details see https://github.com/dotnet/runtime/issues/18696 - var client = new ClientWebSocket - { - Options = - { - Proxy = proxy, KeepAliveInterval = _options.Timeout - } - }; - - await client.ConnectAsync(uri, cancellationToken).ConfigureAwait(false); - - return client; - } -} - diff --git a/src/SecTester.Repeater/Runners/IRequest.cs b/src/SecTester.Repeater/Runners/IRequest.cs index 4131d7f..f77a5be 100644 --- a/src/SecTester.Repeater/Runners/IRequest.cs +++ b/src/SecTester.Repeater/Runners/IRequest.cs @@ -1,14 +1,12 @@ using System; using System.Collections.Generic; using System.Net.Http; -using System.Text.RegularExpressions; namespace SecTester.Repeater.Runners; public interface IRequest { string? Body { get; init; } - Regex? CorrelationIdRegex { get; init; } HttpMethod Method { get; init; } Protocol Protocol { get; init; } Uri Url { get; init; } diff --git a/src/SecTester.Repeater/Runners/IWebSocketFactory.cs b/src/SecTester.Repeater/Runners/IWebSocketFactory.cs deleted file mode 100644 index 11e3ad8..0000000 --- a/src/SecTester.Repeater/Runners/IWebSocketFactory.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Net.WebSockets; -using System.Threading; -using System.Threading.Tasks; - -namespace SecTester.Repeater.Runners; - -public interface IWebSocketFactory -{ - public Task CreateWebSocket(Uri uri, CancellationToken cancellationToken = default); -} - diff --git a/src/SecTester.Repeater/Runners/WebSocketResponseBody.cs b/src/SecTester.Repeater/Runners/WebSocketResponseBody.cs deleted file mode 100644 index a677892..0000000 --- a/src/SecTester.Repeater/Runners/WebSocketResponseBody.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Net.WebSockets; - -namespace SecTester.Repeater.Runners; - -internal sealed class WebSocketResponseBody : ResponseBody -{ - public WebSocketResponseBody(byte[] body, WebSocketCloseStatus? statusCode = default, string? statusDescription = default) : base(body) - { - StatusCode = statusCode; - StatusDescription = statusDescription; - } - - public WebSocketCloseStatus? StatusCode { get; } - public string? StatusDescription { get; } -} - - diff --git a/src/SecTester.Repeater/Runners/WsRequestRunner.cs b/src/SecTester.Repeater/Runners/WsRequestRunner.cs deleted file mode 100644 index 3172248..0000000 --- a/src/SecTester.Repeater/Runners/WsRequestRunner.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.WebSockets; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using SecTester.Core.Extensions; -using SecTester.Repeater.Bus; - -namespace SecTester.Repeater.Runners; - -internal sealed class WsRequestRunner : IRequestRunner -{ - private const WebSocketCloseStatus DefaultStatusCode = WebSocketCloseStatus.NormalClosure; - private const int MaxBufferSize = 1024 * 4; - private readonly SemaphoreSlim _lock = new(1, 1); - - private readonly RequestRunnerOptions _options; - private readonly IWebSocketFactory _webSocketFactory; - - public WsRequestRunner(RequestRunnerOptions options, IWebSocketFactory webSocketFactory) - { - _options = options ?? throw new ArgumentNullException(nameof(options)); - _webSocketFactory = webSocketFactory ?? throw new ArgumentNullException(nameof(webSocketFactory)); - } - - public Protocol Protocol => Protocol.Ws; - - public async Task Run(IRequest request) - { - using var cts = new CancellationTokenSource(_options.Timeout); - - WebSocket? client = null; - - try - { - client = await _webSocketFactory.CreateWebSocket(request.Url, cts.Token).ConfigureAwait(false); - - var result = await SendAndRetrieve(client, request, cts.Token).ConfigureAwait(false); - - return CreateRequestExecutingResult(client, result); - } - catch (Exception err) - { - return CreateRequestExecutingResult(err); - } - finally - { - if (client != null) - { - await CloseSocket(client, cts.Token).ConfigureAwait(false); - } - } - } - - private async Task SendAndRetrieve(WebSocket client, IRequest request, CancellationToken cancellationToken) - { - var message = BuildMessage(request); - - await Send(client, message, cancellationToken).ConfigureAwait(false); - - return await Consume(request, client, cancellationToken).ConfigureAwait(false); - } - - private async Task Send(WebSocket client, ArraySegment message, CancellationToken cancellationToken) - { - using var _ = await _lock.LockAsync(cancellationToken).ConfigureAwait(false); - await client.SendAsync(message, WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false); - } - - private static async Task CloseSocket(WebSocket client, CancellationToken cancellationToken) - { - try - { - if (!client.CloseStatus.HasValue) - { - await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "", cancellationToken).ConfigureAwait(false); - } - - client.Dispose(); - } - catch - { - // noop - } - } - - private static async IAsyncEnumerable ConsumeMessage(WebSocket client, - [EnumeratorCancellation] CancellationToken cancellationToken) - { - using var stream = new MemoryStream(); - var buffer = new ArraySegment(new byte[MaxBufferSize]); - - while (!client.CloseStatus.HasValue) - { - var result = await client.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false); - - if (buffer.Array != null) - { - await stream.WriteAsync(buffer.Array, buffer.Offset, result.Count, cancellationToken).ConfigureAwait(false); - } - - if (!result.CloseStatus.HasValue && !result.EndOfMessage) - { - continue; - } - - stream.Seek(0, SeekOrigin.Begin); - yield return new WebSocketResponseBody(stream.ToArray(), result.CloseStatus, result.CloseStatusDescription); - } - } - - private static ValueTask Consume(IRequest request, WebSocket client, - CancellationToken cancellationToken) - { - return ConsumeMessage(client, cancellationToken) - .FirstAsync(r => request.CorrelationIdRegex is null || request.CorrelationIdRegex.IsMatch(r.ToString()), cancellationToken); - } - - private static RequestExecutingResult CreateRequestExecutingResult(WebSocket client, WebSocketResponseBody result) - { - var closeStatus = result.StatusCode ?? client.CloseStatus ?? DefaultStatusCode; - var statusDescription = result.StatusDescription ?? client.CloseStatusDescription; - - return new RequestExecutingResult - { - Protocol = Protocol.Ws, - Message = statusDescription, - StatusCode = (int)closeStatus, - Body = result.ToString() - }; - } - - private static RequestExecutingResult CreateRequestExecutingResult(Exception exception) - { - var errorCode = GetErrorCode(exception); - - return new RequestExecutingResult - { - Protocol = Protocol.Ws, - Message = exception.Message.TrimEnd(), - ErrorCode = errorCode - }; - } - - private static string? GetErrorCode(Exception err) - { - // TODO: use native errno codes instead - return err switch - { - WebSocketException exception => Enum.GetName(typeof(WebSocketError), exception.WebSocketErrorCode), - _ => null - }; - } - - private static ArraySegment BuildMessage(IRequest message) - { - var buffer = Encoding.Default.GetBytes(message.Body ?? ""); - return new ArraySegment(buffer); - } -} diff --git a/test/SecTester.Repeater.Tests/Extensions/ServiceCollectionExtensionsTests.cs b/test/SecTester.Repeater.Tests/Extensions/ServiceCollectionExtensionsTests.cs index 2e63b58..91e4b39 100644 --- a/test/SecTester.Repeater.Tests/Extensions/ServiceCollectionExtensionsTests.cs +++ b/test/SecTester.Repeater.Tests/Extensions/ServiceCollectionExtensionsTests.cs @@ -68,7 +68,6 @@ public void AddSecTesterRepeater_RegistersRequestRunnerRegistry() var result = provider.GetRequiredService(); result(Protocol.Http).Should().BeOfType(typeof(HttpRequestRunner)); - result(Protocol.Ws).Should().BeOfType(typeof(WsRequestRunner)); } [Fact] diff --git a/test/SecTester.Repeater.Tests/Fixtures/Startup.cs b/test/SecTester.Repeater.Tests/Fixtures/Startup.cs deleted file mode 100644 index e69d399..0000000 --- a/test/SecTester.Repeater.Tests/Fixtures/Startup.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System.Globalization; - -namespace SecTester.Repeater.Tests.Fixtures; - -// This is from https://github.com/aspnet/AspNetCore.Docs/blob/master/aspnetcore/fundamentals/websockets/samples/2.x/WebSocketsSample/Startup.cs -public sealed class Startup -{ - public void Configure(IApplicationBuilder app) - { - app.UseWebSockets(); - app.Use(async (context, next) => - { - switch (context.Request.Path) - { - case "/ws": - if (context.WebSockets.IsWebSocketRequest) - { - var webSocket = await context.WebSockets.AcceptWebSocketAsync(); - await HandleRequest(webSocket); - } - else - { - context.Response.StatusCode = 400; - } - - break; - default: - await next(); - break; - } - }); - } - - private async Task HandleRequest(WebSocket webSocket) - { - while (true) - { - var (result, message) = await ReadRequest(webSocket); - if (result.CloseStatus.HasValue) - { - await webSocket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - return; - } - - await HandleRequest(webSocket, message!); - } - } - - private Task HandleRequest(WebSocket webSocket, byte[] request) - { - var msg = Encoding.Default.GetString(request ?? Array.Empty()).Trim().ToLower(CultureInfo.InvariantCulture); - - return msg switch - { - "ping" => SendEcho(webSocket, "pong"), - "range" => Task.WhenAll(Enumerable.Range(0, 5).Select(idx => SendEcho(webSocket, $"range:{idx}"))), - "chunked" => SendChunkedEcho(webSocket, "ping pong"), - not null when msg.StartsWith("fast") => SendEcho(webSocket, msg), - not null when msg.StartsWith("echo") => SendEcho(webSocket, msg, true), - "close" => webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "normal closure", CancellationToken.None), - "close-output" => webSocket.CloseOutputAsync(WebSocketCloseStatus.InvalidPayloadData, "invalid payload", CancellationToken.None), - _ => SendEcho(webSocket, msg) - }; - } - - - private static async Task<(WebSocketReceiveResult result, byte[]? message)> ReadRequest(WebSocket webSocket) - { - var buffer = new ArraySegment(new byte[8192]); - using var stream = new MemoryStream(); - WebSocketReceiveResult? result; - do - { - result = await webSocket.ReceiveAsync(buffer, CancellationToken.None); - if (result.CloseStatus.HasValue) - { - return (result, null); - } - - if (buffer.Array != null) - { - stream.Write(buffer.Array, buffer.Offset, result.Count); - } - } while (!result.EndOfMessage); - - stream.Seek(0, SeekOrigin.Begin); - - return (result, stream.ToArray()); - } - - private static Encoding GetEncoding() - { - return Encoding.UTF8; - } - - private async Task SendEcho(WebSocket webSocket, string msg, bool slowdown = false) - { - if (slowdown) - { - await Task.Delay(100); - } - - await Send(webSocket, msg, slowdown: slowdown); - } - - private async Task SendChunkedEcho(WebSocket webSocket, string msg, bool slowdown = false) - { - var idx = (int)Math.Floor((double)msg.Length / 2); - - await Send(webSocket, msg[..idx], endOfMessage: false, slowdown: slowdown); - await Send(webSocket, msg[idx..], slowdown: slowdown); - } - - private async Task Send(WebSocket webSocket, string msg, bool endOfMessage = true, bool slowdown = false) - { - if (slowdown) - { - await Task.Delay(100); - } - - var encoding = GetEncoding(); - var bytes = encoding.GetBytes(msg); - var segment = new ArraySegment(bytes); - - await webSocket.SendAsync( - segment, - WebSocketMessageType.Text, - endOfMessage, - CancellationToken.None); - } -} diff --git a/test/SecTester.Repeater.Tests/Fixtures/TestServerApplicationFactory.cs b/test/SecTester.Repeater.Tests/Fixtures/TestServerApplicationFactory.cs deleted file mode 100644 index d32ef9f..0000000 --- a/test/SecTester.Repeater.Tests/Fixtures/TestServerApplicationFactory.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SecTester.Repeater.Tests.Fixtures; - -internal class TestServerApplicationFactory : WebApplicationFactory - where TStartup : class -{ - protected override TestServer CreateServer(IWebHostBuilder builder) => - base.CreateServer( - builder.UseSolutionRelativeContentRoot("").ConfigureLogging(logging => logging.ClearProviders())); - - protected override IWebHostBuilder CreateWebHostBuilder() => - WebHost.CreateDefaultBuilder() - .UseStartup(); - - - -} diff --git a/test/SecTester.Repeater.Tests/Fixtures/TestServerApplicationFixture.cs b/test/SecTester.Repeater.Tests/Fixtures/TestServerApplicationFixture.cs deleted file mode 100644 index 4219a91..0000000 --- a/test/SecTester.Repeater.Tests/Fixtures/TestServerApplicationFixture.cs +++ /dev/null @@ -1,56 +0,0 @@ -using SecTester.Repeater.Tests.Mocks; - -namespace SecTester.Repeater.Tests.Fixtures; - -public class TestServerApplicationFixture : IDisposable, IAsyncDisposable where TStartup : class -{ - private readonly HttpClient _client; - private readonly TestServerApplicationFactory _factory; - - public TestServerApplicationFixture() - { - _factory = new TestServerApplicationFactory(); - _client = _factory.CreateClient(); // This is needed since _factory.Server would otherwise be null - } - - public Uri Url - { - get - { - var uri = new UriBuilder(_factory.Server.BaseAddress) - { - Scheme = "ws", - Path = "ws" - }; - - return uri.Uri; - } - } - - public void Dispose() - { - _factory.Dispose(); - _client.Dispose(); - GC.SuppressFinalize(this); - } - - public async ValueTask DisposeAsync() - { - await _factory.DisposeAsync(); - Dispose(); - } - - public IRequestRunner CreateWsRequestRunner(IWebSocketFactory clientFactory, RequestRunnerOptions? options = default) - { - return new WsRequestRunner(options ?? new RequestRunnerOptions(), clientFactory); - } - - public IRequestRunner CreateWsRequestRunner(RequestRunnerOptions? options = default) - { - return CreateWsRequestRunner(new MockWebSocketFactory(_factory.Server), options); - } -} - - - - diff --git a/test/SecTester.Repeater.Tests/Mocks/MockWebSocketFactory.cs b/test/SecTester.Repeater.Tests/Mocks/MockWebSocketFactory.cs deleted file mode 100644 index bc5a809..0000000 --- a/test/SecTester.Repeater.Tests/Mocks/MockWebSocketFactory.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace SecTester.Repeater.Tests.Mocks; - -public class MockWebSocketFactory : IWebSocketFactory, IDisposable -{ - private readonly TestServer _server; - - public MockWebSocketFactory(TestServer server) - { - _server = server ?? throw new ArgumentNullException(nameof(server)); - } - - public void Dispose() - { - _server.Dispose(); - GC.SuppressFinalize(this); - } - - public Task CreateWebSocket(Uri url, CancellationToken cancellationToken) - { - var ws = _server.CreateWebSocketClient(); - return ws.ConnectAsync(url, cancellationToken); - } -} - diff --git a/test/SecTester.Repeater.Tests/Runners/WsRequestRunnerTests.cs b/test/SecTester.Repeater.Tests/Runners/WsRequestRunnerTests.cs deleted file mode 100644 index fb7f5f3..0000000 --- a/test/SecTester.Repeater.Tests/Runners/WsRequestRunnerTests.cs +++ /dev/null @@ -1,215 +0,0 @@ -using SecTester.Repeater.Tests.Fixtures; - -namespace SecTester.Repeater.Tests.Runners; - -public class WsRequestRunnerTests : IClassFixture>, IAsyncDisposable -{ - private readonly TestServerApplicationFixture _fixture; - private readonly IWebSocketFactory _mockWebSocketFactory = Substitute.For(); - private readonly WebSocket _mockWsClient = Substitute.For(); - - public WsRequestRunnerTests(TestServerApplicationFixture fixture) - { - _fixture = fixture; - } - - public async ValueTask DisposeAsync() - { - _mockWsClient.ClearSubstitute(); - _mockWebSocketFactory.ClearSubstitute(); - await _fixture.DisposeAsync(); - GC.SuppressFinalize(this); - } - - [Fact] - public async Task Run_ReturnsResult_WhenRequestIsSuccessful() - { - // arrange - const string body = "foo"; - var request = new RequestExecutingEvent(_fixture.Url) - { - Body = body - }; - var sut = _fixture.CreateWsRequestRunner(); - - // act - var result = await sut.Run(request); - - // assert - result.Should().BeEquivalentTo(new - { - Protocol = Protocol.Ws, - StatusCode = 1000, - Body = body - }); - } - - [Fact] - public async Task Run_ReturnsResult_WhenResponseIsChunked() - { - // arrange - const string body = "chunked"; - var request = new RequestExecutingEvent(_fixture.Url) - { - Body = body - }; - var sut = _fixture.CreateWsRequestRunner(); - - // act - var result = await sut.Run(request); - - // assert - result.Should().BeEquivalentTo(new - { - Protocol = Protocol.Ws, - StatusCode = 1000, - Body = "ping pong" - }); - } - - [Fact] - public async Task Run_ReturnsResultInCorrectOrder() - { - // arrange - var range = Enumerable.Range(0, 5); - var bodies = range.Select(idx => $"echo:{idx}"); - var requests = bodies.Select(body => new RequestExecutingEvent(_fixture.Url) - { - Body = body - }); - var sut = _fixture.CreateWsRequestRunner(); - - // act - var result = await Task.WhenAll(requests.Select(request => sut.Run(request))); - - // assert - result.Should().BeInAscendingOrder(x => x.Body); - } - - [Fact] - public async Task Run_ReturnsResultMatchedWithCorrelationIdRegex() - { - // arrange - const string body = "range"; - const string expected = "range:2"; - var request = new RequestExecutingEvent(_fixture.Url) - { - Body = body, - CorrelationIdRegex = new Regex(@":2$") - }; - var sut = _fixture.CreateWsRequestRunner(); - - // act - var result = await sut.Run(request); - - // assert - result.Should().BeEquivalentTo(new - { - Body = expected - }); - } - - [Fact] - public async Task Run_WebSocketClosed_ReturnsResponseWithStatusCode() - { - // arrange - const string body = "close-output"; - var request = new RequestExecutingEvent(_fixture.Url) - { - Body = body - }; - var sut = _fixture.CreateWsRequestRunner(); - - // act - var result = await sut.Run(request); - - // assert - result.Should().BeEquivalentTo(new - { - Protocol = Protocol.Ws, - StatusCode = 1007, - Message = "invalid payload" - }); - } - - [Fact] - public async Task Run_WebSocketException_ReturnsResponseWithErrorCode() - { - // arrange - const string body = "foo"; - var request = new RequestExecutingEvent(_fixture.Url) - { - Body = body - }; - var sut = _fixture.CreateWsRequestRunner(_mockWebSocketFactory); - _mockWebSocketFactory.CreateWebSocket(Arg.Any(), Arg.Any()).Returns(_mockWsClient); - _mockWsClient.SendAsync(Arg.Any>(), Arg.Any(), true, Arg.Any()) - .ThrowsAsync(new WebSocketException(WebSocketError.UnsupportedProtocol)); - - // act - var result = await sut.Run(request); - - // assert - result.Should().BeEquivalentTo(new - { - Protocol = Protocol.Ws, - ErrorCode = "UnsupportedProtocol", - Message = "The WebSocket request or response operation was called with unsupported protocol(s)." - }); - } - - [Fact] - public async Task Run_ReturnsResultWithError_WhenRequestTimesOut() - { - // arrange - const string body = "echo"; - var request = new RequestExecutingEvent(_fixture.Url) - { - Body = body - }; - var sut = _fixture.CreateWsRequestRunner(new RequestRunnerOptions - { - Timeout = TimeSpan.FromMilliseconds(50) - }); - - // act - var result = await sut.Run(request); - - // assert - result.Should().BeEquivalentTo(new - { - Protocol = Protocol.Ws, - Message = "The operation was canceled." - }); - } - - [Fact] - public async Task Run_ReturnsResultWithError_WhenConnectionRefused() - { - // arrange - const string body = "echo"; - var url = new Uri("wss://example.com"); - var request = new RequestExecutingEvent(url) - { - Body = body - }; - var sut = _fixture.CreateWsRequestRunner(); - - // act - var result = await sut.Run(request); - - // assert - result.Should().BeEquivalentTo(new - { - Protocol = Protocol.Ws - }, - options => options.Using(ctx => ctx.Subject.Should().Contain("Incomplete handshake")) - .When(info => info.Path.EndsWith(nameof(RequestExecutingResult.Message)))); - } -} - - - - - - diff --git a/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj b/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj index 44b8715..0a9fc3e 100644 --- a/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj +++ b/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj @@ -12,7 +12,6 @@ - From 2b40c0215c86e4f423dfea7c4cf3f6141dd2531b Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Tue, 3 Oct 2023 18:54:14 +0400 Subject: [PATCH 4/8] feat(repeater): refrain from utilizing non standard ports closes #165 --- .../Api/RepeaterIdentity.cs | 3 +- .../Bus/DefaultRepeaterBusFactory.cs | 58 +++++ .../Bus/DefaultRepeaterEventBusFactory.cs | 38 ---- src/SecTester.Repeater/Bus/IRepeaterBus.cs | 15 ++ .../Bus/IRepeaterBusFactory.cs | 6 + .../Bus/IRepeaterEventBusFactory.cs | 8 - src/SecTester.Repeater/Bus/ISocketIoClient.cs | 14 ++ .../Bus/ISocketIoResponse.cs | 11 + ...stExecutingEvent.cs => IncomingRequest.cs} | 7 +- ...ExecutingResult.cs => OutgoingResponse.cs} | 7 +- .../Bus/RegisterRepeaterCommand.cs | 6 - .../Bus/RegisterRepeaterPayload.cs | 3 - .../Bus/RegisterRepeaterResult.cs | 3 - .../Bus/RepeaterRegisteringError.cs | 10 - .../Bus/RepeaterStatusEvent.cs | 6 - .../Bus/RequestExecutingEventListener.cs | 30 --- .../Bus/SocketIoClientWrapper.cs | 29 +++ .../Bus/SocketIoRepeaterBus.cs | 134 +++++++++++ .../DefaultRepeaterFactory.cs | 20 +- .../Extensions/ServiceCollectionExtensions.cs | 3 +- src/SecTester.Repeater/Repeater.cs | 115 ++++------ .../Runners/HttpRequestRunner.cs | 6 +- src/SecTester.Repeater/Runtime.cs | 12 + .../SecTester.Repeater.csproj | 3 +- src/SecTester.Repeater/packages.lock.json | 137 +++++++++++- src/SecTester.Reporter/packages.lock.json | 6 +- src/SecTester.Runner/packages.lock.json | 149 +++++++++++-- src/SecTester.Scan/packages.lock.json | 2 +- test/SecTester.Bus.Tests/packages.lock.json | 6 +- .../Api/DefaultRepeatersTests.cs | 4 +- .../Bus/DefaultRepeaterBusFactoryTests.cs | 57 +++++ .../DefaultRepeaterEventBusFactoryTests.cs | 53 ----- .../Bus/RequestExecutingEventHandlerTests.cs | 58 ----- .../Bus/SocketIoRepeaterBusTests.cs | 210 ++++++++++++++++++ .../DefaultRepeaterFactoryTests.cs | 15 +- .../SecTester.Repeater.Tests/RepeaterTests.cs | 159 +++++-------- .../Runners/HttpRequestRunnerTests.cs | 26 +-- .../SecTester.Repeater.Tests.csproj | 1 - .../packages.lock.json | 89 +++++++- .../packages.lock.json | 8 +- .../SecTester.Runner.Tests/packages.lock.json | 103 +++++++-- test/SecTester.Scan.Tests/packages.lock.json | 6 +- 42 files changed, 1126 insertions(+), 510 deletions(-) create mode 100644 src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs delete mode 100644 src/SecTester.Repeater/Bus/DefaultRepeaterEventBusFactory.cs create mode 100644 src/SecTester.Repeater/Bus/IRepeaterBus.cs create mode 100644 src/SecTester.Repeater/Bus/IRepeaterBusFactory.cs delete mode 100644 src/SecTester.Repeater/Bus/IRepeaterEventBusFactory.cs create mode 100644 src/SecTester.Repeater/Bus/ISocketIoClient.cs create mode 100644 src/SecTester.Repeater/Bus/ISocketIoResponse.cs rename src/SecTester.Repeater/Bus/{RequestExecutingEvent.cs => IncomingRequest.cs} (67%) rename src/SecTester.Repeater/Bus/{RequestExecutingResult.cs => OutgoingResponse.cs} (62%) delete mode 100644 src/SecTester.Repeater/Bus/RegisterRepeaterCommand.cs delete mode 100644 src/SecTester.Repeater/Bus/RegisterRepeaterPayload.cs delete mode 100644 src/SecTester.Repeater/Bus/RegisterRepeaterResult.cs delete mode 100644 src/SecTester.Repeater/Bus/RepeaterRegisteringError.cs delete mode 100644 src/SecTester.Repeater/Bus/RepeaterStatusEvent.cs delete mode 100644 src/SecTester.Repeater/Bus/RequestExecutingEventListener.cs create mode 100644 src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs create mode 100644 src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs create mode 100644 src/SecTester.Repeater/Runtime.cs create mode 100644 test/SecTester.Repeater.Tests/Bus/DefaultRepeaterBusFactoryTests.cs delete mode 100644 test/SecTester.Repeater.Tests/Bus/DefaultRepeaterEventBusFactoryTests.cs delete mode 100644 test/SecTester.Repeater.Tests/Bus/RequestExecutingEventHandlerTests.cs create mode 100644 test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs diff --git a/src/SecTester.Repeater/Api/RepeaterIdentity.cs b/src/SecTester.Repeater/Api/RepeaterIdentity.cs index 5cedc4c..59399c1 100644 --- a/src/SecTester.Repeater/Api/RepeaterIdentity.cs +++ b/src/SecTester.Repeater/Api/RepeaterIdentity.cs @@ -2,8 +2,7 @@ namespace SecTester.Repeater.Api; -internal record RepeaterIdentity(string Id, string Name) +internal record RepeaterIdentity(string Id) { public string Id { get; } = Id ?? throw new ArgumentNullException(nameof(Id)); - public string Name { get; } = Name ?? throw new ArgumentNullException(nameof(Name)); } diff --git a/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs b/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs new file mode 100644 index 0000000..0a8ead0 --- /dev/null +++ b/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using SecTester.Core; +using SecTester.Core.Utils; +using SocketIO.Serializer.MessagePack; +using SocketIOClient; + +namespace SecTester.Repeater.Bus; + +public class DefaultRepeaterBusFactory : IRepeaterBusFactory +{ + private readonly Configuration _config; + private readonly ILoggerFactory _loggerFactory; + private readonly IServiceScopeFactory _scopeFactory; + + public DefaultRepeaterBusFactory(Configuration config, ILoggerFactory loggerFactory, IServiceScopeFactory scopeFactory) + { + _config = config ?? throw new ArgumentNullException(nameof(config)); + _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); + } + + public IRepeaterBus Create(string repeaterId) + { + if (_config.Credentials is null) + { + throw new InvalidOperationException( + "Please provide credentials to establish a connection with the bus." + ); + } + + var url = new Uri(_config.Api); + var options = new SocketIOOptions + { + Path = "/api/ws/v1", + ReconnectionAttempts = 20, + ReconnectionDelayMax = 86400000, + ConnectionTimeout = TimeSpan.FromSeconds(10), + Auth = new List> + { + new("token", _config.Credentials.Token), new("domain", repeaterId) + }, + }; + + var client = new SocketIOClient.SocketIO(url, options) + { + Serializer = new SocketIOMessagePackSerializer() + }; + var wrapper = new SocketIoClientWrapper(client); + + var scope = _scopeFactory.CreateAsyncScope(); + var timerProvider = scope.ServiceProvider.GetRequiredService(); + + return new SocketIoRepeaterBus(url, wrapper, timerProvider, _loggerFactory.CreateLogger()); + } +} diff --git a/src/SecTester.Repeater/Bus/DefaultRepeaterEventBusFactory.cs b/src/SecTester.Repeater/Bus/DefaultRepeaterEventBusFactory.cs deleted file mode 100644 index 2987777..0000000 --- a/src/SecTester.Repeater/Bus/DefaultRepeaterEventBusFactory.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using SecTester.Bus.Dispatchers; -using SecTester.Core; -using SecTester.Core.Bus; - -namespace SecTester.Repeater.Bus; - -public class DefaultRepeaterEventBusFactory : IRepeaterEventBusFactory -{ - private readonly Configuration _config; - private readonly IRmqEventBusFactory _rmqEventBusFactory; - - public DefaultRepeaterEventBusFactory(Configuration config, IRmqEventBusFactory rmqEventBusFactory) - { - _config = config ?? throw new ArgumentNullException(nameof(config)); - _rmqEventBusFactory = rmqEventBusFactory ?? throw new ArgumentNullException(nameof(rmqEventBusFactory)); - } - - public IEventBus Create(string repeaterId) - { - if (_config.Credentials == null) - { - throw new InvalidOperationException( - "Please provide credentials to establish a connection with the bus." - ); - } - - var options = new RmqEventBusOptions(_config.Bus, "app", "EventBus", $"agent:{repeaterId}") - { - Username = "bot", - Password = _config.Credentials!.Token, - HeartbeatInterval = TimeSpan.FromSeconds(30), - ConnectTimeout = TimeSpan.FromSeconds(30) - }; - - return _rmqEventBusFactory.CreateEventBus(options); - } -} diff --git a/src/SecTester.Repeater/Bus/IRepeaterBus.cs b/src/SecTester.Repeater/Bus/IRepeaterBus.cs new file mode 100644 index 0000000..5948db1 --- /dev/null +++ b/src/SecTester.Repeater/Bus/IRepeaterBus.cs @@ -0,0 +1,15 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace SecTester.Repeater.Bus; + +public interface IRepeaterBus : IAsyncDisposable +{ + event Func> RequestReceived; + event Action ErrorOccurred; + event Action UpgradeAvailable; + + Task Connect(); + Task Deploy(string repeaterId, Runtime? runtime = null, CancellationToken? cancellationToken = null); +} diff --git a/src/SecTester.Repeater/Bus/IRepeaterBusFactory.cs b/src/SecTester.Repeater/Bus/IRepeaterBusFactory.cs new file mode 100644 index 0000000..505fa63 --- /dev/null +++ b/src/SecTester.Repeater/Bus/IRepeaterBusFactory.cs @@ -0,0 +1,6 @@ +namespace SecTester.Repeater.Bus; + +public interface IRepeaterBusFactory +{ + IRepeaterBus Create(string repeaterId); +} diff --git a/src/SecTester.Repeater/Bus/IRepeaterEventBusFactory.cs b/src/SecTester.Repeater/Bus/IRepeaterEventBusFactory.cs deleted file mode 100644 index 83226e2..0000000 --- a/src/SecTester.Repeater/Bus/IRepeaterEventBusFactory.cs +++ /dev/null @@ -1,8 +0,0 @@ -using SecTester.Core.Bus; - -namespace SecTester.Repeater.Bus; - -public interface IRepeaterEventBusFactory -{ - IEventBus Create(string repeaterId); -} diff --git a/src/SecTester.Repeater/Bus/ISocketIoClient.cs b/src/SecTester.Repeater/Bus/ISocketIoClient.cs new file mode 100644 index 0000000..fce7b13 --- /dev/null +++ b/src/SecTester.Repeater/Bus/ISocketIoClient.cs @@ -0,0 +1,14 @@ +using System; +using System.Threading.Tasks; + +namespace SecTester.Repeater.Bus; + +public interface ISocketIoClient : IDisposable +{ + public bool Connected { get; } + public Task Connect(); + public Task Disconnect(); + public void On(string eventName, Action callback); + public void Off(string eventName); + public Task EmitAsync(string eventName, params object[] data); +} diff --git a/src/SecTester.Repeater/Bus/ISocketIoResponse.cs b/src/SecTester.Repeater/Bus/ISocketIoResponse.cs new file mode 100644 index 0000000..a37489c --- /dev/null +++ b/src/SecTester.Repeater/Bus/ISocketIoResponse.cs @@ -0,0 +1,11 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace SecTester.Repeater.Bus; + +public interface ISocketIoResponse +{ + public T GetValue(int i = 0); + public Task CallbackAsync(params object[] data); + public Task CallbackAsync(CancellationToken cancellationToken, params object[] data); +} diff --git a/src/SecTester.Repeater/Bus/RequestExecutingEvent.cs b/src/SecTester.Repeater/Bus/IncomingRequest.cs similarity index 67% rename from src/SecTester.Repeater/Bus/RequestExecutingEvent.cs rename to src/SecTester.Repeater/Bus/IncomingRequest.cs index 73855e4..4a6dd2f 100644 --- a/src/SecTester.Repeater/Bus/RequestExecutingEvent.cs +++ b/src/SecTester.Repeater/Bus/IncomingRequest.cs @@ -1,19 +1,14 @@ using System; using System.Collections.Generic; using System.Net.Http; -using System.Text.Json.Serialization; -using System.Text.RegularExpressions; using SecTester.Core.Bus; using SecTester.Repeater.Runners; namespace SecTester.Repeater.Bus; -[MessageType(name: "ExecuteScript")] -public record RequestExecutingEvent(Uri Url) : Event, IRequest +public record IncomingRequest(Uri Url) : Event, IRequest { public string? Body { get; init; } - [JsonPropertyName("correlation_id_regex")] - public Regex? CorrelationIdRegex { get; init; } public HttpMethod Method { get; init; } = HttpMethod.Get; public Protocol Protocol { get; init; } = Protocol.Http; public Uri Url { get; init; } = Url ?? throw new ArgumentNullException(nameof(Url)); diff --git a/src/SecTester.Repeater/Bus/RequestExecutingResult.cs b/src/SecTester.Repeater/Bus/OutgoingResponse.cs similarity index 62% rename from src/SecTester.Repeater/Bus/RequestExecutingResult.cs rename to src/SecTester.Repeater/Bus/OutgoingResponse.cs index 4f72b38..36a01d1 100644 --- a/src/SecTester.Repeater/Bus/RequestExecutingResult.cs +++ b/src/SecTester.Repeater/Bus/OutgoingResponse.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; -using System.Text.Json.Serialization; using SecTester.Repeater.Runners; namespace SecTester.Repeater.Bus; -public record RequestExecutingResult : IResponse +public record OutgoingResponse : IResponse { - [JsonPropertyName("status_code")] public int? StatusCode { get; init; } + public int? StatusCode { get; init; } public string? Body { get; init; } public string? Message { get; init; } - [JsonPropertyName("error_code")] public string? ErrorCode { get; init; } + public string? ErrorCode { get; init; } public Protocol Protocol { get; init; } = Protocol.Http; diff --git a/src/SecTester.Repeater/Bus/RegisterRepeaterCommand.cs b/src/SecTester.Repeater/Bus/RegisterRepeaterCommand.cs deleted file mode 100644 index ad3a94a..0000000 --- a/src/SecTester.Repeater/Bus/RegisterRepeaterCommand.cs +++ /dev/null @@ -1,6 +0,0 @@ -using SecTester.Core.Bus; - -namespace SecTester.Repeater.Bus; - -[MessageType(name: "RepeaterRegistering")] -public record RegisterRepeaterCommand(string Version, string RepeaterId) : Command; diff --git a/src/SecTester.Repeater/Bus/RegisterRepeaterPayload.cs b/src/SecTester.Repeater/Bus/RegisterRepeaterPayload.cs deleted file mode 100644 index 38c27ad..0000000 --- a/src/SecTester.Repeater/Bus/RegisterRepeaterPayload.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace SecTester.Repeater.Bus; - -public record RegisterRepeaterPayload(string? Version = default, RepeaterRegisteringError Error = RepeaterRegisteringError.None); diff --git a/src/SecTester.Repeater/Bus/RegisterRepeaterResult.cs b/src/SecTester.Repeater/Bus/RegisterRepeaterResult.cs deleted file mode 100644 index 82b182d..0000000 --- a/src/SecTester.Repeater/Bus/RegisterRepeaterResult.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace SecTester.Repeater.Bus; - -public record RegisterRepeaterResult(RegisterRepeaterPayload Payload); diff --git a/src/SecTester.Repeater/Bus/RepeaterRegisteringError.cs b/src/SecTester.Repeater/Bus/RepeaterRegisteringError.cs deleted file mode 100644 index 8aa6a8f..0000000 --- a/src/SecTester.Repeater/Bus/RepeaterRegisteringError.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace SecTester.Repeater.Bus; - -public enum RepeaterRegisteringError -{ - NotActive, - Busy, - RequiresToBeUpdated, - NotFound, - None -} diff --git a/src/SecTester.Repeater/Bus/RepeaterStatusEvent.cs b/src/SecTester.Repeater/Bus/RepeaterStatusEvent.cs deleted file mode 100644 index d8bc9ee..0000000 --- a/src/SecTester.Repeater/Bus/RepeaterStatusEvent.cs +++ /dev/null @@ -1,6 +0,0 @@ -using SecTester.Core.Bus; - -namespace SecTester.Repeater.Bus; - -[MessageType(name: "RepeaterStatusUpdated")] -public record RepeaterStatusEvent(string RepeaterId, RepeaterStatus Status) : Event; diff --git a/src/SecTester.Repeater/Bus/RequestExecutingEventListener.cs b/src/SecTester.Repeater/Bus/RequestExecutingEventListener.cs deleted file mode 100644 index 19d1cf3..0000000 --- a/src/SecTester.Repeater/Bus/RequestExecutingEventListener.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Threading.Tasks; -using SecTester.Core.Bus; -using SecTester.Repeater.Runners; - -namespace SecTester.Repeater.Bus; - -public delegate IRequestRunner? RequestRunnerResolver(Protocol key); - -public class RequestExecutingEventListener : IEventListener -{ - private readonly RequestRunnerResolver _requestRunnersAccessor; - - public RequestExecutingEventListener(RequestRunnerResolver requestRunnersAccessor) - { - _requestRunnersAccessor = requestRunnersAccessor; - } - - public async Task Handle(RequestExecutingEvent message) - { - var runner = _requestRunnersAccessor(message.Protocol); - - if (runner == null) - { - throw new InvalidOperationException($"Unsupported protocol {message.Protocol}"); - } - - return (RequestExecutingResult)(await runner.Run(message).ConfigureAwait(false)); - } -} diff --git a/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs b/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs new file mode 100644 index 0000000..8cc3f64 --- /dev/null +++ b/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading.Tasks; + +namespace SecTester.Repeater.Bus; + +public class SocketIoClientWrapper : ISocketIoClient +{ + private readonly SocketIOClient.SocketIO _socketIo; + + public SocketIoClientWrapper(SocketIOClient.SocketIO socketIo) => _socketIo = socketIo ?? throw new ArgumentNullException(nameof(socketIo)); + + public void Dispose() + { + _socketIo.Dispose(); + GC.SuppressFinalize(this); + } + + public bool Connected => _socketIo.Connected; + + public Task Connect() => _socketIo.ConnectAsync(); + + public Task Disconnect() => _socketIo.DisconnectAsync(); + + public void On(string eventName, Action callback) => _socketIo.On(eventName, x => callback(x as ISocketIoResponse)); + + public void Off(string eventName) => _socketIo.Off(eventName); + + public Task EmitAsync(string eventName, params object[] data) => _socketIo.EmitAsync(eventName, data); +} diff --git a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs new file mode 100644 index 0000000..ef708df --- /dev/null +++ b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs @@ -0,0 +1,134 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using System.Timers; +using Microsoft.Extensions.Logging; +using SecTester.Core.Utils; + +namespace SecTester.Repeater.Bus; + +public class SocketIoRepeaterBus : IRepeaterBus +{ + private static readonly TimeSpan PingInterval = TimeSpan.FromSeconds(10); + + private readonly ITimerProvider _heartbeat; + private readonly ISocketIoClient _client; + private readonly ILogger _logger; + private readonly Uri _url; + + public SocketIoRepeaterBus(Uri url, ISocketIoClient client, ITimerProvider heartbeat, ILogger logger) + { + _url = url ?? throw new ArgumentNullException(nameof(url)); + _client = client ?? throw new ArgumentNullException(nameof(client)); + _heartbeat = heartbeat ?? throw new ArgumentNullException(nameof(heartbeat)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + internal record RepeaterVersion(string Version); + internal record RepeaterError(string Message); + internal record RepeaterInfo(string RepeaterId); + + public event Func>? RequestReceived; + public event Action? ErrorOccurred; + public event Action? UpgradeAvailable; + + public async Task Connect() + { + if (_client is not { Connected: true }) + { + DelegateEvents(); + + await _client.Connect().ConfigureAwait(false); + + await SchedulePing().ConfigureAwait(false); + + _logger.LogDebug("Repeater connected to {Url}", _url); + } + } + + private void DelegateEvents() + { + _client.On("error", response => + { + var err = response.GetValue(); + ErrorOccurred?.Invoke(new(err.Message)); + }); + + _client.On("update-available", response => + { + var version = response.GetValue(); + UpgradeAvailable?.Invoke(new(version.Version)); + }); + + _client.On("request", async response => + { + if (RequestReceived == null) + { + return; + } + + var request = response.GetValue(); + var result = await RequestReceived.Invoke(request).ConfigureAwait(false); + await response.CallbackAsync(result).ConfigureAwait(false); + }); + } + + public async ValueTask DisposeAsync() + { + if (_client is { Connected: true }) + { + _heartbeat.Elapsed -= Ping; + _heartbeat.Stop(); + await _client.Disconnect().ConfigureAwait(false); + _logger.LogDebug("Repeater disconnected from {Url}", _url); + } + + _client.Dispose(); + + RequestReceived = null; + ErrorOccurred = null; + UpgradeAvailable = null; + + GC.SuppressFinalize(this); + } + + public async Task Deploy(string repeaterId, Runtime? runtime = null, CancellationToken? cancellationToken = null) + { + try + { + var tcs = new TaskCompletionSource(); + + _client.On("deployed", response => tcs.TrySetResult(response.GetValue())); + + await _client.EmitAsync("deploy", new RepeaterInfo(repeaterId), runtime).ConfigureAwait(false); + + using var _ = cancellationToken?.Register(() => tcs.TrySetCanceled()); + + var result = await tcs.Task.ConfigureAwait(false); + + _logger.LogDebug("Repeater ({RepeaterId}) deployed", result?.RepeaterId); + } + finally + { + _client.Off("deployed"); + } + } + + private async Task SchedulePing() + { + await Ping().ConfigureAwait(false); + _heartbeat.Interval = PingInterval.TotalMilliseconds; + _heartbeat.Elapsed += Ping; + _heartbeat.Start(); + } + + private async void Ping(object sender, ElapsedEventArgs args) + { + await Ping().ConfigureAwait(false); + } + + private async Task Ping() + { + await _client.EmitAsync("ping").ConfigureAwait(false); + } +} diff --git a/src/SecTester.Repeater/DefaultRepeaterFactory.cs b/src/SecTester.Repeater/DefaultRepeaterFactory.cs index 6ea5264..0a1a4a5 100644 --- a/src/SecTester.Repeater/DefaultRepeaterFactory.cs +++ b/src/SecTester.Repeater/DefaultRepeaterFactory.cs @@ -1,10 +1,8 @@ using System; using System.Threading.Tasks; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using SecTester.Core; using SecTester.Core.Logger; -using SecTester.Core.Utils; using SecTester.Repeater.Api; using SecTester.Repeater.Bus; @@ -13,33 +11,31 @@ namespace SecTester.Repeater; public class DefaultRepeaterFactory : IRepeaterFactory { private readonly Configuration _configuration; - private readonly IRepeaterEventBusFactory _eventBusFactory; + private readonly IRepeaterBusFactory _busFactory; private readonly IRepeaters _repeaters; - private readonly IServiceScopeFactory _scopeFactory; private readonly ILoggerFactory _loggerFactory; private readonly IAnsiCodeColorizer _ansiCodeColorizer; + private readonly RequestRunnerResolver _requestRunnerResolver; - public DefaultRepeaterFactory(IServiceScopeFactory scopeFactory, IRepeaters repeaters, IRepeaterEventBusFactory eventBusFactory, Configuration configuration, ILoggerFactory loggerFactory, IAnsiCodeColorizer ansiCodeColorizer) + public DefaultRepeaterFactory(IRepeaters repeaters, IRepeaterBusFactory busFactory, Configuration configuration, ILoggerFactory loggerFactory, IAnsiCodeColorizer ansiCodeColorizer, RequestRunnerResolver requestRunnerResolver) { - _scopeFactory = scopeFactory ?? throw new ArgumentNullException(nameof(scopeFactory)); _repeaters = repeaters ?? throw new ArgumentNullException(nameof(repeaters)); _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); - _eventBusFactory = eventBusFactory ?? throw new ArgumentNullException(nameof(eventBusFactory)); + _busFactory = busFactory ?? throw new ArgumentNullException(nameof(busFactory)); _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _ansiCodeColorizer = ansiCodeColorizer ?? throw new ArgumentNullException(nameof(ansiCodeColorizer)); + _requestRunnerResolver = requestRunnerResolver ?? throw new ArgumentNullException(nameof(requestRunnerResolver)); } public async Task CreateRepeater(RepeaterOptions? options = default) { options ??= new RepeaterOptions(); - string repeaterId = await _repeaters.CreateRepeater($"{options.NamePrefix}-{Guid.NewGuid()}", options.Description).ConfigureAwait(false); - var eventBus = _eventBusFactory.Create(repeaterId); + var repeaterId = await _repeaters.CreateRepeater($"{options.NamePrefix}-{Guid.NewGuid()}", options.Description).ConfigureAwait(false); - var scope = _scopeFactory.CreateAsyncScope(); - var timerProvider = scope.ServiceProvider.GetRequiredService(); + var bus = _busFactory.Create(repeaterId); var version = new Version(_configuration.RepeaterVersion); - return new Repeater(repeaterId, eventBus, version, _loggerFactory.CreateLogger(), timerProvider, _ansiCodeColorizer); + return new Repeater(repeaterId, bus, version, _loggerFactory.CreateLogger(), _ansiCodeColorizer, _requestRunnerResolver); } } diff --git a/src/SecTester.Repeater/Extensions/ServiceCollectionExtensions.cs b/src/SecTester.Repeater/Extensions/ServiceCollectionExtensions.cs index da2383e..eaa0dd6 100644 --- a/src/SecTester.Repeater/Extensions/ServiceCollectionExtensions.cs +++ b/src/SecTester.Repeater/Extensions/ServiceCollectionExtensions.cs @@ -21,8 +21,7 @@ public static IServiceCollection AddSecTesterRepeater(this IServiceCollection co { return collection .AddSingleton(options) - .AddSingleton() - .AddScoped() + .AddSingleton() .AddScoped() .AddScoped() .AddScoped() diff --git a/src/SecTester.Repeater/Repeater.cs b/src/SecTester.Repeater/Repeater.cs index 2ff8040..da13af3 100644 --- a/src/SecTester.Repeater/Repeater.cs +++ b/src/SecTester.Repeater/Repeater.cs @@ -2,33 +2,33 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; -using SecTester.Core.Bus; -using SecTester.Core.Exceptions; using SecTester.Core.Extensions; using SecTester.Core.Logger; -using SecTester.Core.Utils; using SecTester.Repeater.Bus; +using SecTester.Repeater.Runners; namespace SecTester.Repeater; +public delegate IRequestRunner? RequestRunnerResolver(Protocol key); + + public class Repeater : IRepeater { - private static readonly TimeSpan DefaultPingInterval = TimeSpan.FromSeconds(10); - private readonly IEventBus _eventBus; - private readonly ITimerProvider _heartbeat; + private readonly IRepeaterBus _bus; private readonly ILogger _logger; private readonly SemaphoreSlim _semaphore = new(1, 1); private readonly Version _version; private readonly IAnsiCodeColorizer _ansiCodeColorizer; + private readonly RequestRunnerResolver _requestRunnersAccessor; - public Repeater(string repeaterId, IEventBus eventBus, Version version, ILogger logger, ITimerProvider heartbeat, - IAnsiCodeColorizer ansiCodeColorizer) + public Repeater(string repeaterId, IRepeaterBus bus, Version version, ILogger logger, + IAnsiCodeColorizer ansiCodeColorizer, RequestRunnerResolver requestRunnersAccessor) { RepeaterId = repeaterId ?? throw new ArgumentNullException(nameof(repeaterId)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _version = version ?? throw new ArgumentNullException(nameof(version)); - _eventBus = eventBus ?? throw new ArgumentNullException(nameof(eventBus)); - _heartbeat = heartbeat ?? throw new ArgumentNullException(nameof(heartbeat)); + _bus = bus ?? throw new ArgumentNullException(nameof(bus)); + _requestRunnersAccessor = requestRunnersAccessor ?? throw new ArgumentNullException(nameof(requestRunnersAccessor)); _ansiCodeColorizer = ansiCodeColorizer ?? throw new ArgumentNullException(nameof(ansiCodeColorizer)); } @@ -57,9 +57,10 @@ public async Task Start(CancellationToken cancellationToken = default) Status = RunningStatus.Starting; - await Register().ConfigureAwait(false); SubscribeToEvents(); - await SchedulePing().ConfigureAwait(false); + + await _bus.Connect().ConfigureAwait(false); + await _bus.Deploy(RepeaterId, new Runtime(_version.ToString()), cancellationToken).ConfigureAwait(false); Status = RunningStatus.Running; } @@ -70,44 +71,14 @@ public async Task Start(CancellationToken cancellationToken = default) } } - private void SubscribeToEvents() - { - _eventBus.Register(); - } - - private async Task SchedulePing() - { - await SendStatus(RepeaterStatus.Connected).ConfigureAwait(false); - _heartbeat.Interval = DefaultPingInterval.TotalMilliseconds; - _heartbeat.Elapsed += async (_, _) => await SendStatus(RepeaterStatus.Connected).ConfigureAwait(false); - _heartbeat.Start(); - } - - private async Task SendStatus(RepeaterStatus status) - { - var @event = new RepeaterStatusEvent(RepeaterId, status); - await _eventBus.Publish(@event).ConfigureAwait(false); - } - - private async Task Register() - { - var command = new RegisterRepeaterCommand(_version.ToString(), RepeaterId); - var res = await _eventBus.Execute(command).ConfigureAwait(false); - - if (res == null) - { - throw new SecTesterException("Error registering repeater."); - } - - EnsureRegistrationStatus(res.Payload); - } - public async Task Stop(CancellationToken cancellationToken = default) { using var _ = await _semaphore.LockAsync(cancellationToken).ConfigureAwait(false); try { + UnsubscribeFromEvents(); + if (Status != RunningStatus.Running) { return; @@ -115,9 +86,7 @@ public async Task Stop(CancellationToken cancellationToken = default) Status = RunningStatus.Off; - _heartbeat.Stop(); - await SendStatus(RepeaterStatus.Disconnected).ConfigureAwait(false); - _eventBus.Dispose(); + await _bus.DisposeAsync().ConfigureAwait(false); } catch { @@ -125,33 +94,45 @@ public async Task Stop(CancellationToken cancellationToken = default) } } - private void EnsureRegistrationStatus(RegisterRepeaterPayload result) + public async Task HandleIncomingRequest(IncomingRequest message) { - if (result.Error != RepeaterRegisteringError.None) + var runner = _requestRunnersAccessor(message.Protocol); + + if (runner == null) { - HandleRegisterError(result.Error); + var msg = $"Unsupported protocol {message.Protocol}"; + _logger.LogError(msg); + return new OutgoingResponse { Message = msg }; } - else + + return (OutgoingResponse)await runner.Run(message).ConfigureAwait(false); + } + + private void SubscribeToEvents() + { + _bus.RequestReceived += HandleIncomingRequest; + _bus.ErrorOccurred += HandleRegisterError; + _bus.UpgradeAvailable += HandleUpgradeAvailable; + } + + private void UnsubscribeFromEvents() + { + _bus.RequestReceived -= HandleIncomingRequest; + _bus.ErrorOccurred -= HandleRegisterError; + _bus.UpgradeAvailable -= HandleUpgradeAvailable; + } + + private void HandleUpgradeAvailable(Version version) + { + if (version.CompareTo(_version) != 0) { - if (new Version(result.Version!).CompareTo(_version) != 0) - { - _logger.LogWarning("{Prefix}: A new Repeater version ({Version}) is available, please update SecTester", - _ansiCodeColorizer.Colorize(AnsiCodeColor.Yellow, "(!) IMPORTANT"), result.Version); - } + _logger.LogWarning("{Prefix}: A new Repeater version ({Version}) is available, please update SecTester", + _ansiCodeColorizer.Colorize(AnsiCodeColor.Yellow, "(!) IMPORTANT"), version); } } - private void HandleRegisterError(RepeaterRegisteringError error) + private void HandleRegisterError(Exception error) { - throw error switch - { - RepeaterRegisteringError.NotActive => new SecTesterException("Access Refused: The current Repeater is not active."), - RepeaterRegisteringError.NotFound => new SecTesterException("Unauthorized access. Please check your credentials."), - RepeaterRegisteringError.Busy => new SecTesterException( - $"Access Refused: There is an already running Repeater with ID {RepeaterId}"), - RepeaterRegisteringError.RequiresToBeUpdated => new SecTesterException( - $"{_ansiCodeColorizer.Colorize(AnsiCodeColor.Red, "(!) CRITICAL")}: The current running version is no longer supported, please update SecTester."), - _ => new ArgumentOutOfRangeException(nameof(error), error, "Something went wrong. Unknown error.") - }; + _logger.LogError("{Prefix}: {Message}", _ansiCodeColorizer.Colorize(AnsiCodeColor.Red, "(!) IMPORTANT"), error.Message); } } diff --git a/src/SecTester.Repeater/Runners/HttpRequestRunner.cs b/src/SecTester.Repeater/Runners/HttpRequestRunner.cs index 7945067..366b00c 100644 --- a/src/SecTester.Repeater/Runners/HttpRequestRunner.cs +++ b/src/SecTester.Repeater/Runners/HttpRequestRunner.cs @@ -48,7 +48,7 @@ public async Task Run(IRequest request) } catch (Exception err) { - return new RequestExecutingResult + return new OutgoingResponse { Message = err.Message, // TODO: use native errno codes instead @@ -57,7 +57,7 @@ public async Task Run(IRequest request) } } - private async Task CreateRequestExecutingResult(HttpResponseMessage response) + private async Task CreateRequestExecutingResult(HttpResponseMessage response) { var body = await TruncateResponseBody(response).ConfigureAwait(false); var headers = AggregateHeaders(response); @@ -71,7 +71,7 @@ private async Task CreateRequestExecutingResult(HttpResp headers.Replace(contentLength, x => x.Key.Equals(ContentLengthFieldName, StringComparison.OrdinalIgnoreCase)); } - return new RequestExecutingResult + return new OutgoingResponse { Headers = headers, StatusCode = (int)response.StatusCode, diff --git a/src/SecTester.Repeater/Runtime.cs b/src/SecTester.Repeater/Runtime.cs new file mode 100644 index 0000000..4841f6a --- /dev/null +++ b/src/SecTester.Repeater/Runtime.cs @@ -0,0 +1,12 @@ +namespace SecTester.Repeater; + +public record Runtime(string Version) +{ + public bool? ScriptsLoaded { get; init; } + public string? Ci { get; init; } + public string? Os { get; init; } + public string? Arch { get; init; } + public bool? Docker { get; init; } + public string? Distribution { get; init; } + public string? NetVersion { get; init; } +} diff --git a/src/SecTester.Repeater/SecTester.Repeater.csproj b/src/SecTester.Repeater/SecTester.Repeater.csproj index e40d8f9..6f98a64 100644 --- a/src/SecTester.Repeater/SecTester.Repeater.csproj +++ b/src/SecTester.Repeater/SecTester.Repeater.csproj @@ -11,11 +11,12 @@ + + - diff --git a/src/SecTester.Repeater/packages.lock.json b/src/SecTester.Repeater/packages.lock.json index a956db0..f4301ee 100644 --- a/src/SecTester.Repeater/packages.lock.json +++ b/src/SecTester.Repeater/packages.lock.json @@ -21,6 +21,28 @@ "Microsoft.NETCore.Platforms": "1.1.0" } }, + "SocketIO.Serializer.MessagePack": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "lOAZs6AUCDhfMFa4Vu40LeeK/9fP+iMUerI3qzmToDaSa+A2/YQ7D0nvYa1atwTvzvhDZklBKNV5dIzQWWwPJg==", + "dependencies": { + "MessagePack": "2.5.124", + "SocketIO.Core": "3.1.1", + "SocketIO.Serializer.Core": "3.1.1" + } + }, + "SocketIOClient": { + "type": "Direct", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "g8Lauia1Cj5DBXeC9j6vDSJeQEboGmXsnduW06VOBMe+UgqCrenxszYXJg19rAjwO10XRWQorOuU1XNMfYo8Xg==", + "dependencies": { + "SocketIO.Serializer.Core": "3.1.1", + "SocketIO.Serializer.SystemTextJson": "3.1.1", + "System.Collections": "4.3.0" + } + }, "System.Linq.Async": { "type": "Direct", "requested": "[6.0.1, )", @@ -39,6 +61,26 @@ "System.Text.Json": "6.0.0" } }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.5.124", + "contentHash": "jxJPIkCnKwZcg9qtN1WoR99nlcJl/y/HYOTQLxnyXzR4iE5xhZviCPSKXLe08fcF9Tk4hP7mm+mVVdyUfh2ALw==", + "dependencies": { + "MessagePack.Annotations": "2.5.124", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.NET.StringTools": "17.6.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Emit": "4.7.0", + "System.Reflection.Emit.Lightweight": "4.7.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.5.124", + "contentHash": "YTWbSjVlMhe6WaEQ953rrNagRzQxDrp9mCB3W2Yr1TOITlaEv/ZMFvqZSabSs09Gy86Kq7BmvcxKTodv/YNDQA==" + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "7.0.0", @@ -177,11 +219,25 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.6.3", + "contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, "RabbitMQ.Client": { "type": "Transitive", "resolved": "6.4.0", @@ -191,11 +247,53 @@ "System.Threading.Channels": "4.7.1" } }, + "SocketIO.Core": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "L5GSyODDjlub5YdDxfsRtA9uxBNw1Hs/egEMhdOGFyD0c63VmvWQ1A4Gfc4GYKYInz1muP4C8QNblXErnUYDqA==" + }, + "SocketIO.Serializer.Core": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "aFanlt2GOGEy7GTGLyh8f8SwhtmQwoPOHTAYCQ0brjg05Nb3F38sk4hYogpII5imyZ7w0spqwNHwpl7FZ49HOA==", + "dependencies": { + "SocketIO.Core": "3.1.1" + } + }, + "SocketIO.Serializer.SystemTextJson": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "1PKQ+hOAJ4l3ZXlrVgiB3uNY3kLhjzYjnxk/97V6npJrNmhq09Di2EQSpGweQMu6KPbOxjZ7eYVARkGTWIUsjg==", + "dependencies": { + "SocketIO.Core": "3.1.1", + "SocketIO.Serializer.Core": "3.1.1", + "System.Text.Json": "7.0.3" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.ComponentModel.Annotations": { "type": "Transitive", "resolved": "5.0.0", @@ -212,8 +310,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { "System.Buffers": "4.5.1", "System.Numerics.Vectors": "4.4.0", @@ -225,6 +323,14 @@ "resolved": "4.5.0", "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==", + "dependencies": { + "System.Reflection.Emit.ILGeneration": "4.7.0" + } + }, "System.Reflection.Emit.ILGeneration": { "type": "Transitive", "resolved": "4.7.0", @@ -238,6 +344,15 @@ "System.Reflection.Emit.ILGeneration": "4.7.0" } }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -245,25 +360,25 @@ }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "resolved": "7.0.0", + "contentHash": "OP6umVGxc0Z0MvZQBVigj4/U31Pw72ITihDWP9WiWDm+q5aoe0GaJivsfYGq53o6dxH7DcXWiCTl7+0o2CGdmg==", "dependencies": { "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "resolved": "7.0.3", + "contentHash": "AyjhwXN1zTFeIibHimfJn6eAsZ7rTBib79JQpzg8WAuR/HKDu9JGNHTuu3nbbXQ/bgI+U4z6HtZmCHNXB1QXrQ==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", + "System.Memory": "4.5.5", "System.Numerics.Vectors": "4.5.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", + "System.Text.Encodings.Web": "7.0.0", "System.Threading.Tasks.Extensions": "4.5.4" } }, @@ -300,7 +415,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.26.0, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } diff --git a/src/SecTester.Reporter/packages.lock.json b/src/SecTester.Reporter/packages.lock.json index abf7a2f..544e7cd 100644 --- a/src/SecTester.Reporter/packages.lock.json +++ b/src/SecTester.Reporter/packages.lock.json @@ -299,7 +299,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.29.2, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } @@ -317,8 +317,8 @@ "dependencies": { "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.29.2, )", - "SecTester.Core": "[0.29.2, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", "System.Linq.Async": "[6.0.1, )", "System.Text.Json": "[6.0.0, )" } diff --git a/src/SecTester.Runner/packages.lock.json b/src/SecTester.Runner/packages.lock.json index 843f681..0213610 100644 --- a/src/SecTester.Runner/packages.lock.json +++ b/src/SecTester.Runner/packages.lock.json @@ -30,6 +30,26 @@ "System.Text.Json": "6.0.0" } }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.5.124", + "contentHash": "jxJPIkCnKwZcg9qtN1WoR99nlcJl/y/HYOTQLxnyXzR4iE5xhZviCPSKXLe08fcF9Tk4hP7mm+mVVdyUfh2ALw==", + "dependencies": { + "MessagePack.Annotations": "2.5.124", + "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.NET.StringTools": "17.6.3", + "System.Collections.Immutable": "6.0.0", + "System.Reflection.Emit": "4.7.0", + "System.Reflection.Emit.Lightweight": "4.7.0", + "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.5.124", + "contentHash": "YTWbSjVlMhe6WaEQ953rrNagRzQxDrp9mCB3W2Yr1TOITlaEv/ZMFvqZSabSs09Gy86Kq7BmvcxKTodv/YNDQA==" + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "7.0.0", @@ -168,11 +188,25 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.6.3", + "contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, "RabbitMQ.Client": { "type": "Transitive", "resolved": "6.4.0", @@ -182,11 +216,73 @@ "System.Threading.Channels": "4.7.1" } }, + "SocketIO.Core": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "L5GSyODDjlub5YdDxfsRtA9uxBNw1Hs/egEMhdOGFyD0c63VmvWQ1A4Gfc4GYKYInz1muP4C8QNblXErnUYDqA==" + }, + "SocketIO.Serializer.Core": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "aFanlt2GOGEy7GTGLyh8f8SwhtmQwoPOHTAYCQ0brjg05Nb3F38sk4hYogpII5imyZ7w0spqwNHwpl7FZ49HOA==", + "dependencies": { + "SocketIO.Core": "3.1.1" + } + }, + "SocketIO.Serializer.MessagePack": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "lOAZs6AUCDhfMFa4Vu40LeeK/9fP+iMUerI3qzmToDaSa+A2/YQ7D0nvYa1atwTvzvhDZklBKNV5dIzQWWwPJg==", + "dependencies": { + "MessagePack": "2.5.124", + "SocketIO.Core": "3.1.1", + "SocketIO.Serializer.Core": "3.1.1" + } + }, + "SocketIO.Serializer.SystemTextJson": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "1PKQ+hOAJ4l3ZXlrVgiB3uNY3kLhjzYjnxk/97V6npJrNmhq09Di2EQSpGweQMu6KPbOxjZ7eYVARkGTWIUsjg==", + "dependencies": { + "SocketIO.Core": "3.1.1", + "SocketIO.Serializer.Core": "3.1.1", + "System.Text.Json": "7.0.3" + } + }, + "SocketIOClient": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "g8Lauia1Cj5DBXeC9j6vDSJeQEboGmXsnduW06VOBMe+UgqCrenxszYXJg19rAjwO10XRWQorOuU1XNMfYo8Xg==", + "dependencies": { + "SocketIO.Serializer.Core": "3.1.1", + "SocketIO.Serializer.SystemTextJson": "3.1.1", + "System.Collections": "4.3.0" + } + }, "System.Buffers": { "type": "Transitive", "resolved": "4.5.1", "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "l4zZJ1WU2hqpQQHXz1rvC3etVZN+2DLmQMO79FhOTZHMn8tDRr+WU287sbomD0BETlmKDn0ygUgVy9k5xkkJdA==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "System.ComponentModel.Annotations": { "type": "Transitive", "resolved": "5.0.0", @@ -211,8 +307,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", "dependencies": { "System.Buffers": "4.5.1", "System.Numerics.Vectors": "4.4.0", @@ -224,6 +320,14 @@ "resolved": "4.5.0", "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "VR4kk8XLKebQ4MZuKuIni/7oh+QGFmZW3qORd1GvBq/8026OpW501SzT/oypwiQl4TvT8ErnReh/NzY9u+C6wQ==", + "dependencies": { + "System.Reflection.Emit.ILGeneration": "4.7.0" + } + }, "System.Reflection.Emit.ILGeneration": { "type": "Transitive", "resolved": "4.7.0", @@ -237,6 +341,15 @@ "System.Reflection.Emit.ILGeneration": "4.7.0" } }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, "System.Runtime.CompilerServices.Unsafe": { "type": "Transitive", "resolved": "6.0.0", @@ -244,25 +357,25 @@ }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "resolved": "7.0.0", + "contentHash": "OP6umVGxc0Z0MvZQBVigj4/U31Pw72ITihDWP9WiWDm+q5aoe0GaJivsfYGq53o6dxH7DcXWiCTl7+0o2CGdmg==", "dependencies": { "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", + "System.Memory": "4.5.5", "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "resolved": "7.0.3", + "contentHash": "AyjhwXN1zTFeIibHimfJn6eAsZ7rTBib79JQpzg8WAuR/HKDu9JGNHTuu3nbbXQ/bgI+U4z6HtZmCHNXB1QXrQ==", "dependencies": { - "Microsoft.Bcl.AsyncInterfaces": "6.0.0", + "Microsoft.Bcl.AsyncInterfaces": "7.0.0", "System.Buffers": "4.5.1", - "System.Memory": "4.5.4", + "System.Memory": "4.5.5", "System.Numerics.Vectors": "4.5.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0", + "System.Text.Encodings.Web": "7.0.0", "System.Threading.Tasks.Extensions": "4.5.4" } }, @@ -299,7 +412,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.31.0, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } @@ -316,8 +429,10 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.31.0, )", - "SecTester.Core": "[0.31.0, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", + "SocketIO.Serializer.MessagePack": "[3.1.1, )", + "SocketIOClient": "[3.1.1, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -325,7 +440,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Scan": "[0.31.0, )" + "SecTester.Scan": "[0.40.0, )" } }, "sectester.scan": { @@ -333,12 +448,12 @@ "dependencies": { "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.31.0, )", - "SecTester.Core": "[0.31.0, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", "System.Linq.Async": "[6.0.1, )", "System.Text.Json": "[6.0.0, )" } } } } -} +} \ No newline at end of file diff --git a/src/SecTester.Scan/packages.lock.json b/src/SecTester.Scan/packages.lock.json index 4c0a3d7..15f9d14 100644 --- a/src/SecTester.Scan/packages.lock.json +++ b/src/SecTester.Scan/packages.lock.json @@ -302,7 +302,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.29.0, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } diff --git a/test/SecTester.Bus.Tests/packages.lock.json b/test/SecTester.Bus.Tests/packages.lock.json index 253a644..a2f68df 100644 --- a/test/SecTester.Bus.Tests/packages.lock.json +++ b/test/SecTester.Bus.Tests/packages.lock.json @@ -1343,7 +1343,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.29.0, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } @@ -1361,8 +1361,8 @@ "dependencies": { "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.29.0, )", - "SecTester.Core": "[0.29.0, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", "System.Linq.Async": "[6.0.1, )", "System.Text.Json": "[6.0.0, )" } diff --git a/test/SecTester.Repeater.Tests/Api/DefaultRepeatersTests.cs b/test/SecTester.Repeater.Tests/Api/DefaultRepeatersTests.cs index 3800dd6..3921232 100644 --- a/test/SecTester.Repeater.Tests/Api/DefaultRepeatersTests.cs +++ b/test/SecTester.Repeater.Tests/Api/DefaultRepeatersTests.cs @@ -23,7 +23,7 @@ public void Dispose() public async Task CreateRepeater_CreatesRepeater() { // arrange - _commandDispatcher.Execute(Arg.Any()).Returns(new RepeaterIdentity(Id, "foo")); + _commandDispatcher.Execute(Arg.Any()).Returns(new RepeaterIdentity(Id)); // act var result = await _sut.CreateRepeater("foo"); @@ -36,7 +36,7 @@ public async Task CreateRepeater_CreatesRepeater() public async Task CreateRepeater_ThrowsError() { // arrange - _commandDispatcher.Execute(Arg.Any()).Returns(new RepeaterIdentity("", "foo")); + _commandDispatcher.Execute(Arg.Any()).Returns(new RepeaterIdentity("")); // act var act = () => _sut.CreateRepeater("foo"); diff --git a/test/SecTester.Repeater.Tests/Bus/DefaultRepeaterBusFactoryTests.cs b/test/SecTester.Repeater.Tests/Bus/DefaultRepeaterBusFactoryTests.cs new file mode 100644 index 0000000..464bcda --- /dev/null +++ b/test/SecTester.Repeater.Tests/Bus/DefaultRepeaterBusFactoryTests.cs @@ -0,0 +1,57 @@ +namespace SecTester.Repeater.Tests.Bus; + +public class DefaultRepeaterBusFactoryTests : IDisposable +{ + private const string Id = "99138d92-69db-44cb-952a-1cd9ec031e20"; + private const string Hostname = "app.brightsec.com"; + private const string Token = "0zmcwpe.nexr.0vlon8mp7lvxzjuvgjy88olrhadhiukk"; + + private readonly ILoggerFactory _loggerFactory = Substitute.For(); + private readonly ITimerProvider _timerProvider = Substitute.For(); + private readonly IServiceScopeFactory _serviceScopeFactory = Substitute.For(); + + public DefaultRepeaterBusFactoryTests() + { + // ADHOC: since GetRequiredService is part of extension we should explicitly mock an instance method + _serviceScopeFactory.CreateAsyncScope().ServiceProvider.GetService(typeof(ITimerProvider)).Returns(_timerProvider); + } + + public void Dispose() + { + _timerProvider.ClearSubstitute(); + _serviceScopeFactory.ClearSubstitute(); + _loggerFactory.ClearSubstitute(); + GC.SuppressFinalize(this); + } + + [Fact] + public async Task Create_CreatesBus() + { + // arrange + Configuration config = new(Hostname, new Credentials(Token)); + DefaultRepeaterBusFactory sut = new(config, _loggerFactory, _serviceScopeFactory); + + // act + await using var bus = sut.Create(Id); + + // assert + bus.Should().BeAssignableTo(); + } + + [Fact] + public async Task Create_CredentialsNotDefined_ThrowsError() + { + // arrange + Configuration config = new(Hostname); + DefaultRepeaterBusFactory sut = new(config, _loggerFactory, _serviceScopeFactory); + + // act + var act = async () => + { + await using var _ = sut.Create(Id); + }; + + // assert + await act.Should().ThrowAsync(); + } +} diff --git a/test/SecTester.Repeater.Tests/Bus/DefaultRepeaterEventBusFactoryTests.cs b/test/SecTester.Repeater.Tests/Bus/DefaultRepeaterEventBusFactoryTests.cs deleted file mode 100644 index aecd664..0000000 --- a/test/SecTester.Repeater.Tests/Bus/DefaultRepeaterEventBusFactoryTests.cs +++ /dev/null @@ -1,53 +0,0 @@ -using SecTester.Bus.Dispatchers; - -namespace SecTester.Repeater.Tests.Bus; - -public class DefaultRepeaterEventBusFactoryTests : IDisposable -{ - private const string Id = "99138d92-69db-44cb-952a-1cd9ec031e20"; - private const string Hostname = "app.brightsec.com"; - private const string Token = "0zmcwpe.nexr.0vlon8mp7lvxzjuvgjy88olrhadhiukk"; - - private readonly IRmqEventBusFactory _eventBusFactory = Substitute.For(); - - public void Dispose() - { - _eventBusFactory.ClearSubstitute(); - GC.SuppressFinalize(this); - } - - [Fact] - public void Create_CreatesEventBus() - { - // arrange - Configuration config = new(Hostname, new Credentials(Token)); - DefaultRepeaterEventBusFactory sut = new(config, _eventBusFactory); - - // act - using var eventBus = sut.Create(Id); - - // assert - _eventBusFactory.Received() - .CreateEventBus(Arg.Is(x => - x.Username == "bot" && - x.Password == config.Credentials!.Token && - x.Url == config.Bus && - x.ClientQueue.Contains(Id) && - x.Exchange == "EventBus" && - x.AppQueue == "app")); - } - - [Fact] - public void Create_CredentialsNotDefined_ThrowsError() - { - // arrange - Configuration config = new(Hostname); - DefaultRepeaterEventBusFactory sut = new(config, _eventBusFactory); - - // act - var act = () => sut.Create(Id); - - // assert - act.Should().Throw(); - } -} diff --git a/test/SecTester.Repeater.Tests/Bus/RequestExecutingEventHandlerTests.cs b/test/SecTester.Repeater.Tests/Bus/RequestExecutingEventHandlerTests.cs deleted file mode 100644 index cc66427..0000000 --- a/test/SecTester.Repeater.Tests/Bus/RequestExecutingEventHandlerTests.cs +++ /dev/null @@ -1,58 +0,0 @@ -namespace SecTester.Repeater.Tests.Bus; - -public class RequestExecutingEventHandlerTests : IDisposable -{ - private readonly RequestRunnerResolver _resolver = Substitute.For(); - private readonly RequestExecutingEventListener _sut; - - public RequestExecutingEventHandlerTests() - { - _sut = new RequestExecutingEventListener(_resolver); - } - - public void Dispose() - { - _resolver.ClearSubstitute(); - GC.SuppressFinalize(this); - } - - [Fact] - public async Task Handle_RunRequestHavingCorrespondingRunner() - { - // arrange - var @event = new RequestExecutingEvent(new Uri("http://foo.bar")) - { - Protocol = Protocol.Http - }; - var reply = new RequestExecutingResult - { - Protocol = Protocol.Http, - StatusCode = 200, - Body = "text" - }; - _resolver(Protocol.Http)!.Run(@event).Returns(reply); - - // act - var result = await _sut.Handle(@event); - - // assert - result.Should().BeEquivalentTo(reply); - } - - [Fact] - public async Task Handle_NoRunnerFound_ThrowsError() - { - // arrange - var @event = new RequestExecutingEvent(new Uri("http://foo.bar")) - { - Protocol = Protocol.Http - }; - _resolver(Arg.Any()).ReturnsNull(); - - // act - var act = () => _sut.Handle(@event); - - // assert - await act.Should().ThrowAsync().WithMessage($"Unsupported protocol {Protocol.Http}"); - } -} diff --git a/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs b/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs new file mode 100644 index 0000000..7cffd0b --- /dev/null +++ b/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs @@ -0,0 +1,210 @@ +namespace SecTester.Repeater.Tests.Bus; + +public sealed class SocketIoRepeaterBusTests : IDisposable +{ + private static readonly string RepeaterId = "g5MvgM74sweGcK1U6hvs76"; + private static readonly string Url = new("http://example.com"); + + private readonly ISocketIoClient _client = Substitute.For(); + private readonly ITimerProvider _heartbeat = Substitute.For(); + private readonly ILogger _logger = Substitute.For>(); + private readonly ISocketIoResponse _socketIoResponse = Substitute.For(); + private readonly SocketIoRepeaterBus _sut; + + public SocketIoRepeaterBusTests() + { + _sut = new SocketIoRepeaterBus(new Uri(Url), _client, _heartbeat, _logger); + } + + public void Dispose() + { + _socketIoResponse.ClearSubstitute(); + _client.ClearSubstitute(); + _heartbeat.ClearSubstitute(); + _logger.ClearSubstitute(); + + GC.SuppressFinalize(this); + } + + [Fact] + public async Task RequestReceived_ExecutesHandler() + { + // arrange + var result = new OutgoingResponse + { + StatusCode = 204 + }; + _client.Connect().Returns(Task.CompletedTask); + _socketIoResponse.GetValue().Returns(new IncomingRequest(new(Url))); + _client.On("request", Arg.Invoke(_socketIoResponse)); + _sut.RequestReceived += _ => Task.FromResult(result); + + // act + await _sut.Connect(); + + // assert + await _socketIoResponse.Received().CallbackAsync(result); + } + + [Fact] + public async Task ErrorOccurred_ExecutesHandler() + { + // arrange + const string msg = "Something went wrong"; + Exception? result = null; + _client.Connect().Returns(Task.CompletedTask); + _socketIoResponse.GetValue().Returns(new SocketIoRepeaterBus.RepeaterError(msg)); + _client.On("error", Arg.Invoke(_socketIoResponse)); + _sut.ErrorOccurred += err => + { + result = err; + }; + + // act + await _sut.Connect(); + + // assert + result.Should().BeEquivalentTo(new { Message = msg }); + } + + [Fact] + public async Task UpgradeAvailable_ExecutesHandler() + { + // arrange + Version? result = null; + _client.Connect().Returns(Task.CompletedTask); + _socketIoResponse.GetValue().Returns(new SocketIoRepeaterBus.RepeaterVersion("1.1.1")); + _client.On("update-available", Arg.Invoke(_socketIoResponse)); + _sut.UpgradeAvailable += version => + { + result = version; + }; + + // act + await _sut.Connect(); + + // assert + result.Should().BeOfType(); + } + + [Fact] + public async Task Connect_Success() + { + // arrange + _client.Connect().Returns(Task.CompletedTask); + + // act + await _sut.Connect(); + + // assert + await _client.Received().Connect(); + } + + [Fact] + public async Task Connect_AlreadyConnected_DoNothing() + { + // arrange + _client.Connected.Returns(true); + + // act + await _sut.Connect(); + + // assert + await _client.DidNotReceive().Connect(); + } + + [Fact] + public async Task Connect_SchedulesPing() + { + // arrange + _client.Connect().Returns(Task.CompletedTask); + + // act + await _sut.Connect(); + + // assert + _heartbeat.Received().Elapsed += Arg.Any(); + _heartbeat.Received().Start(); + } + + [Fact] + public async Task Connect_ShouldSendPingMessage() + { + // arrange + var elapsedEventArgs = EventArgs.Empty as ElapsedEventArgs; + _client.Connect().Returns(Task.CompletedTask); + await _sut.Connect(); + + // act + _heartbeat.Elapsed += Raise.Event(new object(), elapsedEventArgs); + + // assert + _heartbeat.Interval.Should().BeGreaterOrEqualTo(10_000); + await _client.Received(2).EmitAsync("ping"); + } + + [Fact] + public async Task Deploy_Success() + { + // arrange + _client.On("deployed", Arg.Invoke(_socketIoResponse)); + + // act + await _sut.Deploy(RepeaterId); + + // assert + await _client.Received().EmitAsync("deploy", Arg.Is(x => x.RepeaterId.Equals(RepeaterId, StringComparison.OrdinalIgnoreCase)), Arg.Any()); + } + + [Fact] + public async Task Deploy_GivenCancellationToken_ThrowsError() + { + // arrange + using var cancellationTokenSource = new CancellationTokenSource(); + cancellationTokenSource.Cancel(); + + // act + var act = () => _sut.Deploy(RepeaterId, null, cancellationTokenSource.Token); + + // assert + await act.Should().ThrowAsync(); + } + + [Fact] + public async Task DisposeAsync_Success() + { + // arrange + _client.Connected.Returns(true); + + // act + await _sut.DisposeAsync(); + + // assert + await _client.Received().Disconnect(); + _client.Received().Dispose(); + } + + [Fact] + public async Task DisposeAsync_NotConnectedYet_Success() + { + // act + await _sut.DisposeAsync(); + + // assert + await _client.DidNotReceive().Disconnect(); + _client.Received().Dispose(); + } + + [Fact] + public async Task DisposeAsync_StopsPingMessages() + { + // arrange + _client.Connected.Returns(true); + + // act + await _sut.DisposeAsync(); + + // assert + _heartbeat.Received().Stop(); + } +} diff --git a/test/SecTester.Repeater.Tests/DefaultRepeaterFactoryTests.cs b/test/SecTester.Repeater.Tests/DefaultRepeaterFactoryTests.cs index cb70543..6be5247 100644 --- a/test/SecTester.Repeater.Tests/DefaultRepeaterFactoryTests.cs +++ b/test/SecTester.Repeater.Tests/DefaultRepeaterFactoryTests.cs @@ -6,31 +6,28 @@ public class DefaultRepeaterFactoryTests : IDisposable private const string DefaultNamePrefix = "sectester"; private const string Hostname = "app.brightsec.com"; - private readonly IServiceScopeFactory _serviceScopeFactory = Substitute.For(); - private readonly IRepeaterEventBusFactory _eventBusFactory = Substitute.For(); + private readonly IRepeaterBusFactory _busFactory = Substitute.For(); private readonly Configuration _configuration = new(Hostname); private readonly IRepeaters _repeaters = Substitute.For(); private readonly ILoggerFactory _loggerFactory = Substitute.For(); - private readonly ITimerProvider _timerProvider = Substitute.For(); private readonly IAnsiCodeColorizer _ansiCodeColorizer = Substitute.For(); + private readonly RequestRunnerResolver _resolver = Substitute.For(); + private readonly DefaultRepeaterFactory _sut; public DefaultRepeaterFactoryTests() { - // ADHOC: since GetRequiredService is part of extension we should explicitly mock an instance method - _serviceScopeFactory.CreateAsyncScope().ServiceProvider.GetService(typeof(ITimerProvider)).Returns(_timerProvider); - _sut = new DefaultRepeaterFactory(_serviceScopeFactory, _repeaters, _eventBusFactory, _configuration, _loggerFactory, _ansiCodeColorizer); + _sut = new DefaultRepeaterFactory(_repeaters, _busFactory, _configuration, _loggerFactory, _ansiCodeColorizer, _resolver); } public void Dispose() { _ansiCodeColorizer.ClearSubstitute(); - _timerProvider.ClearSubstitute(); - _serviceScopeFactory.ClearSubstitute(); - _eventBusFactory.ClearSubstitute(); + _busFactory.ClearSubstitute(); _repeaters.ClearSubstitute(); _loggerFactory.ClearSubstitute(); + _resolver.ClearSubstitute(); GC.SuppressFinalize(this); } diff --git a/test/SecTester.Repeater.Tests/RepeaterTests.cs b/test/SecTester.Repeater.Tests/RepeaterTests.cs index fdf8a3d..1dd926d 100644 --- a/test/SecTester.Repeater.Tests/RepeaterTests.cs +++ b/test/SecTester.Repeater.Tests/RepeaterTests.cs @@ -5,42 +5,17 @@ public class RepeaterTests : IDisposable, IAsyncDisposable private const string Version = "42.0.1"; private const string Id = "99138d92-69db-44cb-952a-1cd9ec031e20"; - public static readonly IEnumerable RegistrationErrors = new List - { - new object[] - { - new RegisterRepeaterResult(new RegisterRepeaterPayload(Error: RepeaterRegisteringError.Busy)), $"There is an already running Repeater with ID {Id}" - }, - new object[] - { - new RegisterRepeaterResult(new RegisterRepeaterPayload(Error: RepeaterRegisteringError.NotActive)), "The current Repeater is not active" - }, - new object[] - { - new RegisterRepeaterResult(new RegisterRepeaterPayload(Error: RepeaterRegisteringError.NotFound)), "Unauthorized access" - }, - new object[] - { - new RegisterRepeaterResult(new RegisterRepeaterPayload(Error: RepeaterRegisteringError.RequiresToBeUpdated)), - "The current running version is no longer supported" - }, - new object[] - { - new RegisterRepeaterResult(new RegisterRepeaterPayload(Error: (RepeaterRegisteringError)(-100))), "Something went wrong. Unknown error." - } - }; - - private readonly IEventBus _eventBus = Substitute.For(); + private readonly IRepeaterBus _bus = Substitute.For(); private readonly MockLogger _logger = Substitute.For>(); - private readonly Repeater _sut; - private readonly ITimerProvider _timerProvider = Substitute.For(); private readonly IAnsiCodeColorizer _ansiCodeColorizer = Substitute.For(); + private readonly RequestRunnerResolver _resolver = Substitute.For(); + + private readonly Repeater _sut; public RepeaterTests() { var version = new Version(Version); - _eventBus.Execute(Arg.Any()).Returns(new RegisterRepeaterResult(new RegisterRepeaterPayload(Version))); - _sut = new Repeater(Id, _eventBus, version, _logger, _timerProvider, _ansiCodeColorizer); + _sut = new Repeater(Id, _bus, version, _logger, _ansiCodeColorizer, _resolver); } public async ValueTask DisposeAsync() @@ -55,20 +30,20 @@ public void Dispose() { _ansiCodeColorizer.ClearSubstitute(); _logger.ClearSubstitute(); - _eventBus.ClearSubstitute(); - _timerProvider.ClearSubstitute(); + _bus.ClearSubstitute(); + _resolver.ClearSubstitute(); + GC.SuppressFinalize(this); } [Fact] - public async Task Start_RegistersInApp() + public async Task Start_DeploysItself() { // act await _sut.Start(); // assert - await _eventBus.Received().Execute(Arg.Any()); - await _eventBus.Received().Publish(Arg.Is(x => x.Status == RepeaterStatus.Connected && x.RepeaterId == Id)); + await _bus.Received().Deploy(Id, Arg.Any(), Arg.Any()); } [Fact] @@ -85,34 +60,6 @@ public async Task Start_OperationCancelled_ReThrowsError() await act.Should().ThrowAsync(); } - [Fact] - public async Task Start_RegistrationFailed_RegistersInApp() - { - // arrange - _eventBus.Execute(Arg.Any()).ReturnsNull(); - - // act - var act = () => _sut.Start(); - - // assert - await act.Should().ThrowAsync().WithMessage("Error registering repeater."); - } - - [Fact] - public async Task Start_SendsPingPeriodically() - { - // arrange - var elapsedEventArgs = EventArgs.Empty as ElapsedEventArgs; - - // act - await _sut.Start(); - - // assert - _timerProvider.Interval.Should().BeGreaterOrEqualTo(10_000); - _timerProvider.Elapsed += Raise.Event(new object(), elapsedEventArgs); - await _eventBus.Received(2).Publish(Arg.Is(x => x.Status == RepeaterStatus.Connected && x.RepeaterId == Id)); - } - [Fact] public async Task Start_SetsStatusToRunningJustAfterCall() { @@ -137,65 +84,61 @@ public async Task Start_RepeaterIsStarting_ThrowsError() } [Fact] - public async Task Start_AbortedByError_Restarts() + public async Task Start_NewVersionIsAvailable_ShowsWarning() { // arrange - _eventBus.Execute(Arg.Any()).Returns(null, new RegisterRepeaterResult(new RegisterRepeaterPayload(Version))); + var newVersion = new Regex(@"(\d+)").Replace(Version, x => $"{int.Parse(x.Groups[0].Value) + 1}"); + await _sut.Start(); // act - var act = () => _sut.Start(); + _bus.UpgradeAvailable += Raise.Event>(new Version(newVersion)); // assert - await act.Should().ThrowAsync(); - await act.Should().NotThrowAsync(); + _logger.Received().Log(LogLevel.Warning, Arg.Is(s => s.Contains($"A new Repeater version ({newVersion}) is available"))); } - [Theory] - [MemberData(nameof(RegistrationErrors))] - public async Task Start_ErrorWhileRegistration_ThrowsError(RegisterRepeaterResult input, string expected) + [Fact] + public async Task Stop_RunningRepeater_Stops() { // arrange - _eventBus.Execute(Arg.Any()).Returns(input); + await _sut.Start(); // act - var act = () => _sut.Start(); + await _sut.Stop(); // assert - await act.Should().ThrowAsync().WithMessage($"*{expected}*"); + await _bus.Received().DisposeAsync(); } [Fact] - public async Task Start_NewVersionIsAvailable_ShowsWarning() + public async Task Stop_RunningRepeater_EntersIntoOff() { // arrange - var newVersion = new Regex(@"(\d+)").Replace(Version, x => $"{int.Parse(x.Groups[0].Value) + 1}"); - _eventBus.Execute(Arg.Any()).Returns(new RegisterRepeaterResult(new RegisterRepeaterPayload(newVersion))); + await _sut.Start(); // act - await _sut.Start(); + await _sut.Stop(); // assert - _logger.Received().Log(LogLevel.Warning, Arg.Is(s => s.Contains($"A new Repeater version ({newVersion}) is available"))); + _sut.Status.Should().Be(RunningStatus.Off); } [Fact] - public async Task Stop_RunningRepeater_Stops() + public async Task Stop_RepeaterIsOff_DoesNothing() { - // arrange - await _sut.Start(); - // act await _sut.Stop(); // assert - await _eventBus.Received().Publish(Arg.Is(x => x.Status == RepeaterStatus.Disconnected && x.RepeaterId == Id)); + _sut.Status.Should().Be(RunningStatus.Off); } [Fact] - public async Task Stop_RunningRepeater_EntersIntoOff() + public async Task Stop_RepeaterIsOff_IgnoresSecondCall() { // arrange await _sut.Start(); + await _sut.Stop(); // act await _sut.Stop(); @@ -205,69 +148,77 @@ public async Task Stop_RunningRepeater_EntersIntoOff() } [Fact] - public async Task Stop_RepeaterIsOff_DoesNothing() + public async Task Stop_OperationCancelled_ThrowsError() { // arrange - _eventBus.Execute(Arg.Any()).ReturnsNull(); + using var cancellationTokenSource = new CancellationTokenSource(); + cancellationTokenSource.Cancel(); // act - await _sut.Stop(); + var act = () => _sut.Stop(cancellationTokenSource.Token); // assert - _sut.Status.Should().Be(RunningStatus.Off); + await act.Should().ThrowAsync(); } [Fact] - public async Task Stop_RepeaterIsOff_IgnoresSecondCall() + public async Task DisposeAsync_StopsRepeater() { // arrange await _sut.Start(); - await _sut.Stop(); // act - await _sut.Stop(); + await _sut.DisposeAsync(); // assert - _sut.Status.Should().Be(RunningStatus.Off); + await _bus.Received().DisposeAsync(); } [Fact] - public async Task Stop_StopsSendingPing() + public async Task Start_IncomingRequest_ExecutesRequest() { // arrange + var request = new IncomingRequest(new Uri("http://foo.bar")); + var response = new OutgoingResponse + { + StatusCode = 200, + }; + _resolver(Protocol.Http)!.Run(request).Returns(response); await _sut.Start(); // act - await _sut.Stop(); + _bus.RequestReceived += Raise.Event>>(request); // assert - _timerProvider.Received().Stop(); + await _resolver(Protocol.Http)!.Received().Run(Arg.Any()); } [Fact] - public async Task Stop_OperationCancelled_ReThrowsError() + public async Task Start_IncomingRequest_LogsError() { // arrange - using var cancellationTokenSource = new CancellationTokenSource(); - cancellationTokenSource.Cancel(); + var request = new IncomingRequest(new Uri("http://foo.bar")); + _resolver(Arg.Any()).ReturnsNull(); + await _sut.Start(); // act - var act = () => _sut.Stop(cancellationTokenSource.Token); + _bus.RequestReceived += Raise.Event>>(request); // assert - await act.Should().ThrowAsync(); + _logger.Received().Log(LogLevel.Error, Arg.Is(s => s.Contains($"Unsupported protocol {request.Protocol}"))); } [Fact] - public async Task DisposeAsync_StopsRepeater() + public async Task Start_ErrorReceived_LogsError() { // arrange + var error = new Exception("Something went wrong"); await _sut.Start(); // act - await _sut.DisposeAsync(); + _bus.ErrorOccurred += Raise.Event>(error); // assert - await _eventBus.Received().Publish(Arg.Is(x => x.Status == RepeaterStatus.Disconnected && x.RepeaterId == Id)); + _logger.Received().Log(LogLevel.Error, Arg.Is(s => s.Contains($": {error.Message}"))); } } diff --git a/test/SecTester.Repeater.Tests/Runners/HttpRequestRunnerTests.cs b/test/SecTester.Repeater.Tests/Runners/HttpRequestRunnerTests.cs index 6eee2c7..4061f85 100644 --- a/test/SecTester.Repeater.Tests/Runners/HttpRequestRunnerTests.cs +++ b/test/SecTester.Repeater.Tests/Runners/HttpRequestRunnerTests.cs @@ -51,7 +51,7 @@ public async Task Run_ReturnsResult_WhenRequestIsSuccessful() HeaderFieldValue }) }; - var request = new RequestExecutingEvent(Uri) + var request = new IncomingRequest(Uri) { Method = HttpMethod.Patch, Body = JsonContent, @@ -84,7 +84,7 @@ public async Task Run_ReturnsResultWithDecodedBody() var sut = CreateSut(); var encoding = Encoding.GetEncoding("utf-16"); var expectedByteLength = Buffer.ByteLength(encoding.GetBytes(HtmlBody)); - var request = new RequestExecutingEvent(Uri); + var request = new IncomingRequest(Uri); var content = new StringContent(HtmlBody, encoding, HtmlContentType); _mockHttp.Expect(Url).Respond(HttpStatusCode.OK, content); @@ -118,7 +118,7 @@ public async Task Run_ReturnsResultWithError_WhenRequestTimesOut() { Timeout = TimeSpan.Zero }); - var request = new RequestExecutingEvent(Uri); + var request = new IncomingRequest(Uri); _mockHttp.Expect(Url) .Respond(async () => { @@ -146,7 +146,7 @@ public async Task Run_MaxContentLengthIsLessThan0_SkipsTruncating() { MaxContentLength = -1 }); - var request = new RequestExecutingEvent(Uri); + var request = new IncomingRequest(Uri); var body = string.Concat(Enumerable.Repeat("x", 5)); _mockHttp.Expect(Url).Respond(HttpStatusCode.OK, CustomContentType, body); @@ -165,7 +165,7 @@ public async Task Run_NoContentStatusReceived_SkipsTruncating() { // arrange var sut = CreateSut(); - var request = new RequestExecutingEvent(Uri); + var request = new IncomingRequest(Uri); _mockHttp.Expect(Url).Respond(HttpStatusCode.NoContent); // act @@ -184,7 +184,7 @@ public async Task Run_HeadMethodUsed_SkipsTruncating() { // arrange var sut = CreateSut(); - var request = new RequestExecutingEvent(Uri) + var request = new IncomingRequest(Uri) { Method = HttpMethod.Head }; @@ -206,7 +206,7 @@ public async Task Run_AllowedMimeReceived_SkipsTruncating() { // arrange var sut = CreateSut(); - var request = new RequestExecutingEvent(Uri); + var request = new IncomingRequest(Uri); _mockHttp.Expect(Url).Respond(HttpStatusCode.OK, JsonContentType, JsonContent); // act @@ -240,7 +240,7 @@ public async Task Run_NotAllowedMimeReceived_TruncatesBody() $"{options.MaxContentLength}" }) }; - var request = new RequestExecutingEvent(Uri); + var request = new IncomingRequest(Uri); var body = string.Concat(Enumerable.Repeat("x", 5)); _mockHttp.Expect(Url).Respond(HttpStatusCode.OK, CustomContentType, body); @@ -261,7 +261,7 @@ public async Task Run_HttpStatusException_ReturnsResponse() { // arrange var sut = CreateSut(); - var request = new RequestExecutingEvent(Uri); + var request = new IncomingRequest(Uri); _mockHttp.Expect(Url).Respond(HttpStatusCode.ServiceUnavailable, JsonContentType, JsonContent); // act @@ -280,7 +280,7 @@ public async Task Run_TcpException_ReturnsResponse() { // arrange var sut = CreateSut(); - var request = new RequestExecutingEvent(Uri); + var request = new IncomingRequest(Uri); _mockHttp.Expect(Url).Throw(new SocketException((int)SocketError.ConnectionRefused)); // act @@ -292,7 +292,7 @@ public async Task Run_TcpException_ReturnsResponse() ErrorCode = "ConnectionRefused" }, options => options.Using(ctx => ctx.Subject.Should().BeOfType()) - .When(info => info.Path.EndsWith(nameof(RequestExecutingResult.Message)))); + .When(info => info.Path.EndsWith(nameof(OutgoingResponse.Message)))); } [Fact] @@ -307,7 +307,7 @@ public async Task Run_BypassesStrictHttpValidation() InvalidHostHeaderValue }) }; - var request = new RequestExecutingEvent(Uri) + var request = new IncomingRequest(Uri) { Headers = headers }; @@ -334,7 +334,7 @@ public async Task Run_AcceptsContentHeaders() JsonContentType }) }; - var request = new RequestExecutingEvent(Uri) + var request = new IncomingRequest(Uri) { Method = HttpMethod.Post, Headers = headers, diff --git a/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj b/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj index 0a9fc3e..97f7941 100644 --- a/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj +++ b/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj @@ -10,7 +10,6 @@ - diff --git a/test/SecTester.Repeater.Tests/packages.lock.json b/test/SecTester.Repeater.Tests/packages.lock.json index b95d060..d7051cc 100644 --- a/test/SecTester.Repeater.Tests/packages.lock.json +++ b/test/SecTester.Repeater.Tests/packages.lock.json @@ -99,6 +99,21 @@ "resolved": "3.0.0", "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.5.124", + "contentHash": "jxJPIkCnKwZcg9qtN1WoR99nlcJl/y/HYOTQLxnyXzR4iE5xhZviCPSKXLe08fcF9Tk4hP7mm+mVVdyUfh2ALw==", + "dependencies": { + "MessagePack.Annotations": "2.5.124", + "Microsoft.NET.StringTools": "17.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.5.124", + "contentHash": "YTWbSjVlMhe6WaEQ953rrNagRzQxDrp9mCB3W2Yr1TOITlaEv/ZMFvqZSabSs09Gy86Kq7BmvcxKTodv/YNDQA==" + }, "Microsoft.AspNetCore.TestHost": { "type": "Transitive", "resolved": "6.0.11", @@ -417,6 +432,15 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.6.3", + "contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", @@ -656,6 +680,49 @@ "resolved": "4.3.0", "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" }, + "SocketIO.Core": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "L5GSyODDjlub5YdDxfsRtA9uxBNw1Hs/egEMhdOGFyD0c63VmvWQ1A4Gfc4GYKYInz1muP4C8QNblXErnUYDqA==" + }, + "SocketIO.Serializer.Core": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "aFanlt2GOGEy7GTGLyh8f8SwhtmQwoPOHTAYCQ0brjg05Nb3F38sk4hYogpII5imyZ7w0spqwNHwpl7FZ49HOA==", + "dependencies": { + "SocketIO.Core": "3.1.1" + } + }, + "SocketIO.Serializer.MessagePack": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "lOAZs6AUCDhfMFa4Vu40LeeK/9fP+iMUerI3qzmToDaSa+A2/YQ7D0nvYa1atwTvzvhDZklBKNV5dIzQWWwPJg==", + "dependencies": { + "MessagePack": "2.5.124", + "SocketIO.Core": "3.1.1", + "SocketIO.Serializer.Core": "3.1.1" + } + }, + "SocketIO.Serializer.SystemTextJson": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "1PKQ+hOAJ4l3ZXlrVgiB3uNY3kLhjzYjnxk/97V6npJrNmhq09Di2EQSpGweQMu6KPbOxjZ7eYVARkGTWIUsjg==", + "dependencies": { + "SocketIO.Core": "3.1.1", + "SocketIO.Serializer.Core": "3.1.1", + "System.Text.Json": "7.0.3" + } + }, + "SocketIOClient": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "g8Lauia1Cj5DBXeC9j6vDSJeQEboGmXsnduW06VOBMe+UgqCrenxszYXJg19rAjwO10XRWQorOuU1XNMfYo8Xg==", + "dependencies": { + "SocketIO.Serializer.Core": "3.1.1", + "SocketIO.Serializer.SystemTextJson": "3.1.1", + "System.Collections": "4.3.0" + } + }, "System.AppContext": { "type": "Transitive", "resolved": "4.3.0", @@ -939,8 +1006,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Net.Http": { "type": "Transitive", @@ -1356,19 +1423,19 @@ }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "resolved": "7.0.0", + "contentHash": "OP6umVGxc0Z0MvZQBVigj4/U31Pw72ITihDWP9WiWDm+q5aoe0GaJivsfYGq53o6dxH7DcXWiCTl7+0o2CGdmg==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "resolved": "7.0.3", + "contentHash": "AyjhwXN1zTFeIibHimfJn6eAsZ7rTBib79JQpzg8WAuR/HKDu9JGNHTuu3nbbXQ/bgI+U4z6HtZmCHNXB1QXrQ==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" + "System.Text.Encodings.Web": "7.0.0" } }, "System.Text.RegularExpressions": { @@ -1522,7 +1589,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.26.0, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } @@ -1539,8 +1606,10 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.26.0, )", - "SecTester.Core": "[0.26.0, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", + "SocketIO.Serializer.MessagePack": "[3.1.1, )", + "SocketIOClient": "[3.1.1, )", "System.Linq.Async": "[6.0.1, )" } } diff --git a/test/SecTester.Reporter.Tests/packages.lock.json b/test/SecTester.Reporter.Tests/packages.lock.json index 5354e99..02830eb 100644 --- a/test/SecTester.Reporter.Tests/packages.lock.json +++ b/test/SecTester.Reporter.Tests/packages.lock.json @@ -1337,7 +1337,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.29.2, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } @@ -1354,7 +1354,7 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Scan": "[0.29.2, )" + "SecTester.Scan": "[0.40.0, )" } }, "sectester.scan": { @@ -1362,8 +1362,8 @@ "dependencies": { "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.29.2, )", - "SecTester.Core": "[0.29.2, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", "System.Linq.Async": "[6.0.1, )", "System.Text.Json": "[6.0.0, )" } diff --git a/test/SecTester.Runner.Tests/packages.lock.json b/test/SecTester.Runner.Tests/packages.lock.json index d21bb0a..5ac24be 100644 --- a/test/SecTester.Runner.Tests/packages.lock.json +++ b/test/SecTester.Runner.Tests/packages.lock.json @@ -82,6 +82,21 @@ "resolved": "3.0.0", "contentHash": "AkNshs6dopj8FXsmkkJxvLivN2SyDJQDbjcds5lo9+Y6L4zpcoXdmzXQ3VVN+AIWQr0CTD5A7vkuHGAr2aypZg==" }, + "MessagePack": { + "type": "Transitive", + "resolved": "2.5.124", + "contentHash": "jxJPIkCnKwZcg9qtN1WoR99nlcJl/y/HYOTQLxnyXzR4iE5xhZviCPSKXLe08fcF9Tk4hP7mm+mVVdyUfh2ALw==", + "dependencies": { + "MessagePack.Annotations": "2.5.124", + "Microsoft.NET.StringTools": "17.6.3", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "MessagePack.Annotations": { + "type": "Transitive", + "resolved": "2.5.124", + "contentHash": "YTWbSjVlMhe6WaEQ953rrNagRzQxDrp9mCB3W2Yr1TOITlaEv/ZMFvqZSabSs09Gy86Kq7BmvcxKTodv/YNDQA==" + }, "Microsoft.Bcl.AsyncInterfaces": { "type": "Transitive", "resolved": "7.0.0", @@ -230,6 +245,15 @@ "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, + "Microsoft.NET.StringTools": { + "type": "Transitive", + "resolved": "17.6.3", + "contentHash": "N0ZIanl1QCgvUumEL1laasU0a7sOE5ZwLZVTn0pAePnfhq8P7SvTjF8Axq+CnavuQkmdQpGNXQ1efZtu5kDFbA==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, "Microsoft.NETCore.Platforms": { "type": "Transitive", "resolved": "1.1.0", @@ -469,6 +493,49 @@ "resolved": "4.3.0", "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" }, + "SocketIO.Core": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "L5GSyODDjlub5YdDxfsRtA9uxBNw1Hs/egEMhdOGFyD0c63VmvWQ1A4Gfc4GYKYInz1muP4C8QNblXErnUYDqA==" + }, + "SocketIO.Serializer.Core": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "aFanlt2GOGEy7GTGLyh8f8SwhtmQwoPOHTAYCQ0brjg05Nb3F38sk4hYogpII5imyZ7w0spqwNHwpl7FZ49HOA==", + "dependencies": { + "SocketIO.Core": "3.1.1" + } + }, + "SocketIO.Serializer.MessagePack": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "lOAZs6AUCDhfMFa4Vu40LeeK/9fP+iMUerI3qzmToDaSa+A2/YQ7D0nvYa1atwTvzvhDZklBKNV5dIzQWWwPJg==", + "dependencies": { + "MessagePack": "2.5.124", + "SocketIO.Core": "3.1.1", + "SocketIO.Serializer.Core": "3.1.1" + } + }, + "SocketIO.Serializer.SystemTextJson": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "1PKQ+hOAJ4l3ZXlrVgiB3uNY3kLhjzYjnxk/97V6npJrNmhq09Di2EQSpGweQMu6KPbOxjZ7eYVARkGTWIUsjg==", + "dependencies": { + "SocketIO.Core": "3.1.1", + "SocketIO.Serializer.Core": "3.1.1", + "System.Text.Json": "7.0.3" + } + }, + "SocketIOClient": { + "type": "Transitive", + "resolved": "3.1.1", + "contentHash": "g8Lauia1Cj5DBXeC9j6vDSJeQEboGmXsnduW06VOBMe+UgqCrenxszYXJg19rAjwO10XRWQorOuU1XNMfYo8Xg==", + "dependencies": { + "SocketIO.Serializer.Core": "3.1.1", + "SocketIO.Serializer.SystemTextJson": "3.1.1", + "System.Collections": "4.3.0" + } + }, "System.AppContext": { "type": "Transitive", "resolved": "4.3.0", @@ -754,8 +821,8 @@ }, "System.Memory": { "type": "Transitive", - "resolved": "4.5.4", - "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==" }, "System.Net.Http": { "type": "Transitive", @@ -1171,19 +1238,19 @@ }, "System.Text.Encodings.Web": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "Vg8eB5Tawm1IFqj4TVK1czJX89rhFxJo9ELqc/Eiq0eXy13RK00eubyU6TJE6y+GQXjyV5gSfiewDUZjQgSE0w==", + "resolved": "7.0.0", + "contentHash": "OP6umVGxc0Z0MvZQBVigj4/U31Pw72ITihDWP9WiWDm+q5aoe0GaJivsfYGq53o6dxH7DcXWiCTl7+0o2CGdmg==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0" } }, "System.Text.Json": { "type": "Transitive", - "resolved": "6.0.0", - "contentHash": "zaJsHfESQvJ11vbXnNlkrR46IaMULk/gHxYsJphzSF+07kTjPHv+Oc14w6QEOfo3Q4hqLJgStUaYB9DBl0TmWg==", + "resolved": "7.0.3", + "contentHash": "AyjhwXN1zTFeIibHimfJn6eAsZ7rTBib79JQpzg8WAuR/HKDu9JGNHTuu3nbbXQ/bgI+U4z6HtZmCHNXB1QXrQ==", "dependencies": { "System.Runtime.CompilerServices.Unsafe": "6.0.0", - "System.Text.Encodings.Web": "6.0.0" + "System.Text.Encodings.Web": "7.0.0" } }, "System.Text.RegularExpressions": { @@ -1337,7 +1404,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.31.0, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } @@ -1354,8 +1421,10 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.31.0, )", - "SecTester.Core": "[0.31.0, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", + "SocketIO.Serializer.MessagePack": "[3.1.1, )", + "SocketIOClient": "[3.1.1, )", "System.Linq.Async": "[6.0.1, )" } }, @@ -1363,16 +1432,16 @@ "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Scan": "[0.31.0, )" + "SecTester.Scan": "[0.40.0, )" } }, "sectester.runner": { "type": "Project", "dependencies": { "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Repeater": "[0.31.0, )", - "SecTester.Reporter": "[0.31.0, )", - "SecTester.Scan": "[0.31.0, )" + "SecTester.Repeater": "[0.40.0, )", + "SecTester.Reporter": "[0.40.0, )", + "SecTester.Scan": "[0.40.0, )" } }, "sectester.scan": { @@ -1380,12 +1449,12 @@ "dependencies": { "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.31.0, )", - "SecTester.Core": "[0.31.0, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", "System.Linq.Async": "[6.0.1, )", "System.Text.Json": "[6.0.0, )" } } } } -} +} \ No newline at end of file diff --git a/test/SecTester.Scan.Tests/packages.lock.json b/test/SecTester.Scan.Tests/packages.lock.json index 40e17f6..5ef1f12 100644 --- a/test/SecTester.Scan.Tests/packages.lock.json +++ b/test/SecTester.Scan.Tests/packages.lock.json @@ -1339,7 +1339,7 @@ "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", "Microsoft.Extensions.Http": "[6.0.0, )", "RabbitMQ.Client": "[6.4.0, )", - "SecTester.Core": "[0.29.0, )", + "SecTester.Core": "[0.40.0, )", "System.Text.Json": "[6.0.0, )", "System.Threading.RateLimiting": "[7.0.0, )" } @@ -1357,8 +1357,8 @@ "dependencies": { "Macross.Json.Extensions": "[3.0.0, )", "Microsoft.Extensions.DependencyInjection.Abstractions": "[6.0.0, )", - "SecTester.Bus": "[0.29.0, )", - "SecTester.Core": "[0.29.0, )", + "SecTester.Bus": "[0.40.0, )", + "SecTester.Core": "[0.40.0, )", "System.Linq.Async": "[6.0.1, )", "System.Text.Json": "[6.0.0, )" } From 0293f1c3a43130848b796bb2e30c9f70a056971a Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Tue, 3 Oct 2023 23:26:41 +0400 Subject: [PATCH 5/8] docs(repeater): actualize the guide --- src/SecTester.Repeater/README.md | 53 +++++++++++++++----------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/src/SecTester.Repeater/README.md b/src/SecTester.Repeater/README.md index eef1052..e024f73 100644 --- a/src/SecTester.Repeater/README.md +++ b/src/SecTester.Repeater/README.md @@ -18,7 +18,7 @@ $ dotnet add package SecTester.Repeater ## Usage -To establish a secure connection between the Bright cloud engine and a target on a local network, you just need to use the `RepeaterFactory` constructed with [`Configuration` instance](https://github.com/NeuraLegion/sectester-net/tree/master/src/SecTester.Core#configuration). +To establish a secure connection between the Bright cloud engine and a target on a local network, you just need to use the `IRepeaterFactory` constructed with [`Configuration` instance](https://github.com/NeuraLegion/sectester-net/tree/master/src/SecTester.Core#configuration). ```csharp var repeaterFactory = serviceProvider.GetService(); @@ -49,28 +49,28 @@ The `CreateRepeater` method accepts the options described below: The default `requestRunnerOptions` is as follows: -```js +```json { - timeout: 30000, - maxContentLength: 100, - reuseConnection: false, - allowedMimes: [ - 'text/html', - 'text/plain', - 'text/css', - 'text/javascript', - 'text/markdown', - 'text/xml', - 'application/javascript', - 'application/x-javascript', - 'application/json', - 'application/xml', - 'application/x-www-form-urlencoded', - 'application/msgpack', - 'application/ld+json', - 'application/graphql' - ] -}; + "timeout": 30000, + "maxContentLength": 100, + "reuseConnection": false, + "allowedMimes": [ + "text/html", + "text/plain", + "text/css", + "text/javascript", + "text/markdown", + "text/xml", + "application/javascript", + "application/x-javascript", + "application/json", + "application/xml", + "application/x-www-form-urlencoded", + "application/msgpack", + "application/ld+json", + "application/graphql" + ] +} ``` The `RequestRunnerOptions` exposes the following options that can used to customize the request runner's behavior: [RequestRunnerOptions.cs](https://github.com/NeuraLegion/sectester-net/blob/master/src/SecTester.Repeater/Runners/RequestRunnerOptions.cs) @@ -131,8 +131,7 @@ public class ScanTests: IAsyncDisposable, IAsyncLifetime ### Implementation details -Under the hood `Repeater` register `RequestExecutingEventHandler` in bus, -which in turn uses the `RequestRunner` to proceed with request: +Under the hood `Repeater` uses the `IRequestRunner` to proceed with request: ```csharp public interface IRequestRunner @@ -146,9 +145,7 @@ Task Run(IRequest request); } ``` -Package contains `RequestRunner` implementations for both HTTP and WS protocols. - -To support other protocol new class implementation of `RequestRunner` should be registered in the IoC container: +The package provide a single `RequestRunner` implementations for HTTP protocol. To add support for other protocols, new implementation of `IRequestRunner` should be registered in the IoC container: ```csharp collection.AddScoped(); @@ -156,7 +153,7 @@ collection.AddScoped(); ## Limitations -Custom scripts and self-signed certificates (see [NexPloit CLI](https://www.npmjs.com/package/@neuralegion/nexploit-cli)) are not supported yet. +Custom scripts and self-signed certificates (see [Bright CLI](https://www.npmjs.com/package/@brightsec/cli)) are not supported yet. ## License From ed1f56f4e99bd711a55faab2aeda950b1bf66876 Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Tue, 3 Oct 2023 23:30:49 +0400 Subject: [PATCH 6/8] fix(repeater): change visibility of internal classes --- src/SecTester.Repeater/Bus/ISocketIoClient.cs | 2 +- src/SecTester.Repeater/Bus/ISocketIoResponse.cs | 2 +- src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs | 2 +- src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs | 10 +++++----- src/SecTester.Repeater/SecTester.Repeater.csproj | 1 + .../SecTester.Repeater.Tests.csproj | 1 + 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/SecTester.Repeater/Bus/ISocketIoClient.cs b/src/SecTester.Repeater/Bus/ISocketIoClient.cs index fce7b13..e8e6005 100644 --- a/src/SecTester.Repeater/Bus/ISocketIoClient.cs +++ b/src/SecTester.Repeater/Bus/ISocketIoClient.cs @@ -3,7 +3,7 @@ namespace SecTester.Repeater.Bus; -public interface ISocketIoClient : IDisposable +internal interface ISocketIoClient : IDisposable { public bool Connected { get; } public Task Connect(); diff --git a/src/SecTester.Repeater/Bus/ISocketIoResponse.cs b/src/SecTester.Repeater/Bus/ISocketIoResponse.cs index a37489c..03bdf61 100644 --- a/src/SecTester.Repeater/Bus/ISocketIoResponse.cs +++ b/src/SecTester.Repeater/Bus/ISocketIoResponse.cs @@ -3,7 +3,7 @@ namespace SecTester.Repeater.Bus; -public interface ISocketIoResponse +internal interface ISocketIoResponse { public T GetValue(int i = 0); public Task CallbackAsync(params object[] data); diff --git a/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs b/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs index 8cc3f64..9e10b81 100644 --- a/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs +++ b/src/SecTester.Repeater/Bus/SocketIoClientWrapper.cs @@ -3,7 +3,7 @@ namespace SecTester.Repeater.Bus; -public class SocketIoClientWrapper : ISocketIoClient +internal sealed class SocketIoClientWrapper : ISocketIoClient { private readonly SocketIOClient.SocketIO _socketIo; diff --git a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs index ef708df..c83fd4e 100644 --- a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs +++ b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs @@ -7,7 +7,7 @@ namespace SecTester.Repeater.Bus; -public class SocketIoRepeaterBus : IRepeaterBus +internal sealed class SocketIoRepeaterBus : IRepeaterBus { private static readonly TimeSpan PingInterval = TimeSpan.FromSeconds(10); @@ -16,7 +16,7 @@ public class SocketIoRepeaterBus : IRepeaterBus private readonly ILogger _logger; private readonly Uri _url; - public SocketIoRepeaterBus(Uri url, ISocketIoClient client, ITimerProvider heartbeat, ILogger logger) + internal SocketIoRepeaterBus(Uri url, ISocketIoClient client, ITimerProvider heartbeat, ILogger logger) { _url = url ?? throw new ArgumentNullException(nameof(url)); _client = client ?? throw new ArgumentNullException(nameof(client)); @@ -24,9 +24,9 @@ public SocketIoRepeaterBus(Uri url, ISocketIoClient client, ITimerProvider heart _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } - internal record RepeaterVersion(string Version); - internal record RepeaterError(string Message); - internal record RepeaterInfo(string RepeaterId); + internal sealed record RepeaterVersion(string Version); + internal sealed record RepeaterError(string Message); + internal sealed record RepeaterInfo(string RepeaterId); public event Func>? RequestReceived; public event Action? ErrorOccurred; diff --git a/src/SecTester.Repeater/SecTester.Repeater.csproj b/src/SecTester.Repeater/SecTester.Repeater.csproj index 6f98a64..15eb7f1 100644 --- a/src/SecTester.Repeater/SecTester.Repeater.csproj +++ b/src/SecTester.Repeater/SecTester.Repeater.csproj @@ -17,6 +17,7 @@ + diff --git a/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj b/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj index 97f7941..0a9fc3e 100644 --- a/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj +++ b/test/SecTester.Repeater.Tests/SecTester.Repeater.Tests.csproj @@ -10,6 +10,7 @@ + From d7800a6a5832a21dbef12719f03c30b492b8736b Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Wed, 4 Oct 2023 10:38:50 +0400 Subject: [PATCH 7/8] fix(repeater): get rid of runtime --- src/SecTester.Repeater/Bus/IRepeaterBus.cs | 2 +- src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs | 4 ++-- src/SecTester.Repeater/Repeater.cs | 2 +- src/SecTester.Repeater/Runtime.cs | 12 ------------ .../Bus/SocketIoRepeaterBusTests.cs | 4 ++-- test/SecTester.Repeater.Tests/RepeaterTests.cs | 2 +- 6 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 src/SecTester.Repeater/Runtime.cs diff --git a/src/SecTester.Repeater/Bus/IRepeaterBus.cs b/src/SecTester.Repeater/Bus/IRepeaterBus.cs index 5948db1..af02b33 100644 --- a/src/SecTester.Repeater/Bus/IRepeaterBus.cs +++ b/src/SecTester.Repeater/Bus/IRepeaterBus.cs @@ -11,5 +11,5 @@ public interface IRepeaterBus : IAsyncDisposable event Action UpgradeAvailable; Task Connect(); - Task Deploy(string repeaterId, Runtime? runtime = null, CancellationToken? cancellationToken = null); + Task Deploy(string repeaterId, CancellationToken? cancellationToken = null); } diff --git a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs index c83fd4e..0d7550b 100644 --- a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs +++ b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs @@ -92,7 +92,7 @@ public async ValueTask DisposeAsync() GC.SuppressFinalize(this); } - public async Task Deploy(string repeaterId, Runtime? runtime = null, CancellationToken? cancellationToken = null) + public async Task Deploy(string repeaterId, CancellationToken? cancellationToken = null) { try { @@ -100,7 +100,7 @@ public async Task Deploy(string repeaterId, Runtime? runtime = null, Cancellatio _client.On("deployed", response => tcs.TrySetResult(response.GetValue())); - await _client.EmitAsync("deploy", new RepeaterInfo(repeaterId), runtime).ConfigureAwait(false); + await _client.EmitAsync("deploy", new RepeaterInfo(repeaterId)).ConfigureAwait(false); using var _ = cancellationToken?.Register(() => tcs.TrySetCanceled()); diff --git a/src/SecTester.Repeater/Repeater.cs b/src/SecTester.Repeater/Repeater.cs index da13af3..75e2305 100644 --- a/src/SecTester.Repeater/Repeater.cs +++ b/src/SecTester.Repeater/Repeater.cs @@ -60,7 +60,7 @@ public async Task Start(CancellationToken cancellationToken = default) SubscribeToEvents(); await _bus.Connect().ConfigureAwait(false); - await _bus.Deploy(RepeaterId, new Runtime(_version.ToString()), cancellationToken).ConfigureAwait(false); + await _bus.Deploy(RepeaterId, cancellationToken).ConfigureAwait(false); Status = RunningStatus.Running; } diff --git a/src/SecTester.Repeater/Runtime.cs b/src/SecTester.Repeater/Runtime.cs deleted file mode 100644 index 4841f6a..0000000 --- a/src/SecTester.Repeater/Runtime.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace SecTester.Repeater; - -public record Runtime(string Version) -{ - public bool? ScriptsLoaded { get; init; } - public string? Ci { get; init; } - public string? Os { get; init; } - public string? Arch { get; init; } - public bool? Docker { get; init; } - public string? Distribution { get; init; } - public string? NetVersion { get; init; } -} diff --git a/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs b/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs index 7cffd0b..8e2ee0f 100644 --- a/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs +++ b/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs @@ -153,7 +153,7 @@ public async Task Deploy_Success() await _sut.Deploy(RepeaterId); // assert - await _client.Received().EmitAsync("deploy", Arg.Is(x => x.RepeaterId.Equals(RepeaterId, StringComparison.OrdinalIgnoreCase)), Arg.Any()); + await _client.Received().EmitAsync("deploy", Arg.Is(x => x.RepeaterId.Equals(RepeaterId, StringComparison.OrdinalIgnoreCase))); } [Fact] @@ -164,7 +164,7 @@ public async Task Deploy_GivenCancellationToken_ThrowsError() cancellationTokenSource.Cancel(); // act - var act = () => _sut.Deploy(RepeaterId, null, cancellationTokenSource.Token); + var act = () => _sut.Deploy(RepeaterId, cancellationTokenSource.Token); // assert await act.Should().ThrowAsync(); diff --git a/test/SecTester.Repeater.Tests/RepeaterTests.cs b/test/SecTester.Repeater.Tests/RepeaterTests.cs index 1dd926d..8b72118 100644 --- a/test/SecTester.Repeater.Tests/RepeaterTests.cs +++ b/test/SecTester.Repeater.Tests/RepeaterTests.cs @@ -43,7 +43,7 @@ public async Task Start_DeploysItself() await _sut.Start(); // assert - await _bus.Received().Deploy(Id, Arg.Any(), Arg.Any()); + await _bus.Received().Deploy(Id, Arg.Any()); } [Fact] From b51a7aff99be34c2e0cb0d7f26cd4e208684dfd9 Mon Sep 17 00:00:00 2001 From: Artem Derevnjuk Date: Wed, 4 Oct 2023 10:51:21 +0400 Subject: [PATCH 8/8] fix(repeater): timeout ack --- .../Bus/DefaultRepeaterBusFactory.cs | 17 ++++++++--------- .../Bus/SocketIoRepeaterBus.cs | 13 +++++++------ .../Bus/SocketIoRepeaterBusOptions.cs | 12 ++++++++++++ .../Bus/SocketIoRepeaterBusTests.cs | 9 +++++---- 4 files changed, 32 insertions(+), 19 deletions(-) create mode 100644 src/SecTester.Repeater/Bus/SocketIoRepeaterBusOptions.cs diff --git a/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs b/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs index 0a8ead0..87fefd7 100644 --- a/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs +++ b/src/SecTester.Repeater/Bus/DefaultRepeaterBusFactory.cs @@ -32,19 +32,18 @@ public IRepeaterBus Create(string repeaterId) } var url = new Uri(_config.Api); - var options = new SocketIOOptions + var options = new SocketIoRepeaterBusOptions(url); + var client = new SocketIOClient.SocketIO(url, new SocketIOOptions { - Path = "/api/ws/v1", - ReconnectionAttempts = 20, - ReconnectionDelayMax = 86400000, - ConnectionTimeout = TimeSpan.FromSeconds(10), + Path = options.Path, + ReconnectionAttempts = options.ReconnectionAttempts, + ReconnectionDelayMax = options.ReconnectionDelayMax, + ConnectionTimeout = options.ConnectionTimeout, Auth = new List> { new("token", _config.Credentials.Token), new("domain", repeaterId) }, - }; - - var client = new SocketIOClient.SocketIO(url, options) + }) { Serializer = new SocketIOMessagePackSerializer() }; @@ -53,6 +52,6 @@ public IRepeaterBus Create(string repeaterId) var scope = _scopeFactory.CreateAsyncScope(); var timerProvider = scope.ServiceProvider.GetRequiredService(); - return new SocketIoRepeaterBus(url, wrapper, timerProvider, _loggerFactory.CreateLogger()); + return new SocketIoRepeaterBus(options, wrapper, timerProvider, _loggerFactory.CreateLogger()); } } diff --git a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs index 0d7550b..c0334fe 100644 --- a/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs +++ b/src/SecTester.Repeater/Bus/SocketIoRepeaterBus.cs @@ -14,11 +14,11 @@ internal sealed class SocketIoRepeaterBus : IRepeaterBus private readonly ITimerProvider _heartbeat; private readonly ISocketIoClient _client; private readonly ILogger _logger; - private readonly Uri _url; + private readonly SocketIoRepeaterBusOptions _options; - internal SocketIoRepeaterBus(Uri url, ISocketIoClient client, ITimerProvider heartbeat, ILogger logger) + internal SocketIoRepeaterBus(SocketIoRepeaterBusOptions options, ISocketIoClient client, ITimerProvider heartbeat, ILogger logger) { - _url = url ?? throw new ArgumentNullException(nameof(url)); + _options = options ?? throw new ArgumentNullException(nameof(options)); _client = client ?? throw new ArgumentNullException(nameof(client)); _heartbeat = heartbeat ?? throw new ArgumentNullException(nameof(heartbeat)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); @@ -42,7 +42,7 @@ public async Task Connect() await SchedulePing().ConfigureAwait(false); - _logger.LogDebug("Repeater connected to {Url}", _url); + _logger.LogDebug("Repeater connected to {Url}", _options.Url); } } @@ -67,9 +67,10 @@ private void DelegateEvents() return; } + var ct = new CancellationTokenSource(_options.AckTimeout); var request = response.GetValue(); var result = await RequestReceived.Invoke(request).ConfigureAwait(false); - await response.CallbackAsync(result).ConfigureAwait(false); + await response.CallbackAsync(ct.Token, result).ConfigureAwait(false); }); } @@ -80,7 +81,7 @@ public async ValueTask DisposeAsync() _heartbeat.Elapsed -= Ping; _heartbeat.Stop(); await _client.Disconnect().ConfigureAwait(false); - _logger.LogDebug("Repeater disconnected from {Url}", _url); + _logger.LogDebug("Repeater disconnected from {Url}", _options.Url); } _client.Dispose(); diff --git a/src/SecTester.Repeater/Bus/SocketIoRepeaterBusOptions.cs b/src/SecTester.Repeater/Bus/SocketIoRepeaterBusOptions.cs new file mode 100644 index 0000000..fa5b07e --- /dev/null +++ b/src/SecTester.Repeater/Bus/SocketIoRepeaterBusOptions.cs @@ -0,0 +1,12 @@ +using System; + +namespace SecTester.Repeater.Bus; + +internal record SocketIoRepeaterBusOptions(Uri Url) +{ + public string Path { get; init; } = "/api/ws/v1"; + public TimeSpan ConnectionTimeout { get; init; } = TimeSpan.FromSeconds(10); + public TimeSpan AckTimeout { get; init; } = TimeSpan.FromSeconds(60); + public int ReconnectionDelayMax { get; init; } = 86_400_000; + public int ReconnectionAttempts { get; init; } = 20; +} diff --git a/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs b/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs index 8e2ee0f..ff80976 100644 --- a/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs +++ b/test/SecTester.Repeater.Tests/Bus/SocketIoRepeaterBusTests.cs @@ -3,7 +3,8 @@ namespace SecTester.Repeater.Tests.Bus; public sealed class SocketIoRepeaterBusTests : IDisposable { private static readonly string RepeaterId = "g5MvgM74sweGcK1U6hvs76"; - private static readonly string Url = new("http://example.com"); + private static readonly Uri Url = new("http://example.com"); + private static readonly SocketIoRepeaterBusOptions Options = new(Url); private readonly ISocketIoClient _client = Substitute.For(); private readonly ITimerProvider _heartbeat = Substitute.For(); @@ -13,7 +14,7 @@ public sealed class SocketIoRepeaterBusTests : IDisposable public SocketIoRepeaterBusTests() { - _sut = new SocketIoRepeaterBus(new Uri(Url), _client, _heartbeat, _logger); + _sut = new SocketIoRepeaterBus(Options, _client, _heartbeat, _logger); } public void Dispose() @@ -35,7 +36,7 @@ public async Task RequestReceived_ExecutesHandler() StatusCode = 204 }; _client.Connect().Returns(Task.CompletedTask); - _socketIoResponse.GetValue().Returns(new IncomingRequest(new(Url))); + _socketIoResponse.GetValue().Returns(new IncomingRequest(Url)); _client.On("request", Arg.Invoke(_socketIoResponse)); _sut.RequestReceived += _ => Task.FromResult(result); @@ -43,7 +44,7 @@ public async Task RequestReceived_ExecutesHandler() await _sut.Connect(); // assert - await _socketIoResponse.Received().CallbackAsync(result); + await _socketIoResponse.Received().CallbackAsync(Arg.Any(), result); } [Fact]