From 47beb0818a367354b1e1bcb5530632552aa73273 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 12 Aug 2024 16:41:55 +0200 Subject: [PATCH 01/19] moved regex matcher into regex utils --- dart/lib/src/sentry_client.dart | 12 +++--------- dart/lib/src/utils/regex_utils.dart | 6 ++++++ 2 files changed, 9 insertions(+), 9 deletions(-) create mode 100644 dart/lib/src/utils/regex_utils.dart diff --git a/dart/lib/src/sentry_client.dart b/dart/lib/src/sentry_client.dart index c4ebac3db5..b659d43a47 100644 --- a/dart/lib/src/sentry_client.dart +++ b/dart/lib/src/sentry_client.dart @@ -26,6 +26,7 @@ import 'transport/rate_limiter.dart'; import 'transport/spotlight_http_transport.dart'; import 'transport/task_queue.dart'; import 'utils/isolate_utils.dart'; +import 'utils/regex_utils.dart'; import 'utils/stacktrace_utils.dart'; import 'version.dart'; @@ -196,7 +197,7 @@ class SentryClient { } var message = event.message!.formatted; - return _isMatchingRegexPattern(message, _options.ignoreErrors); + return isMatchingRegexPattern(message, _options.ignoreErrors); } SentryEvent _prepareEvent(SentryEvent event, {dynamic stackTrace}) { @@ -415,7 +416,7 @@ class SentryClient { } var name = transaction.tracer.name; - return _isMatchingRegexPattern(name, _options.ignoreTransactions); + return isMatchingRegexPattern(name, _options.ignoreTransactions); } /// Reports the [envelope] to Sentry.io. @@ -593,11 +594,4 @@ class SentryClient { SentryId.empty(), ); } - - bool _isMatchingRegexPattern(String value, List regexPattern, - {bool caseSensitive = false}) { - final combinedRegexPattern = regexPattern.join('|'); - final regExp = RegExp(combinedRegexPattern, caseSensitive: caseSensitive); - return regExp.hasMatch(value); - } } diff --git a/dart/lib/src/utils/regex_utils.dart b/dart/lib/src/utils/regex_utils.dart new file mode 100644 index 0000000000..05e35702bf --- /dev/null +++ b/dart/lib/src/utils/regex_utils.dart @@ -0,0 +1,6 @@ +bool isMatchingRegexPattern(String value, List regexPattern, + {bool caseSensitive = false}) { + final combinedRegexPattern = regexPattern.join('|'); + final regExp = RegExp(combinedRegexPattern, caseSensitive: caseSensitive); + return regExp.hasMatch(value); +} From 65c28686237e115f79964cf419a52d11fd5d0fd9 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 12 Aug 2024 16:57:29 +0200 Subject: [PATCH 02/19] add allowUrls, denyUrls for web --- .../web_url_filter_event_processor.dart | 35 ++++ dart/lib/src/sentry.dart | 2 + dart/lib/src/sentry_options.dart | 17 ++ .../web_url_filter_event_processor_test.dart | 153 ++++++++++++++++++ 4 files changed, 207 insertions(+) create mode 100644 dart/lib/src/event_processor/web_url_filter_event_processor.dart create mode 100644 dart/test/event_processor/web_url_filter_event_processor_test.dart diff --git a/dart/lib/src/event_processor/web_url_filter_event_processor.dart b/dart/lib/src/event_processor/web_url_filter_event_processor.dart new file mode 100644 index 0000000000..861a53200d --- /dev/null +++ b/dart/lib/src/event_processor/web_url_filter_event_processor.dart @@ -0,0 +1,35 @@ +// We would lose compatibility with old dart versions by adding web to pubspec. +// ignore: depend_on_referenced_packages +import 'package:web/web.dart' as web show window, Window; + +import '../../sentry.dart'; +import '../utils/regex_utils.dart'; + +class WebUrlFilterEventProcessor implements EventProcessor { + WebUrlFilterEventProcessor( + this._options, + ); + + final web.Window _window = web.window; + + final SentryOptions _options; + + @override + SentryEvent? apply(SentryEvent event, Hint hint) { + if (!_options.platformChecker.isWeb) { + return event; + } + final url = event.request?.url ?? _window.location.toString(); + + if (isMatchingRegexPattern(url, _options.allowUrls)) { + if (_options.denyUrls.isNotEmpty && + isMatchingRegexPattern(url, _options.denyUrls)) { + return null; + } else { + return event; + } + } else { + return null; + } + } +} diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index a3ac51e818..e5330f1af9 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -8,6 +8,7 @@ import 'run_zoned_guarded_integration.dart'; import 'event_processor/enricher/enricher_event_processor.dart'; import 'environment/environment_variables.dart'; import 'event_processor/deduplication_event_processor.dart'; +import 'event_processor/web_url_filter_event_processor.dart'; import 'hint.dart'; import 'event_processor/exception/exception_event_processor.dart'; import 'hub.dart'; @@ -86,6 +87,7 @@ class Sentry { options.addEventProcessor(EnricherEventProcessor(options)); options.addEventProcessor(ExceptionEventProcessor(options)); options.addEventProcessor(DeduplicationEventProcessor(options)); + options.addEventProcessor(WebUrlFilterEventProcessor(options)); options.prependExceptionTypeIdentifier(DartExceptionTypeIdentifier()); } diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 2b1771a2b5..d2d7a17858 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -184,12 +184,29 @@ class SentryOptions { /// sent. Events are picked randomly. Default is null (disabled) double? sampleRate; + /// (Web only) Errors only occurring on these Urls will be handled and sent to sentry. + /// If an null or an empty list is used, the SDK will send all errors. + /// To use regex add the `^` and the `$` to the string. + /// + /// If used on a platform other than Web, this setting will be ignored. + List allowUrls = []; + + /// (Web only) Errors occurring on these Urls will be ignored and are not sent to sentry. + /// If an null or an empty list is used, the SDK will send all errors. + /// In combination with `allowUrls` you can block subdomains of the domains listed in allowUrls. + /// To use regex add the `^` and the `$` to the string. + /// + /// If used on a platform other than Web, this setting will be ignored. + List denyUrls = []; + /// The ignoreErrors tells the SDK which errors should be not sent to the sentry server. /// If an null or an empty list is used, the SDK will send all transactions. + /// To use regex add the `^` and the `$` to the string. List ignoreErrors = []; /// The ignoreTransactions tells the SDK which transactions should be not sent to the sentry server. /// If null or an empty list is used, the SDK will send all transactions. + /// To use regex add the `^` and the `$` to the string. List ignoreTransactions = []; final List _inAppExcludes = []; diff --git a/dart/test/event_processor/web_url_filter_event_processor_test.dart b/dart/test/event_processor/web_url_filter_event_processor_test.dart new file mode 100644 index 0000000000..3d2f455168 --- /dev/null +++ b/dart/test/event_processor/web_url_filter_event_processor_test.dart @@ -0,0 +1,153 @@ +@TestOn('browser') +library dart_test; + +import 'package:sentry/sentry.dart'; +import 'package:sentry/sentry_io.dart'; +import 'package:sentry/src/event_processor/web_url_filter_event_processor.dart'; +import 'package:test/test.dart'; + +import '../mocks.dart'; +import '../mocks/mock_platform_checker.dart'; + +// can be tested on command line with +// `dart test -p chrome test/event_processor/web_url_filter_event_processor_test.dart --name web_url_filter` +void main() { + group('web_url_filter', () { + late Fixture fixture; + + setUp(() { + fixture = Fixture(); + }); + + test('returns event if no allowUrl and no denyUrl is set', () { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + + var eventProcessor = fixture.getSut(); + event = eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns null if allowUrl is set and does not match with url', () { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.allowUrls = ["another.url"]; + + var eventProcessor = fixture.getSut(); + event = eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test('returns event if allowUrl is set and does partially match with url', + () { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.allowUrls = ["bar"]; + + var eventProcessor = fixture.getSut(); + event = eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns event if denyUrl is set and does not match with url', () { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.denyUrls = ["another.url"]; + + var eventProcessor = fixture.getSut(); + event = eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns null if denyUrl is set and partially matches with url', () { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.denyUrls = ["bar"]; + + var eventProcessor = fixture.getSut(); + event = eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test( + 'returns null if it is part of the allowed domain, but blocked for subdomain', + () { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'this.is/a/special/url/for-testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test( + 'returns event if it is part of the allowed domain, and not of the blocked for subdomain', + () { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'this.is/a/test/url/for-testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test( + 'returns null if it is not part of the allowed domain, and not of the blocked for subdomain', + () { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'another.url/for/a/test/testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + }); +} + +class Fixture { + SentryOptions options = SentryOptions( + dsn: fakeDsn, + checker: MockPlatformChecker(hasNativeIntegration: false), + ); + WebUrlFilterEventProcessor getSut() { + return WebUrlFilterEventProcessor(options); + } +} From 92f02c33aa4969733f93d8f2e06a0f594eee5db3 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 09:30:14 +0200 Subject: [PATCH 03/19] add changelog entry for allowUrls and denyUrls --- CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e3bd891c6..05721f437a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## Unreleased + +- Support allowUrls and denyUrls for Flutter Web ([#2227](https://github.com/getsentry/sentry-dart/pull/2227)) + ```dart + await SentryFlutter.init( + (options) { + options.dsn = 'https://examplePublicKey@o0.ingest.sentry.io/0'; + options.allowUrls = ["^https://sentry.com.*\$", "my-custom-domain"]; + options.denyUrls = ["^.*ends-with-this\$", "denied-url"]; + ... + }, + appRunner: () => runApp(MyApp()), + ); + ``` + ## 8.7.0 ### Features From 11a06c56287700342c405833c5eab16b59f844c2 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 09:32:16 +0200 Subject: [PATCH 04/19] add conditional import for non web platforms --- .../url_filter/io_url_filter_event_processor.dart | 10 ++++++++++ .../url_filter/url_filter_event_processor.dart | 8 ++++++++ .../web_url_filter_event_processor.dart | 13 +++++++------ dart/lib/src/sentry.dart | 4 ++-- .../web_url_filter_event_processor_test.dart | 2 +- 5 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart create mode 100644 dart/lib/src/event_processor/url_filter/url_filter_event_processor.dart rename dart/lib/src/event_processor/{ => url_filter}/web_url_filter_event_processor.dart (70%) diff --git a/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart b/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart new file mode 100644 index 0000000000..a2874cda67 --- /dev/null +++ b/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart @@ -0,0 +1,10 @@ +import '../../../sentry.dart'; +import 'url_filter_event_processor.dart'; + +UrlFilterEventProcessor urlFilterEventProcessor(SentryOptions options) => + UrlFilterEventProcessor(options); + +class IoUrlFilterEventProcessor implements UrlFilterEventProcessor { + @override + SentryEvent apply(SentryEvent event, Hint hint) => event; +} diff --git a/dart/lib/src/event_processor/url_filter/url_filter_event_processor.dart b/dart/lib/src/event_processor/url_filter/url_filter_event_processor.dart new file mode 100644 index 0000000000..b51911720a --- /dev/null +++ b/dart/lib/src/event_processor/url_filter/url_filter_event_processor.dart @@ -0,0 +1,8 @@ +import '../../../sentry.dart'; +import 'io_url_filter_event_processor.dart' + if (dart.library.html) 'web_url_filter_event_processor.dart'; + +abstract class UrlFilterEventProcessor implements EventProcessor { + factory UrlFilterEventProcessor(SentryOptions options) => + urlFilterEventProcessor(options); +} diff --git a/dart/lib/src/event_processor/web_url_filter_event_processor.dart b/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart similarity index 70% rename from dart/lib/src/event_processor/web_url_filter_event_processor.dart rename to dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart index 861a53200d..80c7d3da60 100644 --- a/dart/lib/src/event_processor/web_url_filter_event_processor.dart +++ b/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart @@ -2,10 +2,14 @@ // ignore: depend_on_referenced_packages import 'package:web/web.dart' as web show window, Window; -import '../../sentry.dart'; -import '../utils/regex_utils.dart'; +import '../../../sentry.dart'; +import '../../utils/regex_utils.dart'; +import 'url_filter_event_processor.dart'; -class WebUrlFilterEventProcessor implements EventProcessor { +UrlFilterEventProcessor urlFilterEventProcessor(SentryOptions options) => + UrlFilterEventProcessor(options); + +class WebUrlFilterEventProcessor implements UrlFilterEventProcessor { WebUrlFilterEventProcessor( this._options, ); @@ -16,9 +20,6 @@ class WebUrlFilterEventProcessor implements EventProcessor { @override SentryEvent? apply(SentryEvent event, Hint hint) { - if (!_options.platformChecker.isWeb) { - return event; - } final url = event.request?.url ?? _window.location.toString(); if (isMatchingRegexPattern(url, _options.allowUrls)) { diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index e5330f1af9..bbdda30cf7 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -8,7 +8,7 @@ import 'run_zoned_guarded_integration.dart'; import 'event_processor/enricher/enricher_event_processor.dart'; import 'environment/environment_variables.dart'; import 'event_processor/deduplication_event_processor.dart'; -import 'event_processor/web_url_filter_event_processor.dart'; +import 'event_processor/url_filter/url_filter_event_processor.dart'; import 'hint.dart'; import 'event_processor/exception/exception_event_processor.dart'; import 'hub.dart'; @@ -87,7 +87,7 @@ class Sentry { options.addEventProcessor(EnricherEventProcessor(options)); options.addEventProcessor(ExceptionEventProcessor(options)); options.addEventProcessor(DeduplicationEventProcessor(options)); - options.addEventProcessor(WebUrlFilterEventProcessor(options)); + options.addEventProcessor(UrlFilterEventProcessor(options)); options.prependExceptionTypeIdentifier(DartExceptionTypeIdentifier()); } diff --git a/dart/test/event_processor/web_url_filter_event_processor_test.dart b/dart/test/event_processor/web_url_filter_event_processor_test.dart index 3d2f455168..b9a229c7b8 100644 --- a/dart/test/event_processor/web_url_filter_event_processor_test.dart +++ b/dart/test/event_processor/web_url_filter_event_processor_test.dart @@ -3,7 +3,7 @@ library dart_test; import 'package:sentry/sentry.dart'; import 'package:sentry/sentry_io.dart'; -import 'package:sentry/src/event_processor/web_url_filter_event_processor.dart'; +import 'package:sentry/src/event_processor/url_filter/web_url_filter_event_processor.dart'; import 'package:test/test.dart'; import '../mocks.dart'; From 934c9d0972f7cb9cfc9ccf1fd06565bfe221a45f Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 10:01:54 +0200 Subject: [PATCH 05/19] fix multiplatform build --- .../url_filter/io_url_filter_event_processor.dart | 4 ++-- .../url_filter/web_url_filter_event_processor.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart b/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart index a2874cda67..0e57d39070 100644 --- a/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart +++ b/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart @@ -1,8 +1,8 @@ import '../../../sentry.dart'; import 'url_filter_event_processor.dart'; -UrlFilterEventProcessor urlFilterEventProcessor(SentryOptions options) => - UrlFilterEventProcessor(options); +UrlFilterEventProcessor urlFilterEventProcessor(SentryOptions _) => + IoUrlFilterEventProcessor(); class IoUrlFilterEventProcessor implements UrlFilterEventProcessor { @override diff --git a/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart b/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart index 80c7d3da60..55cebba701 100644 --- a/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart +++ b/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart @@ -7,7 +7,7 @@ import '../../utils/regex_utils.dart'; import 'url_filter_event_processor.dart'; UrlFilterEventProcessor urlFilterEventProcessor(SentryOptions options) => - UrlFilterEventProcessor(options); + WebUrlFilterEventProcessor(options); class WebUrlFilterEventProcessor implements UrlFilterEventProcessor { WebUrlFilterEventProcessor( From 24c48e9a0594dc328e2b666a5117937fd2e52fbb Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 11:07:37 +0200 Subject: [PATCH 06/19] fix wording in sentry options --- dart/lib/src/sentry_options.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index d2d7a17858..e826013727 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -184,16 +184,16 @@ class SentryOptions { /// sent. Events are picked randomly. Default is null (disabled) double? sampleRate; - /// (Web only) Errors only occurring on these Urls will be handled and sent to sentry. + /// (Web only) Events only occurring on these Urls will be handled and sent to sentry. /// If an null or an empty list is used, the SDK will send all errors. /// To use regex add the `^` and the `$` to the string. /// /// If used on a platform other than Web, this setting will be ignored. List allowUrls = []; - /// (Web only) Errors occurring on these Urls will be ignored and are not sent to sentry. + /// (Web only) Events occurring on these Urls will be ignored and are not sent to sentry. /// If an null or an empty list is used, the SDK will send all errors. - /// In combination with `allowUrls` you can block subdomains of the domains listed in allowUrls. + /// In combination with `allowUrls` you can block subdomains of the domains listed in `allowUrls`. /// To use regex add the `^` and the `$` to the string. /// /// If used on a platform other than Web, this setting will be ignored. From e51012942983a67013fa3bf3e362a709c2a5a31b Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 11:10:25 +0200 Subject: [PATCH 07/19] Update dart/lib/src/utils/regex_utils.dart Co-authored-by: Giancarlo Buenaflor --- dart/lib/src/utils/regex_utils.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/dart/lib/src/utils/regex_utils.dart b/dart/lib/src/utils/regex_utils.dart index 05e35702bf..ba7eb196c1 100644 --- a/dart/lib/src/utils/regex_utils.dart +++ b/dart/lib/src/utils/regex_utils.dart @@ -1,3 +1,4 @@ +@internal bool isMatchingRegexPattern(String value, List regexPattern, {bool caseSensitive = false}) { final combinedRegexPattern = regexPattern.join('|'); From 5d6214427978b89312e43d95499412ccf4cd815f Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 11:11:16 +0200 Subject: [PATCH 08/19] Update dart/lib/src/sentry_options.dart Co-authored-by: Giancarlo Buenaflor --- dart/lib/src/sentry_options.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index e826013727..879a93d20e 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -185,7 +185,7 @@ class SentryOptions { double? sampleRate; /// (Web only) Events only occurring on these Urls will be handled and sent to sentry. - /// If an null or an empty list is used, the SDK will send all errors. + /// If an empty list is used, the SDK will send all errors. /// To use regex add the `^` and the `$` to the string. /// /// If used on a platform other than Web, this setting will be ignored. From f623e075a27da85de2888f325c9a03f7d521a8cd Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 11:11:25 +0200 Subject: [PATCH 09/19] Update dart/lib/src/sentry_options.dart Co-authored-by: Giancarlo Buenaflor --- dart/lib/src/sentry_options.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 879a93d20e..987fb9ccae 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -192,7 +192,7 @@ class SentryOptions { List allowUrls = []; /// (Web only) Events occurring on these Urls will be ignored and are not sent to sentry. - /// If an null or an empty list is used, the SDK will send all errors. + /// If an empty list is used, the SDK will send all errors. /// In combination with `allowUrls` you can block subdomains of the domains listed in `allowUrls`. /// To use regex add the `^` and the `$` to the string. /// From 40cadc967db703ddf7ba76df2a3d4af4de9a9381 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 11:23:33 +0200 Subject: [PATCH 10/19] add tests for isMatchingRegexPattern --- dart/test/utils/regex_utils_test.dart | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 dart/test/utils/regex_utils_test.dart diff --git a/dart/test/utils/regex_utils_test.dart b/dart/test/utils/regex_utils_test.dart new file mode 100644 index 0000000000..ff098ab964 --- /dev/null +++ b/dart/test/utils/regex_utils_test.dart @@ -0,0 +1,24 @@ +import 'package:sentry/src/utils/regex_utils.dart'; +import 'package:test/test.dart'; + +void main() { + group('regex_utils', () { + final testString = "this is a test"; + + test('testString contains string pattern', () { + expect(isMatchingRegexPattern(testString, ["is"]), isTrue); + }); + + test('testString does not contain string pattern', () { + expect(isMatchingRegexPattern(testString, ["not"]), isFalse); + }); + + test('testString contains regex pattern', () { + expect(isMatchingRegexPattern(testString, ["^this.*\$"]), isTrue); + }); + + test('testString does not contain regex pattern', () { + expect(isMatchingRegexPattern(testString, ["^is.*\$"]), isFalse); + }); + }); +} From 2812a968578ddcaf2ebcd402f53af3fca7200475 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 11:24:01 +0200 Subject: [PATCH 11/19] simplified allowUrls and denyUrls handling --- .../web_url_filter_event_processor.dart | 17 +++++++++-------- dart/lib/src/utils/regex_utils.dart | 2 ++ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart b/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart index 55cebba701..fc758faae5 100644 --- a/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart +++ b/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart @@ -22,15 +22,16 @@ class WebUrlFilterEventProcessor implements UrlFilterEventProcessor { SentryEvent? apply(SentryEvent event, Hint hint) { final url = event.request?.url ?? _window.location.toString(); - if (isMatchingRegexPattern(url, _options.allowUrls)) { - if (_options.denyUrls.isNotEmpty && - isMatchingRegexPattern(url, _options.denyUrls)) { - return null; - } else { - return event; - } - } else { + if (_options.allowUrls.isNotEmpty && + !isMatchingRegexPattern(url, _options.allowUrls)) { return null; } + + if (_options.denyUrls.isNotEmpty && + isMatchingRegexPattern(url, _options.denyUrls)) { + return null; + } + + return event; } } diff --git a/dart/lib/src/utils/regex_utils.dart b/dart/lib/src/utils/regex_utils.dart index ba7eb196c1..ba64f7504e 100644 --- a/dart/lib/src/utils/regex_utils.dart +++ b/dart/lib/src/utils/regex_utils.dart @@ -1,3 +1,5 @@ +import 'package:meta/meta.dart'; + @internal bool isMatchingRegexPattern(String value, List regexPattern, {bool caseSensitive = false}) { From c0ffc670b7f7674ece4f1fbfb63e12277feb25d4 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 13:16:26 +0200 Subject: [PATCH 12/19] moved allowUrls and denyUrls from dart to flutter --- dart/lib/src/sentry.dart | 2 - dart/lib/src/sentry_options.dart | 15 -- .../web_url_filter_event_processor_test.dart | 153 ---------------- .../io_url_filter_event_processor.dart | 4 +- .../url_filter_event_processor.dart | 4 +- .../web_url_filter_event_processor.dart | 11 +- flutter/lib/src/sentry_flutter.dart | 2 + flutter/lib/src/sentry_flutter_options.dart | 15 ++ .../url_filter_event_processor_test.dart | 173 ++++++++++++++++++ 9 files changed, 201 insertions(+), 178 deletions(-) delete mode 100644 dart/test/event_processor/web_url_filter_event_processor_test.dart rename {dart => flutter}/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart (65%) rename {dart => flutter}/lib/src/event_processor/url_filter/url_filter_event_processor.dart (67%) rename {dart => flutter}/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart (73%) create mode 100644 flutter/test/event_processor/url_filter_event_processor_test.dart diff --git a/dart/lib/src/sentry.dart b/dart/lib/src/sentry.dart index bbdda30cf7..a3ac51e818 100644 --- a/dart/lib/src/sentry.dart +++ b/dart/lib/src/sentry.dart @@ -8,7 +8,6 @@ import 'run_zoned_guarded_integration.dart'; import 'event_processor/enricher/enricher_event_processor.dart'; import 'environment/environment_variables.dart'; import 'event_processor/deduplication_event_processor.dart'; -import 'event_processor/url_filter/url_filter_event_processor.dart'; import 'hint.dart'; import 'event_processor/exception/exception_event_processor.dart'; import 'hub.dart'; @@ -87,7 +86,6 @@ class Sentry { options.addEventProcessor(EnricherEventProcessor(options)); options.addEventProcessor(ExceptionEventProcessor(options)); options.addEventProcessor(DeduplicationEventProcessor(options)); - options.addEventProcessor(UrlFilterEventProcessor(options)); options.prependExceptionTypeIdentifier(DartExceptionTypeIdentifier()); } diff --git a/dart/lib/src/sentry_options.dart b/dart/lib/src/sentry_options.dart index 987fb9ccae..5cfbd0fdc7 100644 --- a/dart/lib/src/sentry_options.dart +++ b/dart/lib/src/sentry_options.dart @@ -184,21 +184,6 @@ class SentryOptions { /// sent. Events are picked randomly. Default is null (disabled) double? sampleRate; - /// (Web only) Events only occurring on these Urls will be handled and sent to sentry. - /// If an empty list is used, the SDK will send all errors. - /// To use regex add the `^` and the `$` to the string. - /// - /// If used on a platform other than Web, this setting will be ignored. - List allowUrls = []; - - /// (Web only) Events occurring on these Urls will be ignored and are not sent to sentry. - /// If an empty list is used, the SDK will send all errors. - /// In combination with `allowUrls` you can block subdomains of the domains listed in `allowUrls`. - /// To use regex add the `^` and the `$` to the string. - /// - /// If used on a platform other than Web, this setting will be ignored. - List denyUrls = []; - /// The ignoreErrors tells the SDK which errors should be not sent to the sentry server. /// If an null or an empty list is used, the SDK will send all transactions. /// To use regex add the `^` and the `$` to the string. diff --git a/dart/test/event_processor/web_url_filter_event_processor_test.dart b/dart/test/event_processor/web_url_filter_event_processor_test.dart deleted file mode 100644 index b9a229c7b8..0000000000 --- a/dart/test/event_processor/web_url_filter_event_processor_test.dart +++ /dev/null @@ -1,153 +0,0 @@ -@TestOn('browser') -library dart_test; - -import 'package:sentry/sentry.dart'; -import 'package:sentry/sentry_io.dart'; -import 'package:sentry/src/event_processor/url_filter/web_url_filter_event_processor.dart'; -import 'package:test/test.dart'; - -import '../mocks.dart'; -import '../mocks/mock_platform_checker.dart'; - -// can be tested on command line with -// `dart test -p chrome test/event_processor/web_url_filter_event_processor_test.dart --name web_url_filter` -void main() { - group('web_url_filter', () { - late Fixture fixture; - - setUp(() { - fixture = Fixture(); - }); - - test('returns event if no allowUrl and no denyUrl is set', () { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - - var eventProcessor = fixture.getSut(); - event = eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - - test('returns null if allowUrl is set and does not match with url', () { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - fixture.options.allowUrls = ["another.url"]; - - var eventProcessor = fixture.getSut(); - event = eventProcessor.apply(event, Hint()); - - expect(event, isNull); - }); - - test('returns event if allowUrl is set and does partially match with url', - () { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - fixture.options.allowUrls = ["bar"]; - - var eventProcessor = fixture.getSut(); - event = eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - - test('returns event if denyUrl is set and does not match with url', () { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - fixture.options.denyUrls = ["another.url"]; - - var eventProcessor = fixture.getSut(); - event = eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - - test('returns null if denyUrl is set and partially matches with url', () { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - fixture.options.denyUrls = ["bar"]; - - var eventProcessor = fixture.getSut(); - event = eventProcessor.apply(event, Hint()); - - expect(event, isNull); - }); - - test( - 'returns null if it is part of the allowed domain, but blocked for subdomain', - () { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'this.is/a/special/url/for-testing/this-feature', - ), - ); - fixture.options.allowUrls = ["^this.is/.*\$"]; - fixture.options.denyUrls = ["special"]; - - var eventProcessor = fixture.getSut(); - event = eventProcessor.apply(event, Hint()); - - expect(event, isNull); - }); - - test( - 'returns event if it is part of the allowed domain, and not of the blocked for subdomain', - () { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'this.is/a/test/url/for-testing/this-feature', - ), - ); - fixture.options.allowUrls = ["^this.is/.*\$"]; - fixture.options.denyUrls = ["special"]; - - var eventProcessor = fixture.getSut(); - event = eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - - test( - 'returns null if it is not part of the allowed domain, and not of the blocked for subdomain', - () { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'another.url/for/a/test/testing/this-feature', - ), - ); - fixture.options.allowUrls = ["^this.is/.*\$"]; - fixture.options.denyUrls = ["special"]; - - var eventProcessor = fixture.getSut(); - event = eventProcessor.apply(event, Hint()); - - expect(event, isNull); - }); - }); -} - -class Fixture { - SentryOptions options = SentryOptions( - dsn: fakeDsn, - checker: MockPlatformChecker(hasNativeIntegration: false), - ); - WebUrlFilterEventProcessor getSut() { - return WebUrlFilterEventProcessor(options); - } -} diff --git a/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart similarity index 65% rename from dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart rename to flutter/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart index 0e57d39070..b49573bbc5 100644 --- a/dart/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart +++ b/flutter/lib/src/event_processor/url_filter/io_url_filter_event_processor.dart @@ -1,7 +1,7 @@ -import '../../../sentry.dart'; +import '../../../sentry_flutter.dart'; import 'url_filter_event_processor.dart'; -UrlFilterEventProcessor urlFilterEventProcessor(SentryOptions _) => +UrlFilterEventProcessor urlFilterEventProcessor(SentryFlutterOptions _) => IoUrlFilterEventProcessor(); class IoUrlFilterEventProcessor implements UrlFilterEventProcessor { diff --git a/dart/lib/src/event_processor/url_filter/url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/url_filter_event_processor.dart similarity index 67% rename from dart/lib/src/event_processor/url_filter/url_filter_event_processor.dart rename to flutter/lib/src/event_processor/url_filter/url_filter_event_processor.dart index b51911720a..2c8daceacf 100644 --- a/dart/lib/src/event_processor/url_filter/url_filter_event_processor.dart +++ b/flutter/lib/src/event_processor/url_filter/url_filter_event_processor.dart @@ -1,8 +1,8 @@ -import '../../../sentry.dart'; +import '../../../sentry_flutter.dart'; import 'io_url_filter_event_processor.dart' if (dart.library.html) 'web_url_filter_event_processor.dart'; abstract class UrlFilterEventProcessor implements EventProcessor { - factory UrlFilterEventProcessor(SentryOptions options) => + factory UrlFilterEventProcessor(SentryFlutterOptions options) => urlFilterEventProcessor(options); } diff --git a/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart similarity index 73% rename from dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart rename to flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart index fc758faae5..94d89e5ab2 100644 --- a/dart/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart +++ b/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart @@ -2,11 +2,14 @@ // ignore: depend_on_referenced_packages import 'package:web/web.dart' as web show window, Window; -import '../../../sentry.dart'; -import '../../utils/regex_utils.dart'; +import '../../../sentry_flutter.dart'; import 'url_filter_event_processor.dart'; +// ignore: implementation_imports +import 'package:sentry/src//utils/regex_utils.dart'; -UrlFilterEventProcessor urlFilterEventProcessor(SentryOptions options) => +// ignore_for_file: invalid_use_of_internal_member + +UrlFilterEventProcessor urlFilterEventProcessor(SentryFlutterOptions options) => WebUrlFilterEventProcessor(options); class WebUrlFilterEventProcessor implements UrlFilterEventProcessor { @@ -16,7 +19,7 @@ class WebUrlFilterEventProcessor implements UrlFilterEventProcessor { final web.Window _window = web.window; - final SentryOptions _options; + final SentryFlutterOptions _options; @override SentryEvent? apply(SentryEvent event, Hint hint) { diff --git a/flutter/lib/src/sentry_flutter.dart b/flutter/lib/src/sentry_flutter.dart index c688f9862b..5472a87bf7 100644 --- a/flutter/lib/src/sentry_flutter.dart +++ b/flutter/lib/src/sentry_flutter.dart @@ -9,6 +9,7 @@ import 'event_processor/android_platform_exception_event_processor.dart'; import 'event_processor/flutter_enricher_event_processor.dart'; import 'event_processor/flutter_exception_event_processor.dart'; import 'event_processor/platform_exception_event_processor.dart'; +import 'event_processor/url_filter/url_filter_event_processor.dart'; import 'event_processor/widget_event_processor.dart'; import 'file_system_transport.dart'; import 'flutter_exception_type_identifier.dart'; @@ -131,6 +132,7 @@ mixin SentryFlutter { options.addEventProcessor(FlutterEnricherEventProcessor(options)); options.addEventProcessor(WidgetEventProcessor()); + options.addEventProcessor(UrlFilterEventProcessor(options)); if (options.platformChecker.platform.isAndroid) { options.addEventProcessor( diff --git a/flutter/lib/src/sentry_flutter_options.dart b/flutter/lib/src/sentry_flutter_options.dart index 347da9ada3..9823cd5b34 100644 --- a/flutter/lib/src/sentry_flutter_options.dart +++ b/flutter/lib/src/sentry_flutter_options.dart @@ -143,6 +143,21 @@ class SentryFlutterOptions extends SentryOptions { /// See https://api.flutter.dev/flutter/foundation/FlutterErrorDetails/silent.html bool reportSilentFlutterErrors = false; + /// (Web only) Events only occurring on these Urls will be handled and sent to sentry. + /// If an empty list is used, the SDK will send all errors. + /// To use regex add the `^` and the `$` to the string. + /// + /// If used on a platform other than Web, this setting will be ignored. + List allowUrls = []; + + /// (Web only) Events occurring on these Urls will be ignored and are not sent to sentry. + /// If an empty list is used, the SDK will send all errors. + /// In combination with `allowUrls` you can block subdomains of the domains listed in `allowUrls`. + /// To use regex add the `^` and the `$` to the string. + /// + /// If used on a platform other than Web, this setting will be ignored. + List denyUrls = []; + /// Enables Out of Memory Tracking for iOS and macCatalyst. /// See the following link for more information and possible restrictions: /// https://docs.sentry.io/platforms/apple/guides/ios/configuration/out-of-memory/ diff --git a/flutter/test/event_processor/url_filter_event_processor_test.dart b/flutter/test/event_processor/url_filter_event_processor_test.dart new file mode 100644 index 0000000000..92497c57f1 --- /dev/null +++ b/flutter/test/event_processor/url_filter_event_processor_test.dart @@ -0,0 +1,173 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:sentry_flutter/src/event_processor/url_filter/url_filter_event_processor.dart'; + +// can be tested on command line with +// `flutter test --platform=chrome test/event_processor/url_filter_event_processor_test.dart` +void main() { + if (kIsWeb) { + group(UrlFilterEventProcessor, () { + late Fixture fixture; + + setUp(() { + fixture = Fixture(); + }); + + test('returns event if no allowUrl and no denyUrl is set', () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns null if allowUrl is set and does not match with url', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.allowUrls = ["another.url"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test('returns event if allowUrl is set and does partially match with url', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.allowUrls = ["bar"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns event if denyUrl is set and does not match with url', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.denyUrls = ["another.url"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns null if denyUrl is set and partially matches with url', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.denyUrls = ["bar"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test( + 'returns null if it is part of the allowed domain, but blocked for subdomain', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'this.is/a/special/url/for-testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test( + 'returns event if it is part of the allowed domain, and not of the blocked for subdomain', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'this.is/a/test/url/for-testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test( + 'returns null if it is not part of the allowed domain, and not of the blocked for subdomain', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'another.url/for/a/test/testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + }); + } else { + group("ignore allowUrls and denyUrls for non Web", () { + late Fixture fixture; + + setUp(() async { + fixture = Fixture(); + }); + + test('returns the event and ignore allowUrls and denyUrls for non Web', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'another.url/for/a/special/test/testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + }); + } +} + +class Fixture { + SentryFlutterOptions options = SentryFlutterOptions(); + UrlFilterEventProcessor getSut() { + return UrlFilterEventProcessor(options); + } +} From 8523a6d00d1cf3c74bda1c38864e90628cc4d3ee Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Tue, 13 Aug 2024 13:59:32 +0200 Subject: [PATCH 13/19] add event processor for html --- .../html_url_filter_event_processor.dart | 38 +++++++++++++++++++ .../url_filter_event_processor.dart | 3 +- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart diff --git a/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart new file mode 100644 index 0000000000..8242ec478c --- /dev/null +++ b/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart @@ -0,0 +1,38 @@ +import 'dart:html' as html show window, Window; + +import '../../../sentry_flutter.dart'; +import 'url_filter_event_processor.dart'; +// ignore: implementation_imports +import 'package:sentry/src//utils/regex_utils.dart'; + +// ignore_for_file: invalid_use_of_internal_member + +UrlFilterEventProcessor urlFilterEventProcessor(SentryFlutterOptions options) => + WebUrlFilterEventProcessor(options); + +class WebUrlFilterEventProcessor implements UrlFilterEventProcessor { + WebUrlFilterEventProcessor( + this._options, + ); + + final html.Window _window = html.window; + + final SentryFlutterOptions _options; + + @override + SentryEvent? apply(SentryEvent event, Hint hint) { + final url = event.request?.url ?? _window.location.toString(); + + if (_options.allowUrls.isNotEmpty && + !isMatchingRegexPattern(url, _options.allowUrls)) { + return null; + } + + if (_options.denyUrls.isNotEmpty && + isMatchingRegexPattern(url, _options.denyUrls)) { + return null; + } + + return event; + } +} diff --git a/flutter/lib/src/event_processor/url_filter/url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/url_filter_event_processor.dart index 2c8daceacf..5a1e5ed537 100644 --- a/flutter/lib/src/event_processor/url_filter/url_filter_event_processor.dart +++ b/flutter/lib/src/event_processor/url_filter/url_filter_event_processor.dart @@ -1,6 +1,7 @@ import '../../../sentry_flutter.dart'; import 'io_url_filter_event_processor.dart' - if (dart.library.html) 'web_url_filter_event_processor.dart'; + if (dart.library.html) 'html_url_filter_event_processor.dart' + if (dart.library.js_interop) 'web_url_filter_event_processor.dart'; abstract class UrlFilterEventProcessor implements EventProcessor { factory UrlFilterEventProcessor(SentryFlutterOptions options) => From a30a8626adadc387749616df9be07ac1ab2f834a Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Wed, 14 Aug 2024 12:57:15 +0200 Subject: [PATCH 14/19] rephrased documentation and split up tests for web and mobile platform. --- flutter/lib/src/sentry_flutter_options.dart | 4 +- .../io_filter_event_processor_test.dart | 39 ++++ .../web_url_filter_event_processor_test.dart | 149 +++++++++++++++ .../url_filter_event_processor_test.dart | 173 ------------------ 4 files changed, 190 insertions(+), 175 deletions(-) create mode 100644 flutter/test/event_processor/url_filter/io_filter_event_processor_test.dart create mode 100644 flutter/test/event_processor/url_filter/web_url_filter_event_processor_test.dart delete mode 100644 flutter/test/event_processor/url_filter_event_processor_test.dart diff --git a/flutter/lib/src/sentry_flutter_options.dart b/flutter/lib/src/sentry_flutter_options.dart index 9823cd5b34..facb5d0d02 100644 --- a/flutter/lib/src/sentry_flutter_options.dart +++ b/flutter/lib/src/sentry_flutter_options.dart @@ -145,15 +145,15 @@ class SentryFlutterOptions extends SentryOptions { /// (Web only) Events only occurring on these Urls will be handled and sent to sentry. /// If an empty list is used, the SDK will send all errors. - /// To use regex add the `^` and the `$` to the string. + /// `allowUrls` uses regex for the matching. /// /// If used on a platform other than Web, this setting will be ignored. List allowUrls = []; /// (Web only) Events occurring on these Urls will be ignored and are not sent to sentry. /// If an empty list is used, the SDK will send all errors. + /// `denyUrls` uses regex for the matching. /// In combination with `allowUrls` you can block subdomains of the domains listed in `allowUrls`. - /// To use regex add the `^` and the `$` to the string. /// /// If used on a platform other than Web, this setting will be ignored. List denyUrls = []; diff --git a/flutter/test/event_processor/url_filter/io_filter_event_processor_test.dart b/flutter/test/event_processor/url_filter/io_filter_event_processor_test.dart new file mode 100644 index 0000000000..22708b95bb --- /dev/null +++ b/flutter/test/event_processor/url_filter/io_filter_event_processor_test.dart @@ -0,0 +1,39 @@ +@TestOn('vm') +library flutter_test; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:sentry_flutter/src/event_processor/url_filter/url_filter_event_processor.dart'; + +void main() { + group("ignore allowUrls and denyUrls for non Web", () { + late Fixture fixture; + + setUp(() async { + fixture = Fixture(); + }); + + test('returns the event and ignore allowUrls and denyUrls for non Web', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'another.url/for/a/special/test/testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + }); +} + +class Fixture { + SentryFlutterOptions options = SentryFlutterOptions(); + UrlFilterEventProcessor getSut() { + return UrlFilterEventProcessor(options); + } +} diff --git a/flutter/test/event_processor/url_filter/web_url_filter_event_processor_test.dart b/flutter/test/event_processor/url_filter/web_url_filter_event_processor_test.dart new file mode 100644 index 0000000000..c5c9c22451 --- /dev/null +++ b/flutter/test/event_processor/url_filter/web_url_filter_event_processor_test.dart @@ -0,0 +1,149 @@ +@TestOn('browser') +library flutter_test; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:sentry_flutter/src/event_processor/url_filter/url_filter_event_processor.dart'; + +// can be tested on command line with +// `flutter test --platform=chrome test/event_processor/url_filter/web_url_filter_event_processor_test.dart` +void main() { + group(UrlFilterEventProcessor, () { + late Fixture fixture; + + setUp(() { + fixture = Fixture(); + }); + + test('returns event if no allowUrl and no denyUrl is set', () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns null if allowUrl is set and does not match with url', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.allowUrls = ["another.url"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test('returns event if allowUrl is set and does partially match with url', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.allowUrls = ["bar"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns event if denyUrl is set and does not match with url', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.denyUrls = ["another.url"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test('returns null if denyUrl is set and partially matches with url', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'foo.bar', + ), + ); + fixture.options.denyUrls = ["bar"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test( + 'returns null if it is part of the allowed domain, but blocked for subdomain', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'this.is/a/special/url/for-testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + + test( + 'returns event if it is part of the allowed domain, and not of the blocked for subdomain', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'this.is/a/test/url/for-testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNotNull); + }); + + test( + 'returns null if it is not part of the allowed domain, and not of the blocked for subdomain', + () async { + SentryEvent? event = SentryEvent( + request: SentryRequest( + url: 'another.url/for/a/test/testing/this-feature', + ), + ); + fixture.options.allowUrls = ["^this.is/.*\$"]; + fixture.options.denyUrls = ["special"]; + + var eventProcessor = fixture.getSut(); + event = await eventProcessor.apply(event, Hint()); + + expect(event, isNull); + }); + }); +} + +class Fixture { + SentryFlutterOptions options = SentryFlutterOptions(); + UrlFilterEventProcessor getSut() { + return UrlFilterEventProcessor(options); + } +} diff --git a/flutter/test/event_processor/url_filter_event_processor_test.dart b/flutter/test/event_processor/url_filter_event_processor_test.dart deleted file mode 100644 index 92497c57f1..0000000000 --- a/flutter/test/event_processor/url_filter_event_processor_test.dart +++ /dev/null @@ -1,173 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:sentry_flutter/src/event_processor/url_filter/url_filter_event_processor.dart'; - -// can be tested on command line with -// `flutter test --platform=chrome test/event_processor/url_filter_event_processor_test.dart` -void main() { - if (kIsWeb) { - group(UrlFilterEventProcessor, () { - late Fixture fixture; - - setUp(() { - fixture = Fixture(); - }); - - test('returns event if no allowUrl and no denyUrl is set', () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - - test('returns null if allowUrl is set and does not match with url', - () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - fixture.options.allowUrls = ["another.url"]; - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNull); - }); - - test('returns event if allowUrl is set and does partially match with url', - () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - fixture.options.allowUrls = ["bar"]; - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - - test('returns event if denyUrl is set and does not match with url', - () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - fixture.options.denyUrls = ["another.url"]; - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - - test('returns null if denyUrl is set and partially matches with url', - () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); - fixture.options.denyUrls = ["bar"]; - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNull); - }); - - test( - 'returns null if it is part of the allowed domain, but blocked for subdomain', - () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'this.is/a/special/url/for-testing/this-feature', - ), - ); - fixture.options.allowUrls = ["^this.is/.*\$"]; - fixture.options.denyUrls = ["special"]; - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNull); - }); - - test( - 'returns event if it is part of the allowed domain, and not of the blocked for subdomain', - () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'this.is/a/test/url/for-testing/this-feature', - ), - ); - fixture.options.allowUrls = ["^this.is/.*\$"]; - fixture.options.denyUrls = ["special"]; - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - - test( - 'returns null if it is not part of the allowed domain, and not of the blocked for subdomain', - () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'another.url/for/a/test/testing/this-feature', - ), - ); - fixture.options.allowUrls = ["^this.is/.*\$"]; - fixture.options.denyUrls = ["special"]; - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNull); - }); - }); - } else { - group("ignore allowUrls and denyUrls for non Web", () { - late Fixture fixture; - - setUp(() async { - fixture = Fixture(); - }); - - test('returns the event and ignore allowUrls and denyUrls for non Web', - () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'another.url/for/a/special/test/testing/this-feature', - ), - ); - fixture.options.allowUrls = ["^this.is/.*\$"]; - fixture.options.denyUrls = ["special"]; - - var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); - - expect(event, isNotNull); - }); - }); - } -} - -class Fixture { - SentryFlutterOptions options = SentryFlutterOptions(); - UrlFilterEventProcessor getSut() { - return UrlFilterEventProcessor(options); - } -} From 160ddcc42accb81f52cdde0b1848e2c095007cf9 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Wed, 14 Aug 2024 13:42:47 +0200 Subject: [PATCH 15/19] add expected error --- scripts/publish_validation/bin/publish_validation.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/publish_validation/bin/publish_validation.dart b/scripts/publish_validation/bin/publish_validation.dart index ab871e910e..8dec40e851 100644 --- a/scripts/publish_validation/bin/publish_validation.dart +++ b/scripts/publish_validation/bin/publish_validation.dart @@ -34,7 +34,8 @@ void main(List arguments) async { 'lib/src/integrations/connectivity/web_connectivity_provider.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`', 'lib/src/event_processor/enricher/web_enricher_event_processor.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`', 'lib/src/origin_web.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`', - 'lib/src/platform/_web_platform.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`' + 'lib/src/platform/_web_platform.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`', + 'lib/src/event_processor/url_filter/web_url_filter_event_processor.dart: This package does not have web in the dependencies section of pubspec.yaml', ]; // So far the expected errors all start with `* line` From dc06ba9ce04a4b5bb9974dadbf95086392c7779c Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 19 Aug 2024 09:41:09 +0200 Subject: [PATCH 16/19] Update scripts/publish_validation/bin/publish_validation.dart Co-authored-by: Giancarlo Buenaflor --- scripts/publish_validation/bin/publish_validation.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/publish_validation/bin/publish_validation.dart b/scripts/publish_validation/bin/publish_validation.dart index 8dec40e851..0585d7dd00 100644 --- a/scripts/publish_validation/bin/publish_validation.dart +++ b/scripts/publish_validation/bin/publish_validation.dart @@ -35,7 +35,7 @@ void main(List arguments) async { 'lib/src/event_processor/enricher/web_enricher_event_processor.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`', 'lib/src/origin_web.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`', 'lib/src/platform/_web_platform.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`', - 'lib/src/event_processor/url_filter/web_url_filter_event_processor.dart: This package does not have web in the dependencies section of pubspec.yaml', + 'lib/src/event_processor/url_filter/web_url_filter_event_processor.dart: This package does not have web in the `dependencies` section of `pubspec.yaml`', ]; // So far the expected errors all start with `* line` From e38e62563fb536545bf1300ae0262ad9c2f451dd Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 19 Aug 2024 09:41:21 +0200 Subject: [PATCH 17/19] Update flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart Co-authored-by: Giancarlo Buenaflor --- .../url_filter/html_url_filter_event_processor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart index 8242ec478c..7ad66eaade 100644 --- a/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart +++ b/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart @@ -3,7 +3,7 @@ import 'dart:html' as html show window, Window; import '../../../sentry_flutter.dart'; import 'url_filter_event_processor.dart'; // ignore: implementation_imports -import 'package:sentry/src//utils/regex_utils.dart'; +import 'package:sentry/src/utils/regex_utils.dart'; // ignore_for_file: invalid_use_of_internal_member From 5e7ccc1fa3b0b9d5a7c39426afd02ca3b72a690f Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 19 Aug 2024 09:41:45 +0200 Subject: [PATCH 18/19] Update flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart Co-authored-by: Giancarlo Buenaflor --- .../url_filter/web_url_filter_event_processor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart index 94d89e5ab2..af203a36d2 100644 --- a/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart +++ b/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart @@ -5,7 +5,7 @@ import 'package:web/web.dart' as web show window, Window; import '../../../sentry_flutter.dart'; import 'url_filter_event_processor.dart'; // ignore: implementation_imports -import 'package:sentry/src//utils/regex_utils.dart'; +import 'package:sentry/src/utils/regex_utils.dart'; // ignore_for_file: invalid_use_of_internal_member From 102f0e56cc3f4018a3fb1eedf021083978d05c90 Mon Sep 17 00:00:00 2001 From: Martin Haintz Date: Mon, 2 Sep 2024 14:21:54 +0200 Subject: [PATCH 19/19] modified code to go through stacktrace frames --- .../html_url_filter_event_processor.dart | 26 +++- .../web_url_filter_event_processor.dart | 26 +++- .../web_url_filter_event_processor_test.dart | 128 +++++++++++------- 3 files changed, 121 insertions(+), 59 deletions(-) diff --git a/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart index 7ad66eaade..7792f6a333 100644 --- a/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart +++ b/flutter/lib/src/event_processor/url_filter/html_url_filter_event_processor.dart @@ -15,24 +15,40 @@ class WebUrlFilterEventProcessor implements UrlFilterEventProcessor { this._options, ); - final html.Window _window = html.window; - final SentryFlutterOptions _options; @override SentryEvent? apply(SentryEvent event, Hint hint) { - final url = event.request?.url ?? _window.location.toString(); + final frames = _getStacktraceFrames(event); + final lastPath = frames?.first?.absPath; + + if (lastPath == null) { + return event; + } if (_options.allowUrls.isNotEmpty && - !isMatchingRegexPattern(url, _options.allowUrls)) { + !isMatchingRegexPattern(lastPath, _options.allowUrls)) { return null; } if (_options.denyUrls.isNotEmpty && - isMatchingRegexPattern(url, _options.denyUrls)) { + isMatchingRegexPattern(lastPath, _options.denyUrls)) { return null; } return event; } + + Iterable? _getStacktraceFrames(SentryEvent event) { + if (event.exceptions?.isNotEmpty == true) { + return event.exceptions?.first.stackTrace?.frames; + } + if (event.threads?.isNotEmpty == true) { + final stacktraces = event.threads?.map((e) => e.stacktrace); + return stacktraces + ?.where((element) => element != null) + .expand((element) => element!.frames); + } + return null; + } } diff --git a/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart b/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart index af203a36d2..10cfee3478 100644 --- a/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart +++ b/flutter/lib/src/event_processor/url_filter/web_url_filter_event_processor.dart @@ -17,24 +17,40 @@ class WebUrlFilterEventProcessor implements UrlFilterEventProcessor { this._options, ); - final web.Window _window = web.window; - final SentryFlutterOptions _options; @override SentryEvent? apply(SentryEvent event, Hint hint) { - final url = event.request?.url ?? _window.location.toString(); + final frames = _getStacktraceFrames(event); + final lastPath = frames?.first?.absPath; + + if (lastPath == null) { + return event; + } if (_options.allowUrls.isNotEmpty && - !isMatchingRegexPattern(url, _options.allowUrls)) { + !isMatchingRegexPattern(lastPath, _options.allowUrls)) { return null; } if (_options.denyUrls.isNotEmpty && - isMatchingRegexPattern(url, _options.denyUrls)) { + isMatchingRegexPattern(lastPath, _options.denyUrls)) { return null; } return event; } + + Iterable? _getStacktraceFrames(SentryEvent event) { + if (event.exceptions?.isNotEmpty == true) { + return event.exceptions?.first.stackTrace?.frames; + } + if (event.threads?.isNotEmpty == true) { + final stacktraces = event.threads?.map((e) => e.stacktrace); + return stacktraces + ?.where((element) => element != null) + .expand((element) => element!.frames); + } + return null; + } } diff --git a/flutter/test/event_processor/url_filter/web_url_filter_event_processor_test.dart b/flutter/test/event_processor/url_filter/web_url_filter_event_processor_test.dart index c5c9c22451..f16f5c5003 100644 --- a/flutter/test/event_processor/url_filter/web_url_filter_event_processor_test.dart +++ b/flutter/test/event_processor/url_filter/web_url_filter_event_processor_test.dart @@ -30,113 +30,133 @@ void main() { test('returns null if allowUrl is set and does not match with url', () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); + final event = _createEventWithException("foo.bar"); fixture.options.allowUrls = ["another.url"]; var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); + final processedEvent = await eventProcessor.apply(event, Hint()); - expect(event, isNull); + expect(processedEvent, isNull); }); test('returns event if allowUrl is set and does partially match with url', () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); + final event = _createEventWithException("foo.bar"); fixture.options.allowUrls = ["bar"]; var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); + final processedEvent = await eventProcessor.apply(event, Hint()); - expect(event, isNotNull); + expect(processedEvent, isNotNull); }); test('returns event if denyUrl is set and does not match with url', () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); + final event = _createEventWithException("foo.bar"); fixture.options.denyUrls = ["another.url"]; var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); + final processedEvent = await eventProcessor.apply(event, Hint()); - expect(event, isNotNull); + expect(processedEvent, isNotNull); }); test('returns null if denyUrl is set and partially matches with url', () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'foo.bar', - ), - ); + final event = _createEventWithException("foo.bar"); fixture.options.denyUrls = ["bar"]; var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); + final processedEvent = await eventProcessor.apply(event, Hint()); - expect(event, isNull); + expect(processedEvent, isNull); }); test( 'returns null if it is part of the allowed domain, but blocked for subdomain', () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'this.is/a/special/url/for-testing/this-feature', - ), - ); + final event = _createEventWithException( + "this.is/a/special/url/for-testing/this-feature"); + fixture.options.allowUrls = ["^this.is/.*\$"]; fixture.options.denyUrls = ["special"]; var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); + final processedEvent = await eventProcessor.apply(event, Hint()); - expect(event, isNull); + expect(processedEvent, isNull); }); test( 'returns event if it is part of the allowed domain, and not of the blocked for subdomain', () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'this.is/a/test/url/for-testing/this-feature', - ), - ); + final event = _createEventWithException( + "this.is/a/test/url/for-testing/this-feature"); fixture.options.allowUrls = ["^this.is/.*\$"]; fixture.options.denyUrls = ["special"]; var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); + final processedEvent = await eventProcessor.apply(event, Hint()); - expect(event, isNotNull); + expect(processedEvent, isNotNull); }); test( 'returns null if it is not part of the allowed domain, and not of the blocked for subdomain', () async { - SentryEvent? event = SentryEvent( - request: SentryRequest( - url: 'another.url/for/a/test/testing/this-feature', - ), - ); + final event = _createEventWithException( + "another.url/for/a/test/testing/this-feature"); fixture.options.allowUrls = ["^this.is/.*\$"]; fixture.options.denyUrls = ["special"]; var eventProcessor = fixture.getSut(); - event = await eventProcessor.apply(event, Hint()); + final processedEvent = await eventProcessor.apply(event, Hint()); + + expect(processedEvent, isNull); + }); + + test( + 'returns event if denyUrl is set and not matching with url of first exception', + () async { + final frame1 = SentryStackFrame(absPath: "test.url"); + final st1 = SentryStackTrace(frames: [frame1]); + final exception1 = SentryException( + type: "test-type", value: "test-value", stackTrace: st1); + + final frame2 = SentryStackFrame(absPath: "foo.bar"); + final st2 = SentryStackTrace(frames: [frame2]); + final exception2 = SentryException( + type: "test-type", value: "test-value", stackTrace: st2); + + SentryEvent event = SentryEvent(exceptions: [exception1, exception2]); + + fixture.options.denyUrls = ["bar"]; + + var eventProcessor = fixture.getSut(); + final processedEvent = await eventProcessor.apply(event, Hint()); - expect(event, isNull); + expect(processedEvent, isNotNull); + }); + + test( + 'returns event if denyUrl is set and not matching with url of first stacktraceframe', + () async { + final frame1 = SentryStackFrame(absPath: "test.url"); + final st1 = SentryStackTrace(frames: [frame1]); + final thread1 = SentryThread(stacktrace: st1); + + final frame2 = SentryStackFrame(absPath: "foo.bar"); + final st2 = SentryStackTrace(frames: [frame2]); + final thread2 = SentryThread(stacktrace: st2); + + SentryEvent event = SentryEvent(threads: [thread1, thread2]); + + fixture.options.denyUrls = ["bar"]; + + var eventProcessor = fixture.getSut(); + final processedEvent = await eventProcessor.apply(event, Hint()); + + expect(processedEvent, isNotNull); }); }); } @@ -147,3 +167,13 @@ class Fixture { return UrlFilterEventProcessor(options); } } + +SentryEvent _createEventWithException(String url) { + final frame = SentryStackFrame(absPath: url); + final st = SentryStackTrace(frames: [frame]); + final exception = + SentryException(type: "test-type", value: "test-value", stackTrace: st); + SentryEvent event = SentryEvent(exceptions: [exception]); + + return event; +}