Language C#
Sonarqube version : 9.9.2
Edition : Enterprise
I want to customise C# S3649 rule to analyse custom data access framework.
We have a class over SqlCommand like this (not real code, very simplified)
internal class Program
{
static void Main(string[] args)
{
var x = new CustomCommand();
x.BuildCommand("SELECT * FROM " + args[0]);
x.Execute();
}
}
public class CustomCommand
{
private SqlCommand _cmd;
public void BuildCommand(string command)
{
_cmd = new SqlCommand(command);
}
public void Execute()
{
_cmd.ExecuteNonQuery();
}
}
How to configure SAST roslyn custom rules to throw an error on BuildCommand ?
The security engine does not consider command-line arguments as a source of malicious data. I have thus adapted your example and made a simple ASP.NET API with a simple controller:
using Microsoft.AspNetCore.Mvc;
namespace TodoApi.Controllers;
[ApiController]
[Route("[controller]")]
public class ToDoController : ControllerBase
{
[HttpGet("{arg}")]
public ActionResult<string> Get(string arg)
{
var x = new CustomCommand();
x.BuildCommand("SELECT * FROM " + arg);
x.Execute();
return "Done!";
}
}
The goal here (if I understand well) is to declare the method BuildCommand in the CustomCommand class as a skink, i.e. it should not receive malicious content from a source such as the arg argument of the Get method.
You can declare the custom configuration either at a project level or globally. I will assume here that you want to declare it at the project level. When you open your project, click on Project Settings on the top right side of the screen. Then choose General Settings in the dropdown menu and SAST Engine on the left. Since the code is in C#, you enter the custom configuration in the C# custom configuration box:
“sinks” is an array of sinks. In this case, there is only one.
The skink is declared as a JSON object with the following fields:
“methodId” gives the full name of the method in the form: namespace.class.method(arguments)
“args” is an array of indexes for arguments to consider as sinks. In this case, there is only one argument. BuildCommand is an instance method of CustomCommand so the index of the argument is 1 (index 0 is for the implicit this argument).
Save the custom configuration and scan again your project. It will now detect the injection at the level of the vulnerable BuildCommand method call:
But … My SQL query isn’t directly on web controller (Hexagonal architecture, dependency injection, custom web services protocol). How to force S3649 analyse for specific base type/class/filename/assembly
The code with the web controller is just an example. Web controllers are a common source of data. You can also add your own sources the same way as for sinks. SonarQube has already a huge list of sources.
I don’t understand how I can make all class implenting interface, or that are in a folder/or assembly be considered insecure? Given the documentation I would have tried to use the “sources” part but the examples target a particular “methodId” not sources, or folders
If your objective is to scan all your code in a project against S3649, it is already the case, including your custom configuration. There is nothing to force or activate. If you want to use this custom configuration for all your projects, it is also possible. It is under Administration.
“Sources” is about data, not code. It defines which data can potentially be malicious, i.e. which data can be under the control of an attacker. These are, for example, data sent to a web API. SonarQube contains a list of such sources for the most common cases. Several of those sources are related to the web but we also have some directly related to network, files, etc. You need to declare new sources only if you are using a library that we do not support, or in very special cases.
I am not sure it replies to your questions. If it is not the case, can you tell us a bit more about your context and use case?
I have made a lot of test and now, I think I understood you.
My controller working like WCF. All examples are malicious but only TestInject2 throw S3649
public void TestInject(MyDTOClass cs)
{
var x = new CustomCommand();
x.BuildCommand("SELECT * FROM "+ cs.MyProperty);
throw new Exception("SQL INJECTION !!!");
}
public void TestInject2(string bad)
{
var x = new CustomCommand();
x.BuildCommand("SELECT * FROM "+ bad);
throw new Exception("SQL INJECTION !!!");
}
public class MyDTOClass
{
public string MyProperty {get;set;}
}
I have created a simple WCF service similar to your code:
using System;
using System.Data.SqlClient;
using System.ServiceModel;
namespace Sonar.Sample
{
public class CustomCommand
{
private SqlCommand _cmd;
public void BuildCommand(string command)
{
_cmd = new SqlCommand(command);
}
public void Execute()
{
_cmd.ExecuteNonQuery();
}
}
public class MyDtoCLass
{
public string MyProperty { get; set; }
}
[ServiceContract(Namespace= "http://Sonar.Sample")]
public interface ISample
{
[OperationContract]
void TestInject1(MyDtoCLass cs);
[OperationContract]
void TestInject2(string bad);
}
public class SampleService : ISample
{
public void TestInject1(MyDtoCLass cs)
{
var x = new CustomCommand();
x.BuildCommand("SELECT * FROM " + cs.MyProperty);
}
public void TestInject2(string bad)
{
var x = new CustomCommand();
x.BuildCommand("SELECT * FROM " + bad);
}
public static void Main()
{
using (ServiceHost serviceHost = new ServiceHost(typeof(SampleService)))
{
serviceHost.Open();
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
}
}
}
}
Without any custom SAST configuration, Sonar reports an issue when building the SqlCommand:
So in both cases, Sonar does identify correctly the two vulnerabilities.
If the first issue is not detected in your case, there is something in your code that is different from mine and that may prevent Sonar from identifying the vulnerability.
After a lot of tests, i have finaly find the issue !
With your example it’s work, with little set of my DTO Class it’s work but not for all.
The difference is that certain classes were generated by an external tool and have the header “-- Generated by” in the first lines of the files, by removing this part, the errors show up!
I did the test of activating the analysis on the auto-generated files in the C# settings sonar.cs.analyzeGeneratedCode but that did not allow the errors to be reported, so it would seem, given my observations, that no matter the setting the S3649 analyzer (and maybe others?) stops its analysis if it finds a class that is self-generated / generated by an external tool
To reproduce the bug, create new file for DTO “MyDtoClass” like that
/*------------------------------------------------------
Generated by external tool
--------------------------------------------------------*/
namespace Sonar.Sample
{
public class MyDtoCLass
{
public string MyProperty { get; set; }
}
}
Thank you, from my point of view, this parameter should not even have an impact for security analyses, it is not because I have DTOs which are self-generated that the code is not exposed to SQL injection