CVE-2023-28855 (PHP, published 2023-04-05T18:15Z)
Description: value: Fields is a GLPI plugin that allows users to add custom fields on GLPI items forms. Prior to versions 1.13.1 and 1.20.4, lack of access control check allows any authenticated user to write data to any fields container, including those to which they have no configured access. Versions 1.13.1 and 1.20.4 contain a patch for this issue.
Severity: nan (CVSS v3 base score: 6.5)
Weakness: CWE-269 \u2014 Improper Privilege Management
The product does not properly assign, modify, track, or check privileges for an actor, creating an unintended sphere of control for that actor.
Repository: https://github.com/pluginsGLPI/fields
Files changed:
front/container.form.php
inc/container.class.php
inc/field.class.php
inc/profile.class.php
Commit message: Merge pull request from GHSA-52vv-hm4x-8584



Vulnerable code:
    $container->updateFieldsValues($_REQUEST, $_REQUEST['itemtype'], false);
        $profile   = new PluginFieldsProfile();
            $found = $profile->find(['profiles_id' => $_SESSION['glpiactiveprofile']['id'],
                'plugin_fields_containers_id' => $item['id'],
                'right' => ['>=', READ]
            ]);
            $first_found = array_shift($found);
            if (!$first_found || $first_found['right'] == null || $first_found['right'] == 0) {
        if (isset($_SESSION['glpiactiveprofile']['id'])) {
            $profile = new PluginFieldsProfile();
            if (isset($id)) {
                $found = $profile->find(['profiles_id' => $_SESSION['glpiactiveprofile']['id'],
                    'plugin_fields_containers_id' => $id
                ]);
                $first_found = array_shift($found);
                if ($first_found === null || $first_found['right'] == null || $first_found['right'] == 0) {
                     return false;
                }
        //need to check if container is usable on this object entity
        //profile restriction (for reading profile)
        $profile = new PluginFieldsProfile();
        $found = $profile->find(['profiles_id' => $_SESSION['glpiactiveprofile']['id'],
            'plugin_fields_containers_id' => $c_id
        ]);
        $first_found = array_shift($found);
        $canedit = ($first_found['right'] == CREATE);
        Html::requireJs('tinymce');

        $tmp = $fields;
        $first_field = array_shift($tmp);
        $container_obj->getFromDB($first_field['plugin_fields_containers_id']);
        // find profiles (to check if current profile can edit fields)
        $fprofile = new PluginFieldsProfile();
        $found_p = $fprofile->find(
            [
                'profiles_id' => $_SESSION['glpiactiveprofile']['id'],
                'plugin_fields_containers_id' => $first_field['plugin_fields_containers_id'],
            ]
        );
        $first_found_p = array_shift($found_p);

        if ($item instanceof CommonITILObject) {
            $status = $item->fields['status'] ?? null;
            if (
                ($status !== null && in_array($status, $item->getClosedStatusArray()))
                || $first_found_p['right'] != CREATE
            ) {
                $canedit = false;
            }
        } else {
            if ($first_found_p['right'] != CREATE) {
                $canedit = false;
            }

Fixed code:
    $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $_POST['plugin_fields_containers_id']);
    if ($right > READ) {
        $container->updateFieldsValues($_REQUEST, $_REQUEST['itemtype'], false);
    }
            $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $item['id']);
            if ($right < READ) {
        if (isset($_SESSION['glpiactiveprofile']['id']) && $id > 0) {
            $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $id);
            if ($right < READ) {
                return false;

        // check rights on $c_id
        $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $c_id);
        if (($right > READ) === false) {
            return;
        }

        // need to check if container is usable on this object entity
        //profile restriction
        $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $c_id);
        if ($right < READ) {
            return;
        }
        $canedit = $right > READ;

        $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $c_id);
        if ($right < READ) {
            return;
        }

        Html::requireJs('tinymce');

        $first_field = reset($fields);
        if (!$container_obj->getFromDB($first_field['plugin_fields_containers_id'])) {
            return false;
        }

        // check if current profile can edit fields
        $right = PluginFieldsProfile::getRightOnContainer($_SESSION['glpiactiveprofile']['id'], $container_obj->getID());
        if ($right < READ) {
            return;
        }
        $canedit = $right > READ;
        if ($item instanceof CommonITILObject && in_array($item->fields['status'] ?? null, $item->getClosedStatusArray())) {
            $canedit = false;

    public static function getRightOnContainer(int $profile_id, int $container_id): int
    {
        global $DB;

        $container_profile = $DB->request(
            [
                'SELECT'  => ['MAX' => 'right AS right'],
                'FROM'    => self::getTable(),
                'WHERE'   => [
                    'profiles_id' => $profile_id,
                    'plugin_fields_containers_id' => $container_id,
                ],
            ]
        );

        return (int)$container_profile->current()['right'];
    }