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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
**Features**:

- Add InstallableBuild and SizeAnalysis data categories. ([#5084](https://github.com/getsentry/relay/pull/5084))
- Add dynamic PII derivation to `metastructure`. ([#5107](https://github.com/getsentry/relay/pull/5107))

**Internal**:

Expand Down
30 changes: 21 additions & 9 deletions relay-event-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, quote};
use syn::meta::ParseNestedMeta;
use syn::{Ident, Lit, LitBool, LitInt, LitStr};
use syn::{ExprPath, Ident, Lit, LitBool, LitInt, LitStr};
use synstructure::decl_derive;

mod utils;
Expand Down Expand Up @@ -300,19 +300,27 @@ fn parse_type_attributes(s: &synstructure::Structure<'_>) -> syn::Result<TypeAtt
Ok(rv)
}

#[derive(Copy, Clone, Debug)]
#[derive(Clone, Debug)]
enum Pii {
True,
False,
Maybe,
Dynamic(ExprPath),
}

impl Pii {
fn as_tokens(self) -> TokenStream {
fn as_tokens(&self) -> TokenStream {
match self {
Pii::True => quote!(crate::processor::Pii::True),
Pii::False => quote!(crate::processor::Pii::False),
Pii::Maybe => quote!(crate::processor::Pii::Maybe),
Pii::True => quote!(crate::processor::PiiMode::Static(
crate::processor::Pii::True
)),
Pii::False => quote!(crate::processor::PiiMode::Static(
crate::processor::Pii::False
)),
Pii::Maybe => quote!(crate::processor::PiiMode::Static(
crate::processor::Pii::Maybe
)),
Pii::Dynamic(fun) => quote!(crate::processor::PiiMode::Dynamic(#fun)),
}
}
}
Expand Down Expand Up @@ -373,12 +381,14 @@ impl FieldAttrs {
quote!(false)
};

let pii = if let Some(pii) = self.pii.or(type_attrs.pii) {
let pii = if let Some(pii) = self.pii.as_ref().or(type_attrs.pii.as_ref()) {
pii.as_tokens()
} else if let Some(ref parent_attrs) = inherit_from_field_attrs {
quote!(#parent_attrs.pii)
} else {
quote!(crate::processor::Pii::False)
quote!(crate::processor::PiiMode::Static(
crate::processor::Pii::False
))
};

let trim = if let Some(trim) = self.trim.or(type_attrs.trim) {
Expand Down Expand Up @@ -596,6 +606,8 @@ fn parse_pii_value(value: LitStr, meta: &ParseNestedMeta) -> syn::Result<Option<
"true" => Pii::True,
"false" => Pii::False,
"maybe" => Pii::Maybe,
_ => return Err(meta.error("Expected one of `true`, `false`, `maybe`")),
_ => Pii::Dynamic(value.parse().map_err(|_| {
meta.error("Expected one of `true`, `false`, `maybe`, or a function name")
})?),
}))
}
103 changes: 99 additions & 4 deletions relay-event-schema/src/processor/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,15 @@ pub enum Pii {
Maybe,
}

/// A static or dynamic `Pii` value.
#[derive(Debug, Clone, Copy)]
pub enum PiiMode {
/// A static value.
Static(Pii),
/// A dynamic value, computed based on a `ProcessingState`.
Dynamic(fn(&ProcessingState) -> Pii),
}

/// Meta information about a field.
#[derive(Debug, Clone, Copy)]
pub struct FieldAttrs {
Expand All @@ -128,7 +137,7 @@ pub struct FieldAttrs {
/// The maximum number of bytes of this field.
pub max_bytes: Option<usize>,
/// The type of PII on the field.
pub pii: Pii,
pub pii: PiiMode,
/// Whether additional properties should be retained during normalization.
pub retain: bool,
/// Whether the trimming processor is allowed to shorten or drop this field.
Expand Down Expand Up @@ -170,7 +179,7 @@ impl FieldAttrs {
max_chars_allowance: 0,
max_depth: None,
max_bytes: None,
pii: Pii::False,
pii: PiiMode::Static(Pii::False),
retain: false,
trim: true,
}
Expand Down Expand Up @@ -199,7 +208,13 @@ impl FieldAttrs {

/// Sets whether this field contains PII.
pub const fn pii(mut self, pii: Pii) -> Self {
self.pii = pii;
self.pii = PiiMode::Static(pii);
self
}

/// Sets whether this field contains PII dynamically based on the current state.
pub const fn pii_dynamic(mut self, pii: fn(&ProcessingState) -> Pii) -> Self {
self.pii = PiiMode::Dynamic(pii);
self
}

Expand Down Expand Up @@ -442,13 +457,25 @@ impl<'a> ProcessingState<'a> {

/// Derives the attrs for recursion.
pub fn inner_attrs(&self) -> Option<Cow<'_, FieldAttrs>> {
match self.attrs().pii {
match self.pii() {
Pii::True => Some(Cow::Borrowed(&PII_TRUE_FIELD_ATTRS)),
Pii::False => None,
Pii::Maybe => Some(Cow::Borrowed(&PII_MAYBE_FIELD_ATTRS)),
}
}

/// Returns the PII status for this state.
///
/// If the state's `FieldAttrs` contain a fixed PII status,
/// it is returned. If they contain a dynamic PII status (a function),
/// it is applied to this state and the output returned.
pub fn pii(&self) -> Pii {
match self.attrs().pii {
PiiMode::Static(pii) => pii,
PiiMode::Dynamic(pii_fn) => pii_fn(self),
}
}

/// Iterates through this state and all its ancestors up the hierarchy.
///
/// This starts at the top of the stack of processing states and ends at the root. Thus
Expand Down Expand Up @@ -561,6 +588,11 @@ impl Path<'_> {
self.0.attrs()
}

/// Returns the PII status for this path.
pub fn pii(&self) -> Pii {
self.0.pii()
}

/// Iterates through the states in this path.
pub fn iter(&self) -> ProcessingStateIter<'_> {
self.0.iter()
Expand All @@ -585,3 +617,66 @@ impl fmt::Display for Path<'_> {
Ok(())
}
}

#[cfg(test)]
mod tests {

use relay_protocol::{Annotated, Empty, FromValue, IntoValue, Object, SerializableAnnotated};

use crate::processor::attrs::ROOT_STATE;
use crate::processor::{Pii, ProcessValue, ProcessingState, Processor, process_value};

fn pii_from_item_name(state: &ProcessingState) -> Pii {
match state.path_item().and_then(|p| p.key()) {
Some("true_item") => Pii::True,
Some("false_item") => Pii::False,
_ => Pii::Maybe,
}
}

#[derive(Debug, Clone, Empty, IntoValue, FromValue, ProcessValue)]
#[metastructure(pii = "pii_from_item_name")]
struct TestValue(String);

struct TestProcessor;

impl Processor for TestProcessor {
fn process_string(
&mut self,
value: &mut String,
_meta: &mut relay_protocol::Meta,
state: &ProcessingState<'_>,
) -> crate::processor::ProcessingResult where {
match state.pii() {
Pii::True => *value = "true".to_owned(),
Pii::False => *value = "false".to_owned(),
Pii::Maybe => *value = "maybe".to_owned(),
}
Ok(())
}
}

#[test]
fn test_dynamic_pii() {
let mut object: Annotated<Object<TestValue>> = Annotated::from_json(
r#"
{
"false_item": "replace me",
"other_item": "replace me",
"true_item": "replace me"
}
"#,
)
.unwrap();

process_value(&mut object, &mut TestProcessor, &ROOT_STATE).unwrap();

insta::assert_json_snapshot!(SerializableAnnotated(&object), @r###"
{
"false_item": "false",
"other_item": "maybe",
"true_item": "true"
}
"###);
}
}
2 changes: 1 addition & 1 deletion relay-pii/src/attachments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ impl<'a> PiiAttachmentsProcessor<'a> {
state: &ProcessingState<'_>,
encodings: ScrubEncodings,
) -> bool {
let pii = state.attrs().pii;
let pii = state.pii();
if pii == Pii::False {
return false;
}
Expand Down
2 changes: 1 addition & 1 deletion relay-pii/src/generate_selectors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl Processor for GenerateSelectorsProcessor {
// The following skip-conditions are in sync with what the PiiProcessor does.
if state.value_type().contains(ValueType::Boolean)
|| value.is_none()
|| state.attrs().pii == Pii::False
|| state.pii() == Pii::False
{
return Ok(());
}
Expand Down
2 changes: 1 addition & 1 deletion relay-pii/src/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl<'a> PiiProcessor<'a> {
state: &ProcessingState<'_>,
mut value: Option<&mut String>,
) -> ProcessingResult {
let pii = state.attrs().pii;
let pii = state.pii();
if pii == Pii::False {
return Ok(());
}
Expand Down
2 changes: 1 addition & 1 deletion relay-pii/src/selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ impl SelectorSpec {
/// This walks both the selector and the path starting at the end and towards the root
/// to determine if the selector matches the current path.
pub fn matches_path(&self, path: &Path) -> bool {
let pii = path.attrs().pii;
let pii = path.pii();
if pii == Pii::False {
return false;
}
Expand Down
Loading