Skip to content

Commit f492a39

Browse files
committed
Merge branch 'master' into fix/normalize-tags
* master: ref(pii): Use external utf16string crate (#807) feat(pii): Merge padding and masking characters (#810) fix(server): Rate limit outcomes emited only for events on "fast-path" (#809) fix(server): Rate limit outcomes emited only for events (#806)
2 parents 3bad342 + dddfae3 commit f492a39

File tree

17 files changed

+287
-1388
lines changed

17 files changed

+287
-1388
lines changed

CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Add support for measurement ingestion. ([#724](https://github.com/getsentry/relay/pull/724), [#785](https://github.com/getsentry/relay/pull/785))
88
- Add support for scrubbing UTF-16 data in attachments ([#742](https://github.com/getsentry/relay/pull/742), [#784](https://github.com/getsentry/relay/pull/784), [#787](https://github.com/getsentry/relay/pull/787))
99
- Add upstream request metric. ([#793](https://github.com/getsentry/relay/pull/793))
10+
- The padding character in attachment scrubbing has been changed to match the masking character, there is no usability benefit from them being different. ([#810](https://github.com/getsentry/relay/pull/810))
1011

1112
**Bug Fixes**:
1213

@@ -18,6 +19,7 @@
1819

1920
- Project states are now cached separately per DSN public key instead of per project ID. This means that there will be multiple separate cache entries for projects with more than one DSN. ([#778](https://github.com/getsentry/relay/pull/778))
2021
- Relay no longer uses the Sentry endpoint to resolve project IDs by public key. Ingestion for the legacy store endpoint has been refactored to rely on key-based caches only. As a result, the legacy endpoint is supported only on managed Relays. ([#800](https://github.com/getsentry/relay/pull/800))
22+
- Fix rate limit outcomes, now emitted only for error events but not transactions. ([#806](https://github.com/getsentry/relay/pull/806), [#809](https://github.com/getsentry/relay/pull/809))
2123

2224
## 20.9.0
2325

Cargo.lock

+10-5
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

relay-common/src/constants.rs

+5
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ impl DataCategory {
120120
Self::Unknown => "unknown",
121121
}
122122
}
123+
124+
/// Returns true if the DataCategory refers to an error (i.e an error event).
125+
pub fn is_error(self) -> bool {
126+
matches!(self, Self::Error | Self::Default | Self::Security)
127+
}
123128
}
124129

125130
impl fmt::Display for DataCategory {

relay-general/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ pest_derive = "2.1.0"
2727
regex = "1.3.9"
2828
relay-common = { path = "../relay-common" }
2929
relay-general-derive = { path = "derive" }
30-
relay-wstring = { path = "../relay-wstring" }
3130
schemars = { version = "0.8.0-alpha-4", features = ["uuid", "chrono"], optional = true }
3231
scroll = "0.9.0"
3332
serde = { version = "1.0.114", features = ["derive"] }
@@ -37,6 +36,7 @@ sha-1 = "0.8.1"
3736
smallvec = { version = "1.4.0", features = ["serde"] }
3837
uaparser = { version = "0.3.3", optional = true }
3938
url = "2.1.1"
39+
utf16string = "0.2.0"
4040
uuid = { version = "0.8.1", features = ["v4", "serde"] }
4141

4242
[dev-dependencies]

relay-general/src/pii/attachments.rs

+20-13
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ use std::iter::FusedIterator;
44
use regex::bytes::RegexBuilder as BytesRegexBuilder;
55
use regex::{Match, Regex};
66
use smallvec::SmallVec;
7-
8-
use relay_wstring::WStr;
7+
use utf16string::{LittleEndian, WStr};
98

109
use crate::pii::compiledconfig::RuleRef;
1110
use crate::pii::regexes::{get_regex_for_rule_type, ReplaceBehavior};
@@ -106,7 +105,11 @@ fn apply_regex_to_utf16le_bytes(
106105
}
107106

108107
/// Extract the matching encoded slice from the encoded string.
109-
fn get_wstr_match<'a>(all_text: &str, re_match: Match, all_encoded: &'a mut WStr) -> &'a mut WStr {
108+
fn get_wstr_match<'a>(
109+
all_text: &str,
110+
re_match: Match,
111+
all_encoded: &'a mut WStr<LittleEndian>,
112+
) -> &'a mut WStr<LittleEndian> {
110113
let mut encoded_start = 0;
111114
let mut encoded_end = all_encoded.len();
112115

@@ -151,7 +154,7 @@ trait StringMods: AsRef<[u8]> {
151154

152155
/// Apply a PII scrubbing redaction to this string slice.
153156
fn apply_redaction(&mut self, redaction: &Redaction) {
154-
const PADDING: char = 'x';
157+
const PADDING: char = '*';
155158
const MASK: char = '*';
156159

157160
match redaction {
@@ -172,7 +175,7 @@ trait StringMods: AsRef<[u8]> {
172175
}
173176
}
174177

175-
impl StringMods for WStr {
178+
impl StringMods for WStr<LittleEndian> {
176179
fn fill_content(&mut self, fill_char: char) {
177180
// If fill_char is too wide, fill_char.encode_utf16() will panic, fulfilling the
178181
// trait's contract that we must panic if fill_char is too wide.
@@ -325,7 +328,7 @@ impl<'a> FusedIterator for WStrSegmentIter<'a> {}
325328
/// longer match.
326329
struct WStrSegment<'a> {
327330
/// The raw bytes of this segment.
328-
encoded: &'a mut WStr,
331+
encoded: &'a mut WStr<LittleEndian>,
329332
/// The decoded string of this segment.
330333
decoded: String,
331334
}
@@ -469,7 +472,11 @@ impl<'a> PiiAttachmentsProcessor<'a> {
469472
}
470473

471474
/// Scrub a filepath, preserving the basename.
472-
pub fn scrub_utf16_filepath(&self, path: &mut WStr, state: &ProcessingState<'_>) -> bool {
475+
pub fn scrub_utf16_filepath(
476+
&self,
477+
path: &mut WStr<LittleEndian>,
478+
state: &ProcessingState<'_>,
479+
) -> bool {
473480
let index =
474481
path.char_indices().rev().find_map(
475482
|(i, c)| {
@@ -600,7 +607,7 @@ mod tests {
600607
filename: "foo.txt",
601608
value_type: ValueType::Binary,
602609
input: b"before 127.0.0.1 after",
603-
output: b"before [ip]xxxxx after",
610+
output: b"before [ip]***** after",
604611
changed: true,
605612
}
606613
.run();
@@ -614,7 +621,7 @@ mod tests {
614621
filename: "foo.txt",
615622
value_type: ValueType::Binary,
616623
input: utf16le("before 127.0.0.1 after").as_slice(),
617-
output: utf16le("before [ip]xxxxx after").as_slice(),
624+
output: utf16le("before [ip]***** after").as_slice(),
618625
changed: true,
619626
}
620627
.run();
@@ -684,7 +691,7 @@ mod tests {
684691
filename: "foo.txt",
685692
value_type: ValueType::Binary,
686693
input: b"before 127.0.0.1 after",
687-
output: b"before xxxxxxxxx after",
694+
output: b"before ********* after",
688695
changed: true,
689696
}
690697
.run();
@@ -698,7 +705,7 @@ mod tests {
698705
filename: "foo.txt",
699706
value_type: ValueType::Binary,
700707
input: utf16le("before 127.0.0.1 after").as_slice(),
701-
output: utf16le("before xxxxxxxxx after").as_slice(),
708+
output: utf16le("before ********* after").as_slice(),
702709
changed: true,
703710
}
704711
.run();
@@ -734,7 +741,7 @@ mod tests {
734741
filename: "foo.txt",
735742
value_type: ValueType::Binary,
736743
input: (0..255 as u8).collect::<Vec<_>>().as_slice(),
737-
output: &[b'x'; 255],
744+
output: &[b'*'; 255],
738745
changed: true,
739746
}
740747
.run();
@@ -766,7 +773,7 @@ mod tests {
766773
filename: "foo.txt",
767774
value_type: ValueType::Binary,
768775
input: bytes,
769-
output: &vec![b'x'; bytes.len()],
776+
output: &vec![b'*'; bytes.len()],
770777
changed: true,
771778
}
772779
.run()

relay-general/src/pii/minidumps.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ use minidump::{
1515
};
1616
use num_traits::FromPrimitive;
1717
use scroll::Endian;
18-
19-
use relay_wstring::{Utf16Error, WStr};
18+
use utf16string::{Utf16Error, WStr};
2019

2120
use crate::pii::{PiiAttachmentsProcessor, ScrubEncodings};
2221
use crate::processor::{FieldAttrs, Pii, ValueType};

relay-quotas/src/rate_limit.rs

+77
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,21 @@ impl RateLimits {
286286
pub fn longest(&self) -> Option<&RateLimit> {
287287
self.iter().max_by_key(|limit| limit.retry_after)
288288
}
289+
290+
/// Returns the longest rate limit that is error releated.
291+
///
292+
/// The most relevant rate limit from the point of view of an error generating an outcome
293+
/// is the longest rate limit for error messages.
294+
pub fn longest_error(&self) -> Option<&RateLimit> {
295+
let is_event_related = |rate_limit: &&RateLimit| {
296+
rate_limit.categories.is_empty()
297+
|| rate_limit.categories.iter().any(|cat| cat.is_error())
298+
};
299+
300+
self.iter()
301+
.filter(is_event_related)
302+
.max_by_key(|limit| limit.retry_after)
303+
}
289304
}
290305

291306
/// Immutable rate limits iterator.
@@ -651,6 +666,68 @@ mod tests {
651666
"###);
652667
}
653668

669+
#[test]
670+
fn test_rate_limits_longest_error_none() {
671+
let mut rate_limits = RateLimits::new();
672+
673+
rate_limits.add(RateLimit {
674+
categories: smallvec![DataCategory::Transaction],
675+
scope: RateLimitScope::Organization(42),
676+
reason_code: None,
677+
retry_after: RetryAfter::from_secs(1),
678+
});
679+
rate_limits.add(RateLimit {
680+
categories: smallvec![DataCategory::Attachment],
681+
scope: RateLimitScope::Organization(42),
682+
reason_code: None,
683+
retry_after: RetryAfter::from_secs(1),
684+
});
685+
rate_limits.add(RateLimit {
686+
categories: smallvec![DataCategory::Session],
687+
scope: RateLimitScope::Organization(42),
688+
reason_code: None,
689+
retry_after: RetryAfter::from_secs(1),
690+
});
691+
692+
// only non event rate limits so nothing relevant
693+
assert_eq!(rate_limits.longest_error(), None)
694+
}
695+
696+
#[test]
697+
fn test_rate_limits_longest_error() {
698+
let mut rate_limits = RateLimits::new();
699+
rate_limits.add(RateLimit {
700+
categories: smallvec![DataCategory::Transaction],
701+
scope: RateLimitScope::Organization(40),
702+
reason_code: None,
703+
retry_after: RetryAfter::from_secs(100),
704+
});
705+
rate_limits.add(RateLimit {
706+
categories: smallvec![DataCategory::Error],
707+
scope: RateLimitScope::Organization(41),
708+
reason_code: None,
709+
retry_after: RetryAfter::from_secs(5),
710+
});
711+
rate_limits.add(RateLimit {
712+
categories: smallvec![DataCategory::Error],
713+
scope: RateLimitScope::Organization(42),
714+
reason_code: None,
715+
retry_after: RetryAfter::from_secs(7),
716+
});
717+
718+
let rate_limit = rate_limits.longest().unwrap();
719+
insta::assert_ron_snapshot!(rate_limit, @r###"
720+
RateLimit(
721+
categories: [
722+
transaction,
723+
],
724+
scope: Organization(40),
725+
reason_code: None,
726+
retry_after: RetryAfter(100),
727+
)
728+
"###);
729+
}
730+
654731
#[test]
655732
fn test_rate_limits_clean_expired() {
656733
let mut rate_limits = RateLimits::new();

relay-server/src/actors/events.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ impl ProcessingError {
143143
Self::DuplicateItem(_) => Some(Outcome::Invalid(DiscardReason::DuplicateItem)),
144144
Self::NoEventPayload => Some(Outcome::Invalid(DiscardReason::NoEventPayload)),
145145
Self::RateLimited(ref rate_limits) => rate_limits
146-
.longest()
146+
.longest_error()
147147
.map(|r| Outcome::RateLimited(r.reason_code.clone())),
148148

149149
// Processing-only outcomes (Sentry-internal Relays)

relay-server/src/endpoints/common.rs

+15-15
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ pub enum BadStoreRequest {
7979
}
8080

8181
impl BadStoreRequest {
82-
fn to_outcome(&self) -> Outcome {
83-
match self {
82+
fn to_outcome(&self) -> Option<Outcome> {
83+
Some(match self {
8484
BadStoreRequest::UnsupportedProtocolVersion(_) => {
8585
Outcome::Invalid(DiscardReason::AuthVersion)
8686
}
@@ -119,16 +119,14 @@ impl BadStoreRequest {
119119
}
120120

121121
BadStoreRequest::RateLimited(rate_limits) => {
122-
let reason_code = rate_limits
123-
.longest()
124-
.and_then(|limit| limit.reason_code.clone());
125-
126-
Outcome::RateLimited(reason_code)
122+
return rate_limits
123+
.longest_error()
124+
.map(|r| Outcome::RateLimited(r.reason_code.clone()));
127125
}
128126

129127
// should actually never create an outcome
130128
BadStoreRequest::InvalidEventId => Outcome::Invalid(DiscardReason::Internal),
131-
}
129+
})
132130
}
133131
}
134132

@@ -457,13 +455,15 @@ where
457455
metric!(counter(RelayCounters::EnvelopeRejected) += 1);
458456

459457
if is_event {
460-
outcome_producer.do_send(TrackOutcome {
461-
timestamp: start_time,
462-
scoping: *scoping.borrow(),
463-
outcome: error.to_outcome(),
464-
event_id: *event_id.borrow(),
465-
remote_addr,
466-
});
458+
if let Some(outcome) = error.to_outcome() {
459+
outcome_producer.do_send(TrackOutcome {
460+
timestamp: start_time,
461+
scoping: *scoping.borrow(),
462+
outcome,
463+
event_id: *event_id.borrow(),
464+
remote_addr,
465+
});
466+
}
467467
}
468468

469469
if !emit_rate_limit && matches!(error, BadStoreRequest::RateLimited(_)) {

relay-wstring/Cargo.toml

-10
This file was deleted.

0 commit comments

Comments
 (0)