diff --git a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets index 97eed247c57..3a22b628446 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets +++ b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets @@ -61,8 +61,6 @@ Copyright (c) .NET Foundation. All rights reserved. true <_CentralPackageVersionsEnabled Condition="'$(ManagePackageVersionsCentrally)' == 'true' AND '$(CentralPackageVersionsFileImported)' == 'true'">true - - default diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs index 0a3f34a2262..8cfe6240caa 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/RestoreCommand.cs @@ -303,9 +303,12 @@ await _logger.LogAsync(RestoreLogMessage.CreateWarning(NuGetLogCode.NU1803, }); } - AuditUtility.EnabledValue enableAudit = AuditUtility.ParseEnableValue(_request.Project.RestoreMetadata?.RestoreAuditProperties?.EnableAudit); + AuditUtility.EnabledValue enableAudit = AuditUtility.ParseEnableValue( + _request.Project.RestoreMetadata?.RestoreAuditProperties?.EnableAudit, + _request.Project.FilePath, + _logger); telemetry.TelemetryEvent[AuditEnabled] = AuditUtility.GetString(enableAudit); - if (enableAudit == AuditUtility.EnabledValue.ImplicitOptIn || enableAudit == AuditUtility.EnabledValue.ExplicitOptIn) + if (enableAudit != AuditUtility.EnabledValue.ExplicitOptOut) { await PerformAuditAsync(enableAudit, graphs, telemetry, token); } diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/AuditUtility.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/AuditUtility.cs index 9618c7097f7..dde1bab487a 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/AuditUtility.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/AuditUtility.cs @@ -22,7 +22,7 @@ namespace NuGet.Commands.Restore.Utility internal class AuditUtility { private readonly EnabledValue _auditEnabled; - private readonly ProjectModel.RestoreAuditProperties _restoreAuditProperties; + private readonly ProjectModel.RestoreAuditProperties? _restoreAuditProperties; private readonly string _projectFullPath; private readonly IEnumerable _targetGraphs; private readonly IReadOnlyList _vulnerabilityInfoProviders; @@ -49,7 +49,7 @@ internal class AuditUtility public AuditUtility( EnabledValue auditEnabled, - ProjectModel.RestoreAuditProperties restoreAuditProperties, + ProjectModel.RestoreAuditProperties? restoreAuditProperties, string projectFullPath, IEnumerable graphs, IReadOnlyList vulnerabilityInformationProviders, @@ -366,7 +366,7 @@ private static (string severityLabel, NuGetLogCode code) GetSeverityLabelAndCode private PackageVulnerabilitySeverity ParseAuditLevel() { - string? auditLevel = _restoreAuditProperties.AuditLevel?.Trim(); + string? auditLevel = _restoreAuditProperties?.AuditLevel?.Trim(); if (auditLevel == null) { @@ -402,7 +402,7 @@ internal enum NuGetAuditMode { Unknown, Direct, All } // Enum parsing and ToString are a magnitude of times slower than a naive implementation. private NuGetAuditMode ParseAuditMode() { - string? auditMode = _restoreAuditProperties.AuditMode?.Trim(); + string? auditMode = _restoreAuditProperties?.AuditMode?.Trim(); if (auditMode == null) { @@ -426,16 +426,16 @@ private NuGetAuditMode ParseAuditMode() internal enum EnabledValue { - Undefined, + Invalid, ImplicitOptIn, ExplicitOptIn, ExplicitOptOut } // Enum parsing and ToString are a magnitude of times slower than a naive implementation. - public static EnabledValue ParseEnableValue(string value) + public static EnabledValue ParseEnableValue(string? value, string projectFullPath, ILogger logger) { - if (string.Equals(value, "default", StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrEmpty(value) || string.Equals(value, "default", StringComparison.OrdinalIgnoreCase)) { return EnabledValue.ImplicitOptIn; } @@ -449,7 +449,12 @@ public static EnabledValue ParseEnableValue(string value) { return EnabledValue.ExplicitOptOut; } - return EnabledValue.Undefined; + + string messageText = string.Format(Strings.Error_InvalidNuGetAuditValue, value, "true, false"); + RestoreLogMessage message = RestoreLogMessage.CreateError(NuGetLogCode.NU1014, messageText); + message.ProjectPath = projectFullPath; + logger.Log(message); + return EnabledValue.Invalid; } // Enum parsing and ToString are a magnitude of times slower than a naive implementation. @@ -457,7 +462,7 @@ internal static string GetString(EnabledValue enableAudit) { return enableAudit switch { - EnabledValue.Undefined => nameof(EnabledValue.Undefined), + EnabledValue.Invalid => nameof(EnabledValue.Invalid), EnabledValue.ExplicitOptIn => nameof(EnabledValue.ExplicitOptIn), EnabledValue.ExplicitOptOut => nameof(EnabledValue.ExplicitOptOut), EnabledValue.ImplicitOptIn => nameof(EnabledValue.ImplicitOptIn), diff --git a/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs b/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs index ed583c5ca3e..d3120592c71 100644 --- a/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.Commands/Strings.Designer.cs @@ -475,7 +475,7 @@ internal static string Error_InvalidLockFileInput { } /// - /// Looks up a localized string similar to Invalid NuGetAuditLevel value '{0}'. Expected values: {1}. + /// Looks up a localized string similar to Invalid NuGetAuditLevel value '{0}'. Valid values: {1}. /// internal static string Error_InvalidNuGetAuditLevelValue { get { @@ -484,7 +484,7 @@ internal static string Error_InvalidNuGetAuditLevelValue { } /// - /// Looks up a localized string similar to Invalid NuGetAuditMode value '{0}'. Expected values: {1}. + /// Looks up a localized string similar to Invalid NuGetAuditMode value '{0}'. Valid values: {1}. /// internal static string Error_InvalidNuGetAuditModeValue { get { @@ -492,6 +492,15 @@ internal static string Error_InvalidNuGetAuditModeValue { } } + /// + /// Looks up a localized string similar to Invalid NuGetAudit value '{0}'. Valid values: {1}. + /// + internal static string Error_InvalidNuGetAuditValue { + get { + return ResourceManager.GetString("Error_InvalidNuGetAuditValue", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid project-package combination for {0} {1}. DotnetToolReference project style can only contain references of the DotnetTool type. /// diff --git a/src/NuGet.Core/NuGet.Commands/Strings.resx b/src/NuGet.Core/NuGet.Commands/Strings.resx index 5c63c7cd5d4..11aaa512d3c 100644 --- a/src/NuGet.Core/NuGet.Commands/Strings.resx +++ b/src/NuGet.Core/NuGet.Commands/Strings.resx @@ -1069,7 +1069,7 @@ Non-HTTPS access will be removed in a future version. Consider migrating to 'HTT {3} - URL for more info on the known vulnerability - Invalid NuGetAuditLevel value '{0}'. Expected values: {1} + Invalid NuGetAuditLevel value '{0}'. Valid values: {1} Don't translate 'NuGetAuditLevel' {0} - is the value from the customer's project file {1} is the list of valid values "low, moderate, high, critical" (these should not be translated either) @@ -1083,7 +1083,7 @@ Non-HTTPS access will be removed in a future version. Consider migrating to 'HTT {0} is the value supplied - Invalid NuGetAuditMode value '{0}'. Expected values: {1} + Invalid NuGetAuditMode value '{0}'. Valid values: {1} Don't translate 'NuGetAuditMode' {0} - is the value from the customer's project file {1} is the list of valid values "direct, all" @@ -1092,4 +1092,10 @@ Non-HTTPS access will be removed in a future version. Consider migrating to 'HTT NuGetAudit is enabled, but no package sources contain known vulnerability data. Do not translate NuGetAudit + + Invalid NuGetAudit value '{0}'. Valid values: {1} + Don't translate 'NuGetAudit' +{0} - is the value from the customer's project file +{1} is the list of valid values "true, false" + \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs index ebf3e5a5e87..f8df3f82984 100644 --- a/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs +++ b/src/NuGet.Core/NuGet.Common/Errors/NuGetLogCode.cs @@ -122,7 +122,7 @@ public enum NuGetLogCode NU1013 = 1013, /// - /// NuGetAuditLevel input errors + /// NuGetAudit* MSBuild property input errors /// NU1014 = 1014, diff --git a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/ProjectPropertyTests.cs b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/ProjectPropertyTests.cs deleted file mode 100644 index 5323b317718..00000000000 --- a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/ProjectPropertyTests.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (c) .NET Foundation. All rights reserved. -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -using System.IO; -using NuGet.Test.Utility; -using Xunit; - -namespace Dotnet.Integration.Test -{ - [Collection(DotnetIntegrationCollection.Name)] - public class ProjectPropertyTests - { - private DotnetIntegrationTestFixture _msbuildFixture; - - public ProjectPropertyTests(DotnetIntegrationTestFixture fixture) - { - _msbuildFixture = fixture; - } - - [Fact] - public void NuGetAudit_EnabledByDefaultOnNet8AndHigher() - { - // Arrange - using TestDirectory testDirectory = TestDirectory.Create(); - string projectFilePath = Path.Combine(testDirectory.Path, "test.csproj"); - var projectFileContents = @" - - - - - -"; - File.WriteAllText(projectFilePath, projectFileContents); - - // Dotnet.Integration.Tests works by finding the test assembly compile TFM, then finding the SDK that maches the TFM's major version. - // (see TestDotnetCliUtility.GetSdkToTestByAssemblyPath). - // Therefore, we can depend on this file's compile time TFM to tell us if we're testing the .NET 7 or 8 SDK. - string expected = -#if NET8_0_OR_GREATER - "default"; -#else - ""; -#endif - - // Act - var result = _msbuildFixture.RunDotnetExpectSuccess(testDirectory.Path, $"msbuild -t:ValidateNuGetAuditValue -p:ExpectedValue={expected}"); - } - } -} diff --git a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs index cb9ef8fb52b..12690963152 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs @@ -2135,7 +2135,8 @@ public async Task RestoreCommand_RestoreFloatingVersionWithIgnoreFailingLocalSou }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); using (var context = new SourceCacheContext()) @@ -2184,7 +2185,8 @@ public async Task RestoreCommand_RestoreFloatingVersionWithIgnoreFailingHttpSour }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); using (var context = new SourceCacheContext()) @@ -2402,7 +2404,8 @@ public async Task RestoreCommand_RestoreNonExistingWithIgnoreFailingLocalSourceA }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); using (var context = new SourceCacheContext()) @@ -2451,7 +2454,8 @@ public async Task RestoreCommand_RestoreNonExistingWithIgnoreFailingHttpSourceAs }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); using (var context = new SourceCacheContext()) @@ -2500,7 +2504,8 @@ public async Task RestoreCommand_RestoreNonExistingWithIgnoreFailingV3HttpSource }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); using (var context = new SourceCacheContext()) @@ -2549,7 +2554,8 @@ public async Task RestoreCommand_RestoreInexactWithIgnoreFailingLocalSourceAsync }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); using (var context = new SourceCacheContext()) @@ -2598,7 +2604,8 @@ public async Task RestoreCommand_RestoreInexactWithIgnoreFailingHttpSourceAsync( }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); using (var context = new SourceCacheContext()) @@ -2647,7 +2654,8 @@ public async Task RestoreCommand_RestoreInexactWithIgnoreFailingV3HttpSourceAsyn }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); using (var context = new SourceCacheContext()) diff --git a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/UWPRestoreTests.cs b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/UWPRestoreTests.cs index 4670d6c4f71..bb47d8b7fe4 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/UWPRestoreTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/UWPRestoreTests.cs @@ -146,7 +146,8 @@ public async Task UWPRestore_BlankUWPAppWithExcludes() }"); var specPath = Path.Combine(projectDir, "TestProject", "project.json"); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", specPath) + .EnsureProjectJsonRestoreMetadata(); var logger = new TestLogger(); var clientPolicyContext = ClientPolicyContext.GetClientPolicy(NullSettings.Instance, logger); diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/RestoreCommandTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/RestoreCommandTests.cs index 6a691a58841..991c47e93fd 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/RestoreCommandTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/RestoreCommandTests.cs @@ -2787,6 +2787,7 @@ public async Task ExecuteAsync_WithSinglePackage_PopulatesCorrectTelemetry() var projectName = "TestProject"; var projectPath = Path.Combine(pathContext.SolutionRoot, projectName); PackageSpec packageSpec = ProjectTestHelpers.GetPackageSpec(projectName, pathContext.SolutionRoot, "net472", "a"); + packageSpec.RestoreMetadata.RestoreAuditProperties.EnableAudit = bool.TrueString; await SimpleTestPackageUtility.CreateFolderFeedV3Async( pathContext.PackageSource, @@ -2820,36 +2821,71 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var projectInformationEvent = telemetryEvents.Single(e => e.Name.Equals("ProjectRestoreInformation")); - projectInformationEvent.Count.Should().Be(29); - projectInformationEvent["RestoreSuccess"].Should().Be(true); - projectInformationEvent["NoOpResult"].Should().Be(false); - projectInformationEvent["IsCentralVersionManagementEnabled"].Should().Be(false); - projectInformationEvent["NoOpCacheFileEvaluationResult"].Should().Be(false); - projectInformationEvent["IsLockFileEnabled"].Should().Be(false); - projectInformationEvent["IsLockFileValidForRestore"].Should().Be(false); - projectInformationEvent["LockFileEvaluationResult"].Should().Be(true); - projectInformationEvent["NoOpDuration"].Should().NotBeNull(); - projectInformationEvent["TotalUniquePackagesCount"].Should().Be(1); - projectInformationEvent["NewPackagesInstalledCount"].Should().Be(1); - projectInformationEvent["EvaluateLockFileDuration"].Should().NotBeNull(); - projectInformationEvent["CreateRestoreTargetGraphDuration"].Should().NotBeNull(); - projectInformationEvent["GenerateRestoreGraphDuration"].Should().NotBeNull(); - projectInformationEvent["CreateRestoreResultDuration"].Should().NotBeNull(); - projectInformationEvent["WalkFrameworkDependencyDuration"].Should().NotBeNull(); - projectInformationEvent["GenerateAssetsFileDuration"].Should().NotBeNull(); - projectInformationEvent["ValidateRestoreGraphsDuration"].Should().NotBeNull(); - projectInformationEvent["EvaluateDownloadDependenciesDuration"].Should().NotBeNull(); - projectInformationEvent["NoOpCacheFileEvaluateDuration"].Should().NotBeNull(); - projectInformationEvent["StartTime"].Should().NotBeNull(); - projectInformationEvent["EndTime"].Should().NotBeNull(); - projectInformationEvent["OperationId"].Should().NotBeNull(); - projectInformationEvent["Duration"].Should().NotBeNull(); - projectInformationEvent["PackageSourceMapping.IsMappingEnabled"].Should().Be(false); - projectInformationEvent["SourcesCount"].Should().Be(1); - projectInformationEvent["HttpSourcesCount"].Should().Be(0); - projectInformationEvent["LocalSourcesCount"].Should().Be(1); - projectInformationEvent["FallbackFoldersCount"].Should().Be(0); - projectInformationEvent["Audit.Enabled"].Should().Be("Undefined"); + var expectedProperties = new Dictionary>() + { + ["RestoreSuccess"] = value => value.Should().Be(true), + ["NoOpResult"] = value => value.Should().Be(false), + ["IsCentralVersionManagementEnabled"] = value => value.Should().Be(false), + ["NoOpCacheFileEvaluationResult"] = value => value.Should().Be(false), + ["IsLockFileEnabled"] = value => value.Should().Be(false), + ["IsLockFileValidForRestore"] = value => value.Should().Be(false), + ["LockFileEvaluationResult"] = value => value.Should().Be(true), + ["NoOpDuration"] = value => value.Should().NotBeNull(), + ["TotalUniquePackagesCount"] = value => value.Should().Be(1), + ["NewPackagesInstalledCount"] = value => value.Should().Be(1), + ["EvaluateLockFileDuration"] = value => value.Should().NotBeNull(), + ["CreateRestoreTargetGraphDuration"] = value => value.Should().NotBeNull(), + ["GenerateRestoreGraphDuration"] = value => value.Should().NotBeNull(), + ["CreateRestoreResultDuration"] = value => value.Should().NotBeNull(), + ["WalkFrameworkDependencyDuration"] = value => value.Should().NotBeNull(), + ["GenerateAssetsFileDuration"] = value => value.Should().NotBeNull(), + ["ValidateRestoreGraphsDuration"] = value => value.Should().NotBeNull(), + ["EvaluateDownloadDependenciesDuration"] = value => value.Should().NotBeNull(), + ["NoOpCacheFileEvaluateDuration"] = value => value.Should().NotBeNull(), + ["StartTime"] = value => value.Should().NotBeNull(), + ["EndTime"] = value => value.Should().NotBeNull(), + ["OperationId"] = value => value.Should().NotBeNull(), + ["Duration"] = value => value.Should().NotBeNull(), + ["PackageSourceMapping.IsMappingEnabled"] = value => value.Should().Be(false), + ["SourcesCount"] = value => value.Should().Be(1), + ["HttpSourcesCount"] = value => value.Should().Be(0), + ["LocalSourcesCount"] = value => value.Should().Be(1), + ["FallbackFoldersCount"] = value => value.Should().Be(0), + ["WarningCodes"] = value => value.Should().Be("NU1905"), + ["Audit.Enabled"] = value => value.Should().Be("ExplicitOptIn"), + ["Audit.Level"] = value => value.Should().Be(0), + ["Audit.Mode"] = value => value.Should().Be("Unknown"), + ["Audit.Vulnerability.Direct.Count"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Direct.Severity0"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Direct.Severity1"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Direct.Severity2"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Direct.Severity3"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Direct.SeverityInvalid"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Transitive.Count"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Transitive.Severity0"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Transitive.Severity1"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Transitive.Severity2"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Transitive.Severity3"] = value => value.Should().Be(0), + ["Audit.Vulnerability.Transitive.SeverityInvalid"] = value => value.Should().Be(0), + ["Audit.DataSources"] = value => value.Should().Be(0), + ["Audit.Duration.Download"] = value => value.Should().BeOfType(), + ["Audit.Duration.Total"] = value => value.Should().BeOfType(), + }; + + HashSet actualProperties = new(); + foreach (var eventProperty in projectInformationEvent) + { + actualProperties.Add(eventProperty.Key); + } + + expectedProperties.Keys.Except(actualProperties).Should().BeEmpty(); + actualProperties.Except(expectedProperties.Keys).Should().BeEmpty(); + + foreach (var kvp in expectedProperties) + { + object value = projectInformationEvent[kvp.Key]; + kvp.Value(value); + } } [Fact] diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/Utility/AuditUtilityTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/Utility/AuditUtilityTests.cs index 925e8a21033..85bb4589692 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/Utility/AuditUtilityTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/Utility/AuditUtilityTests.cs @@ -31,6 +31,41 @@ public class AuditUtilityTests private static Uri CveUrl = new Uri("https://cve.test/1"); private static VersionRange UpToV2 = new VersionRange(maxVersion: new NuGetVersion(2, 0, 0), includeMaxVersion: false); + [Theory] + [InlineData(null, nameof(AuditUtility.EnabledValue.ImplicitOptIn))] + [InlineData("", nameof(AuditUtility.EnabledValue.ImplicitOptIn))] + [InlineData("default", nameof(AuditUtility.EnabledValue.ImplicitOptIn))] + [InlineData("true", nameof(AuditUtility.EnabledValue.ExplicitOptIn))] + [InlineData("enable", nameof(AuditUtility.EnabledValue.ExplicitOptIn))] + [InlineData("TRUE", nameof(AuditUtility.EnabledValue.ExplicitOptIn))] + [InlineData("false", nameof(AuditUtility.EnabledValue.ExplicitOptOut))] + [InlineData("disable", nameof(AuditUtility.EnabledValue.ExplicitOptOut))] + [InlineData("FALSE", nameof(AuditUtility.EnabledValue.ExplicitOptOut))] + [InlineData("invalid", nameof(AuditUtility.EnabledValue.Invalid))] + public void ParseEnableValue_WithValue_ReturnsExpectedEnum(string input, string expected) + { + // Arrange + AuditUtility.EnabledValue expectedValue = (AuditUtility.EnabledValue)Enum.Parse(typeof(AuditUtility.EnabledValue), expected); + string projectPath = "my.csproj"; + TestLogger? logger = expectedValue == AuditUtility.EnabledValue.Invalid + ? new TestLogger() + : null; + + // Act + AuditUtility.EnabledValue actual = AuditUtility.ParseEnableValue(input, projectPath, logger ?? NullLogger.Instance); + + // Assert + actual.Should().Be(expectedValue); + + if (logger is not null) + { + logger.Errors.Should().Be(1); + RestoreLogMessage message = logger.LogMessages.Cast().Single(); + message.Code.Should().Be(NuGetLogCode.NU1014); + message.Level.Should().Be(LogLevel.Error); + } + } + [Fact] public async Task Check_VulnerabilityProviderWithExceptions_WarningsReplayedToLogger() { @@ -56,7 +91,7 @@ public async Task Check_VulnerabilityProviderWithExceptions_WarningsReplayedToLo } [Theory] - [InlineData("default", false)] + [InlineData("", false)] [InlineData("true", true)] public async Task Check_WithNoVulnerabilitySources_NU1905Warning(string enable, bool expectWarning) { @@ -364,17 +399,12 @@ public async Task Check_MultiTargetingProjectFile_WarningsHaveExpectedProperties await createGraphTasks[1] }; - RestoreAuditProperties restoreAuditProperties = new() - { - EnableAudit = "default", - }; - var log = new TestLogger(); // Act var audit = new AuditUtility( - AuditUtility.ParseEnableValue(restoreAuditProperties.EnableAudit), - restoreAuditProperties, + AuditUtility.ParseEnableValue(null, "/path/proj.csproj", log), + restoreAuditProperties: null, "/path/proj.csproj", graphs, vulnerabilityProviders, @@ -413,7 +443,7 @@ static async Task CreateGraphAsync(DependencyProvider packag private class AuditTestContext { public string ProjectFullPath { get; set; } = RuntimeEnvironmentHelper.IsWindows ? @"n:\proj\proj.csproj" : "/src/proj/proj.csproj"; - public string? Enabled { get; set; } = "default"; + public string? Enabled { get; set; } public string? Level { get; set; } public string? Mode { get; set; } @@ -458,10 +488,8 @@ public VulnerabilityProviderTestContext WithVulnerabilityProvider() public async Task CheckPackageVulnerabilitiesAsync(CancellationToken cancellationToken) { - AuditUtility.EnabledValue enabled; - if (Enabled is null - || (enabled = AuditUtility.ParseEnableValue(Enabled)) == AuditUtility.EnabledValue.Undefined - || enabled == AuditUtility.EnabledValue.ExplicitOptOut) + AuditUtility.EnabledValue enabled = AuditUtility.ParseEnableValue(Enabled, ProjectFullPath, Log); + if (enabled == AuditUtility.EnabledValue.ExplicitOptOut) { throw new InvalidOperationException($"{nameof(Enabled)} must have a value that does not disable NuGetAudit."); } diff --git a/test/TestUtilities/Test.Utility/Commands/ProjectTestHelpers.cs b/test/TestUtilities/Test.Utility/Commands/ProjectTestHelpers.cs index 50c34ec9d60..600faeffc83 100644 --- a/test/TestUtilities/Test.Utility/Commands/ProjectTestHelpers.cs +++ b/test/TestUtilities/Test.Utility/Commands/ProjectTestHelpers.cs @@ -162,6 +162,11 @@ public static PackageSpec WithTestRestoreMetadata(this PackageSpec spec) updated.RestoreMetadata.CentralPackageVersionsEnabled = spec.RestoreMetadata?.CentralPackageVersionsEnabled ?? false; updated.RestoreMetadata.CentralPackageTransitivePinningEnabled = spec.RestoreMetadata?.CentralPackageTransitivePinningEnabled ?? false; + updated.RestoreMetadata.RestoreAuditProperties = new RestoreAuditProperties() + { + EnableAudit = bool.FalseString + }; + // Update the Target Alias. foreach (var framework in updated.TargetFrameworks) { @@ -193,6 +198,10 @@ private static PackageSpec WithProjectJsonTestRestoreMetadata(this PackageSpec s metadata.ProjectUniqueName = msbuildProjectFilePath; metadata.CacheFilePath = NoOpRestoreUtilities.GetProjectCacheFilePath(msbuildProjectExtensionsPath); metadata.ConfigFilePaths = new List(); + metadata.RestoreAuditProperties = new RestoreAuditProperties() + { + EnableAudit = bool.FalseString + }; foreach (var framework in updated.TargetFrameworks) {