diff --git a/Cargo.lock b/Cargo.lock index 57002e5bb3..6db3cd59b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2408,9 +2408,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "0c0b063578492ceec17683ef2f8c5e89121fbd0b172cbc280635ab7567db2738" dependencies = [ "once_cell", "wasm-bindgen", @@ -5779,21 +5779,22 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "7e14915cadd45b529bb8d1f343c4ed0ac1de926144b746e2710f9cd05df6603b" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "e28d1ba982ca7923fd01448d5c30c6864d0a14109560296a162f80f305fb93bb" dependencies = [ "bumpalo", "log", @@ -5805,9 +5806,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "0ca85039a9b469b38336411d6d6ced91f3fc87109a2a27b0c197663f5144dffe" dependencies = [ "cfg-if", "js-sys", @@ -5818,9 +5819,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7c3d463ae3eff775b0c45df9da45d68837702ac35af998361e2c84e7c5ec1b0d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5828,9 +5829,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "7bb4ce89b08211f923caf51d527662b75bdc9c9c7aab40f86dcb9fb85ac552aa" dependencies = [ "proc-macro2", "quote", @@ -5841,9 +5842,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "f143854a3b13752c6950862c906306adb27c7e839f7414cec8fea35beab624c1" dependencies = [ "unicode-ident", ] @@ -5863,9 +5864,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "77e4b637749ff0d92b8fad63aa1f7cff3cbe125fd49c175cd6345e7272638b12" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/crates/defguard_core/src/db/models/enrollment.rs b/crates/defguard_core/src/db/models/enrollment.rs index a2fd97b8ff..8204991123 100644 --- a/crates/defguard_core/src/db/models/enrollment.rs +++ b/crates/defguard_core/src/db/models/enrollment.rs @@ -1,7 +1,7 @@ use chrono::{NaiveDateTime, TimeDelta, Utc}; use reqwest::Url; use sqlx::{Error as SqlxError, PgConnection, PgExecutor, PgPool, query, query_as}; -use tera::{Context, Tera}; +use tera::Context; use thiserror::Error; use tokio::sync::mpsc::UnboundedSender; use tonic::{Code, Status}; @@ -13,7 +13,7 @@ use crate::{ mail::Mail, random::gen_alphanumeric, server_config, - templates::{self, TemplateError}, + templates::{self, TemplateError, safe_tera}, }; pub static ENROLLMENT_TOKEN_TYPE: &str = "ENROLLMENT"; @@ -317,7 +317,7 @@ impl Token { /// - admin_last_name /// - admin_email /// - admin_phone - pub async fn get_welcome_message_context( + async fn get_welcome_message_context( &self, transaction: &mut PgConnection, ) -> Result { @@ -355,7 +355,7 @@ impl Token { let settings = Settings::get_current_settings(); // load configured content as template - let mut tera = Tera::default(); + let mut tera = safe_tera(); tera.add_raw_template("welcome_page", &settings.enrollment_welcome_message()?)?; let context = self.get_welcome_message_context(&mut *transaction).await?; @@ -373,7 +373,7 @@ impl Token { let settings = Settings::get_current_settings(); // load configured content as template - let mut tera = Tera::default(); + let mut tera = safe_tera(); tera.add_raw_template("welcome_email", &settings.enrollment_welcome_email()?)?; let context = self.get_welcome_message_context(&mut *transaction).await?; diff --git a/crates/defguard_core/src/db/models/mod.rs b/crates/defguard_core/src/db/models/mod.rs index 9f6953d57f..265c027f6b 100644 --- a/crates/defguard_core/src/db/models/mod.rs +++ b/crates/defguard_core/src/db/models/mod.rs @@ -241,11 +241,14 @@ impl MFAInfo { pub async fn for_user(pool: &PgPool, user: &User) -> Result, SqlxError> { query_as!( Self, - "SELECT mfa_method \"mfa_method: _\", totp_enabled totp_available, email_mfa_enabled email_available, \ + "SELECT mfa_method \"mfa_method: _\", totp_enabled totp_available, \ + email_mfa_enabled email_available, \ (SELECT count(*) > 0 FROM webauthn WHERE user_id = $1) \"webauthn_available!\" \ FROM \"user\" WHERE \"user\".id = $1", user.id - ).fetch_optional(pool).await + ) + .fetch_optional(pool) + .await } #[must_use] diff --git a/crates/defguard_core/src/db/models/settings.rs b/crates/defguard_core/src/db/models/settings.rs index deb1ff79b4..cd4c28124a 100644 --- a/crates/defguard_core/src/db/models/settings.rs +++ b/crates/defguard_core/src/db/models/settings.rs @@ -10,7 +10,7 @@ global_value!(SETTINGS, Option, None, set_settings, get_settings); /// Initializes global `SETTINGS` struct at program startup pub async fn initialize_current_settings(pool: &PgPool) -> Result<(), sqlx::Error> { - debug!("Initializing global settings strut"); + debug!("Initializing global settings struct"); if let Some(settings) = Settings::get(pool).await? { set_settings(Some(settings)); } else { diff --git a/crates/defguard_core/src/templates.rs b/crates/defguard_core/src/templates.rs index cef8c42b07..8ac28541e6 100644 --- a/crates/defguard_core/src/templates.rs +++ b/crates/defguard_core/src/templates.rs @@ -1,6 +1,9 @@ +use std::collections::HashMap; + use chrono::{Datelike, NaiveDateTime, Utc}; use reqwest::Url; -use tera::{Context, Tera}; +use serde_json::Value; +use tera::{Context, Function, Tera}; use thiserror::Error; use crate::{ @@ -42,13 +45,31 @@ pub enum TemplateError { TemplateError(#[from] tera::Error), } -pub fn get_base_tera( +struct NoOp(&'static str); + +impl Function for NoOp { + fn call(&self, _args: &HashMap) -> tera::Result { + Err(tera::Error::function_not_found(self.0)) + } +} + +/// Return a safe instance of Tera, as Tera is vulnerable to `get_env()` function exploit. +/// See: https://github.com/Keats/tera/issues/677 +pub(crate) fn safe_tera() -> Tera { + let mut tera = Tera::default(); + let noop = NoOp("get_env"); + tera.register_function(noop.0, noop); + + tera +} + +fn get_base_tera( external_context: Option, session: Option<&Session>, ip_address: Option<&str>, device_info: Option<&str>, ) -> Result<(Tera, Context), TemplateError> { - let mut tera = Tera::default(); + let mut tera = safe_tera(); let mut context = external_context.unwrap_or_default(); tera.add_raw_template("base.tera", MAIL_BASE)?; tera.add_raw_template("macros.tera", MAIL_MACROS)?; @@ -450,4 +471,12 @@ mod test { None )); } + + #[test] + fn dg25_8_server_side_template_injection() { + let mut tera = safe_tera(); + tera.add_raw_template("text", "PATH={{ get_env(name=\"PATH\") }}") + .unwrap(); + assert!(tera.render("text", &Context::new()).is_err()); + } }