CVE-2024-27937 (PHP, published 2024-03-18T16:15Z)
Description: value: GLPI is a Free Asset and IT Management Software package, Data center management, ITIL Service Desk, licenses tracking and software auditing. An authenticated user can obtain the email address of all GLPI users. This issue has been patched in version 10.0.13.

Severity: nan (CVSS v3 base score: )
Weakness: NVD-CWE-noinfo \u2014 Insufficient Information
There is insufficient information about the issue to classify it; details are unkown or unspecified.
Repository: https://github.com/glpi-project/glpi
Files changed:
ajax/dropdownAllItems.php
ajax/dropdownConnect.php
ajax/dropdownItilActors.php
ajax/dropdownProjectTaskTicket.php
ajax/dropdownSoftwareLicense.php
ajax/dropdownTicketCategories.php
ajax/dropdownTrackingDeviceType.php
ajax/updateTrackingDeviceType.php
src/CommonDBRelation.php
src/CommonITILObject.php
src/Consumable.php
src/Dropdown.php
src/Item_Ticket.php
src/NetworkPortInstantiation.php
src/Session.php
src/User.php
tests/functional/Session.php
Commit message: Merge pull request from GHSA-98qw-hpg3-2hpj

* Filter entity restriction in dropdowns to match active entities

* Handle string values in entities filtering

Vulnerable code:
    $_POST["entity_restrict"],
                    'entity'      => $_POST['entity_restrict'],
                    'entity'    => $_POST['entity_restrict'],
                    'entity'    => $_POST['entity_restrict'],
        'entity_restrict' => $_POST['entity_restrict'],
        ] + getEntitiesRestrictCriteria('glpi_softwarelicenses', 'entities_id', $_POST['entity_restrict'], true),
$opt = ['entity' => $_POST["entity_restrict"]];
        'entity_restrict'     => $_POST['entity_restrict'],
            'entity_restrict' => $_POST['entity_restrict'],
Item_Ticket::dropdownMyDevices($_POST["userID"], $_POST["entity_restrict"]);
                            $options['entity'] = $_POST['entity_restrict'];
            'entity' => $options['entities_id'] ?? $options['entity_restrict']
                                                              => $input["entities_id"],
        if (
            $post["entity_restrict"] = $entities;
        }
        if (isset($post['entity_restrict']) && 'default' === $post['entity_restrict']) {
            $post['entity_restrict'] = $_SESSION['glpiactiveentities'];
                $entity = $post["entity_restrict"];
            'entity_restrict'    => $p['entity'],

Fixed code:
if (isset($_POST['entity_restrict'])) {
    $_POST['entity_restrict'] = Session::getMatchingActiveEntities($_POST['entity_restrict']);
}

    Session::getMatchingActiveEntities($_POST['entity_restrict']),
                    'entity'      => Session::getMatchingActiveEntities($_POST['entity_restrict']),
                    'entity'    => Session::getMatchingActiveEntities($_POST['entity_restrict']),
                    'entity'    => Session::getMatchingActiveEntities($_POST['entity_restrict']),
        'entity_restrict' => Session::getMatchingActiveEntities($_POST['entity_restrict']),
        ] + getEntitiesRestrictCriteria('glpi_softwarelicenses', 'entities_id', Session::getMatchingActiveEntities($_POST['entity_restrict']), true),
$opt = ['entity' => Session::getMatchingActiveEntities($_POST['entity_restrict'])];
        'entity_restrict'     => Session::getMatchingActiveEntities($_POST['entity_restrict']),
            'entity_restrict' => Session::getMatchingActiveEntities($_POST['entity_restrict']),
