diff --git a/relay-pii/src/processor.rs b/relay-pii/src/processor.rs index 453fdbad89a..23f67512a74 100644 --- a/relay-pii/src/processor.rs +++ b/relay-pii/src/processor.rs @@ -19,8 +19,21 @@ use crate::redactions::Redaction; use crate::regexes::{self, ANYTHING_REGEX, PatternType, ReplaceBehavior}; use crate::utils; +/// Controls how scrubbing rules are applied to attributes. +#[derive(Debug, Clone, Copy)] +pub enum AttributeMode { + /// Treat the attribute as an object and allow referring + /// to individual fields. + Object, + /// Identify the attribute with its value and apply all + /// rules there directly. + ValueOnly, +} + /// A processor that performs PII stripping. pub struct PiiProcessor<'a> { + /// Controls how rules are applied to attributes. + attribute_mode: AttributeMode, compiled_config: &'a CompiledPiiConfig, } @@ -29,7 +42,16 @@ impl<'a> PiiProcessor<'a> { pub fn new(compiled_config: &'a CompiledPiiConfig) -> PiiProcessor<'a> { // this constructor needs to be cheap... a new PiiProcessor is created for each event. Move // any init logic into CompiledPiiConfig::new. - PiiProcessor { compiled_config } + PiiProcessor { + compiled_config, + attribute_mode: AttributeMode::Object, + } + } + + /// Sets an `AttributeMode` on this processor. + pub fn attribute_mode(mut self, attribute_mode: AttributeMode) -> Self { + self.attribute_mode = attribute_mode; + self } fn apply_all_rules( @@ -205,7 +227,7 @@ impl Processor for PiiProcessor<'_> { _meta: &mut Meta, state: &ProcessingState, ) -> ProcessingResult { - utils::process_attributes(value, self, state) + utils::process_attributes(value, self, state, self.attribute_mode) } fn process_user( diff --git a/relay-pii/src/utils.rs b/relay-pii/src/utils.rs index e6757ed888b..ee77ee69771 100644 --- a/relay-pii/src/utils.rs +++ b/relay-pii/src/utils.rs @@ -9,6 +9,8 @@ use relay_event_schema::processor::{ use relay_event_schema::protocol::{AsPair, Attributes, PairList}; use sha1::Sha1; +use crate::AttributeMode; + pub fn process_pairlist( slf: &mut P, value: &mut PairList, @@ -45,35 +47,36 @@ pub fn process_attributes( value: &mut Attributes, slf: &mut P, state: &ProcessingState, + attribute_mode: AttributeMode, ) -> ProcessingResult { - // Check if we're in default rules (no root ValueType) - // This is a workaround to support explicit selectors eg. $log.attributes.KEY.value to work but keep implicit selectors for default rules. - let is_advanced_rules = state - .iter() - .nth(1) - .is_some_and(|s| s.value_type().contains(ValueType::OurLog)); - for (key, annotated_attribute) in value.iter_mut() { if let Some(attribute) = annotated_attribute.value_mut() { - if is_advanced_rules { - // Process normally to allow explicit selectors like $log.attributes.KEY.value to work - let field_value_type = ValueType::for_field(annotated_attribute); - let key_state = state.enter_borrowed(key, state.inner_attrs(), field_value_type); - processor::process_value(annotated_attribute, slf, &key_state)?; - } else { - // For the default rules, we want Pii::True since we want them to always be scrubbed. - let attrs = FieldAttrs::new().pii(Pii::True); - let inner_value = &mut attribute.value.value; - let inner_value_type = ValueType::for_field(inner_value); - let entered = - state.enter_borrowed(key, Some(Cow::Borrowed(&attrs)), inner_value_type); - processor::process_value(inner_value, slf, &entered)?; + match attribute_mode { + AttributeMode::Object => { + // Process normally to allow explicit selectors like $log.attributes.KEY.value to work + let field_value_type = ValueType::for_field(annotated_attribute); + let key_state = + state.enter_borrowed(key, state.inner_attrs(), field_value_type); + processor::process_value(annotated_attribute, slf, &key_state)?; + } + AttributeMode::ValueOnly => { + // For the default rules, we want Pii::True since we want them to always be scrubbed. + let attrs = FieldAttrs::new().pii(Pii::True); + let inner_value = &mut attribute.value.value; + let inner_value_type = ValueType::for_field(inner_value); + let entered = + state.enter_borrowed(key, Some(Cow::Borrowed(&attrs)), inner_value_type); + processor::process_value(inner_value, slf, &entered)?; - let object_value_type = enum_set!(ValueType::Object); - for (key, value) in attribute.other.iter_mut() { - let other_state = - state.enter_borrowed(key, Some(Cow::Borrowed(&attrs)), object_value_type); - processor::process_value(value, slf, &other_state)?; + let object_value_type = enum_set!(ValueType::Object); + for (key, value) in attribute.other.iter_mut() { + let other_state = state.enter_borrowed( + key, + Some(Cow::Borrowed(&attrs)), + object_value_type, + ); + processor::process_value(value, slf, &other_state)?; + } } } } diff --git a/relay-server/src/processing/logs/process.rs b/relay-server/src/processing/logs/process.rs index d3b007160b0..9fdd9e51ca6 100644 --- a/relay-server/src/processing/logs/process.rs +++ b/relay-server/src/processing/logs/process.rs @@ -1,7 +1,7 @@ use relay_event_normalization::{SchemaProcessor, eap}; use relay_event_schema::processor::{ProcessingState, ValueType, process_value}; use relay_event_schema::protocol::{OurLog, OurLogHeader}; -use relay_pii::PiiProcessor; +use relay_pii::{AttributeMode, PiiProcessor}; use relay_protocol::Annotated; use relay_quotas::DataCategory; @@ -106,18 +106,20 @@ fn scrub_log(log: &mut Annotated, ctx: Context<'_>) -> Result<()> { .pii_config() .map_err(|e| Error::PiiConfig(e.clone()))?; + let state = ProcessingState::root().enter_borrowed("", None, [ValueType::OurLog]); + if let Some(ref config) = ctx.project_info.config.pii_config { - let mut processor = PiiProcessor::new(config.compiled()); - let root_state = ProcessingState::root().enter_borrowed("", None, Some(ValueType::OurLog)); - process_value(log, &mut processor, &root_state)?; + let mut processor = PiiProcessor::new(config.compiled()) + // For advanced rules we want to treat attributes as objects. + .attribute_mode(AttributeMode::Object); + process_value(log, &mut processor, &state)?; } if let Some(config) = pii_config_from_scrubbing { - let mut processor = PiiProcessor::new(config.compiled()); - // Use empty root (assumed to be Event) for legacy/default scrubbing rules. - // process_attributes will collapse Attribute into it's value for the default rules. - let root_state = ProcessingState::root(); - process_value(log, &mut processor, root_state)?; + let mut processor = PiiProcessor::new(config.compiled()) + // For "legacy" rules we want to identify attributes with their values. + .attribute_mode(AttributeMode::ValueOnly); + process_value(log, &mut processor, &state)?; } Ok(())