Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using System.Threading.Tasks;
using Skyline.DataMiner.Utils.SecureCoding.Analyzers.Certificates;

#pragma warning disable S2699 // Tests should include assertions is not valid for Roslyn Analyzers Unit Tests.
namespace Skyline.DataMiner.Utils.SecureCoding.Analyzers.Tests.SecureIO
{
[TestClass]
Expand Down Expand Up @@ -39,5 +38,4 @@ public async Task VerifyUsageDiagnostic_NoDiagnosticsThrown()
await analyzerVerifier.RunAsync();
}
}
}
#pragma warning restore S2699 // Tests should include assertions is not valid for Roslyn Analyzers Unit Tests.
}
1 change: 1 addition & 0 deletions Analyzers/Analyzers.Test/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@
using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("Major Code Smell", "S3928:Parameter names used into ArgumentException constructors should match an existing one ", Justification = "ThrowIfNegative is not available in .NET Framework", Scope = "member", Target = "~M:Skyline.DataMiner.Utils.SecureCoding.Analyzers.Tests.AnalyzerVerifierHelper.BuildDiagnosticResult(System.String,Microsoft.CodeAnalysis.DiagnosticSeverity,System.Int32,System.Int32)~Microsoft.CodeAnalysis.Testing.DiagnosticResult")]
[assembly: SuppressMessage("CodeQuality", "IDE0079:Remove unnecessary suppression", Justification = "Tests should include assertions is not a valid remark for Roslyn Analyzers Unit Tests. ", Scope = "namespace", Target = "~N:Skyline.DataMiner.Utils.SecureCoding.Analyzers.Tests.SecureIO")]
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Microsoft.CodeAnalysis.Testing;
using System.Threading.Tasks;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Skyline.DataMiner.Utils.SecureCoding.Analyzers.SecureCryptography;

namespace Skyline.DataMiner.Utils.SecureCoding.Analyzers.Tests.SecureCryptography
{
[TestClass]
public class CryptographicAnalyzerUnitTests
{
[TestMethod]
public async Task VerifyInsecureCryptographicInvocationUsages()
{
var testCode = @"using System;
using System.Security.Cryptography;
using System.Text;

class InsecureFactoryUsage
{
void MD5_Create()
{
using (var md5 = MD5.Create())
{
var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(""example""));
}
}

void SHA1_Create()
{
using (var sha1 = SHA1.Create())
{
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(""example""));
}
}

void DES_Create()
{
using (var des = DES.Create())
{
des.GenerateIV();
}
}

void TripleDES_Create()
{
using (var tripleDes = TripleDES.Create())
{
tripleDes.GenerateIV();
}
}

void RC2_Create()
{
using (var rc2 = RC2.Create())
{
rc2.GenerateIV();
}
}
}";

var expectedDiagnostics = new List<DiagnosticResult>()
{
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 9, 42),
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 17, 43),
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 25, 42),
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 33, 48),
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 41, 42),
};

var analyzerVerifier = AnalyzerVerifierHelper.BuildAnalyzerVerifier<CryptographicAnalyzer>(testCode);
analyzerVerifier.TestState.ExpectedDiagnostics.AddRange(expectedDiagnostics);

await analyzerVerifier.RunAsync();
}

[TestMethod]
public async Task VerifyInsecureCryptographicObjectCreationUsages()
{
var testCode = @"using System;
using System.Security.Cryptography;
using System.Text;

class InsecureConstructorUsage
{
void MD5_Ctor()
{
using (var md5 = new MD5CryptoServiceProvider())
{
var hash = md5.ComputeHash(Encoding.UTF8.GetBytes(""example""));
}
}

void SHA1_Ctor()
{
using (var sha1 = new SHA1Managed())
{
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(""example""));
}
}

void DES_Ctor()
{
using (var des = new DESCryptoServiceProvider())
{
des.GenerateKey();
}
}

void TripleDES_Ctor()
{
using (var tripleDes = new TripleDESCryptoServiceProvider())
{
tripleDes.GenerateKey();
}
}

void RC2_Ctor()
{
using (var rc2 = new RC2CryptoServiceProvider())
{
rc2.GenerateKey();
}
}
}";

var expectedDiagnostics = new List<DiagnosticResult>()
{
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 9, 42),
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 17, 43),
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 25, 42),
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 33, 48),
AnalyzerVerifierHelper.BuildDiagnosticResult(CryptographicAnalyzer.DiagnosticId, DiagnosticSeverity.Warning, 41, 42),
};

var analyzerVerifier = AnalyzerVerifierHelper.BuildAnalyzerVerifier<CryptographicAnalyzer>(testCode);
analyzerVerifier.TestState.ExpectedDiagnostics.AddRange(expectedDiagnostics);

await analyzerVerifier.RunAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@
using System.IO;
using System.Threading.Tasks;
using Skyline.DataMiner.Utils.SecureCoding.Analyzers.SecureIO;
using Skyline.DataMiner.Utils.SecureCoding.SecureIO;

#pragma warning disable S2699 // Tests should include assertions is not valid for Roslyn Analyzers Unit Tests.
namespace Skyline.DataMiner.Utils.SecureCoding.Analyzers.Tests.SecureIO
{
using Skyline.DataMiner.Utils.SecureCoding.SecureIO;
Expand Down Expand Up @@ -150,7 +148,4 @@ public OwningSymbolClassSecond()
await analyzerVerifier.RunAsync();
}
}
}


#pragma warning restore S2699 // Tests should include assertions is not valid for Roslyn Analyzers Unit Tests.
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Skyline.DataMiner.Utils.SecureCoding.Analyzers.SecureReflection;
using Skyline.DataMiner.Utils.SecureCoding.SecureReflection;

#pragma warning disable S2699 // Tests should include assertions is not valid for Roslyn Analyzers Unit Tests.
namespace Skyline.DataMiner.Utils.SecureCoding.Analyzers.Tests.SecureReflection
{
[TestClass]
Expand Down Expand Up @@ -118,5 +117,4 @@ public async Task VerifyBypassCertificateChain()
await analyzerVerifier.RunAsync();
}
}
}
#pragma warning restore S2699 // Tests should include assertions is not valid for Roslyn Analyzers Unit Tests.
}
4 changes: 4 additions & 0 deletions Analyzers/Analyzers/Analyzers.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<AssemblyName>Skyline.SecureCoding.Analyzers</AssemblyName>
</PropertyGroup>

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>false</IsPackable>
Expand Down
116 changes: 116 additions & 0 deletions Analyzers/Analyzers/SecureCryptography/CryptographicAnalyzer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Data;

namespace Skyline.DataMiner.Utils.SecureCoding.Analyzers.SecureCryptography
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CryptographicAnalyzer : DiagnosticAnalyzer
{
private static readonly ImmutableHashSet<string> insecureHashingAlgorithms = ImmutableHashSet.Create(
"System.Security.Cryptography.MD5",
"System.Security.Cryptography.MD5CryptoServiceProvider",
"System.Security.Cryptography.SHA1",
"System.Security.Cryptography.SHA1Managed"
);

private static readonly ImmutableHashSet<string> insecureEncryptionAlgorithms = ImmutableHashSet.Create(
"System.Security.Cryptography.DES",
"System.Security.Cryptography.DESCryptoServiceProvider",
"System.Security.Cryptography.TripleDES",
"System.Security.Cryptography.TripleDESCryptoServiceProvider",
"System.Security.Cryptography.RC2",
"System.Security.Cryptography.RC2CryptoServiceProvider"
);

public const string DiagnosticId = "SLC_SC0007";

public static DiagnosticDescriptor HashingRule => new DiagnosticDescriptor(
DiagnosticId,
title: "Insecure Hashing Algorithm Used",
messageFormat: "The hashing algorithm '{0}' is considered insecure. Use a secure alternative like SHA256, SHA384 or SHA512.",
"Usage",
DiagnosticSeverity.Warning,
helpLinkUri: $"https://github.com/SkylineCommunications/Skyline.DataMiner.Utils.SecureCoding/blob/main/docs/Rules/{DiagnosticId}.md",
isEnabledByDefault: true);

public static DiagnosticDescriptor EncryptionRule => new DiagnosticDescriptor(
DiagnosticId,
title: "Insecure Encyrption Algorithm Used",
messageFormat: "The encryption algorithm '{0}' is considered insecure. Use a secure alternative like AES.",
"Usage",
DiagnosticSeverity.Warning,
helpLinkUri: $"https://github.com/SkylineCommunications/Skyline.DataMiner.Utils.SecureCoding/blob/main/docs/Rules/{DiagnosticId}.md",
isEnabledByDefault: true);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(HashingRule); } }

public override void Initialize(AnalysisContext context)
{
context.EnableConcurrentExecution();

context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

context.RegisterSyntaxNodeAction(AnalyzeInsecureCryptographicCreation, SyntaxKind.ObjectCreationExpression);

context.RegisterSyntaxNodeAction(AnalyzeInsecureCryptographicInvocation, SyntaxKind.InvocationExpression);
}

private static void AnalyzeInsecureCryptographicInvocation(SyntaxNodeAnalysisContext context)
{
var invocation = (InvocationExpressionSyntax)context.Node;
var symbol = context.SemanticModel.GetSymbolInfo(invocation).Symbol as IMethodSymbol;

if (symbol == null || symbol.Name != "Create")
{
return;
}

if (TryGetInsecureAlgorithmDescriptor(symbol.ContainingType.ToString(), out var descriptor))
{
var diagnostic = Diagnostic.Create(descriptor, invocation.GetLocation(), symbol.ContainingType.Name);
context.ReportDiagnostic(diagnostic);
}
}

private static void AnalyzeInsecureCryptographicCreation(SyntaxNodeAnalysisContext context)
{
var objectCreation = (ObjectCreationExpressionSyntax)context.Node;
var typeSymbol = context.SemanticModel.GetSymbolInfo(objectCreation.Type).Symbol as INamedTypeSymbol;

if (typeSymbol == null)
{
return;
}

if (TryGetInsecureAlgorithmDescriptor(typeSymbol.ToString(), out var descriptor))
{
var diagnostic = Diagnostic.Create(descriptor, objectCreation.GetLocation(), typeSymbol.Name);
context.ReportDiagnostic(diagnostic);
}
}

private static bool TryGetInsecureAlgorithmDescriptor(string fullTypeName, out DiagnosticDescriptor descriptor)
{
if (insecureHashingAlgorithms.Contains(fullTypeName))
{
descriptor = HashingRule;
return true;
}

if (insecureEncryptionAlgorithms.Contains(fullTypeName))
{
descriptor = EncryptionRule;
return true;
}

descriptor = null;
return false;
}
}
}
4 changes: 0 additions & 4 deletions SecureCoding/SecureCoding.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,4 @@
<PackageReference Include="System.Security.Cryptography.Pkcs" Version="9.0.2" />
</ItemGroup>

<ItemGroup>
<Folder Include="SecureCryptography\" />
</ItemGroup>

</Project>