Moderate severityNVD Advisory· Published Feb 9, 2022· Updated Apr 23, 2025
Information exposure in xwiki-platform
CVE-2022-23619
Description
XWiki Platform is a generic wiki platform offering runtime services for applications built on top of it. In affected versions it's possible to guess if a user has an account on the wiki by using the "Forgot your password" form, even if the wiki is closed to guest users. This problem has been patched on XWiki 12.10.9, 13.4.1 and 13.6RC1. Users are advised yo update. There are no known workarounds for this issue.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
org.xwiki.platform:xwiki-platform-webMaven | >= 13.5RC1, < 13.6RC1 | 13.6RC1 |
org.xwiki.platform:xwiki-platform-webMaven | >= 13.0.0, < 13.4.1 | 13.4.1 |
org.xwiki.platform:xwiki-platform-webMaven | < 12.10.9 | 12.10.9 |
Affected products
1- Range: >= 13.6.0, < 13.6RC1
Patches
1d8a3cce48e0aXWIKI-18787: Authentication API does not return proper results (#1651)
11 files changed · +222 −213
xwiki-platform-core/pom.xml+18 −0 modified@@ -156,6 +156,24 @@ </differences> </revapi.differences> --> + <revapi.differences> + <criticality>highlight</criticality> + <differences> + <item> + <ignore>true</ignore> + <code>java.method.returnTypeChanged</code> + <old>method javax.mail.internet.InternetAddress org.xwiki.security.authentication.script.AuthenticationScriptService::requestResetPassword(org.xwiki.user.UserReference) throws org.xwiki.security.authentication.ResetPasswordException</old> + <new>method void org.xwiki.security.authentication.script.AuthenticationScriptService::requestResetPassword(org.xwiki.user.UserReference) throws org.xwiki.security.authentication.ResetPasswordException</new> + <justification>Unstable API: this API was not properly designed and shouldn't have returned that in first place.</justification> + </item> + <item> + <ignore>true</ignore> + <code>java.method.removed</code> + <old>method javax.mail.internet.InternetAddress org.xwiki.security.authentication.ResetPasswordRequestResponse::getUserEmail()</old> + <justification>Unstable API: this API was not properly designed and shouldn't have returned that in first place.</justification> + </item> + </differences> + </revapi.differences> </analysisConfiguration> </configuration> </plugin>
xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-test/xwiki-platform-administration-test-docker/src/test/it/org/xwiki/administration/test/ui/ResetPasswordIT.java+8 −6 modified@@ -117,17 +117,19 @@ public void resetForgottenPassword(TestUtils setup) throws Exception // Try to reset the password of a non existent user resetPasswordPage.setUserName("SomeUserThatDoesNotExist"); resetPasswordPage = resetPasswordPage.clickResetPassword(); - assertFalse(resetPasswordPage.isResetPasswordSent()); - assertTrue(resetPasswordPage.getMessage().contains("user does not exist")); + + // there should not have any indication if the user exists or not. + assertTrue(resetPasswordPage.isFormSubmitted()); // Try again - resetPasswordPage = resetPasswordPage.clickRetry(); + resetPasswordPage = ResetPasswordPage.gotoPage(); // Try to reset the password of our user, when he has no email set resetPasswordPage.setUserName(userName); resetPasswordPage.clickResetPassword(); - assertFalse(resetPasswordPage.isResetPasswordSent()); - assertTrue(resetPasswordPage.getMessage().contains("email address not provided")); + + // there should not have any indication if an email address is provided or not. + assertTrue(resetPasswordPage.isFormSubmitted()); // Try again. This time, set the user's email address in the profile setup.loginAsSuperAdmin(); @@ -145,7 +147,7 @@ public void resetForgottenPassword(TestUtils setup) throws Exception "Actual message: " + newResetPasswordPage.getMessage()); // Check the result - assertTrue(resetPasswordPage.isResetPasswordSent()); + assertTrue(resetPasswordPage.isFormSubmitted()); // Check the emails received by the user assertTrue(this.mail.waitForIncomingEmail(1)); MimeMessage[] receivedEmails = this.mail.getReceivedMessages();
xwiki-platform-core/xwiki-platform-administration/xwiki-platform-administration-test/xwiki-platform-administration-test-pageobjects/src/main/java/org/xwiki/administration/test/po/ResetPasswordPage.java+7 −1 modified@@ -67,7 +67,13 @@ public ResetPasswordPage clickResetPassword() return new ResetPasswordPage(); } - public boolean isResetPasswordSent() + /** + * This method only checks if the form was properly submitted and didn't return an error. + * It does not mean that an email was necessarily sent. + * + * @return {@code true} if the form is properly submitted. + */ + public boolean isFormSubmitted() { // If there is no form and we see an info box, then the request was sent. return !getDriver().hasElementWithoutWaiting(By.cssSelector("#resetPasswordForm"))
xwiki-platform-core/xwiki-platform-oldcore/src/main/resources/ApplicationResources.properties+6 −1 modified@@ -2449,7 +2449,7 @@ xe.admin.passwordReset.title=Forgot your password? xe.admin.passwordReset.instructions=Please enter your username to start the password reset process. xe.admin.passwordReset.username.label=Username xe.admin.passwordReset.submit=Reset password -xe.admin.passwordReset.emailSent=An e-mail was sent to {0}. Please follow the instructions in that e-mail to complete the password reset procedure. +xe.admin.passwordReset.emailSentToUsername=An e-mail was sent to the address configured for user "{0}". Please follow the instructions in that e-mail to complete the password reset procedure. xe.admin.passwordReset.login=Login \u00BB xe.admin.passwordReset.error.noUser=The {0} user does not exist. xe.admin.passwordReset.error.ldapUser=The {0} user is an LDAP user. In that case the password has to be changed on the LDAP server. @@ -5501,6 +5501,11 @@ xe.admin.forgotUsername.result=Your username is: {0} xe.admin.forgotUsername.multipleResults=The following usernames are registered with this email address: xe.admin.forgotUsername.error.noAccount=No account is registered using this email address. +####################################### +## until 12.10.9, 13.6RC1, 13.4.1 +####################################### +xe.admin.passwordReset.emailSent=An e-mail was sent to {0}. Please follow the instructions in that e-mail to complete the password reset procedure. + ## Used to indicate where deprecated keys end #@deprecatedend
xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-api/src/main/java/org/xwiki/security/authentication/ResetPasswordRequestResponse.java+0 −7 modified@@ -19,8 +19,6 @@ */ package org.xwiki.security.authentication; -import javax.mail.internet.InternetAddress; - import org.xwiki.stability.Unstable; import org.xwiki.user.UserReference; @@ -38,11 +36,6 @@ public interface ResetPasswordRequestResponse */ UserReference getUserReference(); - /** - * @return the email address of the user for whom the reset password request have been performed. - */ - InternetAddress getUserEmail(); - /** * @return the verification code to be send to the user. */
xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/main/java/org/xwiki/security/authentication/internal/DefaultResetPasswordManager.java+145 −138 modified@@ -29,6 +29,7 @@ import javax.mail.internet.InternetAddress; import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; import org.xwiki.component.annotation.Component; import org.xwiki.localization.ContextualLocalizationManager; import org.xwiki.model.reference.DocumentReference; @@ -110,180 +111,186 @@ public class DefaultResetPasswordManager implements ResetPasswordManager @Inject private Provider<ResetPasswordMailSender> resetPasswordMailSenderProvider; - private void checkUserReference(UserReference userReference) throws ResetPasswordException - { - if (!this.userManager.exists(userReference)) { - String exceptionMessage = - this.localizationManager.getTranslationPlain("xe.admin.passwordReset.error.noUser", - userReference.toString()); - throw new ResetPasswordException(exceptionMessage); - } + @Inject + private Logger logger; + private boolean checkUserReference(UserReference userReference) throws ResetPasswordException + { // FIXME: This check shouldn't be needed if we'd have the proper API to determine which kind of // authentication is used. if (!(userReference instanceof DocumentUserReference)) { throw new ResetPasswordException("Only user having a page on the wiki can reset their password."); } + + return this.userManager.exists(userReference); } @Override public ResetPasswordRequestResponse requestResetPassword(UserReference userReference) throws ResetPasswordException { - this.checkUserReference(userReference); - - UserProperties userProperties = this.userPropertiesResolver.resolve(userReference); - InternetAddress email = userProperties.getEmail(); - - if (email == null) { - String exceptionMessage = - this.localizationManager.getTranslationPlain("xe.admin.passwordReset.error.noEmail"); - throw new ResetPasswordException(exceptionMessage); - } - - DocumentUserReference documentUserReference = (DocumentUserReference) userReference; - DocumentReference reference = documentUserReference.getReference(); - XWikiContext context = this.contextProvider.get(); - - try { - XWikiDocument userDocument = context.getWiki().getDocument(reference, context); - - if (userDocument.getXObject(LDAP_CLASS_REFERENCE) != null) { - String exceptionMessage = - this.localizationManager.getTranslationPlain("xe.admin.passwordReset.error.ldapUser", - userReference.toString()); - throw new ResetPasswordException(exceptionMessage); + if (this.checkUserReference(userReference)) { + UserProperties userProperties = this.userPropertiesResolver.resolve(userReference); + InternetAddress email = userProperties.getEmail(); + + if (email != null) { + DocumentUserReference documentUserReference = (DocumentUserReference) userReference; + DocumentReference reference = documentUserReference.getReference(); + XWikiContext context = this.contextProvider.get(); + + try { + XWikiDocument userDocument = context.getWiki().getDocument(reference, context); + + if (userDocument.getXObject(LDAP_CLASS_REFERENCE) != null) { + String exceptionMessage = + this.localizationManager.getTranslationPlain("xe.admin.passwordReset.error.ldapUser", + userReference.toString()); + throw new ResetPasswordException(exceptionMessage); + } + + BaseObject xObject = userDocument.getXObject(RESET_PASSWORD_REQUEST_CLASS_REFERENCE, true, context); + String verificationCode = context.getWiki().generateRandomString(30); + xObject.set(VERIFICATION_PROPERTY, verificationCode, context); + + String saveComment = + this.localizationManager.getTranslationPlain("xe.admin.passwordReset.versionComment"); + context.getWiki().saveDocument(userDocument, saveComment, true, context); + + return new DefaultResetPasswordRequestResponse(userReference, verificationCode); + } catch (XWikiException e) { + throw new ResetPasswordException( + "Error when reading user document to perform reset password request.", + e); + } + } else { + // In case the mail is not configured, we log a message to the admin. + this.logger.info("User [{}] asked to reset their password, but did not have any email configured.", + userReference); } - - BaseObject xObject = userDocument.getXObject(RESET_PASSWORD_REQUEST_CLASS_REFERENCE, true, context); - String verificationCode = context.getWiki().generateRandomString(30); - xObject.set(VERIFICATION_PROPERTY, verificationCode, context); - - String saveComment = - this.localizationManager.getTranslationPlain("xe.admin.passwordReset.versionComment"); - context.getWiki().saveDocument(userDocument, saveComment, true, context); - - return new DefaultResetPasswordRequestResponse(userReference, email, verificationCode); - } catch (XWikiException e) { - throw new ResetPasswordException("Error when reading user document to perform reset password request.", e); } + return new DefaultResetPasswordRequestResponse(userReference, null); } @Override public void sendResetPasswordEmailRequest(ResetPasswordRequestResponse requestResponse) throws ResetPasswordException { - AuthenticationResourceReference resourceReference = - new AuthenticationResourceReference(AuthenticationAction.RESET_PASSWORD); - - UserReference userReference = requestResponse.getUserReference(); - UserProperties userProperties = this.userPropertiesResolver.resolve(userReference); - String serializedUserReference = this.referenceSerializer.serialize(userReference); - - // FIXME: this should be provided as part of the User API. - String formattedName = ""; - if (!StringUtils.isBlank(userProperties.getFirstName())) { - formattedName += userProperties.getFirstName(); - } - if (!StringUtils.isBlank(userProperties.getLastName())) { - if (!StringUtils.isBlank(formattedName)) { - formattedName += " "; + if (this.checkUserReference(requestResponse.getUserReference())) { + AuthenticationResourceReference resourceReference = + new AuthenticationResourceReference(AuthenticationAction.RESET_PASSWORD); + + UserReference userReference = requestResponse.getUserReference(); + UserProperties userProperties = this.userPropertiesResolver.resolve(userReference); + InternetAddress email = userProperties.getEmail(); + String serializedUserReference = this.referenceSerializer.serialize(userReference); + + // FIXME: this should be provided as part of the User API. + String formattedName = ""; + if (!StringUtils.isBlank(userProperties.getFirstName())) { + formattedName += userProperties.getFirstName(); + } + if (!StringUtils.isBlank(userProperties.getLastName())) { + if (!StringUtils.isBlank(formattedName)) { + formattedName += " "; + } + formattedName += userProperties.getLastName(); + } + if (StringUtils.isBlank(formattedName)) { + formattedName = serializedUserReference; + } + resourceReference.addParameter("u", serializedUserReference); + resourceReference.addParameter("v", requestResponse.getVerificationCode()); + + XWikiContext context = contextProvider.get(); + + ExtendedURL extendedURL = null; + try { + extendedURL = this.resourceReferenceSerializer.serialize(resourceReference); + extendedURL = this.urlNormalizer.normalize(extendedURL); + URL serverURL = context.getURLFactory().getServerURL(context); + URL externalVerificationURL = new URL(serverURL, extendedURL.serialize()); + + this.resetPasswordMailSenderProvider.get() + .sendResetPasswordEmail(formattedName, email, externalVerificationURL); + } catch (SerializeResourceReferenceException | UnsupportedResourceReferenceException + | MalformedURLException e) { + throw new ResetPasswordException("Error when processing information for creating the email.", e); } - formattedName += userProperties.getLastName(); - } - if (StringUtils.isBlank(formattedName)) { - formattedName = serializedUserReference; - } - resourceReference.addParameter("u", serializedUserReference); - resourceReference.addParameter("v", requestResponse.getVerificationCode()); - - XWikiContext context = contextProvider.get(); - - ExtendedURL extendedURL = null; - try { - extendedURL = this.resourceReferenceSerializer.serialize(resourceReference); - extendedURL = this.urlNormalizer.normalize(extendedURL); - URL serverURL = context.getURLFactory().getServerURL(context); - URL externalVerificationURL = new URL(serverURL, extendedURL.serialize()); - - this.resetPasswordMailSenderProvider.get() - .sendResetPasswordEmail(formattedName, requestResponse.getUserEmail(), externalVerificationURL); - } catch (SerializeResourceReferenceException | UnsupportedResourceReferenceException | MalformedURLException e) - { - throw new ResetPasswordException("Error when processing information for creating the email.", e); } } @Override public ResetPasswordRequestResponse checkVerificationCode(UserReference userReference, String verificationCode) throws ResetPasswordException { - this.checkUserReference(userReference); - XWikiContext context = this.contextProvider.get(); - - UserProperties userProperties = this.userPropertiesResolver.resolve(userReference); - InternetAddress email = userProperties.getEmail(); - - DocumentUserReference documentUserReference = (DocumentUserReference) userReference; - DocumentReference reference = documentUserReference.getReference(); - String exceptionMessage = - this.localizationManager.getTranslationPlain("xe.admin.passwordReset.step2.error.wrongParameters", - userReference.toString()); - - try { - XWikiDocument userDocument = context.getWiki().getDocument(reference, context); - BaseObject xObject = userDocument.getXObject(RESET_PASSWORD_REQUEST_CLASS_REFERENCE); - if (xObject == null) { - throw new ResetPasswordException(exceptionMessage); - } + if (this.checkUserReference(userReference)) { + XWikiContext context = this.contextProvider.get(); - String storedVerificationCode = xObject.getStringValue(VERIFICATION_PROPERTY); - BaseClass xClass = xObject.getXClass(context); - PropertyInterface verification = xClass.get(VERIFICATION_PROPERTY); - if (!(verification instanceof PasswordClass)) { - throw new ResetPasswordException("Bad definition of ResetPassword XClass."); - } - PasswordClass passwordClass = (PasswordClass) verification; - String equivalentPassword = - passwordClass.getEquivalentPassword(storedVerificationCode, verificationCode); - - // We ensure to reset the verification code before checking if it's correct to avoid any bruteforce attack. - String newVerificationCode = context.getWiki().generateRandomString(30); - xObject.set(VERIFICATION_PROPERTY, newVerificationCode, context); - String saveComment = this.localizationManager - .getTranslationPlain("xe.admin.passwordReset.step2.versionComment.changeValidationKey"); - context.getWiki().saveDocument(userDocument, saveComment, true, context); - - if (!storedVerificationCode.equals(equivalentPassword)) { - throw new ResetPasswordException(exceptionMessage); - } + DocumentUserReference documentUserReference = (DocumentUserReference) userReference; + DocumentReference reference = documentUserReference.getReference(); + String exceptionMessage = + this.localizationManager.getTranslationPlain("xe.admin.passwordReset.step2.error.wrongParameters", + userReference.toString()); - return new DefaultResetPasswordRequestResponse(userReference, email, newVerificationCode); - } catch (XWikiException e) { - throw new ResetPasswordException("Cannot open user document to check verification code.", e); + try { + XWikiDocument userDocument = context.getWiki().getDocument(reference, context); + BaseObject xObject = userDocument.getXObject(RESET_PASSWORD_REQUEST_CLASS_REFERENCE); + if (xObject == null) { + throw new ResetPasswordException(exceptionMessage); + } + + String storedVerificationCode = xObject.getStringValue(VERIFICATION_PROPERTY); + BaseClass xClass = xObject.getXClass(context); + PropertyInterface verification = xClass.get(VERIFICATION_PROPERTY); + if (!(verification instanceof PasswordClass)) { + throw new ResetPasswordException("Bad definition of ResetPassword XClass."); + } + PasswordClass passwordClass = (PasswordClass) verification; + String equivalentPassword = + passwordClass.getEquivalentPassword(storedVerificationCode, verificationCode); + + // We ensure to reset the verification code before checking if it's correct to avoid + // any bruteforce attack. + String newVerificationCode = context.getWiki().generateRandomString(30); + xObject.set(VERIFICATION_PROPERTY, newVerificationCode, context); + String saveComment = this.localizationManager + .getTranslationPlain("xe.admin.passwordReset.step2.versionComment.changeValidationKey"); + context.getWiki().saveDocument(userDocument, saveComment, true, context); + + if (!storedVerificationCode.equals(equivalentPassword)) { + throw new ResetPasswordException(exceptionMessage); + } + + return new DefaultResetPasswordRequestResponse(userReference, newVerificationCode); + } catch (XWikiException e) { + throw new ResetPasswordException("Cannot open user document to check verification code.", e); + } + } else { + return new DefaultResetPasswordRequestResponse(userReference, null); } } @Override public void resetPassword(UserReference userReference, String newPassword) throws ResetPasswordException { - this.checkUserReference(userReference); - XWikiContext context = this.contextProvider.get(); - - DocumentUserReference documentUserReference = (DocumentUserReference) userReference; - DocumentReference reference = documentUserReference.getReference(); - - try { - XWikiDocument userDocument = context.getWiki().getDocument(reference, context); - userDocument.removeXObjects(RESET_PASSWORD_REQUEST_CLASS_REFERENCE); - BaseObject userXObject = userDocument.getXObject(USER_CLASS_REFERENCE); - userXObject.setStringValue("password", newPassword); - - String saveComment = this.localizationManager.getTranslationPlain( - "xe.admin.passwordReset.step2.versionComment.passwordReset"); - context.getWiki().saveDocument(userDocument, saveComment, true, context); - } catch (XWikiException e) { - throw new ResetPasswordException("Cannot open user document to perform reset password.", e); + if (this.checkUserReference(userReference)) { + XWikiContext context = this.contextProvider.get(); + + DocumentUserReference documentUserReference = (DocumentUserReference) userReference; + DocumentReference reference = documentUserReference.getReference(); + + try { + XWikiDocument userDocument = context.getWiki().getDocument(reference, context); + userDocument.removeXObjects(RESET_PASSWORD_REQUEST_CLASS_REFERENCE); + BaseObject userXObject = userDocument.getXObject(USER_CLASS_REFERENCE); + userXObject.setStringValue("password", newPassword); + + String saveComment = this.localizationManager.getTranslationPlain( + "xe.admin.passwordReset.step2.versionComment.passwordReset"); + context.getWiki().saveDocument(userDocument, saveComment, true, context); + } catch (XWikiException e) { + throw new ResetPasswordException("Cannot open user document to perform reset password.", e); + } } } }
xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/main/java/org/xwiki/security/authentication/internal/DefaultResetPasswordRequestResponse.java+1 −16 modified@@ -19,8 +19,6 @@ */ package org.xwiki.security.authentication.internal; -import javax.mail.internet.InternetAddress; - import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.xwiki.security.authentication.ResetPasswordRequestResponse; @@ -35,19 +33,16 @@ public final class DefaultResetPasswordRequestResponse implements ResetPasswordRequestResponse { private final UserReference userReference; - private final InternetAddress userEmail; private final String verificationCode; /** * Default constructor. * @param reference the user for whom a reset password request is performed. - * @param userEmail the email of the user. * @param verificationCode the code to send for resetting the password. */ - DefaultResetPasswordRequestResponse(UserReference reference, InternetAddress userEmail, String verificationCode) + DefaultResetPasswordRequestResponse(UserReference reference, String verificationCode) { this.userReference = reference; - this.userEmail = userEmail; this.verificationCode = verificationCode; } @@ -59,14 +54,6 @@ public UserReference getUserReference() return userReference; } - /** - * @return the email of the user. - */ - public InternetAddress getUserEmail() - { - return userEmail; - } - /** * @return the code to send for resetting the password. */ @@ -90,7 +77,6 @@ public boolean equals(Object o) return new EqualsBuilder() .append(userReference, that.userReference) - .append(userEmail, that.userEmail) .append(verificationCode, that.verificationCode) .isEquals(); } @@ -100,7 +86,6 @@ public int hashCode() { return new HashCodeBuilder(17, 37) .append(userReference) - .append(userEmail) .append(verificationCode) .toHashCode(); }
xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-default/src/test/java/org/xwiki/security/authentication/internal/DefaultResetPasswordManagerTest.java+32 −32 modified@@ -28,6 +28,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.xwiki.localization.ContextualLocalizationManager; import org.xwiki.model.reference.DocumentReference; import org.xwiki.resource.ResourceReference; @@ -37,6 +38,8 @@ import org.xwiki.security.authentication.ResetPasswordException; import org.xwiki.security.authentication.ResetPasswordManager; import org.xwiki.security.authentication.ResetPasswordRequestResponse; +import org.xwiki.test.LogLevel; +import org.xwiki.test.junit5.LogCaptureExtension; import org.xwiki.test.junit5.mockito.ComponentTest; import org.xwiki.test.junit5.mockito.InjectMockComponents; import org.xwiki.test.junit5.mockito.MockComponent; @@ -59,7 +62,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -100,6 +107,9 @@ class DefaultResetPasswordManagerTest @MockComponent private Provider<ResetPasswordMailSender> resetPasswordMailSenderProvider; + @RegisterExtension + LogCaptureExtension logCapture = new LogCaptureExtension(LogLevel.INFO); + private ResetPasswordMailSender resetPasswordMailSender; private DocumentUserReference userReference; @@ -145,24 +155,19 @@ void requestResetPassword() throws Exception .thenReturn("Save verification code 42"); ResetPasswordRequestResponse expectedResult = - new DefaultResetPasswordRequestResponse(this.userReference, email, - verificationCode); + new DefaultResetPasswordRequestResponse(this.userReference, verificationCode); assertEquals(expectedResult, this.resetPasswordManager.requestResetPassword(this.userReference)); verify(xObject).set(DefaultResetPasswordManager.VERIFICATION_PROPERTY, verificationCode, context); verify(this.xWiki).saveDocument(this.userDocument, "Save verification code 42", true, this.context); } @Test - void requestResetPasswordUnexistingUser() + void requestResetPasswordUnexistingUser() throws ResetPasswordException { when(this.userReference.toString()).thenReturn("user:Foobar"); when(this.userManager.exists(this.userReference)).thenReturn(false); - String exceptionMessage = "User [user:Foobar] doesn't exist"; - when(this.localizationManager.getTranslationPlain("xe.admin.passwordReset.error.noUser", - "user:Foobar")).thenReturn(exceptionMessage); - ResetPasswordException resetPasswordException = assertThrows(ResetPasswordException.class, - () -> this.resetPasswordManager.requestResetPassword(this.userReference)); - assertEquals(exceptionMessage, resetPasswordException.getMessage()); + assertEquals(new DefaultResetPasswordRequestResponse(this.userReference, null), + this.resetPasswordManager.requestResetPassword(this.userReference)); } @Test @@ -180,12 +185,11 @@ void requestResetPasswordNotDocumentReferenceUser() void requestResetPasswordNoEmail() throws Exception { when(this.userManager.exists(this.userReference)).thenReturn(true); - String exceptionMessage = "User has no email address."; - when(this.localizationManager.getTranslationPlain("xe.admin.passwordReset.error.noEmail")) - .thenReturn(exceptionMessage); - ResetPasswordException resetPasswordException = assertThrows(ResetPasswordException.class, - () -> this.resetPasswordManager.requestResetPassword(this.userReference)); - assertEquals(exceptionMessage, resetPasswordException.getMessage()); + assertEquals(new DefaultResetPasswordRequestResponse(this.userReference, null), + this.resetPasswordManager.requestResetPassword(this.userReference)); + when(this.userReference.toString()).thenReturn("foo"); + assertEquals("User [foo] asked to reset their password, but did not have any email configured.", + logCapture.getMessage(0)); } @Test @@ -210,6 +214,7 @@ void requestResetPasswordLdapUser() throws Exception @Test void sendResetPasswordEmailRequest() throws Exception { + when(this.userManager.exists(this.userReference)).thenReturn(true); when(this.referenceSerializer.serialize(this.userReference)).thenReturn("user:Foobar"); when(this.userProperties.getFirstName()).thenReturn("Foo"); when(this.userProperties.getLastName()).thenReturn("Bar"); @@ -231,9 +236,10 @@ void sendResetPasswordEmailRequest() throws Exception when(urlFactory.getServerURL(this.context)).thenReturn(new URL("http://xwiki.org")); InternetAddress email = new InternetAddress("foobar@xwiki.org"); + when(this.userProperties.getEmail()).thenReturn(email); + DefaultResetPasswordRequestResponse requestResponse = - new DefaultResetPasswordRequestResponse(this.userReference, email, - verificationCode); + new DefaultResetPasswordRequestResponse(this.userReference, verificationCode); this.resetPasswordManager.sendResetPasswordEmailRequest(requestResponse); verify(this.resetPasswordMailSender).sendResetPasswordEmail("Foo Bar", email, new URL("http://xwiki.org/xwiki/authenticate/reset?u=user%3AFoobar&v=foobar4242")); @@ -266,24 +272,20 @@ void checkVerificationCode() throws Exception .getTranslationPlain("xe.admin.passwordReset.step2.versionComment.changeValidationKey")) .thenReturn(saveComment); DefaultResetPasswordRequestResponse expected = - new DefaultResetPasswordRequestResponse(this.userReference, email, - newVerificationCode); + new DefaultResetPasswordRequestResponse(this.userReference, newVerificationCode); assertEquals(expected, this.resetPasswordManager.checkVerificationCode(this.userReference, verificationCode)); verify(this.xWiki).saveDocument(this.userDocument, saveComment, true, context); } @Test - void checkVerificationCodeUnexistingUser() + void checkVerificationCodeUnexistingUser() throws ResetPasswordException { when(this.userReference.toString()).thenReturn("user:Foobar"); when(this.userManager.exists(this.userReference)).thenReturn(false); - String exceptionMessage = "User [user:Foobar] doesn't exist"; - when(this.localizationManager.getTranslationPlain("xe.admin.passwordReset.error.noUser", - "user:Foobar")).thenReturn(exceptionMessage); - ResetPasswordException resetPasswordException = assertThrows(ResetPasswordException.class, - () -> this.resetPasswordManager.checkVerificationCode(this.userReference, "some code")); - assertEquals(exceptionMessage, resetPasswordException.getMessage()); + ResetPasswordRequestResponse resetPasswordRequestResponse = + this.resetPasswordManager.checkVerificationCode(this.userReference, "some code"); + assertEquals(new DefaultResetPasswordRequestResponse(this.userReference, null), resetPasswordRequestResponse); } @Test @@ -358,12 +360,10 @@ void resetPasswordUnexistingUser() throws Exception { when(this.userReference.toString()).thenReturn("user:Foobar"); when(this.userManager.exists(this.userReference)).thenReturn(false); - String exceptionMessage = "User [user:Foobar] doesn't exist"; - when(this.localizationManager.getTranslationPlain("xe.admin.passwordReset.error.noUser", - "user:Foobar")).thenReturn(exceptionMessage); - ResetPasswordException resetPasswordException = assertThrows(ResetPasswordException.class, - () -> this.resetPasswordManager.resetPassword(this.userReference, "some password")); - assertEquals(exceptionMessage, resetPasswordException.getMessage()); + this.resetPasswordManager.resetPassword(this.userReference, "some password"); + verify(this.xWiki, never()).getDocument(any(DocumentReference.class), any(XWikiContext.class)); + verify(this.xWiki, never()).saveDocument(any(XWikiDocument.class), anyString(), anyBoolean(), + any(XWikiContext.class)); } @Test
xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-script/src/main/java/org/xwiki/security/authentication/script/AuthenticationScriptService.java+1 −7 modified@@ -27,7 +27,6 @@ import javax.inject.Named; import javax.inject.Provider; import javax.inject.Singleton; -import javax.mail.internet.InternetAddress; import org.slf4j.Logger; import org.xwiki.component.annotation.Component; @@ -190,21 +189,16 @@ public String getAuthenticationURL(String action, Map<String, Object> params) * This method returns the email address used, so that we can display it to the user. * * @param user the user for which to perform a reset password request. - * @return the email address used to send the verification code or {@code null} if the user calling this method - * doesn't have programming rights. * @throws ResetPasswordException if any error occurs for performing the reset password request. * @since 13.1RC1 */ @Unstable - public InternetAddress requestResetPassword(UserReference user) throws ResetPasswordException + public void requestResetPassword(UserReference user) throws ResetPasswordException { if (this.authorizationManager.hasAccess(Right.PROGRAM)) { ResetPasswordRequestResponse resetPasswordRequestResponse = this.resetPasswordManager.requestResetPassword(user); this.resetPasswordManager.sendResetPasswordEmailRequest(resetPasswordRequestResponse); - return resetPasswordRequestResponse.getUserEmail(); - } else { - return null; } }
xwiki-platform-core/xwiki-platform-security/xwiki-platform-security-authentication/xwiki-platform-security-authentication-script/src/test/java/org/xwiki/security/authentication/script/AuthenticationScriptServiceTest.java+2 −3 modified@@ -195,9 +195,8 @@ void requestResetPassword() throws Exception ResetPasswordRequestResponse requestResponse = mock(ResetPasswordRequestResponse.class); when(this.resetPasswordManager.requestResetPassword(userReference)).thenReturn(requestResponse); InternetAddress userEmail = new InternetAddress("acme@xwiki.org"); - when(requestResponse.getUserEmail()).thenReturn(userEmail); - assertEquals(userEmail, this.scriptService.requestResetPassword(userReference)); + this.scriptService.requestResetPassword(userReference); verify(this.resetPasswordManager).sendResetPasswordEmailRequest(requestResponse); } @@ -206,7 +205,7 @@ void requestResetPasswordWithoutPR() throws Exception { when(this.authorizationManager.hasAccess(Right.PROGRAM)).thenReturn(false); - assertNull(this.scriptService.requestResetPassword(mock(UserReference.class))); + this.scriptService.requestResetPassword(mock(UserReference.class)); verify(this.resetPasswordManager, never()).requestResetPassword(any()); verify(this.resetPasswordManager, never()).sendResetPasswordEmailRequest(any()); }
xwiki-platform-core/xwiki-platform-web/xwiki-platform-web-templates/src/main/resources/templates/resetpasswordinline.vm+2 −2 modified@@ -77,14 +77,14 @@ $services.localization.render('xe.admin.passwordReset.instructions') $services.localization.render('xe.admin.passwordReset.error.csrf') #else #try() - #set ($email = $services.security.authentication.requestResetPassword($userName)) + #set ($discard = $services.security.authentication.requestResetPassword($userName)) #end #if ("$!exception" != '') #resetPasswordBoxStart("warning") #displayResetPasswordException() #else #resetPasswordBoxStart("default") - $services.localization.render('xe.admin.passwordReset.emailSent', ["$services.mail.general.obfuscate($email)"]) + $services.localization.render('xe.admin.passwordReset.emailSentToUsername', ["$escapetool.xml($userName)"]) #end #end <div>
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-35fg-hjcr-j65fghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-23619ghsaADVISORY
- github.com/xwiki/xwiki-platform/commit/d8a3cce48e0ac1a0f4a3cea7a19747382d9c9494ghsax_refsource_MISCWEB
- github.com/xwiki/xwiki-platform/security/advisories/GHSA-35fg-hjcr-j65fghsax_refsource_CONFIRMWEB
- jira.xwiki.org/browse/XWIKI-18787ghsax_refsource_MISCWEB
News mentions
0No linked articles in our index yet.