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
26 changes: 24 additions & 2 deletions relay-pii/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}

Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
53 changes: 28 additions & 25 deletions relay-pii/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<P: Processor, T: ProcessValue + AsPair>(
slf: &mut P,
value: &mut PairList<T>,
Expand Down Expand Up @@ -45,35 +47,36 @@ pub fn process_attributes<P: Processor>(
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)?;
}
}
}
}
Expand Down
20 changes: 11 additions & 9 deletions relay-server/src/processing/logs/process.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -106,18 +106,20 @@ fn scrub_log(log: &mut Annotated<OurLog>, 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(())
Expand Down
Loading