-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(scan): provide a default scan factory
closes #68
- Loading branch information
Showing
28 changed files
with
942 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Threading.Tasks; | ||
using Microsoft.Extensions.Logging; | ||
using SecTester.Core; | ||
using SecTester.Core.Utils; | ||
using SecTester.Scan.Models; | ||
using SecTester.Scan.Target.Har; | ||
using Response = SecTester.Scan.Target.Har.Response; | ||
|
||
namespace SecTester.Scan; | ||
|
||
public class DefaultScanFactory : ScanFactory | ||
{ | ||
private static readonly IEnumerable<Discovery> DefaultDiscoveryTypes = new List<Discovery> { Discovery.Archive }; | ||
private readonly Scans _scans; | ||
private readonly ILogger _logger; | ||
private readonly Configuration _configuration; | ||
private readonly SystemTimeProvider _systemTimeProvider; | ||
|
||
public DefaultScanFactory(Configuration configuration, Scans scans, SystemTimeProvider systemTimeProvider, | ||
ILogger logger) | ||
{ | ||
_scans = scans ?? throw new ArgumentNullException(nameof(scans)); | ||
_configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); | ||
_systemTimeProvider = systemTimeProvider ?? throw new ArgumentNullException(nameof(systemTimeProvider)); | ||
_logger = logger ?? throw new ArgumentNullException(nameof(logger)); | ||
} | ||
|
||
public async Task<Scan> CreateScan(ScanSettingsOptions settingsOptions, ScanOptions? options) | ||
{ | ||
var scanConfig = await BuildScanConfig(new ScanSettings(settingsOptions)).ConfigureAwait(false); | ||
var scanId = await _scans.CreateScan(scanConfig).ConfigureAwait(false); | ||
|
||
return new Scan(scanId, _scans, _logger, options ?? new ScanOptions()); | ||
} | ||
|
||
private async Task<ScanConfig> BuildScanConfig(ScanSettingsOptions settingsOptions) | ||
{ | ||
var target = new Target.Target(settingsOptions.Target); | ||
var fileId = await CreateAndUploadHar(target).ConfigureAwait(false); | ||
|
||
return new ScanConfig(settingsOptions.Name!) | ||
{ | ||
FileId = fileId, | ||
Smart = settingsOptions.Smart, | ||
PoolSize = settingsOptions.PoolSize, | ||
SkipStaticParams = settingsOptions.SkipStaticParams, | ||
Module = Module.Dast, | ||
DiscoveryTypes = DefaultDiscoveryTypes, | ||
AttackParamLocations = settingsOptions.AttackParamLocations, | ||
Tests = settingsOptions.Tests, | ||
Repeaters = settingsOptions.RepeaterId is null ? default : new List<string> { settingsOptions.RepeaterId }, | ||
SlowEpTimeout = | ||
settingsOptions.SlowEpTimeout is null ? default : (int)settingsOptions.SlowEpTimeout.Value.TotalSeconds, | ||
TargetTimeout = | ||
settingsOptions.TargetTimeout is null ? default : (int)settingsOptions.TargetTimeout.Value.TotalSeconds | ||
}; | ||
} | ||
|
||
private async Task<string> CreateAndUploadHar(Target.Target target) | ||
{ | ||
var filename = GenerateFileName(target.Url); | ||
var har = await CreateHar(target).ConfigureAwait(false); | ||
|
||
return await _scans.UploadHar(new UploadHarOptions(har, filename, true)).ConfigureAwait(false); | ||
} | ||
|
||
private static string GenerateFileName(string url) | ||
{ | ||
var host = new Uri(url).Host; | ||
|
||
host = host.Length <= HarDefaults.MaxHostLength ? host : host.Substring(0, HarDefaults.MaxHostLength); | ||
|
||
return $"{host.TrimEnd('-')}-{Guid.NewGuid().ToString()}.har"; | ||
} | ||
|
||
private async Task<Entry> CreateHarEntry(Target.Target target) | ||
{ | ||
var request = await target.ToHarRequest().ConfigureAwait(false); | ||
return new Entry(_systemTimeProvider.Now, 0, Timings.Default, Cache.Default, request, Response.Default); | ||
} | ||
|
||
private async Task<Har> CreateHar(Target.Target target) | ||
{ | ||
var entry = await CreateHarEntry(target).ConfigureAwait(false); | ||
|
||
return new Har( | ||
new Log( | ||
new Creator(_configuration.Name, _configuration.Version), | ||
new List<Entry> { entry } | ||
) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using SecTester.Scan.Models; | ||
|
||
namespace SecTester.Scan; | ||
|
||
internal static class ScanDefaults | ||
{ | ||
public const int MinNameLength = 1; | ||
public const int MaxNameLength = 200; | ||
public const int MinPoolSize = 1; | ||
public const int MaxPoolSize = 50; | ||
|
||
public static readonly TimeSpan MinTargetTimeout = TimeSpan.FromSeconds(1); | ||
public static readonly TimeSpan MaxTargetTimeout = TimeSpan.FromSeconds(120); | ||
public static readonly TimeSpan MinSlowEpTimeout = TimeSpan.FromSeconds(100); | ||
|
||
public const bool DefaultSmart = true; | ||
public const bool DefaultSkipStaticParams = true; | ||
public const int DefaultPoolSize = 10; | ||
|
||
public static readonly TimeSpan DefaultTargetTimeout = TimeSpan.FromSeconds(5); | ||
public static readonly TimeSpan DefaultSlowEpTimeout = TimeSpan.FromSeconds(1000); | ||
|
||
public static readonly IEnumerable<AttackParamLocation> DefaultAttackParamLocations = new List<AttackParamLocation> | ||
{ | ||
AttackParamLocation.Body, AttackParamLocation.Query, AttackParamLocation.Fragment | ||
}; | ||
|
||
public static readonly IEnumerable<TestType> TestTypeWhiteList = Enum | ||
.GetValues(typeof(TestType)) | ||
.Cast<TestType>(); | ||
|
||
public static readonly IEnumerable<AttackParamLocation> AttackParamLocationWhiteList = Enum | ||
.GetValues(typeof(AttackParamLocation)) | ||
.Cast<AttackParamLocation>(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using SecTester.Scan.Models; | ||
using SecTester.Scan.Target; | ||
|
||
namespace SecTester.Scan; | ||
|
||
public record ScanSettings : ScanSettingsOptions | ||
{ | ||
private Target.Target _target = null!; | ||
private IEnumerable<TestType> _tests = null!; | ||
|
||
private string? _name; | ||
private TimeSpan? _targetTimeout; | ||
private TimeSpan? _slowEpTimeout; | ||
private int? _poolSize; | ||
private IEnumerable<AttackParamLocation>? _attackParamLocations; | ||
|
||
public TargetOptions Target | ||
{ | ||
get { return _target; } | ||
set | ||
{ | ||
_target = new Target.Target(value); | ||
} | ||
} | ||
|
||
public string? Name | ||
{ | ||
get { return _name; } | ||
set | ||
{ | ||
if (string.IsNullOrWhiteSpace(value) || | ||
value!.Length is < ScanDefaults.MinNameLength or > ScanDefaults.MaxNameLength) | ||
{ | ||
throw new ArgumentException($"Name must be less than {ScanDefaults.MaxNameLength} characters."); | ||
} | ||
|
||
_name = value; | ||
} | ||
} | ||
|
||
public string? RepeaterId { get; set; } | ||
|
||
public bool? Smart { get; set; } | ||
|
||
public int? PoolSize | ||
{ | ||
get { return _poolSize; } | ||
set | ||
{ | ||
if (value is null or < ScanDefaults.MinPoolSize or > ScanDefaults.MaxPoolSize) | ||
{ | ||
throw new ArgumentException("Invalid pool size."); | ||
} | ||
|
||
_poolSize = value; | ||
} | ||
} | ||
|
||
public TimeSpan? SlowEpTimeout | ||
{ | ||
get { return _slowEpTimeout; } | ||
set | ||
{ | ||
if (value is null || value < ScanDefaults.MinSlowEpTimeout) | ||
{ | ||
throw new ArgumentException("Invalid slow entry point timeout."); | ||
} | ||
|
||
_slowEpTimeout = value; | ||
} | ||
} | ||
|
||
public TimeSpan? TargetTimeout | ||
{ | ||
get { return _targetTimeout; } | ||
set | ||
{ | ||
if (value is null || (value < ScanDefaults.MinTargetTimeout || value > ScanDefaults.MaxTargetTimeout)) | ||
{ | ||
throw new ArgumentException("Invalid target connection timeout."); | ||
} | ||
|
||
_targetTimeout = value; | ||
} | ||
} | ||
|
||
public bool? SkipStaticParams { get; set; } | ||
|
||
public IEnumerable<TestType> Tests | ||
{ | ||
get { return _tests; } | ||
set | ||
{ | ||
Assert(value, ScanDefaults.TestTypeWhiteList, | ||
"Unknown test type supplied.", | ||
"Please provide at least one test."); | ||
|
||
_tests = value.Distinct(); | ||
} | ||
} | ||
|
||
public IEnumerable<AttackParamLocation>? AttackParamLocations | ||
{ | ||
get { return _attackParamLocations; } | ||
set | ||
{ | ||
Assert(value, ScanDefaults.AttackParamLocationWhiteList, | ||
"Unknown attack param location supplied.", | ||
"Please provide at least one attack parameter location."); | ||
|
||
_attackParamLocations = value!.Distinct(); | ||
} | ||
} | ||
|
||
public ScanSettings(TargetOptions targetOptions, IEnumerable<TestType> tests, string? repeaterId = default, | ||
string? name = default) | ||
: this(targetOptions, tests, repeaterId, name, ScanDefaults.DefaultSmart, ScanDefaults.DefaultPoolSize, | ||
ScanDefaults.DefaultTargetTimeout, | ||
ScanDefaults.DefaultSlowEpTimeout, ScanDefaults.DefaultSkipStaticParams, ScanDefaults.DefaultAttackParamLocations) | ||
{ | ||
} | ||
|
||
internal ScanSettings(ScanSettingsOptions scanSettingsOptions) | ||
: this(scanSettingsOptions.Target, scanSettingsOptions.Tests, scanSettingsOptions.RepeaterId, | ||
scanSettingsOptions.Name, scanSettingsOptions.Smart, scanSettingsOptions.PoolSize, | ||
scanSettingsOptions.TargetTimeout, scanSettingsOptions.SlowEpTimeout, scanSettingsOptions.SkipStaticParams, | ||
scanSettingsOptions.AttackParamLocations) | ||
{ | ||
} | ||
|
||
private ScanSettings(TargetOptions targetOptions, IEnumerable<TestType> tests, string? repeaterId, | ||
string? name, | ||
bool? smart, int? poolSize, TimeSpan? targetTimeout, | ||
TimeSpan? slowEpTimeout, bool? skipStaticParams, | ||
IEnumerable<AttackParamLocation>? attackParamLocations) | ||
{ | ||
Target = targetOptions; | ||
Tests = tests; | ||
Name = name ?? CreateDefaultName(targetOptions); | ||
RepeaterId = repeaterId; | ||
Smart = smart ?? ScanDefaults.DefaultSmart; | ||
PoolSize = poolSize ?? ScanDefaults.DefaultPoolSize; | ||
TargetTimeout = targetTimeout ?? ScanDefaults.DefaultTargetTimeout; | ||
SlowEpTimeout = slowEpTimeout ?? ScanDefaults.DefaultSlowEpTimeout; | ||
SkipStaticParams = skipStaticParams ?? ScanDefaults.DefaultSkipStaticParams; | ||
AttackParamLocations = attackParamLocations ?? ScanDefaults.DefaultAttackParamLocations; | ||
} | ||
|
||
private static string CreateDefaultName(TargetOptions target) | ||
{ | ||
var uri = new Uri(target.Url); | ||
var name = $"{target.Method ?? HttpMethod.Get} {uri.Host}"; | ||
|
||
return name.Length <= ScanDefaults.MaxNameLength ? name : name.Substring(0, ScanDefaults.MaxNameLength); | ||
} | ||
|
||
private static void Assert<T>(IEnumerable<T>? value, IEnumerable<T> whiteList, | ||
string unknownEntry, string emptyList) | ||
{ | ||
if (value is null || !value.All(whiteList.Contains)) | ||
{ | ||
throw new ArgumentException(unknownEntry); | ||
} | ||
if (!value.Distinct().Any()) | ||
{ | ||
throw new ArgumentException(emptyList); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace SecTester.Scan.Target.Har; | ||
|
||
public record Cache() | ||
{ | ||
public static readonly Cache Default = new(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace SecTester.Scan.Target.Har; | ||
|
||
public record Content(int Size, string MimeType) | ||
{ | ||
public static readonly Content Default = new(-1, "text/plain"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
namespace SecTester.Scan.Target.Har; | ||
|
||
public record Creator(string Name, string Version); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
using System; | ||
|
||
namespace SecTester.Scan.Target.Har; | ||
|
||
public record Entry(DateTime StartedDateTime, int Time, Timings Timings, Cache Cache, Request Request, | ||
Response Response); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
namespace SecTester.Scan.Target.Har; | ||
|
||
public record Har; | ||
public record Har(Log Log); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace SecTester.Scan.Target.Har; | ||
|
||
public static class HarDefaults | ||
{ | ||
public const int MaxHostLength = 200; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
using System.Collections.Generic; | ||
|
||
namespace SecTester.Scan.Target.Har; | ||
|
||
public record Log(Creator Creator, IEnumerable<Entry> Entries, string Version = "1.2"); |
Oops, something went wrong.