<?php

namespace Tests\unit\gulliver\system;

use CodeScanner;
use G;
use ProcessMaker\Core\System;
use Tests\TestCase;

/**
 * @coversDefaultClass \CodeScanner
 *
 * This test require have the following configurations enable:
 * @link https://wiki.processmaker.com/Plugin_Trigger_Code_Security_Scanner_v2
 */
class CodeScannerTest extends TestCase
{
    private $backupEnvIni;
    private $pathBlackListIni;
    private $pathEnvIni;
    private $pathPlugin;
    private $pluginName = 'pmTest'; // Define the name of the plugin for the test

    /**
     * Call the setUp parent method and create some *.ini files
     */
    public function setUp(): void
    {
        parent::setUp(); // TODO: Change the autogenerated stub

        // Define the path of blacklist.ini
        $this->pathBlackListIni = PATH_CONFIG . "blacklist.ini";

        // Creating a custom Blacklist
        if (!file_exists($this->pathBlackListIni)) {
            $myfile = fopen($this->pathBlackListIni, "w");
            fwrite($myfile, ";Classes
            ;=======
            DashletInterface
            
            ;Functions
            ;=========
            eval
            exec
            assert
            preg_replace
            create_function
            
            ;Information Disclosure
            ;======================
            phpinfo
            posix_mkfifo
            posix_getlogin
            posix_ttyname
            getenv
            get_current_user
            proc_get_status
            get_cfg_var
            disk_free_space
            disk_total_space
            diskfreespace
            getcwd
            getmygid
            getmyinode
            getmypid
            getmyuid");
        }

        // Define the path of env.ini
        $this->pathEnvIni = PATH_CONFIG . "env.ini";

        // Create a backup of the current env.ini
        if (file_exists($this->pathEnvIni)) {
            $this->backupEnvIni = file_get_contents($this->pathEnvIni);
        }

        // Configuring the env.ini file
        file_put_contents($this->pathEnvIni, "enable_blacklist = 1;");

        // Define the path of the plugin
        $this->pathPlugin = PATH_PLUGINS . $this->pluginName . PATH_SEP;

        // Create the plugin
        G::mk_dir($this->pathPlugin, 0777);

        // Add a file into the plugin
        if (!file_exists($this->pathPlugin . "test_1.php")) {
            // Create a file in the plugin with PHP code
            $myfile = fopen($this->pathPlugin . "test_1.php", "w");
            fwrite($myfile, "<?php
                phpinfo();"
            );
        }

        // Add a file into the plugin
        if (!file_exists($this->pathPlugin . "test_2.php")) {
            // Create a file in the plugin with PHP code
            $myfile = fopen($this->pathPlugin . "test_2.php", "w");
            fwrite($myfile, "<?php
                phpinfo();"
            );
        }
    }

    /**
     * Call the tearDown parent method and remove some files created
     */
    public function tearDown(): void
    {
        parent::tearDown();

        // Remove the plugin created
        G::rm_dir($this->pathPlugin);

        // Remove the blacklist created
        G::rm_dir($this->pathBlackListIni);

        // Restore the backup of the env.ini
        file_put_contents($this->pathEnvIni, $this->backupEnvIni);
    }

    /**
     * It test the scope obtained with null parameter
     *
     * @covers ::__construct()
     * @covers ::getScope()
     * @test
     */
    public function it_check_get_scope_configured()
    {
        $configurations = System::getSystemConfiguration('', '', config("system.workspace"));

        // Instance with null parameter
        $codeScanner = new CodeScanner(null);
        $scope = $codeScanner->getScope();
        $this->assertEquals($scope, explode(',', str_replace(' ', '', $configurations['code_scanner_scope'])));

        // Instance with string parameter
        $codeScanner = new CodeScanner(config("system.workspace"));
        $scope = $codeScanner->getScope();
        $this->assertEquals($scope, explode(',', str_replace(' ', '', $configurations['code_scanner_scope'])));

        // Instance with bool parameter
        $codeScanner = new CodeScanner(true);
        $scope = $codeScanner->getScope();
        $this->isEmpty($scope);
    }

    /**
     * It tests disable code without black list
     *
     * @covers ::__construct()
     * @covers ::checkDisabledCode()
     * @test
     */
    public function it_check_disabled_code_without_blacklist()
    {
        // If the blacklist.ini was created we need to remove
        G::rm_dir($this->pathBlackListIni);

        // Instance with default parameter
        $codeScanner = new CodeScanner();
        $phpCode = 'phpinfo();';

        // parameter SOURCE
        $result = $codeScanner->checkDisabledCode('SOURCE', $phpCode);
        $this->assertEmpty($result);

        // parameter FILE
        $result = $codeScanner->checkDisabledCode('FILE', $this->pathPlugin . 'test_1.php');
        $this->assertEmpty($result);

        // parameter PATH
        $result = $codeScanner->checkDisabledCode('PATH', $this->pathPlugin);
        $this->assertEmpty($result);
    }

    /**
     * It tests disable code with black list
     * This test require two configurations enable_blacklist and blacklist.ini
     *
     * @covers ::__construct()
     * @covers ::checkDisabledCode()
     * @covers ::checkDisabledCodeInSource()
     * @test
     */
    public function it_check_disabled_code()
    {
        // Check if the blacklist.ini was created
        if (file_exists($this->pathBlackListIni)) {
            $codeScanner = new CodeScanner();
            $phpCode = 'phpinfo();';

            // parameter SOURCE
            $result = $codeScanner->checkDisabledCode('SOURCE', $phpCode);
            $this->assertNotEmpty($result);

            // parameter FILE
            $result = $codeScanner->checkDisabledCode('FILE', $this->pathPlugin . 'test_1.php');
            $this->assertNotEmpty($result);

            // parameter PATH
            $result = $codeScanner->checkDisabledCode('PATH', $this->pathPlugin);
            $this->assertNotEmpty($result);
        } else {
            $this->markTestSkipped(
                'Please check the configurations to the Code Security Scanner'
            );
        }
    }
}
