PHP S3649 False-Negative

SonarQube 8.6.0.39681, PHP
I’ve created simle script for SQL-injection and writing raw user data into file and SonarQube didn’t find any problem about it.
The script looks like

<?php
use Framework\Helper\Request;

$search_text = Request::getString('search_text', $_REQUEST);
$test_obj = new \Common\Model\Dev6265();

$res = $test_obj->checkSql($search_text);

$test_obj->checkFileWrite($search_text);

And the class it uses

<?php

namespace Common\Model;

use Framework\Helper\Php;

class Dev6265
{

    public function checkSql($user_text) {
        $q = "SELECT * FROM b2bcenter.test_table WHERE text LIKE '$user_text%'";
        $db = \App::i()->getDbConnection();
        $res = $db->query($q);
        return $res->fetchAll();
    }

    public function checkFileWrite($user_text) {
        $file = 'out.txt';
        $file_pointer = Php::fopen($file, 'a');
        Php::fwrite($file_pointer, $user_text);
        Php::fclose();
    }

}

This code uses some Framework classes that are also included in scan scope.
We were using RIPS before the project was closed and it could find such issues.

Hello Andrey and welcome to the community! May I ask what edition of SonarQube you are using? Also, could you share the source code of Request::getString? Thanks!

Please note that we also released SonarQube 8.8. recently which - among other things - contains a lot of improvements for the taint-analysis engine.

1 Like

Hello Hedrik
Thanks for your reply
Here is Request::getString implementation

    public static function getString($variable_name, array $source_array, $default_value = false)
    {
        if (
            !isset($source_array[$variable_name])
            || !is_scalar($source_array[$variable_name])
        ) {
            return $default_value;
        }
        return (string)$source_array[$variable_name];
    }

Nothing special there, as you may see.
We are running 8.6 now. We can upgrade it and check the same script on 8.8 version

Thanks! The edition is quite important because the taint-analysis engine (called security engine) is not available in the Community Edition. It is available starting from the Developer Edition. Are you using the Developer Edition (or above)?

I have constructed this test code (I had to guess about some stuff) and tested it with 8.6:

<?php

class App {
    private static $instance = null;

    public static function i()
    {
        if (self::$instance == null) {
          self::$instance = new App();
        }

        return self::$instance;
    }

    public static function getDbConnection()
    {
        return new PDO();
    }
};

function getString($variable_name, array $source_array, $default_value = false)
{
    if (
        !isset($source_array[$variable_name])
        || !is_scalar($source_array[$variable_name])
    ) {
        return $default_value;
    }
    return (string)$source_array[$variable_name];
}

function checkSql($user_text) {
    $q = "SELECT * FROM b2bcenter.test_table WHERE text LIKE '$user_text%'";
    $db = App::i()->getDbConnection();
    $res = $db->query($q);
    return $res->fetchAll();
}

$search_text = getString('search_text', $_REQUEST);
checkSql($search_text);

Here, the SQL injection is found for me. In regards to the checkFileWrite, fwrite is currently not considered a sink and thus it will not raise an issue.

8.6 Developer Edition.
I was doing the test that is mostly relevant to the code we are actually writing. Specially I wanted to find out if the scripts that use external classes (in seperate files) are well analysed.
I can try to make the test with a bit more obvious code, but some parts like getDbConnection won’t be relevant to our actual code

It is no problem if the other classes are in separate files. Is there any chance that you could also share the other involved classes with me privately, e.g. App? Then I can create a test case that matches your code more accurately.

Hello, Hendrik_Buchwald
I’ve researched for a while, why does your scan work well and my not and the problem is in inheritance of the used classes.
I’ve created sample project where one can see the problem:

There are three versions of the same code there. One uses local class, second vendor class, and third (dir ‘ChildUsed’) uses local class that inherits vendor one. If you scan this project, you will see that SonarScan finds issues in the for the first two cases, but doesn’t for the third one.
It’s actually a big deal for our project, beacause we use many classes that have ancestors in our project


2 Likes

Hi Andrey,

great, thanks for the detailed test cases! I will look at them tomorrow to see what we can do about it.

Hi Andrey,

by default the vendor folder is ignored, thus nothing is found. You can change this with the following setting:

sonar.php.exclusions=

The SCM Sensor also ignores files that are specified in .gitignore. Since the vendor folder is one of those you also have to set the following setting:

sonar.scm.exclusions.disabled=true

Hi Hendrik,
I use sonar.scm.exclusions.disabled=true and SonarQube finds issues when using vendor classes. The problem appeares when I use inheritance in the classes I use.
If method getString is implemeted in the used class the problem is found, if this method is implemented in the anchestor of the used class, the problem is not found.

Hendrik_Buchwald,

could you please take a look at the comment above

Thanks for the additional information. I could verify it, I will contact the developers of the analyzer.

Hey @Andrey.B ,

Thanks for the very thorough investigation and the nice reproducer!

With deactivated exclusions of the vendor folder, I could also reproduce it.

The problem is a limitation with static calls and inheritance in our PHP taint analysis. We are aware of it and already have a (non-public) ticket for it. A minimal reproducer is the following:

class A {
    public static function foo($x) {
        echo $x;
    }
}

class B extends A {}
​
A::foo($_GET[1]); // raises an XSS issue
B::foo($_GET[1]); // it should raise an issue, but it doesn't

The problem only exists with static calls. You could verify it by, experimentally, changing the call in your reproducers ChildUsed\Model::checkSql() method to not use a static call. It will raise the expected issue in that case.

class Model
{
    public function checkSql() {
        $request = new Request();
        $search_text = $request->getString('search_text', $_REQUEST);
// .....

Unfortunately, I cannot give you an estimate yet on when the limitation will be fixed. The ticket is in our backlog, but we did not plan for it yet.

As soon that it gets fixed, we will inform you in this thread.

Thanks for your patience!

3 Likes

Hi Karim

Thanks for your answer, I’ve reproduced your case. Dynamic inheritance really works well. I’ve also tried to scan using Singleton construction

    public static function i()
    {
        if (self::$instance === null) {
            self::$instance = new static();
        }
        return self::$instance;
    }

and works well when defining DbConnection (maybe because of the method name), but doesn’t work for user data handler classes. Maybe you could also consider this case.

1 Like

Hey @Andrey.B,

Thank you for the additional case. We’ll have a look at that too :slight_smile: