diff --git a/dotCMS/src/main/java/com/dotmarketing/business/UserAPIImpl.java b/dotCMS/src/main/java/com/dotmarketing/business/UserAPIImpl.java index 26b1f60a1388..06279cfb4d6b 100644 --- a/dotCMS/src/main/java/com/dotmarketing/business/UserAPIImpl.java +++ b/dotCMS/src/main/java/com/dotmarketing/business/UserAPIImpl.java @@ -44,6 +44,7 @@ import java.util.Optional; import static com.dotmarketing.business.UserHelper.validateMaximumLength; +import static com.dotmarketing.business.UserHelper.validateName; import static com.dotmarketing.util.Constants.DONT_RESPECT_FRONT_END_ROLES; /** @@ -442,6 +443,7 @@ public void save(final User userToSave, final User user, final boolean respectFr throw new DotSecurityException( "User doesn't have permission to save the user which is trying to be saved"); } + validateName(userToSave.getFirstName(), userToSave.getLastName()); validateMaximumLength(userToSave.getFirstName(),userToSave.getLastName(),userToSave.getEmailAddress()); userFactory.save(userToSave); PasswordTrackerLocalManager passwordTracker = PasswordTrackerLocalManagerFactory diff --git a/dotCMS/src/main/java/com/dotmarketing/business/UserHelper.java b/dotCMS/src/main/java/com/dotmarketing/business/UserHelper.java index f9dbee600e9e..7d2552678367 100644 --- a/dotCMS/src/main/java/com/dotmarketing/business/UserHelper.java +++ b/dotCMS/src/main/java/com/dotmarketing/business/UserHelper.java @@ -5,7 +5,9 @@ import com.dotmarketing.exception.DotDataException; import com.dotmarketing.exception.DotSecurityException; import com.dotmarketing.util.Logger; +import com.dotmarketing.util.RegEX; import com.dotmarketing.util.UtilMethods; +import com.liferay.util.SystemProperties; import com.liferay.portal.NoSuchUserException; import com.liferay.portal.*; import com.liferay.portal.ejb.UserUtil; @@ -187,6 +189,30 @@ else if (!PwdToolkitUtil.validate(password1) || } } + /** + * Validates that user-supplied names do not contain HTML/control metacharacters + * (e.g. {@code <}, {@code >}, CR/LF). This mirrors the legacy + * {@code UserLocalManagerImpl} check but is enforced here so it also covers the + * modern REST/API save path (e.g. {@code PUT /api/v1/users/current}), which would + * otherwise persist names verbatim and enable stored XSS in the admin UI. + * + * The allowed character set is driven by the configurable + * {@code UserName.regexp.pattern} system property; validation is skipped only when + * that property is not configured. + */ + public static void validateName(final String firstName, final String lastName) throws DotDataException { + final String pattern = GetterUtil.getString(SystemProperties.get("UserName.regexp.pattern")); + if (!UtilMethods.isSet(pattern)) { + return; + } + if (UtilMethods.isSet(firstName) && !RegEX.contains(firstName, pattern)) { + throw new DotDataException("First Name contains invalid characters"); + } + if (UtilMethods.isSet(lastName) && !RegEX.contains(lastName, pattern)) { + throw new DotDataException("Last Name contains invalid characters"); + } + } + public static void validateMaximumLength(final String firstName, final String lastName, final String email) throws DotDataException { if (UtilMethods.exceedsMaxLength(firstName, MAX_FIELD_LENGTH)) { throw new DotDataException("Length of First Name provided exceeds the maximum limit " + MAX_FIELD_LENGTH);