Skip to content

Commit bece03b

Browse files
gruebellalitbcijothomas
authored
fix: validate Baggage key by W3C standards (#2804)
Co-authored-by: Lalit Kumar Bhasin <lalit_fin@yahoo.com> Co-authored-by: Cijo Thomas <cijo.thomas@gmail.com>
1 parent 31b494b commit bece03b

File tree

3 files changed

+46
-11
lines changed

3 files changed

+46
-11
lines changed

opentelemetry/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
- Align `Baggage.remove()` signature with `.get()` to take the key as a reference
1414
- `Baggage` can't be retrieved from the `Context` directly anymore and needs to be accessed via `context.baggage()`
1515
- `with_baggage()` and `current_with_baggage()` override any existing `Baggage` in the `Context`
16+
- `Baggage` keys can't be empty and only allow ASCII visual chars, except `"(),/:;<=>?@[\]{}` (see [RFC7230, Section 3.2.6](https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6))
1617
- Changed `Context` to use a stack to properly handle out of order dropping of `ContextGuard`. This imposes a limit of `65535` nested contexts on a single thread. See #[2378](https://github.com/open-telemetry/opentelemetry-rust/pull/2284) and #[1887](https://github.com/open-telemetry/opentelemetry-rust/issues/1887).
1718
- Added additional `name: Option<&str>` parameter to the `event_enabled` method
1819
on the `Logger` trait. This allows implementations (SDK, processor, exporters)

opentelemetry/benches/baggage.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ const MAX_KEY_VALUE_PAIRS: usize = 64;
1111
// Apple M4 Pro
1212
// Total Number of Cores: 14 (10 performance and 4 efficiency)
1313
// Results:
14-
// set_baggage_static_key_value 12 ns
15-
// set_baggage_static_key 28 ns
16-
// set_baggage_dynamic 60 ns
17-
// set_baggage_dynamic_with_metadata 112 ns
14+
// set_baggage_static_key_value 14 ns
15+
// set_baggage_static_key 26 ns
16+
// set_baggage_dynamic 54 ns
17+
// set_baggage_dynamic_with_metadata 75 ns
1818

1919
fn criterion_benchmark(c: &mut Criterion) {
2020
set_baggage_static_key_value(c);

opentelemetry/src/baggage.rs

+41-7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ static DEFAULT_BAGGAGE: OnceLock<Baggage> = OnceLock::new();
2929
const MAX_KEY_VALUE_PAIRS: usize = 64;
3030
const MAX_LEN_OF_ALL_PAIRS: usize = 8192;
3131

32+
// https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
33+
const INVALID_ASCII_KEY_CHARS: [u8; 17] = [
34+
b'(', b')', b',', b'/', b':', b';', b'<', b'=', b'>', b'?', b'@', b'[', b'\\', b']', b'{',
35+
b'}', b'"',
36+
];
37+
3238
/// Returns the default baggage, ensuring it is initialized only once.
3339
#[inline]
3440
fn get_default_baggage() -> &'static Baggage {
@@ -160,16 +166,14 @@ impl Baggage {
160166
S: Into<BaggageMetadata>,
161167
{
162168
let (key, value, metadata) = (key.into(), value.into(), metadata.into());
163-
if !key.as_str().is_ascii() {
164-
return None;
165-
}
166-
let entry_content_len =
167-
key_value_metadata_bytes_size(key.as_str(), value.as_str(), metadata.as_str());
168169
let entries_count = self.inner.len();
169170
match self.inner.entry(key) {
170171
Entry::Occupied(mut occupied_entry) => {
172+
let key_str = occupied_entry.key().as_str();
173+
let entry_content_len =
174+
key_value_metadata_bytes_size(key_str, value.as_str(), metadata.as_str());
171175
let prev_content_len = key_value_metadata_bytes_size(
172-
occupied_entry.key().as_str(),
176+
key_str,
173177
occupied_entry.get().0.as_str(),
174178
occupied_entry.get().1.as_str(),
175179
);
@@ -181,9 +185,15 @@ impl Baggage {
181185
Some(occupied_entry.insert((value, metadata)))
182186
}
183187
Entry::Vacant(vacant_entry) => {
188+
let key_str = vacant_entry.key().as_str();
189+
if !Self::is_key_valid(key_str.as_bytes()) {
190+
return None;
191+
}
184192
if entries_count == MAX_KEY_VALUE_PAIRS {
185193
return None;
186194
}
195+
let entry_content_len =
196+
key_value_metadata_bytes_size(key_str, value.as_str(), metadata.as_str());
187197
let new_content_len = self.kv_content_len + entry_content_len;
188198
if new_content_len > MAX_LEN_OF_ALL_PAIRS {
189199
return None;
@@ -215,11 +225,18 @@ impl Baggage {
215225
pub fn iter(&self) -> Iter<'_> {
216226
self.into_iter()
217227
}
228+
229+
fn is_key_valid(key: &[u8]) -> bool {
230+
!key.is_empty()
231+
&& key
232+
.iter()
233+
.all(|b| b.is_ascii_graphic() && !INVALID_ASCII_KEY_CHARS.contains(b))
234+
}
218235
}
219236

220237
/// Get the number of bytes for one key-value pair
221238
fn key_value_metadata_bytes_size(key: &str, value: &str, metadata: &str) -> usize {
222-
key.bytes().len() + value.bytes().len() + metadata.bytes().len()
239+
key.len() + value.len() + metadata.len()
223240
}
224241

225242
/// An iterator over the entries of a [`Baggage`].
@@ -619,4 +636,21 @@ mod tests {
619636
baggage.remove("foo");
620637
assert!(baggage.is_empty());
621638
}
639+
640+
#[test]
641+
fn test_insert_invalid_key() {
642+
let mut baggage = Baggage::default();
643+
644+
// empty
645+
baggage.insert("", "1");
646+
assert!(baggage.is_empty());
647+
648+
// non-ascii
649+
baggage.insert("Grüße", "1");
650+
assert!(baggage.is_empty());
651+
652+
// invalid ascii chars
653+
baggage.insert("(example)", "1");
654+
assert!(baggage.is_empty());
655+
}
622656
}

0 commit comments

Comments
 (0)