Revert "Move Go With Replace Detector to the Standard Go Detector" (#1198)
* Revert "go detector (#1194)"
This reverts commit 39058c5561
.
* Update ServiceCollectionExtensions.cs
* Update ServiceCollectionExtensions.cs
This commit is contained in:
Родитель
683f30ee44
Коммит
0707d99b36
|
@ -50,7 +50,7 @@ public class GoComponentDetector : FileComponentDetector
|
|||
|
||||
public override IEnumerable<ComponentType> SupportedComponentTypes { get; } = new[] { ComponentType.Go };
|
||||
|
||||
public override int Version => 8;
|
||||
public override int Version => 7;
|
||||
|
||||
protected override Task<IObservable<ProcessRequest>> OnPrepareDetectionAsync(
|
||||
IObservable<ProcessRequest> processRequests, IDictionary<string, string> detectorArgs)
|
||||
|
@ -282,7 +282,6 @@ public class GoComponentDetector : FileComponentDetector
|
|||
GoGraphTelemetryRecord goGraphTelemetryRecord)
|
||||
{
|
||||
using var reader = new StreamReader(file.Stream);
|
||||
var startString = "require ";
|
||||
|
||||
// There can be multiple require( ) sections in go 1.17+. loop over all of them.
|
||||
while (!reader.EndOfStream)
|
||||
|
@ -298,9 +297,9 @@ public class GoComponentDetector : FileComponentDetector
|
|||
|
||||
// In go >= 1.17, direct dependencies are listed as "require x/y v1.2.3", and transitive dependencies
|
||||
// are listed in the require () section
|
||||
if (line.StartsWith(startString))
|
||||
if (line.StartsWith("require "))
|
||||
{
|
||||
this.TryRegisterDependencyFromModLine(line[startString.Length..], singleFileComponentRecorder);
|
||||
this.TryRegisterDependencyFromModLine(line[8..], singleFileComponentRecorder);
|
||||
}
|
||||
|
||||
line = await reader.ReadLineAsync();
|
||||
|
@ -444,15 +443,7 @@ public class GoComponentDetector : FileComponentDetector
|
|||
continue;
|
||||
}
|
||||
|
||||
GoComponent goComponent;
|
||||
if (dependency.Replace != null)
|
||||
{
|
||||
goComponent = new GoComponent(dependency.Replace.Path, dependency.Replace.Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
goComponent = new GoComponent(dependency.Path, dependency.Version);
|
||||
}
|
||||
var goComponent = new GoComponent(dependency.Path, dependency.Version);
|
||||
|
||||
if (dependency.Indirect)
|
||||
{
|
||||
|
@ -492,7 +483,5 @@ public class GoComponentDetector : FileComponentDetector
|
|||
public string Version { get; set; }
|
||||
|
||||
public bool Indirect { get; set; }
|
||||
|
||||
public GoBuildModule Replace { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,498 @@
|
|||
namespace Microsoft.ComponentDetection.Detectors.Go;
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reactive.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.ComponentDetection.Common;
|
||||
using Microsoft.ComponentDetection.Common.Telemetry.Records;
|
||||
using Microsoft.ComponentDetection.Contracts;
|
||||
using Microsoft.ComponentDetection.Contracts.Internal;
|
||||
using Microsoft.ComponentDetection.Contracts.TypedComponent;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
public class GoComponentWithReplaceDetector : FileComponentDetector, IExperimentalDetector
|
||||
{
|
||||
private static readonly Regex GoSumRegex = new(
|
||||
@"(?<name>.*)\s+(?<version>.*?)(/go\.mod)?\s+(?<hash>.*)",
|
||||
RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
|
||||
|
||||
private readonly HashSet<string> projectRoots = new();
|
||||
|
||||
private readonly ICommandLineInvocationService commandLineInvocationService;
|
||||
private readonly IEnvironmentVariableService envVarService;
|
||||
|
||||
public GoComponentWithReplaceDetector(
|
||||
IComponentStreamEnumerableFactory componentStreamEnumerableFactory,
|
||||
IObservableDirectoryWalkerFactory walkerFactory,
|
||||
ICommandLineInvocationService commandLineInvocationService,
|
||||
IEnvironmentVariableService envVarService,
|
||||
ILogger<GoComponentWithReplaceDetector> logger)
|
||||
{
|
||||
this.ComponentStreamEnumerableFactory = componentStreamEnumerableFactory;
|
||||
this.Scanner = walkerFactory;
|
||||
this.commandLineInvocationService = commandLineInvocationService;
|
||||
this.envVarService = envVarService;
|
||||
this.Logger = logger;
|
||||
}
|
||||
|
||||
public override string Id => "GoWithReplace";
|
||||
|
||||
public override IEnumerable<string> Categories => new[] { Enum.GetName(typeof(DetectorClass), DetectorClass.GoMod) };
|
||||
|
||||
public override IList<string> SearchPatterns { get; } = new List<string> { "go.mod", "go.sum" };
|
||||
|
||||
public override IEnumerable<ComponentType> SupportedComponentTypes { get; } = new[] { ComponentType.Go };
|
||||
|
||||
public override int Version => 1;
|
||||
|
||||
protected override Task<IObservable<ProcessRequest>> OnPrepareDetectionAsync(
|
||||
IObservable<ProcessRequest> processRequests, IDictionary<string, string> detectorArgs)
|
||||
{
|
||||
// Filter out any go.sum process requests if the adjacent go.mod file is present and has a go version >= 1.17
|
||||
var goModProcessRequests = processRequests.Where(processRequest =>
|
||||
{
|
||||
if (Path.GetFileName(processRequest.ComponentStream.Location) != "go.sum")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var goModFile = this.FindAdjacentGoModComponentStreams(processRequest).FirstOrDefault();
|
||||
|
||||
if (goModFile == null)
|
||||
{
|
||||
this.Logger.LogDebug(
|
||||
"go.sum file found without an adjacent go.mod file. Location: {Location}",
|
||||
processRequest.ComponentStream.Location);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// parse the go.mod file to get the go version
|
||||
using var reader = new StreamReader(goModFile.Stream);
|
||||
var goModFileContents = reader.ReadToEnd();
|
||||
goModFile.Stream.Dispose();
|
||||
|
||||
return this.CheckGoModVersion(goModFileContents, processRequest, goModFile);
|
||||
});
|
||||
|
||||
return Task.FromResult(goModProcessRequests);
|
||||
}
|
||||
|
||||
private IEnumerable<ComponentStream> FindAdjacentGoModComponentStreams(ProcessRequest processRequest) =>
|
||||
this.ComponentStreamEnumerableFactory.GetComponentStreams(
|
||||
new FileInfo(processRequest.ComponentStream.Location).Directory,
|
||||
new[] { "go.mod" },
|
||||
(_, _) => false,
|
||||
false)
|
||||
.Select(x =>
|
||||
{
|
||||
// The stream will be disposed at the end of this method, so we need to copy it to a new stream.
|
||||
var memoryStream = new MemoryStream();
|
||||
|
||||
x.Stream.CopyTo(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
return new ComponentStream
|
||||
{
|
||||
Stream = memoryStream,
|
||||
Location = x.Location,
|
||||
Pattern = x.Pattern,
|
||||
};
|
||||
});
|
||||
|
||||
private bool CheckGoModVersion(string goModFileContents, ProcessRequest processRequest, ComponentStream goModFile)
|
||||
{
|
||||
var goVersionMatch = Regex.Match(goModFileContents, @"go\s(?<version>\d+\.\d+)");
|
||||
|
||||
if (!goVersionMatch.Success)
|
||||
{
|
||||
this.Logger.LogDebug(
|
||||
"go.sum file found with an adjacent go.mod file that does not contain a go version. Location: {Location}",
|
||||
processRequest.ComponentStream.Location);
|
||||
return true;
|
||||
}
|
||||
|
||||
var goVersion = goVersionMatch.Groups["version"].Value;
|
||||
if (System.Version.TryParse(goVersion, out var version))
|
||||
{
|
||||
if (version < new Version(1, 17))
|
||||
{
|
||||
this.Logger.LogWarning(
|
||||
"go.mod file at {GoModLocation} does not have a go version >= 1.17. Scanning this go.sum file: {GoSumLocation} which may lead to over reporting components",
|
||||
goModFile.Location,
|
||||
processRequest.ComponentStream.Location);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
this.Logger.LogInformation(
|
||||
"go.sum file found with an adjacent go.mod file that has a go version >= 1.17. Will not scan this go.sum file. Location: {Location}",
|
||||
processRequest.ComponentStream.Location);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
this.Logger.LogWarning(
|
||||
"go.sum file found with an adjacent go.mod file that has an invalid go version. Scanning both for components. Location: {Location}",
|
||||
processRequest.ComponentStream.Location);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override async Task OnFileFoundAsync(ProcessRequest processRequest, IDictionary<string, string> detectorArgs, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var singleFileComponentRecorder = processRequest.SingleFileComponentRecorder;
|
||||
var file = processRequest.ComponentStream;
|
||||
|
||||
var projectRootDirectory = Directory.GetParent(file.Location);
|
||||
if (this.projectRoots.Any(path => projectRootDirectory.FullName.StartsWith(path)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using var record = new GoGraphTelemetryRecord();
|
||||
record.WasGoCliDisabled = false;
|
||||
record.WasGoFallbackStrategyUsed = false;
|
||||
|
||||
var wasGoCliScanSuccessful = false;
|
||||
try
|
||||
{
|
||||
if (!this.IsGoCliManuallyDisabled())
|
||||
{
|
||||
wasGoCliScanSuccessful = await this.UseGoCliToScanAsync(file.Location, singleFileComponentRecorder, record);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.WasGoCliDisabled = true;
|
||||
this.Logger.LogInformation("Go cli scan was manually disabled, fallback strategy performed." +
|
||||
" More info: https://github.com/microsoft/component-detection/blob/main/docs/detectors/go.md#fallback-detection-strategy");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.Logger.LogError(ex, "Failed to detect components using go cli. Location: {Location}", file.Location);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (wasGoCliScanSuccessful)
|
||||
{
|
||||
this.projectRoots.Add(projectRootDirectory.FullName);
|
||||
}
|
||||
else
|
||||
{
|
||||
record.WasGoFallbackStrategyUsed = true;
|
||||
var fileExtension = Path.GetExtension(file.Location).ToUpperInvariant();
|
||||
switch (fileExtension)
|
||||
{
|
||||
case ".MOD":
|
||||
{
|
||||
this.Logger.LogDebug("Found Go.mod: {Location}", file.Location);
|
||||
await this.ParseGoModFileAsync(singleFileComponentRecorder, file, record);
|
||||
break;
|
||||
}
|
||||
|
||||
case ".SUM":
|
||||
{
|
||||
this.Logger.LogDebug("Found Go.sum: {Location}", file.Location);
|
||||
this.ParseGoSumFile(singleFileComponentRecorder, file);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
throw new InvalidOperationException("Unexpected file type detected in go detector");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Maintainability", "CA1508:Avoid dead conditional code", Justification = "False positive")]
|
||||
private async Task<bool> UseGoCliToScanAsync(string location, ISingleFileComponentRecorder singleFileComponentRecorder, GoGraphTelemetryRecord record)
|
||||
{
|
||||
record.WasGraphSuccessful = false;
|
||||
record.DidGoCliCommandFail = false;
|
||||
var projectRootDirectory = Directory.GetParent(location);
|
||||
record.ProjectRoot = projectRootDirectory.FullName;
|
||||
|
||||
var isGoAvailable = await this.commandLineInvocationService.CanCommandBeLocatedAsync("go", null, workingDirectory: projectRootDirectory, new[] { "version" });
|
||||
record.IsGoAvailable = isGoAvailable;
|
||||
|
||||
if (!isGoAvailable)
|
||||
{
|
||||
this.Logger.LogInformation("Go CLI was not found in the system");
|
||||
return false;
|
||||
}
|
||||
|
||||
this.Logger.LogInformation("Go CLI was found in system and will be used to generate dependency graph. " +
|
||||
"Detection time may be improved by activating fallback strategy (https://github.com/microsoft/component-detection/blob/main/docs/detectors/go.md#fallback-detection-strategy). " +
|
||||
"But, it will introduce noise into the detected components.");
|
||||
var goDependenciesProcess = await this.commandLineInvocationService.ExecuteCommandAsync("go", null, workingDirectory: projectRootDirectory, new[] { "list", "-mod=readonly", "-m", "-json", "all" });
|
||||
if (goDependenciesProcess.ExitCode != 0)
|
||||
{
|
||||
this.Logger.LogError("Go CLI command \"go list -m -json all\" failed with error: {GoDependenciesProcessStdErr}", goDependenciesProcess.StdErr);
|
||||
this.Logger.LogError("Go CLI could not get dependency build list at location: {Location}. Fallback go.sum/go.mod parsing will be used.", location);
|
||||
record.DidGoCliCommandFail = true;
|
||||
record.GoCliCommandError = goDependenciesProcess.StdErr;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.RecordBuildDependencies(goDependenciesProcess.StdOut, singleFileComponentRecorder);
|
||||
|
||||
var generateGraphProcess = await this.commandLineInvocationService.ExecuteCommandAsync("go", null, workingDirectory: projectRootDirectory, new List<string> { "mod", "graph" }.ToArray());
|
||||
if (generateGraphProcess.ExitCode == 0)
|
||||
{
|
||||
this.PopulateDependencyGraph(generateGraphProcess.StdOut, singleFileComponentRecorder);
|
||||
record.WasGraphSuccessful = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void TryRegisterDependencyFromModLine(string line, ISingleFileComponentRecorder singleFileComponentRecorder)
|
||||
{
|
||||
if (line.Trim().StartsWith("//"))
|
||||
{
|
||||
// this is a comment line, ignore it
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.TryToCreateGoComponentFromModLine(line, out var goComponent))
|
||||
{
|
||||
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(goComponent));
|
||||
}
|
||||
else
|
||||
{
|
||||
var lineTrim = line.Trim();
|
||||
this.Logger.LogWarning("Line could not be parsed for component [{LineTrim}]", lineTrim);
|
||||
singleFileComponentRecorder.RegisterPackageParseFailure(lineTrim);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ParseGoModFileAsync(
|
||||
ISingleFileComponentRecorder singleFileComponentRecorder,
|
||||
IComponentStream file,
|
||||
GoGraphTelemetryRecord goGraphTelemetryRecord)
|
||||
{
|
||||
using var reader = new StreamReader(file.Stream);
|
||||
var startString = "require ";
|
||||
|
||||
// There can be multiple require( ) sections in go 1.17+. loop over all of them.
|
||||
while (!reader.EndOfStream)
|
||||
{
|
||||
var line = await reader.ReadLineAsync();
|
||||
|
||||
while (line != null && !line.StartsWith("require ("))
|
||||
{
|
||||
if (line.StartsWith("go "))
|
||||
{
|
||||
goGraphTelemetryRecord.GoModVersion = line[3..].Trim();
|
||||
}
|
||||
|
||||
// In go >= 1.17, direct dependencies are listed as "require x/y v1.2.3", and transitive dependencies
|
||||
// are listed in the require () section
|
||||
if (line.StartsWith(startString))
|
||||
{
|
||||
this.TryRegisterDependencyFromModLine(line[startString.Length..], singleFileComponentRecorder);
|
||||
}
|
||||
|
||||
line = await reader.ReadLineAsync();
|
||||
}
|
||||
|
||||
// Stopping at the first ) restrict the detection to only the require section.
|
||||
while ((line = await reader.ReadLineAsync()) != null && !line.EndsWith(")"))
|
||||
{
|
||||
this.TryRegisterDependencyFromModLine(line, singleFileComponentRecorder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryToCreateGoComponentFromModLine(string line, out GoComponent goComponent)
|
||||
{
|
||||
var lineComponents = Regex.Split(line.Trim(), @"\s+");
|
||||
|
||||
if (lineComponents.Length < 2)
|
||||
{
|
||||
goComponent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var name = lineComponents[0];
|
||||
var version = lineComponents[1];
|
||||
goComponent = new GoComponent(name, version);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// For more information about the format of the go.sum file
|
||||
// visit https://golang.org/cmd/go/#hdr-Module_authentication_using_go_sum
|
||||
private void ParseGoSumFile(
|
||||
ISingleFileComponentRecorder singleFileComponentRecorder,
|
||||
IComponentStream file)
|
||||
{
|
||||
using var reader = new StreamReader(file.Stream);
|
||||
|
||||
string line;
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
{
|
||||
if (this.TryToCreateGoComponentFromSumLine(line, out var goComponent))
|
||||
{
|
||||
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(goComponent));
|
||||
}
|
||||
else
|
||||
{
|
||||
var lineTrim = line.Trim();
|
||||
this.Logger.LogWarning("Line could not be parsed for component [{LineTrim}]", lineTrim);
|
||||
singleFileComponentRecorder.RegisterPackageParseFailure(lineTrim);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryToCreateGoComponentFromSumLine(string line, out GoComponent goComponent)
|
||||
{
|
||||
var m = GoSumRegex.Match(line);
|
||||
if (m.Success)
|
||||
{
|
||||
goComponent = new GoComponent(m.Groups["name"].Value, m.Groups["version"].Value, m.Groups["hash"].Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
goComponent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This command only adds edges between parent and child components, it does not add nor remove any entries from the existing build list.
|
||||
/// </summary>
|
||||
private void PopulateDependencyGraph(string goGraphOutput, ISingleFileComponentRecorder componentRecorder)
|
||||
{
|
||||
// Yes, go always returns \n even on Windows
|
||||
var graphRelationships = goGraphOutput.Split('\n');
|
||||
|
||||
foreach (var relationship in graphRelationships)
|
||||
{
|
||||
var components = relationship.Split(' ');
|
||||
if (components.Length != 2)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(relationship))
|
||||
{
|
||||
// normally the last line is an empty string
|
||||
continue;
|
||||
}
|
||||
|
||||
this.Logger.LogWarning("Unexpected relationship output from go mod graph: {Relationship}", relationship);
|
||||
continue;
|
||||
}
|
||||
|
||||
var isParentParsed = this.TryCreateGoComponentFromRelationshipPart(components[0], out var parentComponent);
|
||||
var isChildParsed = this.TryCreateGoComponentFromRelationshipPart(components[1], out var childComponent);
|
||||
|
||||
if (!isParentParsed)
|
||||
{
|
||||
// These are explicit dependencies, we already have those recorded
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isChildParsed)
|
||||
{
|
||||
if (this.IsModuleInBuildList(componentRecorder, parentComponent) && this.IsModuleInBuildList(componentRecorder, childComponent))
|
||||
{
|
||||
componentRecorder.RegisterUsage(new DetectedComponent(childComponent), parentComponentId: parentComponent.Id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Logger.LogWarning("Failed to parse components from relationship string {Relationship}", relationship);
|
||||
componentRecorder.RegisterPackageParseFailure(relationship);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsModuleInBuildList(ISingleFileComponentRecorder singleFileComponentRecorder, GoComponent component)
|
||||
{
|
||||
return singleFileComponentRecorder.GetComponent(component.Id) != null;
|
||||
}
|
||||
|
||||
private void RecordBuildDependencies(string goListOutput, ISingleFileComponentRecorder singleFileComponentRecorder)
|
||||
{
|
||||
var goBuildModules = new List<GoBuildModule>();
|
||||
var reader = new JsonTextReader(new StringReader(goListOutput))
|
||||
{
|
||||
SupportMultipleContent = true,
|
||||
};
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
var serializer = new JsonSerializer();
|
||||
var buildModule = serializer.Deserialize<GoBuildModule>(reader);
|
||||
|
||||
goBuildModules.Add(buildModule);
|
||||
}
|
||||
|
||||
foreach (var dependency in goBuildModules)
|
||||
{
|
||||
if (dependency.Main)
|
||||
{
|
||||
// main is the entry point module (superfluous as we already have the file location)
|
||||
continue;
|
||||
}
|
||||
|
||||
GoComponent goComponent;
|
||||
if (dependency.Replace != null)
|
||||
{
|
||||
goComponent = new GoComponent(dependency.Replace.Path, dependency.Replace.Version);
|
||||
}
|
||||
else
|
||||
{
|
||||
goComponent = new GoComponent(dependency.Path, dependency.Version);
|
||||
}
|
||||
|
||||
if (dependency.Indirect)
|
||||
{
|
||||
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(goComponent));
|
||||
}
|
||||
else
|
||||
{
|
||||
singleFileComponentRecorder.RegisterUsage(new DetectedComponent(goComponent), isExplicitReferencedDependency: true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryCreateGoComponentFromRelationshipPart(string relationship, out GoComponent goComponent)
|
||||
{
|
||||
var componentParts = relationship.Split('@');
|
||||
if (componentParts.Length != 2)
|
||||
{
|
||||
goComponent = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
goComponent = new GoComponent(componentParts[0], componentParts[1]);
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsGoCliManuallyDisabled()
|
||||
{
|
||||
return this.envVarService.IsEnvironmentVariableValueTrue("DisableGoCliScan");
|
||||
}
|
||||
|
||||
private class GoBuildModule
|
||||
{
|
||||
public string Path { get; set; }
|
||||
|
||||
public bool Main { get; set; }
|
||||
|
||||
public string Version { get; set; }
|
||||
|
||||
public bool Indirect { get; set; }
|
||||
|
||||
public GoBuildModule Replace { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
namespace Microsoft.ComponentDetection.Orchestrator.Experiments.Configs;
|
||||
|
||||
using Microsoft.ComponentDetection.Contracts;
|
||||
using Microsoft.ComponentDetection.Detectors.Go;
|
||||
|
||||
/// <summary>
|
||||
/// Validating the <see cref="GoComponentWithReplaceDetector"/>.
|
||||
/// </summary>
|
||||
public class GoDetectorReplaceExperiment : IExperimentConfiguration
|
||||
{
|
||||
public string Name => "GoWithReplace";
|
||||
|
||||
public bool IsInControlGroup(IComponentDetector componentDetector) => componentDetector is GoComponentDetector;
|
||||
|
||||
public bool IsInExperimentGroup(IComponentDetector componentDetector) => componentDetector is GoComponentWithReplaceDetector;
|
||||
|
||||
public bool ShouldRecord(IComponentDetector componentDetector, int numComponents) => true;
|
||||
}
|
|
@ -63,6 +63,7 @@ public static class ServiceCollectionExtensions
|
|||
services.AddSingleton<IExperimentConfiguration, SimplePipExperiment>();
|
||||
services.AddSingleton<IExperimentConfiguration, RustCliDetectorExperiment>();
|
||||
services.AddSingleton<IExperimentConfiguration, VcpkgExperiment>();
|
||||
services.AddSingleton<IExperimentConfiguration, GoDetectorReplaceExperiment>();
|
||||
|
||||
// Detectors
|
||||
// CocoaPods
|
||||
|
@ -79,6 +80,7 @@ public static class ServiceCollectionExtensions
|
|||
|
||||
// Go
|
||||
services.AddSingleton<IComponentDetector, GoComponentDetector>();
|
||||
services.AddSingleton<IComponentDetector, GoComponentWithReplaceDetector>();
|
||||
|
||||
// Gradle
|
||||
services.AddSingleton<IComponentDetector, GradleComponentDetector>();
|
||||
|
|
|
@ -368,82 +368,6 @@ replace (
|
|||
await this.TestGoSumDetectorWithValidFile_ReturnsSuccessfullyAsync();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoGraphReplaceAsync()
|
||||
{
|
||||
var buildDependencies = @"{
|
||||
""Path"": ""some-package"",
|
||||
""Version"": ""v1.2.3"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11"",
|
||||
""Replace"": {
|
||||
""Path"": ""some-package"",
|
||||
""Version"": ""v1.2.4"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11"",
|
||||
}
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""test"",
|
||||
""Version"": ""v2.0.0"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""other"",
|
||||
""Version"": ""v1.2.0"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""a"",
|
||||
""Version"": ""v1.5.0"",
|
||||
""Time"": ""2020-05-19T17:02:07Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}";
|
||||
var goGraph = "example.com/mainModule some-package@v1.2.3\nsome-package@v1.2.3 other@v1.0.0\nsome-package@v1.2.3 other@v1.2.0\ntest@v2.0.0 a@v1.5.0";
|
||||
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go", null, It.IsAny<DirectoryInfo>(), new[] { "list", "-mod=readonly", "-m", "-json", "all" }))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 0,
|
||||
StdOut = buildDependencies,
|
||||
});
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go", null, It.IsAny<DirectoryInfo>(), new[] { "mod", "graph" }))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 0,
|
||||
StdOut = goGraph,
|
||||
});
|
||||
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(false);
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", string.Empty)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().HaveCount(4);
|
||||
detectedComponents.Should().NotContain(component => component.Component.Id == "other v1.0.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "other v1.2.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "some-package v1.2.4 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "test v2.0.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "a v1.5.0 - Go");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoGraphHappyPathAsync()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,579 @@
|
|||
namespace Microsoft.ComponentDetection.Detectors.Tests;
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentAssertions;
|
||||
using Microsoft.ComponentDetection.Contracts;
|
||||
using Microsoft.ComponentDetection.Contracts.TypedComponent;
|
||||
using Microsoft.ComponentDetection.Detectors.Go;
|
||||
using Microsoft.ComponentDetection.TestsUtilities;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
|
||||
[TestClass]
|
||||
[TestCategory("Governance/All")]
|
||||
[TestCategory("Governance/ComponentDetection")]
|
||||
public class GoComponentWithReplaceDetectorTests : BaseDetectorTest<GoComponentWithReplaceDetector>
|
||||
{
|
||||
private readonly Mock<ICommandLineInvocationService> commandLineMock;
|
||||
private readonly Mock<IEnvironmentVariableService> envVarService;
|
||||
|
||||
public GoComponentWithReplaceDetectorTests()
|
||||
{
|
||||
this.commandLineMock = new Mock<ICommandLineInvocationService>();
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(false);
|
||||
this.DetectorTestUtility.AddServiceMock(this.commandLineMock);
|
||||
|
||||
this.envVarService = new Mock<IEnvironmentVariableService>();
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(true);
|
||||
this.DetectorTestUtility.AddServiceMock(this.envVarService);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoModDetectorWithValidFile_ReturnsSuccessfullyAsync()
|
||||
{
|
||||
var goMod =
|
||||
@"module github.com/Azure/azure-storage-blob-go
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-pipeline-go v0.2.1
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
)";
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", goMod)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().HaveCount(4);
|
||||
|
||||
var discoveredComponents = detectedComponents.ToArray();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/Azure/azure-pipeline-go v0.2.1 - Go").Should().ContainSingle();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/dgrijalva/jwt-go v3.2.0+incompatible - Go").Should().ContainSingle();
|
||||
discoveredComponents.Where(component => component.Component.Id == "gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 - Go").Should().ContainSingle();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/kr/pretty v0.1.0 - Go").Should().ContainSingle();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoModDetector_CommentsOnFile_CommentsAreIgnoredAsync()
|
||||
{
|
||||
var goMod =
|
||||
@"module github.com/Azure/azure-storage-blob-go
|
||||
|
||||
require (
|
||||
// comment
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
)";
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", goMod)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().ContainSingle("there is only one component definition on the file");
|
||||
|
||||
var discoveredComponents = detectedComponents.ToArray();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/kr/pretty v0.1.0 - Go").Should().ContainSingle();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoSumDetectorWithValidFile_ReturnsSuccessfullyAsync()
|
||||
{
|
||||
var goSum =
|
||||
@"
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
)";
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.sum", goSum)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().HaveCount(6);
|
||||
var typedComponents = detectedComponents.Select(d => d.Component).ToList();
|
||||
typedComponents.Should().Contain(
|
||||
new GoComponent("github.com/golang/mock", "v1.1.1", "h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A="));
|
||||
typedComponents.Should().Contain(
|
||||
new GoComponent("github.com/golang/mock", "v1.2.0", "h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A="));
|
||||
typedComponents.Should().Contain(
|
||||
new GoComponent("github.com/golang/protobuf", "v0.0.0-20161109072736-4bd1920723d7", "h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U="));
|
||||
typedComponents.Should().Contain(
|
||||
new GoComponent("github.com/golang/protobuf", "v1.2.0", "h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U="));
|
||||
typedComponents.Should().Contain(
|
||||
new GoComponent("github.com/golang/protobuf", "v1.3.1", "h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg="));
|
||||
typedComponents.Should().Contain(
|
||||
new GoComponent("github.com/golang/protobuf", "v1.3.2", "h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs="));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoModDetector_MultipleSpaces_ReturnsSuccessfullyAsync()
|
||||
{
|
||||
var goMod =
|
||||
@"module github.com/Azure/azure-storage-blob-go
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-pipeline-go v0.2.1
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
)";
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", goMod)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().HaveCount(4);
|
||||
|
||||
var discoveredComponents = detectedComponents.ToArray();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/Azure/azure-pipeline-go v0.2.1 - Go").Should().ContainSingle();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/dgrijalva/jwt-go v3.2.0+incompatible - Go").Should().ContainSingle();
|
||||
discoveredComponents.Where(component => component.Component.Id == "gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 - Go").Should().ContainSingle();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/kr/pretty v0.1.0 - Go").Should().ContainSingle();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoModDetector_ComponentsWithMultipleLocations_ReturnsSuccessfullyAsync()
|
||||
{
|
||||
var goMod1 =
|
||||
@"module github.com/Azure/azure-storage-blob-go
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-pipeline-go v0.2.1
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
|
||||
github.com/Azure/go-autorest v10.15.2+incompatible
|
||||
)";
|
||||
var goMod2 =
|
||||
@"module github.com/Azure/azure-storage-blob-go
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-pipeline-go v0.2.1
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127
|
||||
github.com/Azure/go-autorest v10.15.2+incompatible
|
||||
)";
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", goMod1)
|
||||
.WithFile("go.mod", goMod2, fileLocation: Path.Join(Path.GetTempPath(), "another-location", "go.mod"))
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
componentRecorder.GetDetectedComponents().Count().Should().Be(4);
|
||||
|
||||
var dependencyGraphs = componentRecorder.GetDependencyGraphsByLocation();
|
||||
dependencyGraphs.Keys.Should().HaveCount(2);
|
||||
|
||||
var firstGraph = dependencyGraphs.Values.First();
|
||||
var secondGraph = dependencyGraphs.Values.Skip(1).First();
|
||||
|
||||
firstGraph.GetComponents().Should().BeEquivalentTo(secondGraph.GetComponents());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoModDetectorInvalidFiles_DoesNotFailAsync()
|
||||
{
|
||||
var invalidGoMod =
|
||||
@" #/bin/sh
|
||||
lorem ipsum
|
||||
four score and seven bugs ago
|
||||
$#26^#25%4";
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", invalidGoMod)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
componentRecorder.GetDetectedComponents().Count().Should().Be(0);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoModDetector_SkipsGoSumFilesAsync()
|
||||
{
|
||||
var goMod =
|
||||
@"module contoso.com/greetings
|
||||
go 1.18
|
||||
|
||||
require github.com/go-sql-driver/mysql v1.7.1 // indirect";
|
||||
|
||||
var goSum =
|
||||
@"github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=";
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", goMod)
|
||||
.WithFile("go.mod", goMod, new[] { "go.mod" })
|
||||
.WithFile("go.sum", goSum)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
componentRecorder.GetDetectedComponents().Should().ContainSingle();
|
||||
|
||||
var component = componentRecorder.GetDetectedComponents().First();
|
||||
component.Component.Id.Should().Be("github.com/go-sql-driver/mysql v1.7.1 - Go");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoModDetector_HandlesTwoRequiresSectionsAsync()
|
||||
{
|
||||
var goMod =
|
||||
@"module microsoft/component-detection
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
rsc.io/quote v1.5.2
|
||||
)
|
||||
|
||||
require (
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
|
||||
rsc.io/sampler v1.3.0 // indirect
|
||||
)";
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", goMod)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
componentRecorder.GetDetectedComponents().Should().HaveCount(4);
|
||||
|
||||
var expectedComponentIds = new[]
|
||||
{
|
||||
"github.com/go-sql-driver/mysql v1.7.1 - Go", "rsc.io/quote v1.5.2 - Go",
|
||||
"golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c - Go", "rsc.io/sampler v1.3.0 - Go",
|
||||
};
|
||||
|
||||
componentRecorder.GetDetectedComponents().Select(c => c.Component.Id).Should().BeEquivalentTo(expectedComponentIds);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoSumDetection_TwoEntriesForTheSameComponent_ReturnsSuccessfullyAsync()
|
||||
{
|
||||
var goSum =
|
||||
@"
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM=
|
||||
github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4=
|
||||
)";
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.sum", goSum)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().ContainSingle();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoModDetector_DetectorOnlyDetectInsideRequireSectionAsync()
|
||||
{
|
||||
var goMod =
|
||||
@"module github.com/Azure/azure-storage-blob-go
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-pipeline-go v0.2.1
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
)
|
||||
replace (
|
||||
github.com/Azure/go-autorest => github.com/Azure/go-autorest v13.3.2+incompatible
|
||||
github.com/docker/distribution => github.com/docker/distribution v0.0.0-20191216044856-a8371794149d
|
||||
)
|
||||
";
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", goMod)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().HaveCount(2);
|
||||
|
||||
var discoveredComponents = detectedComponents.ToArray();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/Azure/azure-pipeline-go v0.2.1 - Go").Should().ContainSingle();
|
||||
discoveredComponents.Where(component => component.Component.Id == "github.com/kr/pretty v0.1.0 - Go").Should().ContainSingle();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoCommandNotFoundAsync()
|
||||
{
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(false);
|
||||
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(false);
|
||||
|
||||
await this.TestGoSumDetectorWithValidFile_ReturnsSuccessfullyAsync();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoCommandThrowsAsync()
|
||||
{
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(() => throw new InvalidOperationException("Some horrible error occured"));
|
||||
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(false);
|
||||
|
||||
await this.TestGoSumDetectorWithValidFile_ReturnsSuccessfullyAsync();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoGraphCommandFailsAsync()
|
||||
{
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go mod graph", null, It.IsAny<DirectoryInfo>(), It.IsAny<string>()))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 1,
|
||||
});
|
||||
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(false);
|
||||
|
||||
await this.TestGoSumDetectorWithValidFile_ReturnsSuccessfullyAsync();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoGraphCommandThrowsAsync()
|
||||
{
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go mod graph", null, It.IsAny<DirectoryInfo>(), It.IsAny<string>()))
|
||||
.ReturnsAsync(() => throw new InvalidOperationException("Some horrible error occured"));
|
||||
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(false);
|
||||
|
||||
await this.TestGoSumDetectorWithValidFile_ReturnsSuccessfullyAsync();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoGraphReplaceAsync()
|
||||
{
|
||||
var buildDependencies = @"{
|
||||
""Path"": ""some-package"",
|
||||
""Version"": ""v1.2.3"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11"",
|
||||
""Replace"": {
|
||||
""Path"": ""some-package"",
|
||||
""Version"": ""v1.2.4"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11"",
|
||||
}
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""test"",
|
||||
""Version"": ""v2.0.0"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""other"",
|
||||
""Version"": ""v1.2.0"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""a"",
|
||||
""Version"": ""v1.5.0"",
|
||||
""Time"": ""2020-05-19T17:02:07Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}";
|
||||
var goGraph = "example.com/mainModule some-package@v1.2.3\nsome-package@v1.2.3 other@v1.0.0\nsome-package@v1.2.3 other@v1.2.0\ntest@v2.0.0 a@v1.5.0";
|
||||
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go", null, It.IsAny<DirectoryInfo>(), new[] { "list", "-mod=readonly", "-m", "-json", "all" }))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 0,
|
||||
StdOut = buildDependencies,
|
||||
});
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go", null, It.IsAny<DirectoryInfo>(), new[] { "mod", "graph" }))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 0,
|
||||
StdOut = goGraph,
|
||||
});
|
||||
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(false);
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", string.Empty)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().HaveCount(4);
|
||||
detectedComponents.Should().NotContain(component => component.Component.Id == "other v1.0.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "other v1.2.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "some-package v1.2.4 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "test v2.0.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "a v1.5.0 - Go");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoGraphHappyPathAsync()
|
||||
{
|
||||
var buildDependencies = @"{
|
||||
""Path"": ""some-package"",
|
||||
""Version"": ""v1.2.3"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""test"",
|
||||
""Version"": ""v2.0.0"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""other"",
|
||||
""Version"": ""v1.2.0"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""a"",
|
||||
""Version"": ""v1.5.0"",
|
||||
""Time"": ""2020-05-19T17:02:07Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}";
|
||||
var goGraph = "example.com/mainModule some-package@v1.2.3\nsome-package@v1.2.3 other@v1.0.0\nsome-package@v1.2.3 other@v1.2.0\ntest@v2.0.0 a@v1.5.0";
|
||||
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go", null, It.IsAny<DirectoryInfo>(), new[] { "list", "-mod=readonly", "-m", "-json", "all" }))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 0,
|
||||
StdOut = buildDependencies,
|
||||
});
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go", null, It.IsAny<DirectoryInfo>(), new[] { "mod", "graph" }))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 0,
|
||||
StdOut = goGraph,
|
||||
});
|
||||
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(false);
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", string.Empty)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().HaveCount(4);
|
||||
detectedComponents.Should().NotContain(component => component.Component.Id == "other v1.0.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "other v1.2.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "some-package v1.2.3 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "test v2.0.0 - Go");
|
||||
detectedComponents.Should().ContainSingle(component => component.Component.Id == "a v1.5.0 - Go");
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoGraphCyclicDependenciesAsync()
|
||||
{
|
||||
var buildDependencies = @"{
|
||||
""Path"": ""github.com/prometheus/common"",
|
||||
""Version"": ""v0.32.1"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""github.com/prometheus/client_golang"",
|
||||
""Version"": ""v1.11.0"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}" + "\n" + @"{
|
||||
""Path"": ""github.com/prometheus/client_golang"",
|
||||
""Version"": ""v1.12.1"",
|
||||
""Time"": ""2021-12-06T23:04:27Z"",
|
||||
""Indirect"": true,
|
||||
""GoMod"": ""C:\\test\\go.mod"",
|
||||
""GoVersion"": ""1.11""
|
||||
}";
|
||||
var goGraph = @"
|
||||
github.com/prometheus/common@v0.32.1 github.com/prometheus/client_golang@v1.11.0
|
||||
github.com/prometheus/client_golang@v1.12.1 github.com/prometheus/common@v0.32.1";
|
||||
this.commandLineMock.Setup(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
|
||||
.ReturnsAsync(true);
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go", null, It.IsAny<DirectoryInfo>(), new[] { "list", "-mod=readonly", "-m", "-json", "all" }))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 0,
|
||||
StdOut = buildDependencies,
|
||||
});
|
||||
|
||||
this.commandLineMock.Setup(x => x.ExecuteCommandAsync("go", null, It.IsAny<DirectoryInfo>(), new[] { "mod", "graph" }))
|
||||
.ReturnsAsync(new CommandLineExecutionResult
|
||||
{
|
||||
ExitCode = 0,
|
||||
StdOut = goGraph,
|
||||
});
|
||||
|
||||
this.envVarService.Setup(x => x.IsEnvironmentVariableValueTrue("DisableGoCliScan")).Returns(false);
|
||||
|
||||
var (scanResult, componentRecorder) = await this.DetectorTestUtility
|
||||
.WithFile("go.mod", string.Empty)
|
||||
.ExecuteDetectorAsync();
|
||||
|
||||
scanResult.ResultCode.Should().Be(ProcessingResultCode.Success);
|
||||
|
||||
var detectedComponents = componentRecorder.GetDetectedComponents();
|
||||
detectedComponents.Should().HaveCount(3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public async Task TestGoDetector_GoCliRequiresEnvVarToRunAsync()
|
||||
{
|
||||
await this.TestGoSumDetectorWithValidFile_ReturnsSuccessfullyAsync();
|
||||
|
||||
this.commandLineMock.Verify(x => x.CanCommandBeLocatedAsync("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()), Times.Never);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче