Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions crates/defguard_core/src/db/models/enrollment.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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";
Expand Down Expand Up @@ -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<Context, TokenError> {
Expand Down Expand Up @@ -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?;
Expand All @@ -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?;
Expand Down
7 changes: 5 additions & 2 deletions crates/defguard_core/src/db/models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,14 @@ impl MFAInfo {
pub async fn for_user(pool: &PgPool, user: &User<Id>) -> Result<Option<Self>, 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]
Expand Down
2 changes: 1 addition & 1 deletion crates/defguard_core/src/db/models/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ global_value!(SETTINGS, Option<Settings>, 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 {
Expand Down
35 changes: 32 additions & 3 deletions crates/defguard_core/src/templates.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand Down Expand Up @@ -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<String, Value>) -> tera::Result<Value> {
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<Context>,
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)?;
Expand Down Expand Up @@ -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());
}
}