-
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): implement CI discovery provider
closes #88
- Loading branch information
Showing
14 changed files
with
534 additions
and
97 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 |
---|---|---|
|
@@ -4,4 +4,6 @@ public interface CiDiscovery | |
{ | ||
CiServer? Server { get; } | ||
bool IsCi { get; } | ||
|
||
bool IsPr { get; } | ||
} |
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 |
---|---|---|
@@ -1,23 +1,50 @@ | ||
using System; | ||
using System.IO; | ||
using System.Collections; | ||
using System.Linq; | ||
using System.Reflection; | ||
using SecTester.Scan.CI.Lib; | ||
using System.Text.Json; | ||
|
||
namespace SecTester.Scan.CI; | ||
|
||
// TODO ([email protected]): rework using 'ci-info' compatible implementation | ||
internal class DefaultCiDiscovery : CiDiscovery | ||
{ | ||
public CiServer? Server { get; } | ||
|
||
public bool IsCi => Server != null; | ||
|
||
public bool IsPr { get; } | ||
|
||
public DefaultCiDiscovery() | ||
|
||
public DefaultCiDiscovery(IDictionary? env = default) | ||
{ | ||
var vendorsContent = | ||
ResourceUtils.GetEmbeddedFileContent<DefaultCiDiscovery>(@"SecTester.Scan.CI.Lib.vendors.json"); | ||
} | ||
env ??= Environment.GetEnvironmentVariables(); | ||
|
||
var vendors = JsonSerializer.Deserialize<Vendor[]>( | ||
ResourceUtils.GetEmbeddedResourceContent<DefaultCiDiscovery>("SecTester.Scan.CI.vendors.json"), | ||
new JsonSerializerOptions() { PropertyNameCaseInsensitive = true }); | ||
|
||
if (vendors is null) | ||
{ | ||
return; | ||
} | ||
|
||
var matcher = new VendorMatcher(env); | ||
|
||
var vendor = vendors.FirstOrDefault(x => matcher.MatchEnv(x.Env)); | ||
|
||
if (vendor is null) | ||
{ | ||
return; | ||
} | ||
|
||
|
||
Server = typeof(CiServer) | ||
.GetProperties(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly) | ||
.Where(x => x.Name.Equals(vendor.Constant, StringComparison.OrdinalIgnoreCase)) | ||
.Select(x => x.GetValue(null)) | ||
.Cast<CiServer>() | ||
.FirstOrDefault() ?? new CiServer(vendor.Name); | ||
|
||
IsPr = matcher.MatchPr(vendor.Pr); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
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,29 @@ | ||
using System; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Reflection; | ||
|
||
namespace SecTester.Scan.CI; | ||
|
||
internal static class ResourceUtils | ||
{ | ||
public static string GetEmbeddedResourceContent<T>(string resourceName) | ||
where T : class | ||
{ | ||
if (string.IsNullOrWhiteSpace(resourceName)) | ||
{ | ||
throw new ArgumentNullException(nameof(resourceName)); | ||
} | ||
|
||
var assembly = typeof(T).GetTypeInfo().Assembly; | ||
|
||
using var stream = assembly.GetManifestResourceStream(resourceName); | ||
if (stream is null) | ||
{ | ||
throw new InvalidOperationException($"Could not get stream for {resourceName} resource."); | ||
} | ||
|
||
using StreamReader reader = new StreamReader(stream); | ||
return reader.ReadToEnd(); | ||
} | ||
} |
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,9 @@ | ||
using System.Text.Json; | ||
|
||
namespace SecTester.Scan.CI; | ||
|
||
internal record Vendor(string Name, string Constant) | ||
{ | ||
public JsonElement Env { get; init; } | ||
public JsonElement Pr { get; init; } | ||
} |
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,96 @@ | ||
using System.Collections; | ||
using System.Linq; | ||
using System.Text.Json; | ||
|
||
namespace SecTester.Scan.CI; | ||
|
||
internal class VendorMatcher | ||
{ | ||
private readonly IDictionary _env; | ||
|
||
public VendorMatcher(IDictionary env) | ||
{ | ||
_env = env; | ||
} | ||
|
||
public bool MatchEnv(JsonElement envElement) | ||
{ | ||
return envElement.ValueKind switch | ||
{ | ||
JsonValueKind.String => envElement.GetString() is not null && _env.Contains(envElement.GetString()!), | ||
JsonValueKind.Object => MatchInnerAny(envElement) || MatchInnerEnvIncludes(envElement) || | ||
MatchAllProperties(envElement), | ||
JsonValueKind.Array => envElement.EnumerateArray() | ||
.All(x => x.ValueKind == JsonValueKind.String && x.GetString() is not null && _env.Contains(x.GetString()!)), | ||
_ => false | ||
}; | ||
} | ||
|
||
|
||
public bool MatchPr(JsonElement prElement) | ||
{ | ||
return prElement.ValueKind switch | ||
{ | ||
JsonValueKind.String => prElement.GetString() is not null && _env.Contains(prElement.GetString()!), | ||
JsonValueKind.Object => MatchInnerEnvNe(prElement) || MatchEnv(prElement), | ||
_ => false | ||
}; | ||
} | ||
|
||
private bool MatchAllProperties(JsonElement obj) | ||
{ | ||
return obj.EnumerateObject().All(x => | ||
x.Value.ValueKind == JsonValueKind.String && _env.Contains(x.Name) && | ||
x.Value.ValueEquals(_env[x.Name]?.ToString())); | ||
} | ||
|
||
private bool MatchInnerAny(JsonElement obj) | ||
{ | ||
var any = obj.EnumerateObject().FirstOrDefault(x => x.NameEquals("any")); | ||
|
||
return any.Value.ValueKind == JsonValueKind.Array && any.Value.EnumerateArray() | ||
.Any(x => x.ValueKind == JsonValueKind.String && x.GetString() is not null && _env.Contains(x.GetString()!)); | ||
} | ||
|
||
private bool MatchInnerEnvIncludes(JsonElement obj) | ||
{ | ||
var enumerable = obj.EnumerateObject(); | ||
var env = enumerable.FirstOrDefault(x => x.NameEquals("env")); | ||
|
||
if (env.Value.ValueKind == JsonValueKind.String && env.Value.GetString() is not null && | ||
_env.Contains(env.Value.GetString()!)) | ||
{ | ||
var envVarValue = _env[env.Value.GetString()!]?.ToString(); | ||
|
||
var includes = enumerable.FirstOrDefault(x => x.NameEquals("includes")); | ||
|
||
if (includes.Value.ValueKind == JsonValueKind.String && includes.Value.GetString() is not null) | ||
{ | ||
return envVarValue?.Contains(includes.Value.GetString()!) ?? false; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
private bool MatchInnerEnvNe(JsonElement obj) | ||
{ | ||
var enumerable = obj.EnumerateObject(); | ||
var env = enumerable.FirstOrDefault(x => x.NameEquals("env")); | ||
|
||
if (env.Value.ValueKind == JsonValueKind.String && env.Value.GetString() is not null && | ||
_env.Contains(env.Value.GetString()!)) | ||
{ | ||
var envVarValue = _env[env.Value.GetString()!]?.ToString(); | ||
|
||
var ne = enumerable.FirstOrDefault(x => x.NameEquals("ne")); | ||
|
||
if (ne.Value.ValueKind == JsonValueKind.String && ne.Value.ValueEquals("false")) | ||
{ | ||
return !string.IsNullOrEmpty(envVarValue); | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
File renamed without changes.
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
Oops, something went wrong.