Skip to content

Commit 6b814a2

Browse files
authored
NR-359870 fix for the attribute validator crash (#340)
* NR-359870 moved the attribute validator to utils so it can be static and used anywhere it's needed * moved the attribute validator to the BlockAttributeValidator class to try to address concerns. * Moved from the BlockAttributeValidator to an attribute validator that's it's own class * Changed it to pass the AttributeValidator in as a parameter to the handled exception, so it can be replaced for testing
1 parent 9b5f166 commit 6b814a2

File tree

12 files changed

+166
-82
lines changed

12 files changed

+166
-82
lines changed

Agent.xcodeproj/project.pbxproj

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,9 @@
15001500
F8678AAE2CDBC62B008FD2A2 /* NRAutoCollectLogStressTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F8678AAC2CDBC62B008FD2A2 /* NRAutoCollectLogStressTest.m */; };
15011501
F8728E412ACC9D5A0056F641 /* NRMANetworkMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F8728E402ACC9D5A0056F641 /* NRMANetworkMonitor.m */; };
15021502
F8728E422ACC9D5A0056F641 /* NRMANetworkMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F8728E402ACC9D5A0056F641 /* NRMANetworkMonitor.m */; };
1503+
F87925652D441AC300576F5D /* NRMAAttributeValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = F87925642D441AC300576F5D /* NRMAAttributeValidator.m */; };
1504+
F87925662D441AC300576F5D /* NRMAAttributeValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = F87925642D441AC300576F5D /* NRMAAttributeValidator.m */; };
1505+
F87925672D441AC300576F5D /* NRMAAttributeValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = F87925642D441AC300576F5D /* NRMAAttributeValidator.m */; };
15031506
F87954D529E89D5F00319FCD /* NRMAWKWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0209ABB324E704A600E45C90 /* NRMAWKWebViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
15041507
F88C2CF72A2FA7AC00373EFE /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F88C2CE32A2FA7AC00373EFE /* PrivacyInfo.xcprivacy */; };
15051508
F89167372BC9D1270085BCFC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F88C2CE32A2FA7AC00373EFE /* PrivacyInfo.xcprivacy */; };
@@ -2472,6 +2475,8 @@
24722475
F8678AAC2CDBC62B008FD2A2 /* NRAutoCollectLogStressTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NRAutoCollectLogStressTest.m; sourceTree = "<group>"; };
24732476
F8728E402ACC9D5A0056F641 /* NRMANetworkMonitor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NRMANetworkMonitor.m; sourceTree = "<group>"; };
24742477
F8728E562ACC9F840056F641 /* NRMANetworkMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NRMANetworkMonitor.h; sourceTree = "<group>"; };
2478+
F87925642D441AC300576F5D /* NRMAAttributeValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NRMAAttributeValidator.m; sourceTree = "<group>"; };
2479+
F879257B2D441ACC00576F5D /* NRMAAttributeValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NRMAAttributeValidator.h; sourceTree = "<group>"; };
24752480
F88C2CE32A2FA7AC00373EFE /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
24762481
F89167382BC9D1540085BCFC /* libc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libc++.tbd"; path = "Platforms/WatchOS.platform/Developer/SDKs/WatchOS10.2.sdk/usr/lib/libc++.tbd"; sourceTree = DEVELOPER_DIR; };
24772482
F891A6BD2CB6F5C1007675F4 /* NRAutoLogCollector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NRAutoLogCollector.m; sourceTree = "<group>"; };
@@ -3202,9 +3207,7 @@
32023207
34544D122A3142F900B5A8D1 /* NRMAEventManager.h */,
32033208
34544D132A3142F900B5A8D1 /* NRMAEventManager.m */,
32043209
34544D2D2A32489C00B5A8D1 /* NRMAAnalyticEventProtocol.h */,
3205-
342C4B402A3BCE9300116992 /* AttributeValidatorProtocol.h */,
3206-
342C4B562A3BD15A00116992 /* BlockAttributeValidator.h */,
3207-
342C4B572A3BD15A00116992 /* BlockAttributeValidator.m */,
3210+
F879257C2D441DBF00576F5D /* AttributeValidator */,
32083211
F8FBFB1E2A83CEBD00CDC8C5 /* Constants.m */,
32093212
F8FBFB212A83CEDC00CDC8C5 /* Constants.h */,
32103213
340D64122AA92FCE00E63CC1 /* PersistentEventStore.h */,
@@ -3885,6 +3888,18 @@
38853888
path = "Logging Stress Tests";
38863889
sourceTree = "<group>";
38873890
};
3891+
F879257C2D441DBF00576F5D /* AttributeValidator */ = {
3892+
isa = PBXGroup;
3893+
children = (
3894+
342C4B402A3BCE9300116992 /* AttributeValidatorProtocol.h */,
3895+
342C4B562A3BD15A00116992 /* BlockAttributeValidator.h */,
3896+
342C4B572A3BD15A00116992 /* BlockAttributeValidator.m */,
3897+
F879257B2D441ACC00576F5D /* NRMAAttributeValidator.h */,
3898+
F87925642D441AC300576F5D /* NRMAAttributeValidator.m */,
3899+
);
3900+
path = AttributeValidator;
3901+
sourceTree = "<group>";
3902+
};
38883903
F8FBFA3C2A71A32400CDC8C5 /* Events */ = {
38893904
isa = PBXGroup;
38903905
children = (
@@ -5359,6 +5374,7 @@
53595374
02FF49EF24DC645B00115469 /* NRMAAppInstallMetricGenerator.m in Sources */,
53605375
02FF4A7724DC64FC00115469 /* NRTimer.m in Sources */,
53615376
02FF484024DC614200115469 /* NRMALastActivityTraceController.m in Sources */,
5377+
F87925652D441AC300576F5D /* NRMAAttributeValidator.m in Sources */,
53625378
02FF489924DC61D200115469 /* NRMAURLSessionTaskDelegateBase.m in Sources */,
53635379
02FF4A9F24DC652E00115469 /* NRMAMemoryVitals.m in Sources */,
53645380
02FF4AB124DC652E00115469 /* NRMAJSON.m in Sources */,
@@ -5591,6 +5607,7 @@
55915607
02FF4C0224E3201400115469 /* NRMAHTTPTransactions.m in Sources */,
55925608
02FF4C0424E3201400115469 /* NRMAMachineMeasurementConsumer.m in Sources */,
55935609
2B33E5D32AA9160E00AEB7B4 /* NRMASessionEvent.m in Sources */,
5610+
F87925662D441AC300576F5D /* NRMAAttributeValidator.m in Sources */,
55945611
F8FBFA632A78416300CDC8C5 /* NRMAMobileEvent.m in Sources */,
55955612
02FF4C0524E3201400115469 /* NRMAMetric.m in Sources */,
55965613
F8E202DF2B07BA61008E0B7B /* NRMAOfflineStorage.m in Sources */,
@@ -5870,6 +5887,7 @@
58705887
348232F22BC5F20B0070FAC3 /* NRMAHarvesterConfiguration.m in Sources */,
58715888
348233122BC5F21B0070FAC3 /* NRMAExceptionHandlerJSONKeys.m in Sources */,
58725889
3482333A2BC5F2490070FAC3 /* NRMAClassNode.m in Sources */,
5890+
F87925672D441AC300576F5D /* NRMAAttributeValidator.m in Sources */,
58735891
3482334C2BC5F2710070FAC3 /* NRMAURLSessionTaskSearch.m in Sources */,
58745892
348232DA2BC5F2050070FAC3 /* NRMAHarvestableTrace.m in Sources */,
58755893
3482329B2BC5F1D70070FAC3 /* NRMAMeasurementException.m in Sources */,

Agent/Analytics/BlockAttributeValidator.m renamed to Agent/Analytics/AttributeValidator/BlockAttributeValidator.m

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
//
88

99
#import "BlockAttributeValidator.h"
10+
#import "Constants.h"
11+
#import "NRMAAnalytics.h"
12+
#import "NRLogger.h"
1013

1114
@implementation BlockAttributeValidator
1215

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// NRMAAttributeValidator.h
3+
// Agent
4+
//
5+
// Created by Mike Bruin on 1/24/25.
6+
// Copyright © 2025 New Relic. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
#import "AttributeValidatorProtocol.h"
11+
12+
@interface NRMAAttributeValidator : NSObject <AttributeValidatorProtocol>
13+
14+
@end
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// NRMAAttributeValidator.m
3+
// Agent
4+
//
5+
// Created by Mike Bruin on 1/24/25.
6+
// Copyright © 2025 New Relic. All rights reserved.
7+
//
8+
9+
#import "NRMAAttributeValidator.h"
10+
#import "Constants.h"
11+
#import "NRMAAnalytics.h"
12+
#import "NRLogger.h"
13+
14+
@implementation NRMAAttributeValidator
15+
16+
- (BOOL)eventTypeValidator:(NSString *)eventType {
17+
return YES;
18+
}
19+
20+
- (BOOL)nameValidator:(NSString *)name {
21+
if ([name length] == 0) {
22+
NRLOG_AGENT_ERROR(@"invalid attribute: name length = 0");
23+
return false;
24+
}
25+
if ([name hasPrefix:@" "]) {
26+
NRLOG_AGENT_ERROR(@"invalid attribute: name prefix = \" \"");
27+
return false;
28+
}
29+
// check if attribute name is reserved or attribute name matches reserved prefix.
30+
for (NSString* key in [NRMAAnalytics reservedKeywords]) {
31+
if ([key isEqualToString:name]) {
32+
NRLOG_AGENT_ERROR(@"invalid attribute: name prefix disallowed");
33+
return false;
34+
}
35+
}
36+
for (NSString* key in [NRMAAnalytics reservedPrefixes]) {
37+
if ([name hasPrefix:key]) {
38+
NRLOG_AGENT_ERROR(@"invalid attribute: name prefix disallowed");
39+
return false;
40+
}
41+
}
42+
43+
// check if attribute name exceeds max length.
44+
if ([name length] > kNRMA_Attrib_Max_Name_Length) {
45+
NRLOG_AGENT_ERROR(@"invalid attribute: name length exceeds limit");
46+
return false;
47+
}
48+
return true;
49+
}
50+
51+
- (BOOL)valueValidator:(id)value {
52+
if ([value isKindOfClass:[NSString class]]) {
53+
if ([(NSString*)value length] == 0) {
54+
NRLOG_AGENT_ERROR(@"invalid attribute: value length = 0");
55+
return false;
56+
}
57+
else if ([(NSString*)value length] >= kNRMA_Attrib_Max_Value_Size_Bytes) {
58+
NRLOG_AGENT_ERROR(@"invalid attribute: value exceeded maximum byte size exceeded");
59+
return false;
60+
}
61+
}
62+
if (value == nil || [value isKindOfClass:[NSNull class]]) {
63+
NRLOG_AGENT_ERROR(@"invalid attribute: value cannot be nil");
64+
return false;
65+
}
66+
67+
return true;
68+
}
69+
70+
@end

Agent/Analytics/NRMAAnalytics.mm

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
#import "NRMAPayload.h"
3030
#import "NRMANetworkErrorEvent.h"
3131
#import "NRMASAM.h"
32-
#import "BlockAttributeValidator.h"
32+
#import "NRMAAttributeValidator.h"
3333
#import "NRMASessionEvent.h"
3434

3535
//******************* THIS FILE HAS ARC DISABLED *******************
@@ -116,59 +116,9 @@ - (id) initWithSessionStartTimeMS:(long long) sessionStartTime {
116116
PersistentEventStore *eventStore = [[PersistentEventStore alloc] initWithFilename:filename andMinimumDelay:.025];
117117

118118
_eventManager = [[NRMAEventManager alloc] initWithPersistentStore:eventStore];
119-
_attributeValidator = [[BlockAttributeValidator alloc] initWithNameValidator:^BOOL(NSString *name) {
120-
if ([name length] == 0) {
121-
NRLOG_AGENT_ERROR(@"invalid attribute: name length = 0");
122-
return false;
123-
}
124-
if ([name hasPrefix:@" "]) {
125-
NRLOG_AGENT_ERROR(@"invalid attribute: name prefix = \" \"");
126-
return false;
127-
}
128-
// check if attribute name is reserved or attribute name matches reserved prefix.
129-
for (NSString* key in [NRMAAnalytics reservedKeywords]) {
130-
if ([key isEqualToString:name]) {
131-
NRLOG_AGENT_ERROR(@"invalid attribute: name prefix disallowed");
132-
return false;
133-
}
134-
}
135-
for (NSString* key in [NRMAAnalytics reservedPrefixes]) {
136-
if ([name hasPrefix:key]) {
137-
NRLOG_AGENT_ERROR(@"invalid attribute: name prefix disallowed");
138-
return false;
139-
}
140-
}
141-
142-
// check if attribute name exceeds max length.
143-
if ([name length] > kNRMA_Attrib_Max_Name_Length) {
144-
NRLOG_AGENT_ERROR(@"invalid attribute: name length exceeds limit");
145-
return false;
146-
}
147-
return true;
148-
149-
} valueValidator:^BOOL(id value) {
150-
if ([value isKindOfClass:[NSString class]]) {
151-
if ([(NSString*)value length] == 0) {
152-
NRLOG_AGENT_ERROR(@"invalid attribute: value length = 0");
153-
return false;
154-
}
155-
else if ([(NSString*)value length] >= kNRMA_Attrib_Max_Value_Size_Bytes) {
156-
NRLOG_AGENT_ERROR(@"invalid attribute: value exceeded maximum byte size exceeded");
157-
return false;
158-
}
159-
}
160-
if (value == nil || [value isKindOfClass:[NSNull class]]) {
161-
NRLOG_AGENT_ERROR(@"invalid attribute: value cannot be nil");
162-
return false;
163-
}
164-
165-
return true;
166-
} andEventTypeValidator:^BOOL(NSString *eventType) {
167-
return YES;
168-
}];
119+
_attributeValidator = [[NRMAAttributeValidator alloc] init];
169120
_sessionAttributeManager = [[NRMASAM alloc] initWithAttributeValidator:_attributeValidator];
170121

171-
172122
NSString* attributes = [self sessionAttributeJSONString];
173123
if (attributes != nil && [attributes length] > 0) {
174124
NSDictionary* dictionary = [NSJSONSerialization JSONObjectWithData:[attributes dataUsingEncoding:NSUTF8StringEncoding]

Agent/General/NewRelicAgentInternal.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
#import "NRMAUDIDManager.h"
5555
#import "NRMASupportMetricHelper.h"
5656
#import "NRAutoLogCollector.h"
57+
#import "NRMAAttributeValidator.h"
5758

5859

5960
// Support for teardown and re-setup of the agent within a process lifetime for our test harness
@@ -568,7 +569,8 @@ - (void) onSessionStart {
568569
sessionStartTime:self.appSessionStartDate
569570
agentConfiguration:self.agentConfiguration
570571
platform:[NewRelicInternalUtils osName]
571-
sessionId:[self currentSessionId]];
572+
sessionId:[self currentSessionId]
573+
attributeValidator:[[NRMAAttributeValidator alloc] init]];
572574

573575
if (status != NotReachable) { // Because we support offline mode check if we're online before sending the handled exceptions
574576
[self.handledExceptionsController processAndPublishPersistedReports];

Agent/HandledException/NRMAHandledExceptions.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ extern const NSString* kHexBackupStoreFolder;
2424
sessionStartTime:(NSDate*)sessionStartDate
2525
agentConfiguration:(NRMAAgentConfiguration*)agentConfiguration
2626
platform:(NSString*)platform
27-
sessionId:(NSString*)sessionId;
27+
sessionId:(NSString*)sessionId
28+
attributeValidator:(id<AttributeValidatorProtocol>) attributeValidator;
2829

2930
- (void) recordHandledException:(NSException*) exception
3031
attributes:(NSDictionary* _Nullable)attributes;

Agent/HandledException/NRMAHandledExceptions.mm

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#import "NRMABool.h"
2323
#import "NRMASupportMetricHelper.h"
2424
#import "Constants.h"
25+
#import "NRMAAttributeValidator.h"
2526

2627
@interface NRMAAnalytics(Protected)
2728
// Because the NRMAAnalytics class interfaces with non Objective-C++ files, we cannot expose the API on the header. Therefore, we must use this reference.
@@ -39,6 +40,7 @@ @implementation NRMAHandledExceptions {
3940
NewRelic::Hex::HexUploadPublisher* _publisher;
4041

4142
NRMAAnalytics *analyticsParent;
43+
id<AttributeValidatorProtocol> _attributeValidator;
4244
}
4345

4446
- (void) dealloc {
@@ -49,6 +51,7 @@ - (void) dealloc {
4951

5052
self.sessionId = nil;
5153
self.sessionStartDate = nil;
54+
[_attributeValidator release];
5255

5356
[super dealloc];
5457
}
@@ -57,7 +60,8 @@ - (instancetype) initWithAnalyticsController:(NRMAAnalytics*)analytics
5760
sessionStartTime:(NSDate*)sessionStartDate
5861
agentConfiguration:(NRMAAgentConfiguration*)agentConfiguration
5962
platform:(NSString*)platform
60-
sessionId:(NSString*)sessionId {
63+
sessionId:(NSString*)sessionId
64+
attributeValidator:(id<AttributeValidatorProtocol>) attributeValidator {
6165
if (analytics == nil || sessionStartDate == nil || [agentConfiguration applicationToken] == nil || platform == nil || sessionId == nil) {
6266
NSMutableArray* missingParams = [[NSMutableArray new] autorelease];
6367
if ([agentConfiguration applicationToken] == nil) [missingParams addObject:@"appToken"];
@@ -71,6 +75,8 @@ - (instancetype) initWithAnalyticsController:(NRMAAnalytics*)analytics
7175
self = [super init];
7276
if (self) {
7377
analyticsParent = analytics;
78+
79+
_attributeValidator = [attributeValidator retain];
7480

7581
_analytics = std::shared_ptr<NewRelic::AnalyticsController>([analytics analyticsController]);
7682
self.sessionStartDate = sessionStartDate;
@@ -198,7 +204,8 @@ - (void) recordError:(NSError * _Nonnull)error
198204
resultMap,
199205
[self createThreadVector:callstack length:frames]
200206
);
201-
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[analyticsParent getAttributeValidator]] autorelease];
207+
208+
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease];
202209

203210
if (attributes != nil) {
204211
[contextAdapter addAttributesNewValidation:attributes];
@@ -218,7 +225,7 @@ - (void) recordError:(NSError * _Nonnull)error
218225
[self createThreadVector:callstack length:frames]
219226
);
220227

221-
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[analyticsParent getAttributeValidator]] autorelease];
228+
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease];
222229

223230
if (attributes != nil) {
224231
[contextAdapter addAttributes:attributes];
@@ -268,7 +275,7 @@ - (void) recordHandledException:(NSException*)exception
268275

269276
[self checkOffline:report];
270277

271-
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[analyticsParent getAttributeValidator]] autorelease];
278+
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease];
272279

273280
if (attributes != nil) {
274281
[contextAdapter addAttributesNewValidation:attributes];
@@ -287,7 +294,7 @@ - (void) recordHandledException:(NSException*)exception
287294

288295
[self checkOffline:report];
289296

290-
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[analyticsParent getAttributeValidator]] autorelease];
297+
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease];
291298

292299
if (attributes != nil) {
293300
[contextAdapter addAttributes:attributes];
@@ -371,7 +378,7 @@ - (void) recordHandledExceptionWithStackTrace:(NSDictionary*)exceptionDictionary
371378
report->setAttributeNoValidation("timeSinceLoad", [[[NSDate new] autorelease] timeIntervalSinceDate:self.sessionStartDate]);
372379
[self checkOffline:report];
373380

374-
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[analyticsParent getAttributeValidator]] autorelease];
381+
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease];
375382

376383
if (exceptionDictionary != nil) {
377384
[contextAdapter addAttributesNewValidation:exceptionDictionary];
@@ -388,7 +395,7 @@ - (void) recordHandledExceptionWithStackTrace:(NSDictionary*)exceptionDictionary
388395
report->setAttribute("timeSinceLoad", [[[NSDate new] autorelease] timeIntervalSinceDate:self.sessionStartDate]);
389396
[self checkOffline:report];
390397

391-
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[analyticsParent getAttributeValidator]] autorelease];
398+
NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease];
392399

393400
if (exceptionDictionary != nil) {
394401
[contextAdapter addAttributes:exceptionDictionary];

0 commit comments

Comments
 (0)