S3649 - Need more example for SAST custom configuration

Hi,

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 ?

i don’t understand the actual docs Security engine custom configuration

Can you give some example with basic real class ? Like how to check SqlCommand injection ?

Thank’s

Hi,

Thank you very much for your Feedback.

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:

The custom configuration is in JSON:

{
    "S3649": {
        "sinks": [
            {
                "methodId": "TodoApi.CustomCommand.BuildCommand(string)",
                "args": [
                    1
                ]
            }
        ]
    }
}
  • “S3649” is the code of the rule (Database queries should not be vulnerable to injection attacks).
  • “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:

I hope this answers your questions. Do not hesitate to reply if it is not clear.

Best regards,
Sebastien

4 Likes

Thank you very much !

This is exactly what I needed.

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

Hi,

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.

Best regards,
Sebastien

Hi,

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

Thank’s for your help

Hi,

I do not understand well your use case.

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?

Best regards,
Sebastien

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;}
}

Hi,

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:

There are two execution flows associated with the vulnerability, one from TestInject1, and one from TestInject2:

So Sonar is able to trace correctly sources to sinks without customization.

  • With the following custom SAST configuration:
{
  "S3649": {
    "sinks": [
      {
        "methodId": "Sonar.Sample.CustomCommand.BuildCommand(string)",
        "args": [
          1
        ]
      }
    ]
  }
}

Sonar reports two vulnerabilities:

  • 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.

1 Like

Thank you,

I’m going to analyze all of this and try to understand the differences between our code

Hi,

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 :slight_smile:

/*------------------------------------------------------
Generated by external tool
--------------------------------------------------------*/

namespace Sonar.Sample
{
    public class MyDtoCLass
    {
        public string MyProperty { get; set; }
    }
}

Hi,

I was able to reproduce the behavior you see. It looks like a bug, like if the setting was ignored. I will investigate and let you know.

Best regards
Sebastien

2 Likes

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 :slight_smile: