Skip to content

Commit

Permalink
Fix S2115 FN: Support multi-line string interpolation inside a raw st…
Browse files Browse the repository at this point in the history
…ring literal (#6359)

Fix S2115 FN: Support multi-line string interpolation inside a raw string literal
  • Loading branch information
cristian-ambrosini-sonarsource committed Nov 15, 2022
1 parent 21555c2 commit 899fa9f
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,9 @@

using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
using SonarAnalyzer.Common;
using SonarAnalyzer.Helpers;
using SonarAnalyzer.Json;
using SonarAnalyzer.Json.Parsing;

Expand All @@ -42,9 +34,9 @@ public sealed class DatabasePasswordsShouldBeSecure : TrackerHotspotDiagnosticAn
private const string DiagnosticId = "S2115";
private const string MessageFormat = "Use a secure password when connecting to this database.";

private static readonly Regex Sanitizers = new Regex(@"((integrated[_\s]security)|(trusted[_\s]connection))=(sspi|yes|true)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private static readonly Regex Sanitizers = new(@"((integrated[_\s]security)|(trusted[_\s]connection))=(sspi|yes|true)", RegexOptions.Compiled | RegexOptions.IgnoreCase);

private readonly MemberDescriptor[] trackedInvocations =
private static readonly MemberDescriptor[] TrackedInvocations =
{
new MemberDescriptor(KnownType.Microsoft_EntityFrameworkCore_DbContextOptionsBuilder, "UseSqlServer"),
new MemberDescriptor(KnownType.Microsoft_EntityFrameworkCore_SqlServerDbContextOptionsExtensions, "UseSqlServer"),
Expand All @@ -60,15 +52,15 @@ public sealed class DatabasePasswordsShouldBeSecure : TrackerHotspotDiagnosticAn
new MemberDescriptor(KnownType.Microsoft_EntityFrameworkCore_NpgsqlDbContextOptionsBuilderExtensions, "UseNpgsql"),
};

protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance;

public DatabasePasswordsShouldBeSecure()
: base(AnalyzerConfiguration.AlwaysEnabled, DiagnosticId, MessageFormat) { }

protected override ILanguageFacade<SyntaxKind> Language => CSharpFacade.Instance;

protected override void Initialize(TrackerInput input)
{
var inv = Language.Tracker.Invocation;
inv.Track(input, inv.MatchMethod(trackedInvocations), HasEmptyPasswordArgument());
inv.Track(input, inv.MatchMethod(TrackedInvocations), HasEmptyPasswordArgument());
}

protected override void Initialize(SonarAnalysisContext context)
Expand Down Expand Up @@ -153,7 +145,10 @@ private void ReportEmptyPassword(JsonNode doc, string appSettingsPath, Compilati
private static ArgumentSyntax ConnectionStringArgument(SeparatedSyntaxList<ArgumentSyntax> argumentList) =>
// Where(cond).First() is more efficient than First(cond)
argumentList.Where(a => a.NameColon?.Name.Identifier.ValueText == "connectionString").FirstOrDefault()
?? argumentList.Where(a => a.Expression.IsAnyKind(SyntaxKind.StringLiteralExpression, SyntaxKind.InterpolatedStringExpression, SyntaxKind.AddExpression)).FirstOrDefault()
?? argumentList.Where(a => a.Expression.IsAnyKind(
SyntaxKind.StringLiteralExpression,
SyntaxKind.InterpolatedStringExpression,
SyntaxKind.AddExpression)).FirstOrDefault()
?? argumentList.FirstOrDefault();

// For both interpolated strings and concatenation chain, it's easier to search in the string representation of the tree, rather than doing string searches for each individual
Expand All @@ -169,7 +164,9 @@ private static bool HasEmptyPasswordAndNoSanitizers(ExpressionSyntax expression)
|| connectionString.Contains("Password=;")
// this is an edge case, for a string interpolation or concatenation the toString() will contain the ending "
// we prefer to keep it like this for the simplicity of the implementation
|| connectionString.EndsWith("Password=\"");
|| connectionString.EndsWith("Password=\"")
// raw string literals
|| connectionString.EndsWith("Password=\"\"\"");

private static bool HasSanitizers(string connectionString) =>
Sanitizers.IsMatch(connectionString);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
optionsBuilder.UseSqlServer("""Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password="""); // Noncompliant {{Use a secure password when connecting to this database.}}

string[] test = new string[] { "FirstTest", "SecondTest" };

optionsBuilder.UseSqlServer($"""Server={test[0]};Database=myDataBase;User Id=myUsername;Password="""); // Noncompliant

optionsBuilder.UseSqlServer($"Server={test
[0]};Database=myDataBase;User Id=myUsername;Password="); // Noncompliant@-1 {{Use a secure password when connecting to this database.}}
optionsBuilder.UseSqlServer($"Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password={test
[1]}"); // Compliant

optionsBuilder.UseSqlServer($$"""Server={{test
[0]}};Database=myDataBase;User Id=myUsername;Password="""); // FN
[0]}};Database=myDataBase;User Id=myUsername;Password="""); // Noncompliant@-1
optionsBuilder.UseSqlServer($$"""Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password={{test
[1]}}"""); // Compliant
}
Expand Down

0 comments on commit 899fa9f

Please sign in to comment.