From 6a3119d677999fb12eba1e10d0e2be3d31843300 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20=C5=9Al=C4=99zak?= Date: Wed, 10 Sep 2025 09:17:49 +0200 Subject: [PATCH] fix password reset grpc sending unparsed user agent --- .../defguard_core/src/grpc/password_reset.rs | 20 ++++++++++--------- crates/defguard_core/src/grpc/utils.rs | 3 ++- crates/defguard_core/src/handlers/mail.rs | 8 ++++---- crates/defguard_core/src/handlers/user.rs | 4 ++-- crates/defguard_core/src/headers.rs | 3 ++- crates/defguard_core/templates/base.tera | 2 +- 6 files changed, 22 insertions(+), 18 deletions(-) diff --git a/crates/defguard_core/src/grpc/password_reset.rs b/crates/defguard_core/src/grpc/password_reset.rs index 6af863a8e..0403857e5 100644 --- a/crates/defguard_core/src/grpc/password_reset.rs +++ b/crates/defguard_core/src/grpc/password_reset.rs @@ -18,6 +18,7 @@ use crate::{ mail::{send_password_reset_email, send_password_reset_success_email}, user::check_password_strength, }, + headers::get_device_info, mail::Mail, server_config, }; @@ -99,13 +100,14 @@ impl PasswordResetServer { debug!("Starting password reset request"); let ip_address; - let user_agent; + let device_info; if let Some(ref info) = req_device_info { ip_address = info.ip_address.clone(); - user_agent = info.user_agent.clone().unwrap_or_default(); + let agent = info.user_agent.clone().unwrap_or_default(); + device_info = get_device_info(&agent); } else { ip_address = String::new(); - user_agent = String::new(); + device_info = String::new(); } let email = request.email; @@ -152,14 +154,13 @@ impl PasswordResetServer { error!("Failed to commit transaction"); Status::internal("unexpected error") })?; - send_password_reset_email( &user, &self.mail_tx, config.enrollment_url.clone(), &enrollment.id, Some(&ip_address), - Some(&user_agent), + Some(&device_info), )?; info!( @@ -255,13 +256,14 @@ impl PasswordResetServer { let enrollment = self.validate_session(request.token.as_ref()).await?; let ip_address; - let user_agent; + let device_info; if let Some(ref info) = req_device_info { ip_address = info.ip_address.clone(); - user_agent = info.user_agent.clone().unwrap_or_default(); + let agent = info.user_agent.clone().unwrap_or_default(); + device_info = get_device_info(&agent); } else { ip_address = String::new(); - user_agent = String::new(); + device_info = String::new(); } if let Err(err) = check_password_strength(&request.password) { @@ -302,7 +304,7 @@ impl PasswordResetServer { &user, &self.mail_tx, Some(&ip_address), - Some(&user_agent), + Some(&device_info), )?; // Prepare event context and push the event diff --git a/crates/defguard_core/src/grpc/utils.rs b/crates/defguard_core/src/grpc/utils.rs index 226abbb11..0e5772d57 100644 --- a/crates/defguard_core/src/grpc/utils.rs +++ b/crates/defguard_core/src/grpc/utils.rs @@ -216,6 +216,7 @@ pub(crate) fn parse_client_info(info: &Option) -> Result<(IpAddr, St msg })?; let user_agent = info.user_agent.clone().unwrap_or_else(String::new); + let escaped_agent = tera::escape_html(&user_agent); - Ok((ip, user_agent)) + Ok((ip, escaped_agent)) } diff --git a/crates/defguard_core/src/handlers/mail.rs b/crates/defguard_core/src/handlers/mail.rs index 67aa52cdd..a50ff1989 100644 --- a/crates/defguard_core/src/handlers/mail.rs +++ b/crates/defguard_core/src/handlers/mail.rs @@ -39,8 +39,8 @@ static EMAIL_MFA_CODE_EMAIL_SUBJECT: &str = "Your Multi-Factor Authentication Co static GATEWAY_DISCONNECTED: &str = "Defguard: Gateway disconnected"; static GATEWAY_RECONNECTED: &str = "Defguard: Gateway reconnected"; -pub static EMAIL_PASSOWRD_RESET_START_SUBJECT: &str = "Defguard: Password reset"; -pub static EMAIL_PASSOWRD_RESET_SUCCESS_SUBJECT: &str = "Defguard: Password reset success"; +pub static EMAIL_PASSWORD_RESET_START_SUBJECT: &str = "Defguard: Password reset"; +pub static EMAIL_PASSWORD_RESET_SUCCESS_SUBJECT: &str = "Defguard: Password reset success"; #[derive(Clone, Deserialize)] pub struct TestMail { @@ -461,7 +461,7 @@ pub fn send_password_reset_email( let mail = Mail { to: user.email.clone(), - subject: EMAIL_PASSOWRD_RESET_START_SUBJECT.into(), + subject: EMAIL_PASSWORD_RESET_START_SUBJECT.into(), content: templates::email_password_reset_mail(service_url, token, ip_address, device_info)?, attachments: Vec::new(), result_tx: None, @@ -491,7 +491,7 @@ pub fn send_password_reset_success_email( let mail = Mail { to: user.email.clone(), - subject: EMAIL_PASSOWRD_RESET_SUCCESS_SUBJECT.into(), + subject: EMAIL_PASSWORD_RESET_SUCCESS_SUBJECT.into(), content: templates::email_password_reset_success_mail(ip_address, device_info)?, attachments: Vec::new(), result_tx: None, diff --git a/crates/defguard_core/src/handlers/user.rs b/crates/defguard_core/src/handlers/user.rs index 909d10943..ecb90446e 100644 --- a/crates/defguard_core/src/handlers/user.rs +++ b/crates/defguard_core/src/handlers/user.rs @@ -8,7 +8,7 @@ use serde_json::json; use super::{ AddUserData, ApiResponse, ApiResult, PasswordChange, PasswordChangeSelf, - StartEnrollmentRequest, Username, mail::EMAIL_PASSOWRD_RESET_START_SUBJECT, + StartEnrollmentRequest, Username, mail::EMAIL_PASSWORD_RESET_START_SUBJECT, user_for_admin_or_self, }; use crate::{ @@ -1086,7 +1086,7 @@ pub async fn reset_password( let mail = Mail { to: user.email.clone(), - subject: EMAIL_PASSOWRD_RESET_START_SUBJECT.into(), + subject: EMAIL_PASSWORD_RESET_START_SUBJECT.into(), content: templates::email_password_reset_mail( config.enrollment_url.clone(), enrollment.id.clone().as_str(), diff --git a/crates/defguard_core/src/headers.rs b/crates/defguard_core/src/headers.rs index c9b1bd796..95c090a9b 100644 --- a/crates/defguard_core/src/headers.rs +++ b/crates/defguard_core/src/headers.rs @@ -24,7 +24,8 @@ pub(crate) static USER_AGENT_PARSER: LazyLock = LazyLock::new(| #[must_use] pub(crate) fn get_device_info(user_agent: &str) -> String { - let client = USER_AGENT_PARSER.parse(user_agent); + let escaped = tera::escape_html(user_agent); + let client = USER_AGENT_PARSER.parse(&escaped); get_user_agent_device(&client) } diff --git a/crates/defguard_core/templates/base.tera b/crates/defguard_core/templates/base.tera index 181fee1de..10b0f07e4 100644 --- a/crates/defguard_core/templates/base.tera +++ b/crates/defguard_core/templates/base.tera @@ -261,7 +261,7 @@ {% endif %} {% if device_type %}

- Device type: {{ device_type | safe }} + Device type: {{ device_type }}

{% endif %}