Item_Ticket::dropdownMyDevices($_POST["userID"], Session::getMatchingActiveEntities($_POST['entity_restrict']));
                            $options['entity'] = Session::getMatchingActiveEntities($_POST['entity_restrict']);
            'entity' => Session::getMatchingActiveEntities($options['entities_id'] ?? $options['entity_restrict']),
                                                              => Session::getMatchingActiveEntities($input["entities_id"]),
        $params['entity'] = Session::getMatchingActiveEntities($params['entity']);
        if (isset($post['entity_restrict']) && 'default' === $post['entity_restrict']) {
            $post['entity_restrict'] = $_SESSION['glpiactiveentities'];
        } elseif (
            $post["entity_restrict"] = Session::getMatchingActiveEntities($entities);
        if (isset($post['entity_restrict'])) {
            $post['entity_restrict'] = Session::getMatchingActiveEntities($post['entity_restrict']);
        }

                $entity = Session::getMatchingActiveEntities($post['entity_restrict']);
                $entity_restrict = Session::getMatchingActiveEntities($entity_restrict);
            $entity_restrict = Session::getMatchingActiveEntities($entity_restrict);
        $entity_restrict = Session::getMatchingActiveEntities($entity_restrict);

            'entity_restrict'    => Session::getMatchingActiveEntities($p['entity']),
    /**
     * Filter given entities ID list to return only these tht are matching current active entities in session.
     *
     * @since 10.0.13
     *
     * @param int|int[] $entities_ids
     *
     * @return int|int[]
     */
    public static function getMatchingActiveEntities(/*int|array*/ $entities_ids)/*: int|array*/
    {
        if (
            (int)$entities_ids === -1
            || (is_array($entities_ids) && count($entities_ids) === 1 && (int)reset($entities_ids) === -1)
        ) {
            // Special value that is generally used to fallback to all active entities.
            return $entities_ids;
        }

        if (!is_array($entities_ids) && !is_int($entities_ids) && !ctype_digit($entities_ids)) {
            // Unexpected value type.
            return [];
        }

        $active_entities_ids = $_SESSION['glpiactiveentities'] ?? [];

        if (!is_array($entities_ids) && in_array((int)$entities_ids, $active_entities_ids, true)) {
            return (int)$entities_ids;
        }

        $filtered = [];
        foreach ((array)$entities_ids as $entity_id) {
            if (
                (is_int($entity_id) || ctype_digit($entity_id))
                && in_array((int)$entity_id, $active_entities_ids, true)
            ) {
                $filtered[] = (int)$entity_id;
            }
        }
        return $filtered;
    }


        $p['entity'] = Session::getMatchingActiveEntities($p['entity']);

    protected function entitiesRestricProvider(): iterable
    {
        // Special case for -1
        foreach ([-1, "-1", [-1], ["-1"]] as $value) {
            yield [
                'entity_restrict' => $value,
                'active_entities' => [0, 1, 2, 3],
                'result'          => is_array($value) ? [-1] : -1,
            ];
        }

        // Integer input, matching
        yield [
            'entity_restrict' => 2,
            'active_entities' => [0, 1, 2, 3],
            'result'          => 2,
        ];

        // String input, matching
        yield [
            'entity_restrict' => '2',
            'active_entities' => [0, 1, 2, 3],
            'result'          => 2,
        ];

        // Integer input, NOT matching
        yield [
            'entity_restrict' => 7,
            'active_entities' => [0, 1, 2, 3],
            'result'          => [],
        ];

        // String input, matching
        yield [
            'entity_restrict' => '7',
            'active_entities' => [0, 1, 2, 3],
            'result'          => [],
        ];

        // Array input, ALL matching
        yield [
            'entity_restrict' => [0, '2', 3],
            'active_entities' => [0, 1, 2, 3],
            'result'          => [0, 2, 3],
        ];

        // Array input, PARTIAL matching
        yield [
            'entity_restrict' => [0, '2', 3, 12, 54, 96],
            'active_entities' => [0, 1, 2, 3],
            'result'          => [0, '2', 3],
        ];

        // Array input, NONE matching
        yield [
            'entity_restrict' => [0, '2', 3, 12, 54, 96],
            'active_entities' => [1, 4],
            'result'          => [],
        ];

        // Empty active entities
        yield [
            'entity_restrict' => [0, '2', 3, 12, 54, 96],
            'active_entities' => null,
            'result'          => [],
        ];

        // Unexpected unique value
        yield [
            'entity_restrict' => 'not a valid value',
            'active_entities' => [0, 1, 2, 3],
            'result'          => [],
        ];

        // Unexpected value in an array
        yield [
            'entity_restrict' => [0, 'not a valid value', 3],
            'active_entities' => [0, 1, 2, 3],
            'result'          => [0, 3],
        ];
    }

    /**
     * @dataProvider entitiesRestricProvider
     */
    public function testGetMatchingActiveEntities(/*int|array*/ $entity_restrict, ?array $active_entities, /*int|array*/ $result): void
    {
        $_SESSION['glpiactiveentities'] = $active_entities;
        $this->variable(\Session::getMatchingActiveEntities($entity_restrict))->isEqualTo($result);
    }