escape username in password recovery email

This commit is contained in:
Athou
2026-11-02 12:34:14 +01:00
parent 0c280c4dc4
commit ddff408afe
2 changed files with 43 additions and 1 deletions

View File

@@ -35,6 +35,7 @@ import com.commafeed.frontend.model.request.RegistrationRequest;
import com.commafeed.security.AuthenticationContext; import com.commafeed.security.AuthenticationContext;
import com.commafeed.security.Roles; import com.commafeed.security.Roles;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import com.google.common.html.HtmlEscapers;
import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.PermitAll;
import jakarta.annotation.security.RolesAllowed; import jakarta.annotation.security.RolesAllowed;
@@ -412,7 +413,7 @@ public class UserREST {
Urls.removeTrailingSlash(config.passwordRecoveryPublicBaseUrl().orElseThrow()); Urls.removeTrailingSlash(config.passwordRecoveryPublicBaseUrl().orElseThrow());
return String.format( return String.format(
"You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.", "You asked for password recovery for account '%s', <a href='%s'>follow this link</a> to change your password. Ignore this if you didn't request a password recovery.",
user.getName(), callbackUrl(user, publicUrl)); HtmlEscapers.htmlEscaper().escape(user.getName()), callbackUrl(user, publicUrl));
} }
private String callbackUrl(User user, String publicUrl) throws URISyntaxException { private String callbackUrl(User user, String publicUrl) throws URISyntaxException {

View File

@@ -4,6 +4,7 @@ import com.commafeed.TestConstants;
import com.commafeed.frontend.model.Settings; import com.commafeed.frontend.model.Settings;
import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest; import com.commafeed.frontend.model.request.PasswordResetConfirmationRequest;
import com.commafeed.frontend.model.request.PasswordResetRequest; import com.commafeed.frontend.model.request.PasswordResetRequest;
import com.commafeed.frontend.model.request.RegistrationRequest;
import com.commafeed.integration.BaseIT; import com.commafeed.integration.BaseIT;
import io.quarkus.mailer.MockMailbox; import io.quarkus.mailer.MockMailbox;
@@ -17,6 +18,7 @@ import jakarta.ws.rs.core.HttpHeaders;
import org.jsoup.Jsoup; import org.jsoup.Jsoup;
import org.jsoup.nodes.Element; import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
@@ -119,6 +121,45 @@ class UserIT extends BaseIT {
RestAssured.given().get("rest/user/settings").then().statusCode(200); RestAssured.given().get("rest/user/settings").then().statusCode(200);
} }
@Test
void resetPasswordEscapesUsernameInEmail() {
String name = "<a href=//evil.example>CLICK</a>";
String email = "victim@commafeed.com";
RegistrationRequest registrationReq = new RegistrationRequest();
registrationReq.setName(name);
registrationReq.setPassword("MyPassword123!");
registrationReq.setEmail(email);
RestAssured.given()
.body(registrationReq)
.contentType(ContentType.JSON)
.post("rest/user/register")
.then()
.statusCode(200);
PasswordResetRequest req = new PasswordResetRequest();
req.setEmail(email);
RestAssured.given()
.body(req)
.contentType(ContentType.JSON)
.post("rest/user/passwordReset")
.then()
.statusCode(200);
List<MailMessage> mails = mailbox.getMailMessagesSentTo(email);
Assertions.assertEquals(1, mails.size());
String html = mails.getFirst().getHtml();
Assertions.assertTrue(
html.startsWith(
"You asked for password recovery for account '&lt;a href=//evil.example&gt;CLICK&lt;/a&gt;'"));
Elements links = Jsoup.parse(html).select("a");
Assertions.assertEquals(1, links.size());
Assertions.assertTrue(
links.getFirst().attr("href").startsWith("https://commafeed.example.com"));
}
@Test @Test
void saveSettings() { void saveSettings() {
Settings settings = Settings settings =