From 041f5cd6863ec0d2e3d95f57c7a985e941d871eb Mon Sep 17 00:00:00 2001 From: duncanp Date: Tue, 10 Jul 2018 13:24:52 +0100 Subject: [PATCH] FIx SonarSec #155 - handle array creation --- .../ControlFlowGraph/UcfgIdentifier.cs | 3 ++ .../UcfgInstructionFactory.cs | 38 +++++++++++++ .../Security/Framework/UcfgVerifier.cs | 6 +++ .../Security/Ucfg/UcfgBuilder_Instructions.cs | 54 ++++++++++++++++++- 4 files changed, 100 insertions(+), 1 deletion(-) diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/ControlFlowGraph/UcfgIdentifier.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/ControlFlowGraph/UcfgIdentifier.cs index e7ddcf8c0a5..3141dfe9255 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/ControlFlowGraph/UcfgIdentifier.cs +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/ControlFlowGraph/UcfgIdentifier.cs @@ -105,6 +105,9 @@ public static bool IsMethodId(UcfgIdentifier identifier) => public static UcfgIdentifier CreateTypeId(INamedTypeSymbol typeSymbol) => Create(typeSymbol.ConstructedFrom.ToDisplayString()); + public static UcfgIdentifier CreateArrayTypeId(IArrayTypeSymbol arrayTypeSymbol) => + Create(arrayTypeSymbol.ToDisplayString()); + private static UcfgIdentifier Create(string id) => id == null ? Unknown : new UcfgIdentifier(id); } diff --git a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/ControlFlowGraph/UcfgInstructionFactory.cs b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/ControlFlowGraph/UcfgInstructionFactory.cs index 30f013cb998..2e47b65c968 100644 --- a/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/ControlFlowGraph/UcfgInstructionFactory.cs +++ b/sonaranalyzer-dotnet/src/SonarAnalyzer.CSharp/ControlFlowGraph/UcfgInstructionFactory.cs @@ -52,6 +52,9 @@ public IEnumerable Create(SyntaxNode syntaxNode) case ObjectCreationExpressionSyntax objectCreation: return ProcessObjectCreationExpression(objectCreation); + case ArrayCreationExpressionSyntax arrayCreation: + return ProcessArrayCreationExpression(arrayCreation); + case IdentifierNameSyntax identifierName: return ProcessIdentifierName(identifierName); @@ -141,6 +144,23 @@ private IEnumerable ProcessObjectCreationExpression(ObjectCreationE new[] { expressionService.GetExpression(objectCreationExpression) }.Concat(arguments).ToArray())); } + private IEnumerable ProcessArrayCreationExpression(ArrayCreationExpressionSyntax arrayCreationExpression) + { + var arrayTypeSymbol = semanticModel.GetTypeInfo(arrayCreationExpression).Type as IArrayTypeSymbol; + if (arrayTypeSymbol == null) + { + return NoInstructions; + } + + // A call that constructs an array should look like: + // Code: var x = new string[42]; + // %0 := new string[] // <-- created by this method + // x = __id [ %0 ] // <-- created by the method that handles the assignment + + return CreateNewArray(arrayCreationExpression, arrayTypeSymbol, + expressionService.CreateVariable(arrayTypeSymbol)); + } + private IEnumerable ProcessGenericName(GenericNameSyntax genericName) { var namedTypeSymbol = GetSymbol(genericName) as INamedTypeSymbol; @@ -433,6 +453,24 @@ private IEnumerable CreateNewObject(ObjectCreationExpressionSyntax return new[] { instruction }; } + private IEnumerable CreateNewArray(ArrayCreationExpressionSyntax syntaxNode, + IArrayTypeSymbol arrayTypeSymbol, UcfgExpression callTarget) + { + expressionService.Associate(syntaxNode, callTarget); + + var instruction = new Instruction + { + NewObject = new NewObject + { + Location = syntaxNode.GetUcfgLocation(), + Type = UcfgIdentifier.CreateArrayTypeId(arrayTypeSymbol).ToString() + } + }; + callTarget.ApplyAsTarget(instruction); + + return new[] { instruction }; + } + private ISymbol GetSymbol(SyntaxNode syntaxNode) => semanticModel.GetSymbolInfo(syntaxNode).Symbol; } diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Security/Framework/UcfgVerifier.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Security/Framework/UcfgVerifier.cs index db240460a3d..6622aaf90d7 100644 --- a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Security/Framework/UcfgVerifier.cs +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Security/Framework/UcfgVerifier.cs @@ -77,6 +77,12 @@ public static void VerifyInstructions(string codeSnippet, string methodName, boo .Select(UcfgTestHelper.ToTestString) .ToList(); + // Dump the results to output to make it easier to visually check the results. + // To see the text, select the test in the Test Explorer and click the "Output" + // button in the result window. + System.Console.WriteLine($"Expected:\r\n{string.Join(System.Environment.NewLine, expectedInstructions)}\r\n"); + System.Console.WriteLine($"Actual:\r\n{string.Join(System.Environment.NewLine, actualInstructions)}"); + actualInstructions.Should().Equal(expectedInstructions); } diff --git a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Security/Ucfg/UcfgBuilder_Instructions.cs b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Security/Ucfg/UcfgBuilder_Instructions.cs index e5447769c5d..e25e92225bd 100644 --- a/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Security/Ucfg/UcfgBuilder_Instructions.cs +++ b/sonaranalyzer-dotnet/tests/SonarAnalyzer.UnitTest/Security/Ucfg/UcfgBuilder_Instructions.cs @@ -109,7 +109,7 @@ public string Bar(string s, Func a) } [TestMethod] - public void Assignments_Static_Poperty_On_Generic_Class() + public void Assignments_Static_Property_On_Generic_Class() { const string code = @" namespace Namespace @@ -669,6 +669,58 @@ public void Foo() // %8 := Namespace.Class1.Property.set [ %5 %6 ] } } +}"; + UcfgVerifier.VerifyInstructions(code, "Foo"); + } + + [TestMethod] + public void ArrayCreation_WithNew() + { + const string code = @" +namespace Namespace +{ + public class Class1 + { + public void Foo() + { + // Primitive type + var x0 = new string[42]; // %0 := new string[] + // x0 := __id [ %0 ] + + // Array of objects + var x1 = new Class1[0]; // %1 := new Namespace.Class1[] + // x1 := __id [ %1 ] + + // Multi-rank arrays + var x2 = new int[10,2]; // %2 := new int[*,*] + // x2 := __id [ %2 ] + + // Arrau with initializer (initializer is ignored) + var x3 = new string[] { ""aaa"", ""bbb"", ""ccc"" }; // %3 := new string[] + // x3 := __id [ %3 ] + } + } +}"; + + var x3 = new string[] { "aaa", "bbb", "ccc" }; + + UcfgVerifier.VerifyInstructions(code, "Foo"); + } + + [TestMethod] [Ignore] // TODO: this method currently throws a null-ref + public void ArrayCreation_WithCreateInstance() + { + const string code = @" +namespace Namespace +{ + public class Class1 + { + public void Foo() + { + var a = System.Array.CreateInstance(typeof(string), 10); // %0 = System.Array.CreateInstance(System.Type, int) [ System.Array.CreateInstance ] + // %1 := __id [ %0 ] + } + } }"; UcfgVerifier.VerifyInstructions(code, "Foo"); }