CVE-2022-36090 (Java, published 2022-09-08T15:15Z)
Description: value: XWiki Platform Old Core is a core package for XWiki Platform, a generic wiki platform. Prior to versions 13.1.0.5 and 14.3-rc-1, some resources are missing a check for inactive (not yet activated or disabled) users in XWiki, including the REST service. This means a disabled user can enable themselves using a REST call. On the same way some resources handler created by extensions are not protected by default, so an inactive user could perform actions for such extensions. This issue has existed since at least version 1.1 of XWiki for instance configured with the email activation required for new users. Now it's more critical for versions 11.3-rc-1 and later since the maintainers provided the capability to disable user without deleting them and encouraged using that feature. XWiki 14.3-rc-1 and XWiki 13.10.5 contain a patch. There is no workaround for this other than upgrading XWiki.
Severity: nan (CVSS v3 base score: 8.1)
Weakness: NVD-CWE-Other \u2014 Other
NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
Repository: https://github.com/xwiki/xwiki-platform
Files changed:
xwiki-platform-core/xwiki-platform-flamingo/xwiki-platform-flamingo-skin/xwiki-platform-flamingo-skin-resources/src/main/resources/flamingo/drawer.vm
xwiki-platform-core/xwiki-platform-notifications/xwiki-platform-notifications-rest/src/main/java/org/xwiki/notifications/rest/internal/DefaultNotificationsResource.java
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/XWiki.java
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/XWikiContext.java
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/api/XWiki.java
xwiki-platform-core/xwiki-platform-oldcore/src/main/java/com/xpn/xwiki/user/api/XWikiUser.java
xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authorization/xwiki-platform-security-authorization-bridge/src/main/java/org/xwiki/security/authorization/internal/XWikiCachingRightService.java
Commit message: XWIKI-19559: Refactor handling of users in oldcore

  * Add property for inactive users in XWikiContext
  * Ensure that all calls to checkAuth in XS goes through
    XWiki#checkAuth so that inactive users are handled
  * Improve code of XWiki#prepareDocuments to simplify it
  * Ensure that the login page is not displayed if a inactive user
    logged in, in XWikiCachingRightService
  * Improve a bit the code of the drawer to display the logout link in
    case of logged in inactive user


