<commit_before><?php declare(strict_types=1);
/**
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @copyright 2022 <NAME>
 * @see https://www.elabftw.net Official website
 * @license AGPL-3.0
 * @package elabftw
 */

namespace Elabftw\Services\AdvancedSearchQuery\Visitors;

class VisibilityFieldHelper
{
    public string $possibleInput;

    public function __construct(private string $userInput, private array $visArr)
    {
    }

    public function getArr(): array
    {
        // Convert team groups names to the corresponding IDs for SQL query.
        // $visArr is injected TeamGroups::getVisibilityList()
        $visArrFlipped = array_flip(array_map('strtolower', $this->visArr));
        $onlyStringsArr = array_filter($visArrFlipped, 'is_string');
        $searchArr = $visArrFlipped + array_combine($onlyStringsArr, $onlyStringsArr);
        $this->possibleInput = "'" . implode("', '", array_keys($searchArr)) . "'";

        // Emulate SQL LIKE search functionality so the user can use the same placeholders
        $pattern = '/^' . str_replace(array('%', '_'), array('.*', '.'), $this->userInput) . '$/i';
        // Filter visibility entries based on user input
        $filteredArr = preg_grep($pattern, array_keys($searchArr)) ?: array();

        // Return a unique list of visibility entries that can be used in the SQL where clause
        return array_unique(array_intersect_key(array_values($searchArr), $filteredArr));
    }
}<commit_msg>Prevent potential Regular expression Denial of Service (#3568)<commit_after><?php declare(strict_types=1);
/**
 * @author <NAME> <<EMAIL>>
 * @author <NAME> <<EMAIL>>
 * @copyright 2022 <NAME>
 * @see https://www.elabftw.net Official website
 * @license AGPL-3.0
 * @package elabftw
 */

namespace Elabftw\Services\AdvancedSearchQuery\Visitors;

use function array_combine;
use function array_filter;
use function array_flip;
use function array_intersect_key;
use function array_keys;
use function array_map;
use function array_unique;
use function array_values;
use function implode;
use function preg_grep;
use function preg_quote;
use function str_replace;

class VisibilityFieldHelper
{
    public string $possibleInput;

    public function __construct(private string $userInput, private array $visArr)
    {
    }

    public function getArr(): array
    {
        // Convert team groups names to the corresponding IDs for SQL query.
        // $visArr is injected TeamGroups::getVisibilityList()
        $visArrFlipped = array_flip(array_map('strtolower', $this->visArr));
        $onlyStringsArr = array_filter($visArrFlipped, 'is_string');
        $searchArr = $visArrFlipped + array_combine($onlyStringsArr, $onlyStringsArr);
        $this->possibleInput = "'" . implode("', '", array_keys($searchArr)) . "'";

        // Emulate SQL LIKE search functionality so the user can use the same placeholders
        $pattern = '/^' . str_replace(array('%', '_'), array('.*', '.'), preg_quote($this->userInput, '/')) . '$/i';
        // Filter visibility entries based on user input
        $filteredArr = preg_grep($pattern, array_keys($searchArr)) ?: array();

        // Return a unique list of visibility entries that can be used in the SQL where clause
        return array_unique(array_intersect_key(array_values($searchArr), $filteredArr));
    }
}
{'fraction_non_alphanumeric': 0.12683658170914544, 'fraction_numerical': 0.005397301349325337, 'mean_word_length': 3.8558951965065504, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 23, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2893045', 'n_tokens_mistral': 987, 'n_tokens_neox': 960, 'n_words': 271}