Skip to content

Commit

Permalink
Bump bugsnag-android to v6. Changed redactedKeys and discardClasses t…
Browse files Browse the repository at this point in the history
…ypes to RegExp (#249)

* Bump bugsnag-android to v6. Changed redactedKeys and discardClasses types to RegExp

* Regular expressions sent to native clients now carry RegExp flags

* Update features/fixtures/app/lib/scenarios/start_bugsnag_scenario.dart

Co-authored-by: Jason <lemnik@users.noreply.github.com>

---------

Co-authored-by: Robert <robert.smartbear@gmail.com>
Co-authored-by: Jason <lemnik@users.noreply.github.com>
  • Loading branch information
3 people authored Jul 9, 2024
1 parent d387c79 commit 0ce7510
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ class DiscardClassesScenario extends Scenario {

await bugsnag.start(
endpoints: endpoints,
discardClasses: const {
'_Exception',
}
discardClasses: {
RegExp('_Exception'),
},
);
try {
throw Exception('this should be discarded');
Expand Down
54 changes: 27 additions & 27 deletions features/fixtures/app/lib/scenarios/start_bugsnag_scenario.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,33 @@ class StartBugsnagScenario extends Scenario {
@override
Future<void> run() async {
await bugsnag.start(
apiKey: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
appType: 'test',
appVersion: '1.2.3',
versionCode: 4321,
context: 'awesome',
user: BugsnagUser(
id: '123',
name: 'From Config',
),
redactedKeys: {'secret'},
releaseStage: 'testing',
enabledReleaseStages: {'testing'},
enabledBreadcrumbTypes: {BugsnagEnabledBreadcrumbType.error},
endpoints: endpoints,
featureFlags: const [
BugsnagFeatureFlag('demo-mode'),
BugsnagFeatureFlag('sample-group', '123'),
],
metadata: const {
'custom': {
'foo': 'bar',
'secret': 'should be hidden',
'password': 'not redacted'
}
},
sendThreads: BugsnagThreadSendPolicy.never
);
apiKey: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
appType: 'test',
appVersion: '1.2.3',
versionCode: 4321,
context: 'awesome',
user: BugsnagUser(
id: '123',
name: 'From Config',
),
redactedKeys: {RegExp('secret')},
releaseStage: 'testing',
enabledReleaseStages: {'testing'},
enabledBreadcrumbTypes: {BugsnagEnabledBreadcrumbType.error},
endpoints: endpoints,
featureFlags: const [
BugsnagFeatureFlag('demo-mode'),
BugsnagFeatureFlag('sample-group', '123'),
],
metadata: const {
'custom': {
'foo': 'bar',
'secret': 'should be hidden',
'password': 'not redacted'
}
},
sendThreads: BugsnagThreadSendPolicy.never,
);
await bugsnag.notify(
Exception('Exception with attached info'),
StackTrace.current,
Expand Down
2 changes: 1 addition & 1 deletion packages/bugsnag_flutter/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ android {
}

dependencies {
implementation 'com.bugsnag:bugsnag-android:5.31.3'
implementation 'com.bugsnag:bugsnag-android:6.6.0'
testImplementation 'junit:junit:4.12'
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

class BugsnagFlutter {

Expand Down Expand Up @@ -120,11 +121,11 @@ Void start(@Nullable JSONObject args) throws Exception {
configuration.setPersistUser(arguments.optBoolean("persistUser", configuration.getPersistUser()));

if (arguments.has("redactedKeys")) {
configuration.setRedactedKeys(unwrap(arguments.optJSONArray("redactedKeys"), new HashSet<>()));
configuration.setRedactedKeys(regexSetFromArray(arguments.optJSONArray("redactedKeys")));
}

if (arguments.has("discardClasses")) {
configuration.setDiscardClasses(unwrap(arguments.optJSONArray("discardClasses"), new HashSet<>()));
configuration.setDiscardClasses(regexSetFromArray(arguments.optJSONArray("discardClasses")));
}

if (arguments.has("enabledReleaseStages")) {
Expand Down Expand Up @@ -344,6 +345,29 @@ Void markLaunchCompleted(@Nullable Void args) {
return null;
}

Set<Pattern> regexSetFromArray(JSONArray array) throws JSONException {
HashSet<Pattern> result = new HashSet<>(array.length());
for (int i = 0; i < array.length(); i++) {
JSONObject element = array.getJSONObject(i);
String pattern = getString(element, "pattern");
int flags = 0;
if (element.optBoolean("isDotAll")) {
flags |= Pattern.DOTALL;
}
if (!element.optBoolean("isCaseSensitive")) {
flags |= Pattern.CASE_INSENSITIVE;
}
if (element.optBoolean("isMultiLine")) {
flags |= Pattern.MULTILINE;
}

if (pattern != null) {
result.add(Pattern.compile(pattern, flags));
}
}
return result;
}

JSONObject getLastRunInfo(@Nullable Void args) throws JSONException {
LastRunInfo lastRunInfo = Bugsnag.getLastRunInfo();
return (lastRunInfo == null) ? null : new JSONObject()
Expand Down
29 changes: 27 additions & 2 deletions packages/bugsnag_flutter/ios/Classes/BugsnagFlutterPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,31 @@ static void DyldImageAdded(const struct mach_header *mh, intptr_t slide) {
return [value isKindOfClass:[NSString class]] ? value : nil;
}

static NSArray *jsonToRegularExpressions(NSArray *source) {
NSMutableArray *result = [NSMutableArray new];
for (NSDictionary *element in source) {
NSString *pattern = element[@"pattern"];
NSInteger options = 0;
if ([element[@"isDotAll"] boolValue]) {
options |= NSRegularExpressionDotMatchesLineSeparators;
}
if (![element[@"isCaseSensitive"] boolValue]) {
options |= NSRegularExpressionCaseInsensitive;
}
if ([element[@"isMultiLine"] boolValue]) {
options |= NSRegularExpressionAnchorsMatchLines;
}
NSError *error = nil;
NSRegularExpression *expression = [NSRegularExpression regularExpressionWithPattern:pattern options:options error:&error];
if (expression != nil) {
[result addObject: expression];
} else if (error) {
NSLog(@"Error encountered while parsing regular expression %@", error);
}
}
return result;
}

@interface BugsnagEvent (BugsnagFlutterPlugin)

@property (nullable, nonatomic) NSArray *projectPackages;
Expand Down Expand Up @@ -278,12 +303,12 @@ - (void)start:(NSDictionary *)arguments {

NSArray *redactedKeys = arguments[@"redactedKeys"];
if ([redactedKeys isKindOfClass:[NSArray class]]) {
configuration.redactedKeys = [NSSet setWithArray:redactedKeys];
configuration.redactedKeys = [NSSet setWithArray: jsonToRegularExpressions(redactedKeys)];
}

NSArray *discardClasses = arguments[@"discardClasses"];
if ([discardClasses isKindOfClass:[NSArray class]]) {
configuration.discardClasses = [NSSet setWithArray:discardClasses];
configuration.discardClasses = [NSSet setWithArray: jsonToRegularExpressions(discardClasses)];
}

NSArray *enabledReleaseStages = arguments[@"enabledReleaseStages"];
Expand Down
77 changes: 40 additions & 37 deletions packages/bugsnag_flutter/lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'config.dart';
import 'enum_utils.dart';
import 'last_run_info.dart';
import 'model.dart';
import 'regexp_json.dart';

final _notifier = {
'name': 'Flutter Bugsnag Notifier',
Expand All @@ -21,7 +22,6 @@ final _notifier = {
};

abstract class BugsnagClient {

final Map<String, Stopwatch> _openNetworkRequests = {};

/// An utility error handling function that will send reported errors to
Expand Down Expand Up @@ -256,45 +256,47 @@ abstract class BugsnagClient {
}

void _onRequestStarted(String requestId) {
final stopwatch = Stopwatch()..start();
_openNetworkRequests[requestId] = stopwatch;
final stopwatch = Stopwatch()..start();
_openNetworkRequests[requestId] = stopwatch;
}

void _onRequestComplete(String requestId, dynamic data) {
final stopwatch = _openNetworkRequests.remove(requestId);
if (stopwatch != null && data is Map<String, dynamic>) {
final duration = stopwatch.elapsedMilliseconds;
final String? clientName = data["client"];
if (clientName == null) return;

String params = "";
final url = data["url"];
final splitUrl = url.split("?");
if (splitUrl != null && splitUrl.length > 1) {
params = splitUrl.last;
}
final int? statusCode = data["status_code"];
if (statusCode == null) return;

final String status = statusCode < 400 ? "succeeded" : "failed";
// Assuming leaveBreadcrumb is a predefined method to log the event
leaveBreadcrumb("$clientName request $status", metadata: {
final stopwatch = _openNetworkRequests.remove(requestId);
if (stopwatch != null && data is Map<String, dynamic>) {
final duration = stopwatch.elapsedMilliseconds;
final String? clientName = data["client"];
if (clientName == null) return;

String params = "";
final url = data["url"];
final splitUrl = url.split("?");
if (splitUrl != null && splitUrl.length > 1) {
params = splitUrl.last;
}
final int? statusCode = data["status_code"];
if (statusCode == null) return;

final String status = statusCode < 400 ? "succeeded" : "failed";
// Assuming leaveBreadcrumb is a predefined method to log the event
leaveBreadcrumb(
"$clientName request $status",
metadata: {
"duration": duration,
"method": data["http_method"],
"url": splitUrl.first,
if(params.isNotEmpty)
"urlParams": params,
if(data["request_content_length"] != null && data["request_content_length"] > 0)
"requestContentLength": data["request_content_length"],
if(data["response_content_length"] != null && data["response_content_length"] > 0)
"responseContentLength": data["response_content_length"],
if (params.isNotEmpty) "urlParams": params,
if (data["request_content_length"] != null &&
data["request_content_length"] > 0)
"requestContentLength": data["request_content_length"],
if (data["response_content_length"] != null &&
data["response_content_length"] > 0)
"responseContentLength": data["response_content_length"],
"status": statusCode,
},
type: BugsnagBreadcrumbType.request,
);
}
},
type: BugsnagBreadcrumbType.request,
);
}
}

}

mixin DelegateClient implements BugsnagClient {
Expand Down Expand Up @@ -742,8 +744,8 @@ class Bugsnag extends BugsnagClient with DelegateClient {
int launchDurationMillis = 5000,
bool sendLaunchCrashesSynchronously = true,
int appHangThresholdMillis = appHangThresholdFatalOnly,
Set<String> redactedKeys = const {'password'},
Set<String> discardClasses = const {},
Set<RegExp>? redactedKeys,
Set<RegExp> discardClasses = const {},
Set<String>? enabledReleaseStages,
Set<BugsnagEnabledBreadcrumbType>? enabledBreadcrumbTypes,
BugsnagProjectPackages projectPackages =
Expand Down Expand Up @@ -786,8 +788,10 @@ class Bugsnag extends BugsnagClient with DelegateClient {
'launchDurationMillis': launchDurationMillis,
'sendLaunchCrashesSynchronously': sendLaunchCrashesSynchronously,
'appHangThresholdMillis': appHangThresholdMillis,
'redactedKeys': List<String>.from(redactedKeys),
'discardClasses': List<String>.from(discardClasses),
'redactedKeys': List<dynamic>.from(redactedKeys?.map((e) => e.toJson()) ??
{RegExp('password').toJson()}),
'discardClasses':
List<dynamic>.from(discardClasses.map((e) => e.toJson())),
if (enabledReleaseStages != null)
'enabledReleaseStages': enabledReleaseStages.toList(),
'enabledBreadcrumbTypes':
Expand Down Expand Up @@ -833,7 +837,6 @@ class Bugsnag extends BugsnagClient with DelegateClient {

return const <String>{};
}

}

/// In order to determine where a crash happens Bugsnag needs to know which
Expand Down
8 changes: 8 additions & 0 deletions packages/bugsnag_flutter/lib/src/regexp_json.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
extension RegExpJSON on RegExp {
dynamic toJson() => <String, dynamic>{
'pattern': pattern,
'isDotAll': isDotAll,
'isCaseSensitive': isCaseSensitive,
'isMultiLine': isMultiLine,
};
}

0 comments on commit 0ce7510

Please sign in to comment.