Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Do not accept pattern_type from user input in push rules #15088

Merged
merged 12 commits into from
Feb 28, 2023
11 changes: 5 additions & 6 deletions rust/src/push/base_rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use serde_json::Value;
use super::KnownCondition;
use crate::push::EventMatchCondition;
use crate::push::PushRule;
use crate::push::RelatedEventMatchCondition;
use crate::push::RelatedEventMatchTypeCondition;
use crate::push::SetTweak;
use crate::push::TweakValue;
use crate::push::{Action, ExactEventMatchCondition, SimpleJsonValue};
Expand Down Expand Up @@ -129,11 +129,10 @@ pub const BASE_APPEND_OVERRIDE_RULES: &[PushRule] = &[
PushRule {
rule_id: Cow::Borrowed("global/override/.im.nheko.msc3664.reply"),
priority_class: 5,
conditions: Cow::Borrowed(&[Condition::Known(KnownCondition::RelatedEventMatch(
RelatedEventMatchCondition {
key: Some(Cow::Borrowed("sender")),
pattern: None,
pattern_type: Some(Cow::Borrowed("user_id")),
conditions: Cow::Borrowed(&[Condition::Known(KnownCondition::RelatedEventMatchType(
RelatedEventMatchTypeCondition {
key: Cow::Borrowed("sender"),
pattern_type: Cow::Borrowed("user_id"),
rel_type: Cow::Borrowed("m.in_reply_to"),
include_fallbacks: None,
},
Expand Down
67 changes: 40 additions & 27 deletions rust/src/push/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet};

use crate::push::JsonValue;
Expand All @@ -24,7 +25,7 @@ use regex::Regex;
use super::{
utils::{get_glob_matcher, get_localpart_from_id, GlobMatchType},
Action, Condition, ExactEventMatchCondition, FilteredPushRules, KnownCondition,
RelatedEventMatchCondition, SimpleJsonValue,
SimpleJsonValue,
};

lazy_static! {
Expand Down Expand Up @@ -282,8 +283,34 @@ impl PushRuleEvaluator {
KnownCondition::ExactEventMatch(exact_event_match) => {
self.match_exact_event_match(exact_event_match)?
}
KnownCondition::RelatedEventMatch(event_match) => {
self.match_related_event_match(event_match, user_id)?
KnownCondition::RelatedEventMatch(event_match) => self.match_related_event_match(
&event_match.rel_type.clone(),
event_match.include_fallbacks,
event_match.key.clone(),
event_match.pattern.clone(),
)?,
KnownCondition::RelatedEventMatchType(event_match) => {
// The `pattern_type` can either be "user_id" or "user_localpart",
// either way if we don't have a `user_id` then the condition can't
// match.
let user_id = if let Some(user_id) = user_id {
user_id
} else {
return Ok(false);
};

let pattern = match &*event_match.pattern_type {
"user_id" => user_id,
"user_localpart" => get_localpart_from_id(user_id)?,
_ => return Ok(false),
};

self.match_related_event_match(
&event_match.rel_type.clone(),
event_match.include_fallbacks,
Some(event_match.key.clone()),
Some(Cow::Borrowed(pattern)),
)?
}
KnownCondition::ExactEventPropertyContains(exact_event_match) => {
self.match_exact_event_property_contains(exact_event_match)?
Expand Down Expand Up @@ -395,59 +422,45 @@ impl PushRuleEvaluator {
/// Evaluates a `related_event_match` condition. (MSC3664)
fn match_related_event_match(
&self,
event_match: &RelatedEventMatchCondition,
user_id: Option<&str>,
rel_type: &str,
include_fallbacks: Option<bool>,
key: Option<Cow<str>>,
pattern: Option<Cow<str>>,
) -> Result<bool, Error> {
// First check if related event matching is enabled...
if !self.related_event_match_enabled {
return Ok(false);
}

// get the related event, fail if there is none.
let event = if let Some(event) = self.related_events_flattened.get(&*event_match.rel_type) {
let event = if let Some(event) = self.related_events_flattened.get(rel_type) {
event
} else {
return Ok(false);
};

// If we are not matching fallbacks, don't match if our special key indicating this is a
// fallback relation is not present.
if !event_match.include_fallbacks.unwrap_or(false)
&& event.contains_key("im.vector.is_falling_back")
{
if !include_fallbacks.unwrap_or(false) && event.contains_key("im.vector.is_falling_back") {
return Ok(false);
}

// if we have no key, accept the event as matching, if it existed without matching any
// fields.
let key = if let Some(key) = &event_match.key {
let key = if let Some(key) = key {
clokep marked this conversation as resolved.
Show resolved Hide resolved
key
} else {
return Ok(true);
};

let pattern = if let Some(pattern) = &event_match.pattern {
// There was a key, so we *must* have a pattern to go with it.
let pattern = if let Some(pattern) = pattern {
pattern
} else if let Some(pattern_type) = &event_match.pattern_type {
// The `pattern_type` can either be "user_id" or "user_localpart",
// either way if we don't have a `user_id` then the condition can't
// match.
let user_id = if let Some(user_id) = user_id {
user_id
} else {
return Ok(false);
};

match &**pattern_type {
"user_id" => user_id,
"user_localpart" => get_localpart_from_id(user_id)?,
_ => return Ok(false),
}
} else {
return Ok(false);
};

self.match_event_match(event, key, pattern)
self.match_event_match(event, &key, &pattern)
}

/// Evaluates a `exact_event_property_contains` condition. (MSC3758)
Expand Down
15 changes: 14 additions & 1 deletion rust/src/push/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ pub enum KnownCondition {
ExactEventMatch(ExactEventMatchCondition),
#[serde(rename = "im.nheko.msc3664.related_event_match")]
RelatedEventMatch(RelatedEventMatchCondition),
// Identical to related_event_match but gives predefined patterns. Cannot be added by users.
#[serde(skip_deserializing, rename = "im.nheko.msc3664.related_event_match")]
RelatedEventMatchType(RelatedEventMatchTypeCondition),
#[serde(rename = "org.matrix.msc3966.exact_event_property_contains")]
ExactEventPropertyContains(ExactEventMatchCondition),
#[serde(rename = "org.matrix.msc3952.is_user_mention")]
Expand Down Expand Up @@ -395,8 +398,18 @@ pub struct RelatedEventMatchCondition {
pub key: Option<Cow<'static, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pattern: Option<Cow<'static, str>>,
pub rel_type: Cow<'static, str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub pattern_type: Option<Cow<'static, str>>,
pub include_fallbacks: Option<bool>,
}

/// The body of a [`Condition::RelatedEventMatch`] that uses user_id or user_localpart as a pattern.
#[derive(Serialize, Debug, Clone)]
pub struct RelatedEventMatchTypeCondition {
// This is only used if pattern_type exists (and thus key must exist), so is
// a bit simpler than RelatedEventMatchCondition.
pub key: Cow<'static, str>,
pub pattern_type: Cow<'static, str>,
pub rel_type: Cow<'static, str>,
#[serde(skip_serializing_if = "Option::is_none")]
pub include_fallbacks: Option<bool>,
Expand Down