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 @@ -11,6 +11,7 @@
- Generate `sentry.name` attributes for spans without names. ([#5143](https://github.com/getsentry/relay/pull/5143))
- Add integration endpoints for OTLP. ([#5176](https://github.com/getsentry/relay/pull/5176))
- Emit a metric to record keep/drop decisions in Dynamic Sampling. ([#5164](https://github.com/getsentry/relay/pull/5164))
- Trim event tag keys & values to 200 chars instead of dropping them. ([#5198](https://github.com/getsentry/relay/pull/5198))

**Bug Fixes**:

Expand Down
1 change: 1 addition & 0 deletions py/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Introduces a project scope sampling rule type. ([#5077](https://github.com/getsentry/relay/pull/5077))
- Add InstallableBuild and SizeAnalysis data categories. ([#5084](https://github.com/getsentry/relay/pull/5084))
- Add `retentions` to the project configuration. ([#5135](https://github.com/getsentry/relay/pull/5135))
- Normalization: Trim event tag keys & values to 200 chars instead of dropping them. ([#5198](https://github.com/getsentry/relay/pull/5198))

## 0.9.16

Expand Down
101 changes: 89 additions & 12 deletions relay-event-normalization/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -668,20 +668,16 @@ fn normalize_event_tags(event: &mut Event) {

for tag in tags.iter_mut() {
let _ = processor::apply(tag, |tag, _| {
if let Some(key) = tag.key() {
if key.is_empty() {
tag.0 = Annotated::from_error(Error::nonempty(), None);
} else if bytecount::num_chars(key.as_bytes()) > MaxChars::TagKey.limit() {
tag.0 = Annotated::from_error(Error::new(ErrorKind::ValueTooLong), None);
}
if let Some(key) = tag.key()
&& key.is_empty()
{
tag.0 = Annotated::from_error(Error::nonempty(), None);
}

if let Some(value) = tag.value() {
if value.is_empty() {
tag.1 = Annotated::from_error(Error::nonempty(), None);
} else if bytecount::num_chars(value.as_bytes()) > MaxChars::TagValue.limit() {
tag.1 = Annotated::from_error(Error::new(ErrorKind::ValueTooLong), None);
}
if let Some(value) = tag.value()
&& value.is_empty()
{
tag.1 = Annotated::from_error(Error::nonempty(), None);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Tag Length Validation Regression

The commit removed the unconditional length validation for tag keys and values. This logic now only applies when config.enable_trimming is true, which defaults to false. As a result, overly long tags will pass through unchecked and untrimmed by default, a regression from previous behavior and contrary to the PR's intent.

Fix in Cursor Fix in Web

}

Ok(())
Expand Down Expand Up @@ -5225,4 +5221,85 @@ mod tests {
}
"###);
}

#[test]
fn test_tags_are_trimmed() {
let json = r#"
{
"tags": {
"key": "too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_",
"too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_": "value"
}
}
"#;

let mut event = Annotated::<Event>::from_json(json).unwrap();

normalize_event(
&mut event,
&NormalizationConfig {
enable_trimming: true,
..NormalizationConfig::default()
},
);

insta::assert_debug_snapshot!(get_value!(event.tags!), @r###"
Tags(
PairList(
[
TagEntry(
"key",
Annotated(
"too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__lo...",
Meta {
remarks: [
Remark {
ty: Substituted,
rule_id: "!limit",
range: Some(
(
197,
200,
),
),
},
],
errors: [],
original_length: Some(
210,
),
original_value: None,
},
),
),
TagEntry(
Annotated(
"too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__long_too__lo...",
Meta {
remarks: [
Remark {
ty: Substituted,
rule_id: "!limit",
range: Some(
(
197,
200,
),
),
},
],
errors: [],
original_length: Some(
210,
),
original_value: None,
},
),
"value",
),
],
),
)
"###);
}
}
4 changes: 0 additions & 4 deletions relay-event-normalization/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,13 @@ pub use sentry_release_parser::{validate_environment, validate_release};
/// Must be aligned with the `max_chars` field in the metastructure of the
/// payload's attribute.
enum MaxChars {
TagKey,
TagValue,
Distribution,
Logger,
}

impl MaxChars {
pub fn limit(self) -> usize {
match self {
Self::TagKey => 200,
Self::TagValue => 200,
Self::Distribution => 64,
Self::Logger => 64,
}
Expand Down
32 changes: 0 additions & 32 deletions relay-event-normalization/src/normalize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1283,38 +1283,6 @@ mod tests {
);
}

#[test]
fn test_too_long_tags() {
let mut event = Annotated::new(Event {
tags: Annotated::new(Tags(PairList(
vec![Annotated::new(TagEntry(
Annotated::new("foobar".to_owned()),
Annotated::new("...........................................................................................................................................................................................................".to_owned()),
)), Annotated::new(TagEntry(
Annotated::new("foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo".to_owned()),
Annotated::new("bar".to_owned()),
))]),
)),
..Event::default()
});

normalize_event(&mut event, &NormalizationConfig::default());

assert_eq!(
get_value!(event.tags!),
&Tags(PairList(vec![
Annotated::new(TagEntry(
Annotated::new("foobar".to_owned()),
Annotated::from_error(Error::new(ErrorKind::ValueTooLong), None),
)),
Annotated::new(TagEntry(
Annotated::from_error(Error::new(ErrorKind::ValueTooLong), None),
Annotated::new("bar".to_owned()),
)),
]))
);
}

#[test]
fn test_too_long_distribution() {
let json = r#"{
Expand Down
82 changes: 80 additions & 2 deletions relay-event-normalization/src/trimming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,8 +439,8 @@ mod tests {
use crate::MaxChars;
use chrono::DateTime;
use relay_event_schema::protocol::{
Breadcrumb, Context, Contexts, Event, Exception, ExtraValue, SentryTags, Span, SpanId,
TagEntry, Tags, Timestamp, TraceId, Values,
Breadcrumb, Context, Contexts, Event, Exception, ExtraValue, PairList, SentryTags, Span,
SpanId, TagEntry, Tags, Timestamp, TraceId, Values,
};
use relay_protocol::{Map, Remark, SerializableAnnotated, get_value};
use similar_asserts::assert_eq;
Expand Down Expand Up @@ -1118,4 +1118,82 @@ mod tests {
assert!(get_value!(event.spans[1].start_timestamp).is_some());
assert!(get_value!(event.spans[1].timestamp).is_some());
}

#[test]
fn test_too_long_tags() {
let mut event = Annotated::new(Event {
tags: Annotated::new(Tags(PairList(
vec![Annotated::new(TagEntry(
Annotated::new("foobar".to_owned()),
Annotated::new("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".to_owned()),
)), Annotated::new(TagEntry(
Annotated::new("foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo".to_owned()),
Annotated::new("bar".to_owned()),
))]),
)),
..Event::default()
});

let mut processor = TrimmingProcessor::new();
processor::process_value(&mut event, &mut processor, ProcessingState::root()).unwrap();

insta::assert_debug_snapshot!(get_value!(event.tags!), @r###"
Tags(
PairList(
[
TagEntry(
"foobar",
Annotated(
"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx...",
Meta {
remarks: [
Remark {
ty: Substituted,
rule_id: "!limit",
range: Some(
(
197,
200,
),
),
},
],
errors: [],
original_length: Some(
203,
),
original_value: None,
},
),
),
TagEntry(
Annotated(
"foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo...",
Meta {
remarks: [
Remark {
ty: Substituted,
rule_id: "!limit",
range: Some(
(
197,
200,
),
),
},
],
errors: [],
original_length: Some(
203,
),
original_value: None,
},
),
"bar",
),
],
),
)
"###);
}
}