diff --git a/CHANGELOG.md b/CHANGELOG.md index 70d2bf86d47..c0668f615a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,7 @@ - Retry 500 responses from objectstore. ([#6162](https://github.com/getsentry/relay/pull/6162)) - Remove the `metrics_extracted` and `spans_extracted` flags from the transactions processing pipeline. ([#6190](https://github.com/getsentry/relay/pull/6190), [#6200](https://github.com/getsentry/relay/pull/6200)) - Convert TUS uploads to Objectstore multipart uploads. ([#6172](https://github.com/getsentry/relay/pull/6172)) +- Stop accepting the deprecated Expect-CT, HPKP, and Expect-Staple security reports. ([#6230](https://github.com/getsentry/relay/pull/6230)) ## 26.6.0 diff --git a/relay-event-schema/src/protocol/security_report.rs b/relay-event-schema/src/protocol/security_report.rs index 499600cf569..3382ee10a54 100644 --- a/relay-event-schema/src/protocol/security_report.rs +++ b/relay-event-schema/src/protocol/security_report.rs @@ -1155,9 +1155,6 @@ impl ExpectStaple { #[derive(Clone, Debug, PartialEq, Eq)] pub enum SecurityReportType { Csp, - ExpectCt, - ExpectStaple, - Hpkp, Unsupported, } @@ -1173,9 +1170,6 @@ impl SecurityReportType { #[serde(rename = "type")] ty: Option, csp_report: Option, - known_pins: Option, - expect_staple_report: Option, - expect_ct_report: Option, } let helper: SecurityReport = serde_json::from_slice(data)?; @@ -1186,12 +1180,6 @@ impl SecurityReportType { Some(SecurityReportType::Csp) } else if let Some(CspViolationType::Other) = helper.ty { Some(SecurityReportType::Unsupported) - } else if helper.known_pins.is_some() { - Some(SecurityReportType::Hpkp) - } else if helper.expect_staple_report.is_some() { - Some(SecurityReportType::ExpectStaple) - } else if helper.expect_ct_report.is_some() { - Some(SecurityReportType::ExpectCt) } else { None }) @@ -2030,7 +2018,8 @@ mod tests { } #[test] - fn test_security_report_type_deserializer_recognizes_expect_ct_reports() { + fn test_security_report_type_deserializer_rejects_expect_ct_reports() { + // Expect-CT is no longer a supported report type: the classifier must not recognize it. let expect_ct_report_text = r#"{ "expect-ct-report": { "date-time": "2014-04-06T13:00:50Z", @@ -2055,11 +2044,12 @@ mod tests { }"#; let report_type = SecurityReportType::from_json(expect_ct_report_text.as_bytes()).unwrap(); - assert_eq!(report_type, Some(SecurityReportType::ExpectCt)); + assert_eq!(report_type, None); } #[test] - fn test_security_report_type_deserializer_recognizes_expect_staple_reports() { + fn test_security_report_type_deserializer_rejects_expect_staple_reports() { + // Expect-Staple is no longer a supported report type: the classifier must not recognize it. let expect_staple_report_text = r#"{ "expect-staple-report": { "date-time": "2014-04-06T13:00:50Z", @@ -2074,11 +2064,12 @@ mod tests { }"#; let report_type = SecurityReportType::from_json(expect_staple_report_text.as_bytes()).unwrap(); - assert_eq!(report_type, Some(SecurityReportType::ExpectStaple)); + assert_eq!(report_type, None); } #[test] - fn test_security_report_type_deserializer_recognizes_hpkp_reports() { + fn test_security_report_type_deserializer_rejects_hpkp_reports() { + // HPKP is no longer a supported report type: the classifier must not recognize it. let hpkp_report_text = r#"{ "date-time": "2014-04-06T13:00:50Z", "hostname": "www.example.com", @@ -2098,7 +2089,7 @@ mod tests { }"#; let report_type = SecurityReportType::from_json(hpkp_report_text.as_bytes()).unwrap(); - assert_eq!(report_type, Some(SecurityReportType::Hpkp)); + assert_eq!(report_type, None); } #[test] diff --git a/relay-server/src/endpoints/security_report.rs b/relay-server/src/endpoints/security_report.rs index 0d502f9ab71..73028f35161 100644 --- a/relay-server/src/endpoints/security_report.rs +++ b/relay-server/src/endpoints/security_report.rs @@ -81,9 +81,6 @@ fn is_security_mime(mime: Mime) -> bool { (ty, subty, suffix), ("application", "json", None) | ("application", "csp-report", None) - | ("application", "expect-ct-report", None) - | ("application", "expect-ct-report", Some("json")) - | ("application", "expect-staple-report", None) | ("application", "reports", Some("json")) ) } diff --git a/relay-server/src/processing/errors/errors/raw_security.rs b/relay-server/src/processing/errors/errors/raw_security.rs index f486be84bd5..dbb3083f214 100644 --- a/relay-server/src/processing/errors/errors/raw_security.rs +++ b/relay-server/src/processing/errors/errors/raw_security.rs @@ -1,7 +1,5 @@ use relay_base_schema::events::EventType; -use relay_event_schema::protocol::{ - Csp, Event, ExpectCt, ExpectStaple, Hpkp, LenientString, Metrics, SecurityReportType, -}; +use relay_event_schema::protocol::{Csp, Event, LenientString, Metrics, SecurityReportType}; use relay_protocol::Annotated; use relay_quotas::DataCategory; @@ -83,15 +81,6 @@ fn event_from_security_report( let (apply_result, event_type) = match report_type { SecurityReportType::Csp => (Csp::apply_to_event(data, &mut event), EventType::Csp), - SecurityReportType::ExpectCt => ( - ExpectCt::apply_to_event(data, &mut event), - EventType::ExpectCt, - ), - SecurityReportType::ExpectStaple => ( - ExpectStaple::apply_to_event(data, &mut event), - EventType::ExpectStaple, - ), - SecurityReportType::Hpkp => (Hpkp::apply_to_event(data, &mut event), EventType::Hpkp), SecurityReportType::Unsupported => return Err(ProcessingError::UnsupportedSecurityType), }; diff --git a/tests/integration/fixtures/security_report/expect_ct.input.json b/tests/integration/fixtures/security_report/expect_ct.input.json deleted file mode 100644 index 566ffd6ccc1..00000000000 --- a/tests/integration/fixtures/security_report/expect_ct.input.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "expect-ct-report": { - "date-time": "2014-04-06T13:00:50Z", - "hostname": "www.example.com", - "port": 443, - "effective-expiration-date": "2014-05-01T12:40:50Z", - "served-certificate-chain": [ - "-----BEGIN CERTIFICATE-----\nABC\n-----END CERTIFICATE-----" - ], - "validated-certificate-chain": [ - "-----BEGIN CERTIFICATE-----\nCDE\n-----END CERTIFICATE-----" - ], - "scts": [ - { - "version": 1, - "status": "invalid", - "source": "embedded", - "serialized_sct": "ABCD==" - } - ] - } -} diff --git a/tests/integration/fixtures/security_report/expect_ct.no_processing.output.json b/tests/integration/fixtures/security_report/expect_ct.no_processing.output.json deleted file mode 100644 index 15c99040563..00000000000 --- a/tests/integration/fixtures/security_report/expect_ct.no_processing.output.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "type": "expectct", - "contexts": { - "browser": { - "browser": "Chrome 74.0.3729", - "name": "Chrome", - "type": "browser", - "version": "74.0.3729" - }, - "client_os": { - "os": "Windows >=10", - "name": "Windows", - "type": "os", - "version": ">=10" - } - }, - "culprit": "www.example.com", - "level": "error", - "logentry": { - "formatted": "Expect-CT failed for 'www.example.com'" - }, - "logger": "csp", - "platform": "other", - "project": 42, - "release": "01d5c3165d9fbc5c8bdcf9550a1d6793a80fc02b", - "environment": "production", - "grouping_config": { - "enhancements": "eJybzDhxY05qemJypZWRgaGlroGxrqHRBABbEwcC", - "id": "legacy:2019-03-12" - }, - "key_id": "123", - "request": { - "headers": [ - [ - "User-Agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" - ] - ], - "url": "www.example.com" - }, - "tags": [ - [ - "hostname", - "www.example.com" - ], - [ - "port", - "443" - ] - ], - "expectct": { - "date_time": "2014-04-06T13:00:50+00:00", - "hostname": "www.example.com", - "port": 443, - "effective_expiration_date": "2014-05-01T12:40:50+00:00", - "served_certificate_chain": [ - "-----BEGIN CERTIFICATE-----\nABC\n-----END CERTIFICATE-----" - ], - "validated_certificate_chain": [ - "-----BEGIN CERTIFICATE-----\nCDE\n-----END CERTIFICATE-----" - ], - "scts": [ - { - "version": 1, - "status": "invalid", - "source": "embedded", - "serialized_sct": "ABCD==" - } - ] - }, - "user": { - "ip_address": "127.0.0.1", - "sentry_user": "ip:127.0.0.1" - }, - "version": "7" -} diff --git a/tests/integration/fixtures/security_report/expect_staple.input.json b/tests/integration/fixtures/security_report/expect_staple.input.json deleted file mode 100644 index 047e973052e..00000000000 --- a/tests/integration/fixtures/security_report/expect_staple.input.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "expect-staple-report": { - "date-time": "2014-04-06T13:00:50Z", - "hostname": "www.example.com", - "port": 443, - "response-status": "ERROR_RESPONSE", - "cert-status": "REVOKED", - "effective-expiration-date": "2014-05-01T12:40:50Z", - "served-certificate-chain": [ - "-----BEGIN CERTIFICATE-----\nABC\n-----END CERTIFICATE-----" - ], - "validated-certificate-chain": [ - "-----BEGIN CERTIFICATE-----\nCDE\n-----END CERTIFICATE-----" - ] - } -} diff --git a/tests/integration/fixtures/security_report/expect_staple.no_processing.output.json b/tests/integration/fixtures/security_report/expect_staple.no_processing.output.json deleted file mode 100644 index 98f6efcaa82..00000000000 --- a/tests/integration/fixtures/security_report/expect_staple.no_processing.output.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "type": "expectstaple", - "contexts": { - "browser": { - "browser": "Chrome 74.0.3729", - "name": "Chrome", - "type": "browser", - "version": "74.0.3729" - }, - "client_os": { - "os": "Windows >=10", - "name": "Windows", - "type": "os", - "version": ">=10" - } - }, - "culprit": "www.example.com", - "level": "error", - "logentry": { - "formatted": "Expect-Staple failed for 'www.example.com'" - }, - "logger": "csp", - "platform": "other", - "project": 42, - "release": "01d5c3165d9fbc5c8bdcf9550a1d6793a80fc02b", - "environment": "production", - "grouping_config": { - "enhancements": "eJybzDhxY05qemJypZWRgaGlroGxrqHRBABbEwcC", - "id": "legacy:2019-03-12" - }, - "key_id": "123", - "request": { - "headers": [ - [ - "User-Agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" - ] - ], - "url": "www.example.com" - }, - "tags": [ - [ - "hostname", - "www.example.com" - ], - [ - "port", - "443" - ], - [ - "response_status", - "ERROR_RESPONSE" - ], - [ - "cert_status", - "REVOKED" - ] - ], - "expectstaple": { - "date_time": "2014-04-06T13:00:50+00:00", - "hostname": "www.example.com", - "port": 443, - "effective_expiration_date": "2014-05-01T12:40:50+00:00", - "response_status": "ERROR_RESPONSE", - "cert_status": "REVOKED", - "served_certificate_chain": [ - "-----BEGIN CERTIFICATE-----\nABC\n-----END CERTIFICATE-----" - ], - "validated_certificate_chain": [ - "-----BEGIN CERTIFICATE-----\nCDE\n-----END CERTIFICATE-----" - ] - }, - "user": { - "ip_address": "127.0.0.1", - "sentry_user": "ip:127.0.0.1" - }, - "version": "7" -} diff --git a/tests/integration/fixtures/security_report/hpkp.input.json b/tests/integration/fixtures/security_report/hpkp.input.json deleted file mode 100644 index bc1c76f1d83..00000000000 --- a/tests/integration/fixtures/security_report/hpkp.input.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "date-time": "2014-04-06T13:00:50Z", - "hostname": "www.example.com", - "port": 443, - "effective-expiration-date": "2014-05-01T12:40:50Z", - "include-subdomains": false, - "served-certificate-chain": [ - "-----BEGIN CERTIFICATE-----\n MIIEBDCCAuygBQUAMEIxCzAJBgNVBAYTAlVT\n -----END CERTIFICATE-----" - ], - "validated-certificate-chain": [ - "-----BEGIN CERTIFICATE-----\n MIIEBDCCAuygAwIBAgIDCzAJBgNVBAYTAlVT\n -----END CERTIFICATE-----" - ], - "known-pins": [ - "pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\"", - "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" - ] -} diff --git a/tests/integration/fixtures/security_report/hpkp.no_processing.output.json b/tests/integration/fixtures/security_report/hpkp.no_processing.output.json deleted file mode 100644 index 7cc4f1e57bd..00000000000 --- a/tests/integration/fixtures/security_report/hpkp.no_processing.output.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "type": "hpkp", - "contexts": { - "browser": { - "browser": "Chrome 74.0.3729", - "name": "Chrome", - "type": "browser", - "version": "74.0.3729" - }, - "client_os": { - "os": "Windows >=10", - "name": "Windows", - "type": "os", - "version": ">=10" - } - }, - "level": "error", - "logentry": { - "formatted": "Public key pinning validation failed for 'www.example.com'" - }, - "logger": "csp", - "platform": "other", - "project": 42, - "release": "01d5c3165d9fbc5c8bdcf9550a1d6793a80fc02b", - "environment": "production", - "grouping_config": { - "enhancements": "eJybzDhxY05qemJypZWRgaGlroGxrqHRBABbEwcC", - "id": "legacy:2019-03-12" - }, - "key_id": "123", - "request": { - "headers": [ - [ - "User-Agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36" - ] - ], - "url": "www.example.com" - }, - "tags": [ - [ - "hostname", - "www.example.com" - ], - [ - "port", - "443" - ], - [ - "include-subdomains", - "false" - ] - ], - "hpkp": { - "date_time": "2014-04-06T13:00:50+00:00", - "hostname": "www.example.com", - "port": 443, - "effective_expiration_date": "2014-05-01T12:40:50+00:00", - "include_subdomains": false, - "served_certificate_chain": [ - "-----BEGIN CERTIFICATE-----\n MIIEBDCCAuygBQUAMEIxCzAJBgNVBAYTAlVT\n -----END CERTIFICATE-----" - ], - "validated_certificate_chain": [ - "-----BEGIN CERTIFICATE-----\n MIIEBDCCAuygAwIBAgIDCzAJBgNVBAYTAlVT\n -----END CERTIFICATE-----" - ], - "known_pins": [ - "pin-sha256=\"d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM=\"", - "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" - ] - }, - "user": { - "ip_address": "127.0.0.1", - "sentry_user": "ip:127.0.0.1" - }, - "version": "7" -} diff --git a/tests/integration/test_security_report.py b/tests/integration/test_security_report.py index 8b9ead2c5a7..161019ff869 100644 --- a/tests/integration/test_security_report.py +++ b/tests/integration/test_security_report.py @@ -3,6 +3,9 @@ import pytest from requests.exceptions import HTTPError +from sentry_relay.consts import DataCategory + +from .asserts import time_within_delta CSP_IGNORED_FIELDS = ( "event_id", @@ -10,9 +13,6 @@ "received", "ingest_path", ) -EXPECT_CT_IGNORED_FIELDS = ("event_id", "ingest_path") -EXPECT_STAPLE_IGNORED_FIELDS = ("event_id", "ingest_path") -HPKP_IGNORED_FIELDS = ("event_id", "ingest_path") def get_security_report(envelope): @@ -238,18 +238,12 @@ def test_deprication_reports_with_processing( ("csp_chrome", CSP_IGNORED_FIELDS), ("csp_chrome_blocked_asset", CSP_IGNORED_FIELDS), ("csp_firefox_blocked_asset", CSP_IGNORED_FIELDS), - ("expect_ct", EXPECT_CT_IGNORED_FIELDS), - ("expect_staple", EXPECT_STAPLE_IGNORED_FIELDS), - ("hpkp", HPKP_IGNORED_FIELDS), ], ids=( "csp", "csp_chrome", "csp_chrome_blocked_asset", "csp_firefox_blocked_asset", - "expect_ct", - "expect_staple", - "hpkp", ), ) def test_security_report(mini_sentry, relay, test_case, json_fixture_provider): @@ -285,6 +279,89 @@ def test_security_report(mini_sentry, relay, test_case, json_fixture_provider): assert event == expected_evt +@pytest.mark.parametrize( + "payload", + [ + { + "expect-ct-report": { + "hostname": "www.example.com", + "port": 443, + "effective-expiration-date": "2014-05-01T12:40:50Z", + } + }, + { + "expect-staple-report": { + "hostname": "www.example.com", + "port": 443, + "response-status": "ERROR_RESPONSE", + "cert-status": "REVOKED", + } + }, + { + "hostname": "www.example.com", + "port": 443, + "known-pins": ['pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="'], + }, + ], + ids=("expect_ct", "expect_staple", "hpkp"), +) +def test_security_report_rejects_deprecated_types(mini_sentry, relay, payload): + """ + Expect-CT, Expect-Staple and HPKP are no longer supported. Relay must reject such + reports with an ``invalid`` outcome and forward nothing upstream. + """ + proj_id = 42 + relay = relay(mini_sentry, {"outcomes": {"emit_outcomes": True}}) + mini_sentry.add_full_project_config(proj_id) + + resp = relay.send_security_report( + project_id=proj_id, + content_type="application/json", + payload=payload, + release="01d5c3165d9fbc5c8bdcf9550a1d6793a80fc02b", + environment="production", + ) + assert resp.status_code == 200 + + assert mini_sentry.get_outcomes(n=1) == [ + { + "category": DataCategory.SECURITY.value, + "outcome": 3, # Invalid + "reason": "security_report_type", + "quantity": 1, + "timestamp": time_within_delta(), + } + ] + + assert mini_sentry.captured_envelopes.empty() + + +def test_security_report_rejects_dedicated_content_types(mini_sentry, relay): + """ + The dedicated ``application/expect-ct-report`` and ``application/expect-staple-report`` + content types are no longer accepted and are rejected at the endpoint. + """ + proj_id = 42 + relay = relay(mini_sentry) + mini_sentry.add_full_project_config(proj_id) + + for content_type in ( + "application/expect-ct-report", + "application/expect-staple-report", + ): + with pytest.raises(HTTPError) as excinfo: + relay.send_security_report( + project_id=proj_id, + content_type=content_type, + payload={"expect-ct-report": {"hostname": "www.example.com"}}, + release="01d5c3165d9fbc5c8bdcf9550a1d6793a80fc02b", + environment="production", + ) + assert excinfo.value.response.status_code == 415 + + assert mini_sentry.captured_envelopes.empty() + + def split_header(header_val): return [x.strip() for x in header_val.split(",")]