Skip to content

Commit

Permalink
Port linter rule secure-params-in-nested-deploy
Browse files Browse the repository at this point in the history
  • Loading branch information
Stephen Weatherford authored and Stephen Weatherford committed Jul 28, 2022
1 parent 8e74bc9 commit c317dc3
Show file tree
Hide file tree
Showing 26 changed files with 1,181 additions and 76 deletions.
17 changes: 17 additions & 0 deletions src/Bicep.Core.UnitTests/Assertions/StringAssertionsExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using FluentAssertions.Primitives;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Bicep.Core.Parsing;
using System.Collections.Generic;
using System;

namespace Bicep.Core.UnitTests.Assertions
{
Expand Down Expand Up @@ -93,5 +95,20 @@ public static AndConstraint<StringAssertions> HaveLengthLessThanOrEqualTo(this S

return new AndConstraint<StringAssertions>(instance);
}

// Adds StringComparison to StringAssertions.NotContainAny
public static AndConstraint<StringAssertions> NotContainAny(this StringAssertions instance, IEnumerable<string> values, StringComparison stringComparison, string because = "", params object[] becauseArgs)
{
IEnumerable<string> enumerable = values.Where((string v) => Contains(instance.Subject, v, stringComparison));
Execute.Assertion.ForCondition(!enumerable.Any()).BecauseOf(because, becauseArgs).FailWith("Did not expect {context:string} {0} to contain any of the strings: {1}{reason}.",
instance.Subject, enumerable);
return new AndConstraint<StringAssertions>(instance);
}

private static bool Contains(string actual, string expected, StringComparison comparison)
{
return (actual ?? string.Empty).Contains(expected ?? string.Empty, comparison);
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using Bicep.Core.UnitTests.Assertions;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Collections.Generic;
using System;

namespace Bicep.Core.UnitTests.AssertionsTests
{
[TestClass]
public class StringAssertionsExtensionsTests
{
[DataRow(
"hello there",
new[] { "Hello", "Dolly" },
StringComparison.InvariantCulture,
null)]
[DataRow(
"hello there",
new[] { "Hello", "Dolly" },
StringComparison.InvariantCultureIgnoreCase,
"Did not expect string \"hello there\" to contain any of the strings: {\"Hello\"} because I said so.")]
[DataTestMethod]
public void NotContainAny_WithStringComparison(string text, IEnumerable<string> values, StringComparison stringComparison, string? expectedFailureMessage)
{
string? actualMessage;
try
{
text.Should().NotContainAny(values, stringComparison, "because I said so");
actualMessage = null;
}
catch (Exception ex)
{
actualMessage = ex.Message;
}

actualMessage.Should().Be(expectedFailureMessage);
}
}
}
3 changes: 3 additions & 0 deletions src/Bicep.Core.UnitTests/Bicep.Core.UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,7 @@
<ItemGroup>
<PackageReference Update="Nerdbank.GitVersioning" Version="3.5.107" />
</ItemGroup>
<ItemGroup>
<Folder Include="AssertionsTests\" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
Expand Down Expand Up @@ -118,7 +119,7 @@ public void RuleConfigs_RuleDescriptionsShouldBeConsistent()
string description = new Regex("^(.+) See https://.+").Match(descriptionWithLink)?.Groups[1].Value ?? "<couldn't find rule description>";
description.Should().MatchRegex("^[A-Z]", "all rule descriptions should start with a capital letter");
description.Should().EndWith(".", "all rule descriptions should end with a period");
description.Should().NotContainAny("Don't", "don't", "Do not", "do not"); // Use "Should" type of language generally (less impolite)
description.Should().NotContainAny(new[] { "don't", "do not" }, StringComparison.InvariantCultureIgnoreCase, "Use \"Should\" type of language generally (less impolite)");
description.Should().NotContain("\"", "use single quotes instead of double quotes in rule descriptions");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ namespace Bicep.Core.UnitTests.Diagnostics.LinterRuleTests
[TestClass]
public class AdminUsernameShouldNotBeLiteralRuleTests : LinterRuleTestsBase
{
private void CompileAndTest(string text, int expectedErrorCount, OnCompileErrors onCompileErrors = OnCompileErrors.Fail)
private void CompileAndTest(string text, int expectedErrorCount, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors)
{
AssertLinterRuleDiagnostics(AdminUsernameShouldNotBeLiteralRule.Code, text, expectedErrorCount, onCompileErrors);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Bicep.Core.UnitTests.Diagnostics.LinterRuleTests
[TestClass]
public class ArtifactsParametersRuleTests : LinterRuleTestsBase
{
private void CompileAndTest(string bicepText, string[] expectedMessagesForCode, OnCompileErrors onCompileErrors = OnCompileErrors.Fail)
private void CompileAndTest(string bicepText, string[] expectedMessagesForCode, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors)
{
base.AssertLinterRuleDiagnostics(ArtifactsParametersRule.Code, bicepText, expectedMessagesForCode, onCompileErrors);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
// Licensed under the MIT License.

using Bicep.Core.Analyzers.Linter;
using Bicep.Core.Configuration;
using Bicep.Core.Diagnostics;
using Bicep.Core.Text;
using Bicep.Core.UnitTests.Assertions;
using Bicep.Core.UnitTests.Utils;
using FluentAssertions;
using FluentAssertions.Execution;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace Bicep.Core.UnitTests.Diagnostics.LinterRuleTests
Expand All @@ -17,62 +20,109 @@ public class LinterRuleTestsBase
{
public enum OnCompileErrors
{
Fail, // Assertion fails if there are compiler errors
Ignore,
Ignore, // Ignore any compile errors
IncludeErrors, // Include compile errors in the list of messages to expect
IncludeErrorsAndWarnings, // Include compile errors and warnings in the list of messages to expect
}

protected void AssertLinterRuleDiagnostics(string ruleCode, string bicepText, string[] expectedMessagesForCode, OnCompileErrors onCompileErrors = OnCompileErrors.Fail)
public enum IncludePosition
{
AssertLinterRuleDiagnostics(ruleCode, bicepText, onCompileErrors, diags =>
None,
LineNumber,
}

private string FormatDiagnostic(IDiagnostic diagnostic, ImmutableArray<int> lineStarts, IncludePosition includePosition)
{
if (includePosition == IncludePosition.LineNumber)
{
diags.Select(d => d.Message).Should().BeEquivalentTo(expectedMessagesForCode);
});
var position = TextCoordinateConverter.GetPosition(lineStarts, diagnostic.Span.Position);
return $"[{position.line}] {diagnostic.Message}";
}
else
{
return diagnostic.Message;
}
}

protected void AssertLinterRuleDiagnostics(string ruleCode, string bicepText, int expectedDiagnosticCountForCode, OnCompileErrors onCompileErrors = OnCompileErrors.Fail)
static protected (string fileName, string fileContents) MainBicepFile(string bicepText)
{
return ("main.bicep", bicepText);
}

static protected (string fileName, string fileContents) ConfigFile(string configurationText)
{
return ("bicepconfig.json", configurationText);
}

static protected (string fileName, string fileContents)[] BicepFiles(string bicepText, string? configurationText)
{
List<(string fileName, string fileContents)> files = new List<(string fileName, string fileContents)>();
files.Add(MainBicepFile(bicepText));
if (configurationText is not null)
{
files.Add(ConfigFile(configurationText));
}

return files.ToArray();
}

protected void AssertLinterRuleDiagnostics(string ruleCode, string bicepText, string[] expectedMessagesForCode, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors, IncludePosition includePosition = IncludePosition.None, RootConfiguration? configuration = null)
{
AssertLinterRuleDiagnostics(ruleCode, bicepText, onCompileErrors, diags =>
{
var lineStarts = TextCoordinateConverter.GetLineStarts(bicepText);
var messages = diags.Select(d => FormatDiagnostic(d, lineStarts, includePosition));
messages.Should().BeEquivalentTo(expectedMessagesForCode);
},
configuration);
}

protected void AssertLinterRuleDiagnostics(string ruleCode, string bicepText, int expectedDiagnosticCountForCode, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors, RootConfiguration? configuration = null)
{
AssertLinterRuleDiagnostics(ruleCode, bicepText, onCompileErrors, diags =>
{
diags.Should().HaveCount(expectedDiagnosticCountForCode);
});
},
configuration);
}

protected void AssertLinterRuleDiagnostics(string ruleCode, string bicepText, Action<IEnumerable<IDiagnostic>> assertAction)
protected void AssertLinterRuleDiagnostics(string ruleCode, string bicepText, Action<IEnumerable<IDiagnostic>> assertAction, RootConfiguration? configuration = null)
{
AssertLinterRuleDiagnostics(ruleCode, bicepText, OnCompileErrors.Fail, assertAction);
AssertLinterRuleDiagnostics(ruleCode, bicepText, OnCompileErrors.IncludeErrors, assertAction, configuration);
}

protected void AssertLinterRuleDiagnostics(string ruleCode, string bicepText, OnCompileErrors onCompileErrors, Action<IEnumerable<IDiagnostic>> assertAction)
protected void AssertLinterRuleDiagnostics(string ruleCode, string bicepText, OnCompileErrors onCompileErrors, Action<IEnumerable<IDiagnostic>> assertAction, RootConfiguration? configuration = null)
{
RunWithDiagnosticAnnotations(
bicepText,
diag => diag.Code == ruleCode || (onCompileErrors == OnCompileErrors.Fail && diag.Level == DiagnosticLevel.Error),
onCompileErrors,
assertAction);
bicepText,
diag =>
diag.Code == ruleCode
|| (IsCompilerDiagnostic(diag) && onCompileErrors == OnCompileErrors.IncludeErrors && diag.Level == DiagnosticLevel.Error)
|| (IsCompilerDiagnostic(diag) && onCompileErrors == OnCompileErrors.IncludeErrorsAndWarnings && (diag.Level == DiagnosticLevel.Error || diag.Level == DiagnosticLevel.Warning)),
onCompileErrors,
assertAction,
configuration);
}

private static void RunWithDiagnosticAnnotations(string bicepText, Func<IDiagnostic, bool> filterFunc, OnCompileErrors onCompileErrors, Action<IEnumerable<IDiagnostic>> assertAction)
private static void RunWithDiagnosticAnnotations(string bicepText, Func<IDiagnostic, bool> filterFunc, OnCompileErrors onCompileErrors, Action<IEnumerable<IDiagnostic>> assertAction, RootConfiguration? configuration)
{
var result = CompilationHelper.Compile(bicepText);
var context = new CompilationHelper.CompilationHelperContext();
var result = CompilationHelper.Compile(context, bicepText);
using (new AssertionScope().WithFullSource(result.BicepFile))
{
result.Should().NotHaveDiagnosticsWithCodes(new[] { LinterAnalyzer.LinterRuleInternalError }, "There should never be linter LinterRuleInternalError errors");

if (onCompileErrors == OnCompileErrors.Fail)
{
var compileErrors = result.Diagnostics.Where(d => d.Level == DiagnosticLevel.Error);
DiagnosticAssertions.DoWithDiagnosticAnnotations(
result.Compilation.SourceFileGrouping.EntryPoint,
compileErrors,
diags => diags.Should().HaveCount(0));
}

IDiagnostic[] diagnosticsMatchingCode = result.Diagnostics.Where(filterFunc).ToArray();
DiagnosticAssertions.DoWithDiagnosticAnnotations(
result.Compilation.SourceFileGrouping.EntryPoint,
result.Diagnostics.Where(filterFunc),
assertAction);
}
}

private static bool IsCompilerDiagnostic(IDiagnostic diagnostic)
{
return diagnostic.Code.StartsWith("BCP");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@ public ExpectedCodeFix(string description, string replacementText)
}
}

protected void ExpectPass(string bicepText, OnCompileErrors onCompileErrors = OnCompileErrors.Fail)
protected void ExpectPass(string bicepText, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors)
{
AssertLinterRuleDiagnostics(NoLocationExprOutsideParamsRule.Code, bicepText, new string[] { }, onCompileErrors);
}

protected void ExpectFail(string bicepText, string expectedMessage, OnCompileErrors onCompileErrors = OnCompileErrors.Fail)
protected void ExpectFail(string bicepText, string expectedMessage, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors)
{
AssertLinterRuleDiagnostics(NoLocationExprOutsideParamsRule.Code, bicepText, new string[] { expectedMessage }, onCompileErrors);
}

protected void ExpectFailWithFix(string bicepText, string expectedMessage, ExpectedCodeFix expectedFix, OnCompileErrors onCompileErrors = OnCompileErrors.Fail)
protected void ExpectFailWithFix(string bicepText, string expectedMessage, ExpectedCodeFix expectedFix, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors)
{
AssertLinterRuleDiagnostics(
NoLocationExprOutsideParamsRule.Code,
Expand Down Expand Up @@ -126,7 +126,7 @@ public void If_DeploymentOrResourceGroup_Object_WithoutLocationProperty_ShouldPa
",
OnCompileErrors.Ignore)]
[DataTestMethod]
public void If_Not_DeploymentOrResourceGroup_OrWithIncorrectNamespace_ShouldPass(string text, OnCompileErrors onCompileErrors = OnCompileErrors.Fail)
public void If_Not_DeploymentOrResourceGroup_OrWithIncorrectNamespace_ShouldPass(string text, OnCompileErrors onCompileErrors = OnCompileErrors.IncludeErrors)
{
ExpectPass(text, onCompileErrors);
}
Expand Down
Loading

0 comments on commit c317dc3

Please sign in to comment.