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 @@ -9,6 +9,7 @@
**Internal**:

- Add data categories for LogItem and LogByte. ([#4455](https://github.com/getsentry/relay/pull/4455))
- Add option to drop transaction attachments. ([#4466](https://github.com/getsentry/relay/pull/4466))

## 25.1.0

Expand Down
8 changes: 8 additions & 0 deletions relay-dynamic-config/src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ pub struct Options {
)]
pub http_span_allowed_hosts: Vec<String>,

/// Whether or not relay should drop attachments submitted with transactions.
#[serde(
rename = "relay.drop-transaction-attachments",
deserialize_with = "default_on_error",
skip_serializing_if = "is_default"
)]
pub drop_transaction_attachments: bool,

/// Deprecated, still forwarded for older downstream Relays.
#[doc(hidden)]
#[serde(
Expand Down
4 changes: 4 additions & 0 deletions relay-server/src/services/outcome.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ pub enum DiscardReason {

/// (Relay) A required feature is not enabled.
FeatureDisabled(Feature),

/// An attachment was submitted with a transaction.
TransactionAttachment,
}

impl DiscardReason {
Expand Down Expand Up @@ -506,6 +509,7 @@ impl DiscardReason {
DiscardReason::Profiling(reason) => reason,
DiscardReason::InvalidSpan => "invalid_span",
DiscardReason::FeatureDisabled(_) => "feature_disabled",
DiscardReason::TransactionAttachment => "transaction_attachment",
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions relay-server/src/services/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ mod replay;
mod report;
mod session;
mod span;
mod transaction;
pub use span::extract_transaction_span;

mod standalone;
Expand Down Expand Up @@ -1628,6 +1629,8 @@ impl EnvelopeProcessorService {

let global_config = self.inner.global_config.current();

transaction::drop_invalid_items(managed_envelope, &global_config);

// We extract the main event from the envelope.
let extraction_result = event::extract(
managed_envelope,
Expand Down Expand Up @@ -1757,6 +1760,8 @@ impl EnvelopeProcessorService {
//
// Unconditionally scrub to make sure PII is removed as early as possible.
event::scrub(&mut event, project_info.clone())?;

// TODO: remove once `relay.drop-transaction-attachments` has graduated.
attachment::scrub(managed_envelope, project_info.clone());

if_processing!(self.inner.config, {
Expand Down
19 changes: 19 additions & 0 deletions relay-server/src/services/processor/transaction.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//! Processing logic specific to transaction envelopes.

use relay_dynamic_config::GlobalConfig;

use crate::envelope::ItemType;
use crate::services::outcome::{DiscardReason, Outcome};
use crate::utils::{ItemAction, ManagedEnvelope};

/// Drops attachments in transaction envelopes.
pub fn drop_invalid_items(envelope: &mut ManagedEnvelope, global_config: &GlobalConfig) {
if global_config.options.drop_transaction_attachments {
envelope.retain_items(|item| match item.ty() {
&ItemType::Attachment => {
ItemAction::Drop(Outcome::Invalid(DiscardReason::TransactionAttachment))
}
_ => ItemAction::Keep,
});
}
}
53 changes: 43 additions & 10 deletions tests/integration/test_attachments.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from unittest import mock
import pytest
import uuid
import json
Expand Down Expand Up @@ -372,15 +373,22 @@ def test_view_hierarchy_processing(
outcomes_consumer.assert_empty()


@pytest.mark.parametrize("drop_transaction_attachments", [False, True])
def test_event_with_attachment(
mini_sentry,
relay_with_processing,
attachments_consumer,
outcomes_consumer,
drop_transaction_attachments,
):
project_id = 42
event_id = "515539018c9b4260a6f999572f1661ee"

if drop_transaction_attachments:
mini_sentry.global_config["options"][
"relay.drop-transaction-attachments"
] = True

mini_sentry.add_full_project_config(project_id)
relay = relay_with_processing()
attachments_consumer = attachments_consumer()
Expand Down Expand Up @@ -438,17 +446,42 @@ def test_event_with_attachment(
"rate_limited": False,
}

attachment = attachments_consumer.get_individual_attachment()
assert attachment["attachment"].pop("id")
assert attachment == {
"type": "attachment",
"attachment": expected_attachment,
"event_id": event_id,
"project_id": project_id,
}
if drop_transaction_attachments:
attachments_consumer.assert_empty()
assert outcomes_consumer.get_outcomes() == [
{
"timestamp": mock.ANY,
"org_id": 1,
"project_id": 42,
"key_id": 123,
"outcome": 3,
"reason": "transaction_attachment",
"category": 4,
"quantity": 22,
},
{
"timestamp": mock.ANY,
"org_id": 1,
"project_id": 42,
"key_id": 123,
"outcome": 3,
"reason": "transaction_attachment",
"category": 22,
"quantity": 1,
},
]
else:
attachment = attachments_consumer.get_individual_attachment()
assert attachment["attachment"].pop("id")
assert attachment == {
"type": "attachment",
"attachment": expected_attachment,
"event_id": event_id,
"project_id": project_id,
}

_, event = attachments_consumer.get_event()
assert event["event_id"] == event_id
_, event = attachments_consumer.get_event()
assert event["event_id"] == event_id


def test_form_data_is_rejected(
Expand Down
Loading