Skip to content

Commit 70c149c

Browse files
authored
Allow opt-in go cli scanning (#12)
* Wrap up opt-in behavior * Verify needs to be called after * 🎨 * 🎨 * Fix service creation * πŸ› οΈ * πŸ› fix
1 parent 11935c1 commit 70c149c

8 files changed

Lines changed: 119 additions & 23 deletions

File tree

β€Ž.vscode/launch.jsonβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"request": "launch",
1111
"preLaunchTask": "build",
1212
// If you have changed target frameworks, make sure to update the program path.
13-
"program": "${workspaceFolder}/src/Microsoft.ComponentDetection/bin/Debug/net6.0/Microsoft.ComponentDetection.dll",
13+
"program": "${workspaceFolder}/src/Microsoft.ComponentDetection/bin/Debug/netcoreapp3.1/Microsoft.ComponentDetection.dll",
1414
"args": ["scan", "--Debug", "--Verbosity", "Verbose", "--Output", "${workspaceFolder}/scan-output", "--SourceDirectory", "${workspaceFolder}"],
1515
"cwd": "${workspaceFolder}/src/Microsoft.ComponentDetection",
1616
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console

β€Žsrc/Microsoft.ComponentDetection.Common/DetectorDependencies.csβ€Ž

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,8 @@ public class DetectorDependencies : IDetectorDependencies
2626

2727
[Import]
2828
public IDockerService DockerService { get; set; }
29+
30+
[Import]
31+
public IEnvironmentVariableService EnvironmentVariableService { get; set; }
2932
}
3033
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Composition;
3+
using Microsoft.ComponentDetection.Contracts;
4+
5+
namespace Microsoft.ComponentDetection.Common
6+
{
7+
[Export(typeof(IEnvironmentVariableService))]
8+
public class EnvironmentVariableService : IEnvironmentVariableService
9+
{
10+
public bool DoesEnvironmentVariableExist(string name)
11+
{
12+
var enabledVar = Environment.GetEnvironmentVariable(name);
13+
return !string.IsNullOrEmpty(enabledVar);
14+
}
15+
}
16+
}

β€Žsrc/Microsoft.ComponentDetection.Contracts/IDetectorDependencies.csβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ public interface IDetectorDependencies
1515
IObservableDirectoryWalkerFactory DirectoryWalkerFactory { get; set; }
1616

1717
IDockerService DockerService { get; set; }
18+
19+
IEnvironmentVariableService EnvironmentVariableService { get; set; }
1820
}
1921
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace Microsoft.ComponentDetection.Contracts
2+
{
3+
public interface IEnvironmentVariableService
4+
{
5+
bool DoesEnvironmentVariableExist(string name);
6+
}
7+
}

β€Žsrc/Microsoft.ComponentDetection.Contracts/Internal/InjectionParameters.csβ€Ž

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ internal InjectionParameters(IDetectorDependencies detectorDependencies)
2525
fileUtilityServiceStatic = detectorDependencies.FileUtilityService;
2626
observableDirectoryWalkerFactoryServiceStatic = detectorDependencies.DirectoryWalkerFactory;
2727
dockerServiceStatic = detectorDependencies.DockerService;
28+
environmentVariableServiceStatic = detectorDependencies.EnvironmentVariableService;
2829
}
2930

3031
private static ILogger loggerStatic;
@@ -35,6 +36,8 @@ internal InjectionParameters(IDetectorDependencies detectorDependencies)
3536
private static IObservableDirectoryWalkerFactory observableDirectoryWalkerFactoryServiceStatic;
3637
private static IDockerService dockerServiceStatic;
3738

39+
private static IEnvironmentVariableService environmentVariableServiceStatic;
40+
3841
[Export(typeof(ILogger))]
3942
public ILogger Logger => loggerStatic;
4043

@@ -55,5 +58,8 @@ internal InjectionParameters(IDetectorDependencies detectorDependencies)
5558

5659
[Export(typeof(IDockerService))]
5760
public IDockerService DockerService => dockerServiceStatic;
61+
62+
[Export(typeof(IEnvironmentVariableService))]
63+
public IEnvironmentVariableService EnvironmentVariableService => environmentVariableServiceStatic;
5864
}
5965
}

