Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,17 @@

public void Dispose()
{
try { Directory.Delete(_keyDirectory, recursive: true); } catch { /* best effort */ }
// Best-effort cleanup of the per-test key directory; only the failures a
// filesystem delete can actually produce are worth swallowing.
try
{
Directory.Delete(_keyDirectory, recursive: true);
}
catch (IOException)
{
}
Comment on lines +154 to +156
catch (UnauthorizedAccessException)
{
}
Comment on lines +157 to +159
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,12 @@

namespace DevSecOpsSentinel.Infrastructure.Tests;

public sealed class PublicRepositoryScannerTests
public sealed class PublicRepositoryScannerTests : IDisposable
{
private readonly MemoryCache _cache = new(new MemoryCacheOptions());

public void Dispose() => _cache.Dispose();

private const string VulnerableWorkflow = """
name: CI
on:
Expand Down Expand Up @@ -133,7 +137,7 @@ public async Task A_repository_with_a_workflows_directory_but_no_yml_files_repor
Assert.Equal(PublicScanStatus.NoWorkflows, result.Status);
}

private static PublicRepositoryScanner Scanner(FakeGitHub github) =>
private PublicRepositoryScanner Scanner(FakeGitHub github) =>
new(
github,
new WorkflowAnalysisService(
Expand All @@ -144,7 +148,7 @@ private static PublicRepositoryScanner Scanner(FakeGitHub github) =>
RuleDiscovery.All(),
new NeverResolvesActionReferenceResolver(),
new GitHubOptions())),
new MemoryCache(new MemoryCacheOptions()),
_cache,
TimeProvider.System,
NullLogger<PublicRepositoryScanner>.Instance);

Expand Down Expand Up @@ -193,27 +197,37 @@ protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
// Ownership of these responses transfers to the caller: the scanner
// disposes the listing via `using`, and GetStringAsync disposes the raw
// response internally. Built in methods that return them directly so the
// transfer is visible to analysis.
string url = request.RequestUri!.ToString();
return Task.FromResult(url.Contains("/contents/.github/workflows")
? BuildListingResponse()
: BuildRawResponse(url));
}

if (url.Contains("/contents/.github/workflows"))
private HttpResponseMessage BuildListingResponse()
{
fake.ListingRequests++;
HttpResponseMessage response = new(fake.ListingStatus)
{
fake.ListingRequests++;
HttpResponseMessage response = new(fake.ListingStatus)
{
Content = new StringContent(fake._listingBody, Encoding.UTF8, "application/json")
};
if (fake.RateLimitRemaining is not null)
{
response.Headers.Add("X-RateLimit-Remaining", fake.RateLimitRemaining);
}

return Task.FromResult(response);
Content = new StringContent(fake._listingBody, Encoding.UTF8, "application/json")
};
if (fake.RateLimitRemaining is not null)
{
response.Headers.Add("X-RateLimit-Remaining", fake.RateLimitRemaining);
}

return response;
}

private HttpResponseMessage BuildRawResponse(string url)
{
fake.RawRequests.Add(url);
return Task.FromResult(fake._raw.TryGetValue(url, out string? content)
return fake._raw.TryGetValue(url, out string? content)
? new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(content) }
: new HttpResponseMessage(HttpStatusCode.NotFound));
: new HttpResponseMessage(HttpStatusCode.NotFound);
}
}
}
Expand Down