Vulnerable code:
          <a href="$xwiki.getURL('XWiki.XWikiLogout', 'logout', "xredirect=$escapetool.url($xwiki.relativeRequestURL)")" id="tmLogout" rel="nofollow">$services.icon.renderHTML('log-out') $escapetool.xml($services.localization.render('logout'))</a>
        XWikiUser xWikiUser = getXWikiContext().getWiki().getAuthService().checkAuth(getXWikiContext());
        XWikiUser xWikiUser = getXWikiContext().getWiki().getAuthService().checkAuth(getXWikiContext());
        return getAuthService().checkAuth(context);
        if (!hasAccess) {
        // User is disabled: the mail address is marked as checked
        } else if (user.isDisabled(context) && user.isEmailChecked(context)) {
            String action = context.getAction();
            /*
             * Allow inactive users to see skins, ressources, SSX, JSX and downloads they could have seen as guest. The
             * rational behind this behaviour is that inactive users should be able to access the same UI that guests
             * are used to see, including custom icons, panels, and so on...
             */
            if (!((action.equals("skin") && (doc.getSpace().equals("skins") || doc.getSpace().equals("resources")))
                || ((action.equals("skin") || action.equals("download") || action.equals("ssx") || action.equals("jsx"))
                    && getRightService().hasAccessLevel("view", XWikiRightService.GUEST_USER_FULLNAME,
                        doc.getPrefixedFullName(), context))
                || ((action.equals("view") && doc.getFullName().equals("XWiki.AccountValidation"))))) {
                Object[] args = { user.getUser() };
                setPhonyDocument(reference, context, vcontext);
                throw new XWikiException(XWikiException.MODULE_XWIKI_USER, XWikiException.ERROR_XWIKI_USER_DISABLED,
                    "User {0} account is disabled", null, args);
            }
        // User actually needs to activate his mail address.
        } else if (user.isDisabled(context) && !user.isEmailChecked(context)) {
            boolean allow = false;
            String action = context.getAction();
            /*
             * Allow inactive users to see skins, ressources, SSX, JSX and downloads they could have seen as guest. The
             * rational behind this behaviour is that inactive users should be able to access the same UI that guests
             * are used to see, including custom icons, panels, and so on...
             */
            if ((action.equals("skin") && (doc.getSpace().equals("skins") || doc.getSpace().equals("resources")))
                || ((action.equals("skin") || action.equals("download") || action.equals("ssx") || action.equals("jsx"))
                    && getRightService().hasAccessLevel("view", XWikiRightService.GUEST_USER_FULLNAME,
                        doc.getPrefixedFullName(), context))
                || ((action.equals("view") && doc.getFullName().equals("XWiki.AccountValidation")))) {
                allow = true;
            } else {
                String allowed = getConfiguration().getProperty("xwiki.inactiveuser.allowedpages", "");
                if (context.getAction().equals("view") && !allowed.equals("")) {
                    String[] allowedList = StringUtils.split(allowed, " ,");
                    for (String element : allowedList) {
                        if (element.equals(doc.getFullName())) {
                            allow = true;
                            break;
                        }
                    }
                }
            }
            if (!allow) {
                Object[] args = { context.getUser() };
                setPhonyDocument(reference, context, vcontext);
                throw new XWikiException(XWikiException.MODULE_XWIKI_USER, XWikiException.ERROR_XWIKI_USER_INACTIVE,
                    "User {0} account is inactive", null, args);
            }
        return this.context.getWiki().getAuthService().checkAuth(this.context);
        return this.context.getWiki().getAuthService().checkAuth(username, password, rememberme, this.context);
        if (context.getUserReference() == null && !DELETE_ACTION.equals(action) && !LOGIN_ACTION.equals(action)) {

Fixed code:
        #define ($logoutLink)
          <a href="$xwiki.getURL('XWiki.XWikiLogout', 'logout', "xredirect=$escapetool.url($xwiki.relativeRequestURL)")" id="tmLogout" rel="nofollow">$services.icon.renderHTML('log-out') $escapetool.xml($services.localization.render('logout'))</a>
        #end
          $logoutLink
        #elseif($xcontext.user == 'XWiki.XWikiGuest' && $xcontext.inactiveUserReference)
          <a href="$xwiki.getURL($xcontext.inactiveUserReference, 'view')" class="brand-user" id="tmUser">$!xwiki.getUserName($xcontext.inactiveUserReference, false)</a>
          $logoutLink
        XWikiUser xWikiUser = getXWikiContext().getWiki().checkAuth(getXWikiContext());
        XWikiUser xWikiUser = getXWikiContext().getWiki().checkAuth(getXWikiContext());
    /**
     * Authenticate the user from the context and check if the user is disabled or not.
     * If the user is disabled, the method returns {@code null} but set the reference of the authenticated user in the
     * context with the {@link XWikiContext#INACTIVE_USER_REFERENCE} property.
     *
     * @param context the context used to authenticate the user.
     * @return an {@link XWikiUser} if the user is authenticated and enabled, else {@code null}.
     * @throws XWikiException in case of problem when dealing with the authentication.
     */
        XWikiUser user = getAuthService().checkAuth(context);
        if (user != null && user.isDisabled(context)) {
            context.put(XWikiContext.INACTIVE_USER_REFERENCE, user.getUserReference());
            user = null;
        }
        return user;
        XWikiUser inactiveUser = null;
        if (context.getUserReference() == null && context.get(XWikiContext.INACTIVE_USER_REFERENCE) != null) {
            inactiveUser = new XWikiUser((DocumentReference) context.get(XWikiContext.INACTIVE_USER_REFERENCE));
        }
        if (inactiveUser != null && context.getAction().equals("view")) {
            this.handleInactiveUserViewAction(inactiveUser, context, reference, vcontext);
        }
        else if (!hasAccess) {
    private void handleInactiveUserViewAction(XWikiUser inactiveUser, XWikiContext context, DocumentReference reference,
        VelocityContext vcontext) throws XWikiException
    {
        if (inactiveUser.isEmailChecked(context)) {
            Object[] args = { inactiveUser.getUser() };
            setPhonyDocument(reference, context, vcontext);
            throw new XWikiException(XWikiException.MODULE_XWIKI_USER, XWikiException.ERROR_XWIKI_USER_DISABLED,
                "User {0} account is disabled", null, args);
        } else if (!reference.getLocalDocumentReference().equals(XWikiUser.ACCOUNT_VALIDATION_DOCUMENT_REFERENCE)) {
            String allowed = getConfiguration().getProperty("xwiki.inactiveuser.allowedpages", "");
            boolean allow = false;
            if (!StringUtils.isEmpty(allowed)) {
                XWikiDocument doc = this.getDocument(reference, context);
                String[] allowedList = StringUtils.split(allowed, " ,");
                for (String element : allowedList) {
                    if (element.equals(doc.getFullName())) {
                        allow = true;
                        break;
                    }
                }
            }

            if (!allow) {
                Object[] args = { inactiveUser.getUser() };
                setPhonyDocument(reference, context, vcontext);
                throw new XWikiException(XWikiException.MODULE_XWIKI_USER, XWikiException.ERROR_XWIKI_USER_INACTIVE,
                    "User {0} account is inactive", null, args);
            }
        }
    }

    /**
     * The reference of a logged-in inactive user: in such case the context user reference is guest, so we store
     * the actual logged-in user with that key.
     *
     * @since 14.3RC1
     * @since 13.10.5
     */
    @Unstable
    public static final String INACTIVE_USER_REFERENCE = "inactiveUserReference";

        return this.context.getWiki().checkAuth(this.context);
        XWikiUser user =
            this.context.getWiki().getAuthService().checkAuth(username, password, rememberme, this.context);
        if (user.isDisabled(this.context)) {
            this.context.put(XWikiContext.INACTIVE_USER_REFERENCE, user.getUserReference());
            user = null;
        }
        return user;
import org.xwiki.model.reference.LocalDocumentReference;
    public static final LocalDocumentReference ACCOUNT_VALIDATION_DOCUMENT_REFERENCE =
        new LocalDocumentReference(XWiki.SYSTEM_SPACE, "AccountValidation");

        // Also we don't show the login if an inactive user logged in.
        if (context.getUserReference() == null && !DELETE_ACTION.equals(action) && !LOGIN_ACTION.equals(action)
            && context.get(XWikiContext.INACTIVE_USER_REFERENCE) == null) {