β€Žsrc/Microsoft.ComponentDetection.Detectors/go/GoComponentDetector.csβ€Ž

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Linq;
66
using System.Text.RegularExpressions;
77
using System.Threading.Tasks;
8+
using Microsoft.ComponentDetection.Common;
89
using Microsoft.ComponentDetection.Common.Telemetry.Records;
910
using Microsoft.ComponentDetection.Contracts;
1011
using Microsoft.ComponentDetection.Contracts.Internal;
@@ -18,6 +19,9 @@ public class GoComponentDetector : FileComponentDetector
1819
[Import]
1920
public ICommandLineInvocationService CommandLineInvocationService { get; set; }
2021

22+
[Import]
23+
public IEnvironmentVariableService EnvVarService { get; set; }
24+
2125
private static readonly Regex GoSumRegex = new Regex(
2226
@"(?<name>.*)\s+(?<version>.*?)(/go\.mod)?\s+(?<hash>.*)",
2327
RegexOptions.Compiled | RegexOptions.ExplicitCapture | RegexOptions.IgnoreCase);
@@ -34,35 +38,62 @@ public class GoComponentDetector : FileComponentDetector
3438

3539
private HashSet<string> projectRoots = new HashSet<string>();
3640

37-
protected override Task OnFileFound(ProcessRequest processRequest, IDictionary<string, string> detectorArgs)
41+
protected override async Task OnFileFound(ProcessRequest processRequest, IDictionary<string, string> detectorArgs)
3842
{
3943
var singleFileComponentRecorder = processRequest.SingleFileComponentRecorder;
4044
var file = processRequest.ComponentStream;
41-
42-
var fileExtension = Path.GetExtension(file.Location).ToLowerInvariant();
43-
switch (fileExtension)
45+
46+
var projectRootDirectory = Directory.GetParent(file.Location);
47+
if (projectRoots.Any(path => projectRootDirectory.FullName.StartsWith(path)))
4448
{
45-
case ".mod":
46-
{
47-
Logger.LogVerbose("Found Go.mod: " + file.Location);
48-
ParseGoModFile(singleFileComponentRecorder, file);
49-
break;
50-
}
51-
52-
case ".sum":
53-
{
54-
Logger.LogVerbose("Found Go.sum: " + file.Location);
55-
ParseGoSumFile(singleFileComponentRecorder, file);
56-
break;
57-
}
49+
return;
50+
}
5851

59-
default:
52+
var wasGoCliScanSuccessful = false;
53+
try
54+
{
55+
if (IsGoCliManuallyEnabled())
56+
{
57+
Logger.LogInfo("Go cli scan was manually enabled");
58+
wasGoCliScanSuccessful = await UseGoCliToScan(file.Location, singleFileComponentRecorder);
59+
}
60+
}
61+
catch
62+
{
63+
Logger.LogInfo("Failed to detect components using go cli.");
64+
}
65+
finally
66+
{
67+
if (wasGoCliScanSuccessful)
68+
{
69+
projectRoots.Add(projectRootDirectory.FullName);
70+
}
71+
else
72+
{
73+
var fileExtension = Path.GetExtension(file.Location).ToLowerInvariant();
74+
switch (fileExtension)
6075
{
61-
throw new Exception("Unexpected file type detected in go detector");
76+
case ".mod":
77+
{
78+
Logger.LogVerbose("Found Go.mod: " + file.Location);
79+
ParseGoModFile(singleFileComponentRecorder, file);
80+
break;
81+
}
82+
83+
case ".sum":
84+
{
85+
Logger.LogVerbose("Found Go.sum: " + file.Location);
86+
ParseGoSumFile(singleFileComponentRecorder, file);
87+
break;
88+
}
89+
90+
default:
91+
{
92+
throw new Exception("Unexpected file type detected in go detector");
93+
}
6294
}
95+
}
6396
}
64-
65-
return Task.CompletedTask;
6697
}
6798

6899
private async Task<bool> UseGoCliToScan(string location, ISingleFileComponentRecorder singleFileComponentRecorder)
@@ -223,5 +254,10 @@ private bool TryCreateGoComponentFromRelationshipPart(string relationship, out G
223254
goComponent = new GoComponent(componentParts[0], componentParts[1]);
224255
return true;
225256
}
257+
258+
private bool IsGoCliManuallyEnabled()
259+
{
260+
return EnvVarService.DoesEnvironmentVariableExist("EnableGoCliScan");
261+
}
226262
}
227263
}

β€Žtest/Microsoft.ComponentDetection.Detectors.Tests/GoComponentDetectorTests.csβ€Ž

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Microsoft.VisualStudio.TestTools.UnitTesting;
1212
using Moq;
1313
using Microsoft.ComponentDetection.TestsUtilities;
14+
using Microsoft.ComponentDetection.Common;
1415

1516
namespace Microsoft.ComponentDetection.Detectors.Tests
1617
{
@@ -21,18 +22,25 @@ public class GoComponentDetectorTests
2122
{
2223
private DetectorTestUtility<GoComponentDetector> detectorTestUtility;
2324
private Mock<ICommandLineInvocationService> commandLineMock;
25+
26+
private Mock<IEnvironmentVariableService> envVarService;
2427
private ScanRequest scanRequest;
2528

2629
[TestInitialize]
2730
public void TestInitialize()
2831
{
2932
commandLineMock = new Mock<ICommandLineInvocationService>();
33+
envVarService = new Mock<IEnvironmentVariableService>();
34+
3035
var loggerMock = new Mock<ILogger>();
3136

37+
envVarService.Setup(x => x.DoesEnvironmentVariableExist("EnableGoCliScan")).Returns(false);
38+
3239
var detector = new GoComponentDetector
3340
{
3441
CommandLineInvocationService = commandLineMock.Object,
3542
Logger = loggerMock.Object,
43+
EnvVarService = envVarService.Object,
3644
};
3745

3846
var tempPath = Path.GetTempPath();
@@ -254,6 +262,8 @@ public async Task TestGoDetector_GoCommandNotFound()
254262
commandLineMock.Setup(x => x.CanCommandBeLocated("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
255263
.ReturnsAsync(false);
256264

265+
envVarService.Setup(x => x.DoesEnvironmentVariableExist("EnableGoCliScan")).Returns(true);
266+
257267
await TestGoSumDetectorWithValidFile_ReturnsSuccessfully();
258268
}
259269

@@ -263,6 +273,8 @@ public async Task TestGoDetector_GoCommandThrows()
263273
commandLineMock.Setup(x => x.CanCommandBeLocated("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()))
264274
.ReturnsAsync(() => throw new Exception("Some horrible error occured"));
265275

276+
envVarService.Setup(x => x.DoesEnvironmentVariableExist("EnableGoCliScan")).Returns(true);
277+
266278
await TestGoSumDetectorWithValidFile_ReturnsSuccessfully();
267279
}
268280

@@ -278,6 +290,8 @@ public async Task TestGoDetector_GoGraphCommandFails()
278290
ExitCode = 1,
279291
});
280292

293+
envVarService.Setup(x => x.DoesEnvironmentVariableExist("EnableGoCliScan")).Returns(true);
294+
281295
await TestGoSumDetectorWithValidFile_ReturnsSuccessfully();
282296
}
283297

@@ -290,6 +304,8 @@ public async Task TestGoDetector_GoGraphCommandThrows()
290304
commandLineMock.Setup(x => x.ExecuteCommand("go mod graph", null, It.IsAny<DirectoryInfo>(), It.IsAny<string>()))
291305
.ReturnsAsync(() => throw new Exception("Some horrible error occured"));
292306

307+
envVarService.Setup(x => x.DoesEnvironmentVariableExist("EnableGoCliScan")).Returns(true);
308+
293309
await TestGoSumDetectorWithValidFile_ReturnsSuccessfully();
294310
}
295311

@@ -308,14 +324,24 @@ public async Task TestGoDetector_GoGraphHappyPath()
308324
StdOut = goGraph,
309325
});
310326

327+
envVarService.Setup(x => x.DoesEnvironmentVariableExist("EnableGoCliScan")).Returns(true);
328+
311329
var (scanResult, componentRecorder) = await detectorTestUtility
312330
.WithFile("go.mod", string.Empty)
313331
.ExecuteDetector();
314332

315333
Assert.AreEqual(ProcessingResultCode.Success, scanResult.ResultCode);
316334

317335
var detectedComponents = componentRecorder.GetDetectedComponents();
318-
Assert.AreEqual(0, detectedComponents.Count());
336+
Assert.AreEqual(4, detectedComponents.Count());
337+
}
338+
339+
[TestMethod]
340+
public async Task TestGoDetector_GoCliRequiresEnvVarToRun()
341+
{
342+
await TestGoSumDetectorWithValidFile_ReturnsSuccessfully();
343+
344+
commandLineMock.Verify(x => x.CanCommandBeLocated("go", null, It.IsAny<DirectoryInfo>(), It.IsAny<string[]>()), Times.Never);
319345
}
320346
}
321347
}

0 commit comments

Comments
Β (0)