diff --git a/Agent.xcodeproj/project.pbxproj b/Agent.xcodeproj/project.pbxproj index d76fdf9c..0f9a9b96 100644 --- a/Agent.xcodeproj/project.pbxproj +++ b/Agent.xcodeproj/project.pbxproj @@ -1500,6 +1500,9 @@ F8678AAE2CDBC62B008FD2A2 /* NRAutoCollectLogStressTest.m in Sources */ = {isa = PBXBuildFile; fileRef = F8678AAC2CDBC62B008FD2A2 /* NRAutoCollectLogStressTest.m */; }; F8728E412ACC9D5A0056F641 /* NRMANetworkMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F8728E402ACC9D5A0056F641 /* NRMANetworkMonitor.m */; }; F8728E422ACC9D5A0056F641 /* NRMANetworkMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = F8728E402ACC9D5A0056F641 /* NRMANetworkMonitor.m */; }; + F87925652D441AC300576F5D /* NRMAAttributeValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = F87925642D441AC300576F5D /* NRMAAttributeValidator.m */; }; + F87925662D441AC300576F5D /* NRMAAttributeValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = F87925642D441AC300576F5D /* NRMAAttributeValidator.m */; }; + F87925672D441AC300576F5D /* NRMAAttributeValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = F87925642D441AC300576F5D /* NRMAAttributeValidator.m */; }; F87954D529E89D5F00319FCD /* NRMAWKWebViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0209ABB324E704A600E45C90 /* NRMAWKWebViewTests.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; }; F88C2CF72A2FA7AC00373EFE /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F88C2CE32A2FA7AC00373EFE /* PrivacyInfo.xcprivacy */; }; F89167372BC9D1270085BCFC /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F88C2CE32A2FA7AC00373EFE /* PrivacyInfo.xcprivacy */; }; @@ -2472,6 +2475,8 @@ F8678AAC2CDBC62B008FD2A2 /* NRAutoCollectLogStressTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NRAutoCollectLogStressTest.m; sourceTree = ""; }; F8728E402ACC9D5A0056F641 /* NRMANetworkMonitor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NRMANetworkMonitor.m; sourceTree = ""; }; F8728E562ACC9F840056F641 /* NRMANetworkMonitor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NRMANetworkMonitor.h; sourceTree = ""; }; + F87925642D441AC300576F5D /* NRMAAttributeValidator.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NRMAAttributeValidator.m; sourceTree = ""; }; + F879257B2D441ACC00576F5D /* NRMAAttributeValidator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NRMAAttributeValidator.h; sourceTree = ""; }; F88C2CE32A2FA7AC00373EFE /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 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; }; F891A6BD2CB6F5C1007675F4 /* NRAutoLogCollector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NRAutoLogCollector.m; sourceTree = ""; }; @@ -3202,9 +3207,7 @@ 34544D122A3142F900B5A8D1 /* NRMAEventManager.h */, 34544D132A3142F900B5A8D1 /* NRMAEventManager.m */, 34544D2D2A32489C00B5A8D1 /* NRMAAnalyticEventProtocol.h */, - 342C4B402A3BCE9300116992 /* AttributeValidatorProtocol.h */, - 342C4B562A3BD15A00116992 /* BlockAttributeValidator.h */, - 342C4B572A3BD15A00116992 /* BlockAttributeValidator.m */, + F879257C2D441DBF00576F5D /* AttributeValidator */, F8FBFB1E2A83CEBD00CDC8C5 /* Constants.m */, F8FBFB212A83CEDC00CDC8C5 /* Constants.h */, 340D64122AA92FCE00E63CC1 /* PersistentEventStore.h */, @@ -3885,6 +3888,18 @@ path = "Logging Stress Tests"; sourceTree = ""; }; + F879257C2D441DBF00576F5D /* AttributeValidator */ = { + isa = PBXGroup; + children = ( + 342C4B402A3BCE9300116992 /* AttributeValidatorProtocol.h */, + 342C4B562A3BD15A00116992 /* BlockAttributeValidator.h */, + 342C4B572A3BD15A00116992 /* BlockAttributeValidator.m */, + F879257B2D441ACC00576F5D /* NRMAAttributeValidator.h */, + F87925642D441AC300576F5D /* NRMAAttributeValidator.m */, + ); + path = AttributeValidator; + sourceTree = ""; + }; F8FBFA3C2A71A32400CDC8C5 /* Events */ = { isa = PBXGroup; children = ( @@ -5359,6 +5374,7 @@ 02FF49EF24DC645B00115469 /* NRMAAppInstallMetricGenerator.m in Sources */, 02FF4A7724DC64FC00115469 /* NRTimer.m in Sources */, 02FF484024DC614200115469 /* NRMALastActivityTraceController.m in Sources */, + F87925652D441AC300576F5D /* NRMAAttributeValidator.m in Sources */, 02FF489924DC61D200115469 /* NRMAURLSessionTaskDelegateBase.m in Sources */, 02FF4A9F24DC652E00115469 /* NRMAMemoryVitals.m in Sources */, 02FF4AB124DC652E00115469 /* NRMAJSON.m in Sources */, @@ -5591,6 +5607,7 @@ 02FF4C0224E3201400115469 /* NRMAHTTPTransactions.m in Sources */, 02FF4C0424E3201400115469 /* NRMAMachineMeasurementConsumer.m in Sources */, 2B33E5D32AA9160E00AEB7B4 /* NRMASessionEvent.m in Sources */, + F87925662D441AC300576F5D /* NRMAAttributeValidator.m in Sources */, F8FBFA632A78416300CDC8C5 /* NRMAMobileEvent.m in Sources */, 02FF4C0524E3201400115469 /* NRMAMetric.m in Sources */, F8E202DF2B07BA61008E0B7B /* NRMAOfflineStorage.m in Sources */, @@ -5870,6 +5887,7 @@ 348232F22BC5F20B0070FAC3 /* NRMAHarvesterConfiguration.m in Sources */, 348233122BC5F21B0070FAC3 /* NRMAExceptionHandlerJSONKeys.m in Sources */, 3482333A2BC5F2490070FAC3 /* NRMAClassNode.m in Sources */, + F87925672D441AC300576F5D /* NRMAAttributeValidator.m in Sources */, 3482334C2BC5F2710070FAC3 /* NRMAURLSessionTaskSearch.m in Sources */, 348232DA2BC5F2050070FAC3 /* NRMAHarvestableTrace.m in Sources */, 3482329B2BC5F1D70070FAC3 /* NRMAMeasurementException.m in Sources */, diff --git a/Agent/Analytics/AttributeValidatorProtocol.h b/Agent/Analytics/AttributeValidator/AttributeValidatorProtocol.h similarity index 100% rename from Agent/Analytics/AttributeValidatorProtocol.h rename to Agent/Analytics/AttributeValidator/AttributeValidatorProtocol.h diff --git a/Agent/Analytics/BlockAttributeValidator.h b/Agent/Analytics/AttributeValidator/BlockAttributeValidator.h similarity index 93% rename from Agent/Analytics/BlockAttributeValidator.h rename to Agent/Analytics/AttributeValidator/BlockAttributeValidator.h index 41ca93f8..81eaeaf1 100644 --- a/Agent/Analytics/BlockAttributeValidator.h +++ b/Agent/Analytics/AttributeValidator/BlockAttributeValidator.h @@ -22,8 +22,6 @@ NS_ASSUME_NONNULL_BEGIN valueValidator:(ValueValidator)valueValidator andEventTypeValidator:(EventTypeValidator)eventTypeValidator; -+ (BlockAttributeValidator *) attributeValidator; - @end NS_ASSUME_NONNULL_END diff --git a/Agent/Analytics/AttributeValidator/BlockAttributeValidator.m b/Agent/Analytics/AttributeValidator/BlockAttributeValidator.m new file mode 100644 index 00000000..edebc175 --- /dev/null +++ b/Agent/Analytics/AttributeValidator/BlockAttributeValidator.m @@ -0,0 +1,41 @@ +// +// BlockAttributeValidator.m +// Agent +// +// Created by Steve Malsam on 6/15/23. +// Copyright © 2023 New Relic. All rights reserved. +// + +#import "BlockAttributeValidator.h" +#import "Constants.h" +#import "NRMAAnalytics.h" +#import "NRLogger.h" + +@implementation BlockAttributeValidator + +- (nonnull instancetype)initWithNameValidator:(nonnull NameValidator)nameValidator + valueValidator:(nonnull ValueValidator)valueValidator + andEventTypeValidator:(nonnull EventTypeValidator)eventTypeValidator { + self = [super init]; + if(self) { + _nameValidator = nameValidator; + _valueValidator = valueValidator; + _eventTypeValidator = eventTypeValidator; + } + + return self; +} + +- (BOOL)eventTypeValidator:(NSString *)eventType { + return self.eventTypeValidator(eventType); +} + +- (BOOL)nameValidator:(NSString *)name { + return self.nameValidator(name); +} + +- (BOOL)valueValidator:(id)value { + return self.valueValidator(value); +} + +@end diff --git a/Agent/Analytics/AttributeValidator/NRMAAttributeValidator.h b/Agent/Analytics/AttributeValidator/NRMAAttributeValidator.h new file mode 100644 index 00000000..f4d427f3 --- /dev/null +++ b/Agent/Analytics/AttributeValidator/NRMAAttributeValidator.h @@ -0,0 +1,14 @@ +// +// NRMAAttributeValidator.h +// Agent +// +// Created by Mike Bruin on 1/24/25. +// Copyright © 2025 New Relic. All rights reserved. +// + +#import +#import "AttributeValidatorProtocol.h" + +@interface NRMAAttributeValidator : NSObject + +@end diff --git a/Agent/Analytics/AttributeValidator/NRMAAttributeValidator.m b/Agent/Analytics/AttributeValidator/NRMAAttributeValidator.m new file mode 100644 index 00000000..1e88d00d --- /dev/null +++ b/Agent/Analytics/AttributeValidator/NRMAAttributeValidator.m @@ -0,0 +1,70 @@ +// +// NRMAAttributeValidator.m +// Agent +// +// Created by Mike Bruin on 1/24/25. +// Copyright © 2025 New Relic. All rights reserved. +// + +#import "NRMAAttributeValidator.h" +#import "Constants.h" +#import "NRMAAnalytics.h" +#import "NRLogger.h" + +@implementation NRMAAttributeValidator + +- (BOOL)eventTypeValidator:(NSString *)eventType { + return YES; +} + +- (BOOL)nameValidator:(NSString *)name { + if ([name length] == 0) { + NRLOG_AGENT_ERROR(@"invalid attribute: name length = 0"); + return false; + } + if ([name hasPrefix:@" "]) { + NRLOG_AGENT_ERROR(@"invalid attribute: name prefix = \" \""); + return false; + } + // check if attribute name is reserved or attribute name matches reserved prefix. + for (NSString* key in [NRMAAnalytics reservedKeywords]) { + if ([key isEqualToString:name]) { + NRLOG_AGENT_ERROR(@"invalid attribute: name prefix disallowed"); + return false; + } + } + for (NSString* key in [NRMAAnalytics reservedPrefixes]) { + if ([name hasPrefix:key]) { + NRLOG_AGENT_ERROR(@"invalid attribute: name prefix disallowed"); + return false; + } + } + + // check if attribute name exceeds max length. + if ([name length] > kNRMA_Attrib_Max_Name_Length) { + NRLOG_AGENT_ERROR(@"invalid attribute: name length exceeds limit"); + return false; + } + return true; +} + +- (BOOL)valueValidator:(id)value { + if ([value isKindOfClass:[NSString class]]) { + if ([(NSString*)value length] == 0) { + NRLOG_AGENT_ERROR(@"invalid attribute: value length = 0"); + return false; + } + else if ([(NSString*)value length] >= kNRMA_Attrib_Max_Value_Size_Bytes) { + NRLOG_AGENT_ERROR(@"invalid attribute: value exceeded maximum byte size exceeded"); + return false; + } + } + if (value == nil || [value isKindOfClass:[NSNull class]]) { + NRLOG_AGENT_ERROR(@"invalid attribute: value cannot be nil"); + return false; + } + + return true; +} + +@end diff --git a/Agent/Analytics/BlockAttributeValidator.m b/Agent/Analytics/BlockAttributeValidator.m deleted file mode 100644 index f3d5123f..00000000 --- a/Agent/Analytics/BlockAttributeValidator.m +++ /dev/null @@ -1,101 +0,0 @@ -// -// BlockAttributeValidator.m -// Agent -// -// Created by Steve Malsam on 6/15/23. -// Copyright © 2023 New Relic. All rights reserved. -// - -#import "BlockAttributeValidator.h" -#import "Constants.h" -#import "NRMAAnalytics.h" -#import "NRLogger.h" - -@implementation BlockAttributeValidator - -- (nonnull instancetype)initWithNameValidator:(nonnull NameValidator)nameValidator - valueValidator:(nonnull ValueValidator)valueValidator - andEventTypeValidator:(nonnull EventTypeValidator)eventTypeValidator { - self = [super init]; - if(self) { - _nameValidator = nameValidator; - _valueValidator = valueValidator; - _eventTypeValidator = eventTypeValidator; - } - - return self; -} - -- (BOOL)eventTypeValidator:(NSString *)eventType { - return self.eventTypeValidator(eventType); -} - -- (BOOL)nameValidator:(NSString *)name { - return self.nameValidator(name); -} - -- (BOOL)valueValidator:(id)value { - return self.valueValidator(value); -} - -static BlockAttributeValidator *_attributeValidator; -+ (BlockAttributeValidator *) attributeValidator -{ - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - _attributeValidator = [[BlockAttributeValidator alloc] initWithNameValidator:^BOOL(NSString *name) { - if ([name length] == 0) { - NRLOG_AGENT_ERROR(@"invalid attribute: name length = 0"); - return false; - } - if ([name hasPrefix:@" "]) { - NRLOG_AGENT_ERROR(@"invalid attribute: name prefix = \" \""); - return false; - } - // check if attribute name is reserved or attribute name matches reserved prefix. - for (NSString* key in [NRMAAnalytics reservedKeywords]) { - if ([key isEqualToString:name]) { - NRLOG_AGENT_ERROR(@"invalid attribute: name prefix disallowed"); - return false; - } - } - for (NSString* key in [NRMAAnalytics reservedPrefixes]) { - if ([name hasPrefix:key]) { - NRLOG_AGENT_ERROR(@"invalid attribute: name prefix disallowed"); - return false; - } - } - - // check if attribute name exceeds max length. - if ([name length] > kNRMA_Attrib_Max_Name_Length) { - NRLOG_AGENT_ERROR(@"invalid attribute: name length exceeds limit"); - return false; - } - return true; - - } valueValidator:^BOOL(id value) { - if ([value isKindOfClass:[NSString class]]) { - if ([(NSString*)value length] == 0) { - NRLOG_AGENT_ERROR(@"invalid attribute: value length = 0"); - return false; - } - else if ([(NSString*)value length] >= kNRMA_Attrib_Max_Value_Size_Bytes) { - NRLOG_AGENT_ERROR(@"invalid attribute: value exceeded maximum byte size exceeded"); - return false; - } - } - if (value == nil || [value isKindOfClass:[NSNull class]]) { - NRLOG_AGENT_ERROR(@"invalid attribute: value cannot be nil"); - return false; - } - - return true; - } andEventTypeValidator:^BOOL(NSString *eventType) { - return YES; - }]; - }); - - return _attributeValidator; -} - -@end diff --git a/Agent/Analytics/NRMAAnalytics.mm b/Agent/Analytics/NRMAAnalytics.mm index a3543853..16fa2c4f 100644 --- a/Agent/Analytics/NRMAAnalytics.mm +++ b/Agent/Analytics/NRMAAnalytics.mm @@ -29,7 +29,7 @@ #import "NRMAPayload.h" #import "NRMANetworkErrorEvent.h" #import "NRMASAM.h" -#import "BlockAttributeValidator.h" +#import "NRMAAttributeValidator.h" #import "NRMASessionEvent.h" //******************* THIS FILE HAS ARC DISABLED ******************* @@ -116,7 +116,7 @@ - (id) initWithSessionStartTimeMS:(long long) sessionStartTime { PersistentEventStore *eventStore = [[PersistentEventStore alloc] initWithFilename:filename andMinimumDelay:.025]; _eventManager = [[NRMAEventManager alloc] initWithPersistentStore:eventStore]; - _attributeValidator = [BlockAttributeValidator attributeValidator]; + _attributeValidator = [[NRMAAttributeValidator alloc] init]; _sessionAttributeManager = [[NRMASAM alloc] initWithAttributeValidator:_attributeValidator]; NSString* attributes = [self sessionAttributeJSONString]; @@ -207,6 +207,7 @@ - (void) dealloc { [__eventTypeRegex release]; [_eventManager dealloc]; [_sessionAttributeManager dealloc]; + [_attributeValidator release]; [_sessionStartTime release]; [super dealloc]; diff --git a/Agent/HandledException/NRMAHandledExceptions.mm b/Agent/HandledException/NRMAHandledExceptions.mm index c55344e5..be2d7243 100644 --- a/Agent/HandledException/NRMAHandledExceptions.mm +++ b/Agent/HandledException/NRMAHandledExceptions.mm @@ -22,7 +22,7 @@ #import "NRMABool.h" #import "NRMASupportMetricHelper.h" #import "Constants.h" -#import "BlockAttributeValidator.h" +#import "NRMAAttributeValidator.h" @interface NRMAAnalytics(Protected) // Because the NRMAAnalytics class interfaces with non Objective-C++ files, we cannot expose the API on the header. Therefore, we must use this reference. @@ -40,6 +40,7 @@ @implementation NRMAHandledExceptions { NewRelic::Hex::HexUploadPublisher* _publisher; NRMAAnalytics *analyticsParent; + id _attributeValidator; } - (void) dealloc { @@ -50,6 +51,7 @@ - (void) dealloc { self.sessionId = nil; self.sessionStartDate = nil; + _attributeValidator = nil; [super dealloc]; } @@ -72,6 +74,8 @@ - (instancetype) initWithAnalyticsController:(NRMAAnalytics*)analytics self = [super init]; if (self) { analyticsParent = analytics; + + _attributeValidator = [[NRMAAttributeValidator alloc] init]; _analytics = std::shared_ptr([analytics analyticsController]); self.sessionStartDate = sessionStartDate; @@ -199,7 +203,8 @@ - (void) recordError:(NSError * _Nonnull)error resultMap, [self createThreadVector:callstack length:frames] ); - NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[BlockAttributeValidator attributeValidator]] autorelease]; + + NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease]; if (attributes != nil) { [contextAdapter addAttributesNewValidation:attributes]; @@ -219,7 +224,7 @@ - (void) recordError:(NSError * _Nonnull)error [self createThreadVector:callstack length:frames] ); - NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[BlockAttributeValidator attributeValidator]] autorelease]; + NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease]; if (attributes != nil) { [contextAdapter addAttributes:attributes]; @@ -269,7 +274,7 @@ - (void) recordHandledException:(NSException*)exception [self checkOffline:report]; - NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[BlockAttributeValidator attributeValidator]] autorelease]; + NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease]; if (attributes != nil) { [contextAdapter addAttributesNewValidation:attributes]; @@ -288,7 +293,7 @@ - (void) recordHandledException:(NSException*)exception [self checkOffline:report]; - NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[BlockAttributeValidator attributeValidator]] autorelease]; + NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease]; if (attributes != nil) { [contextAdapter addAttributes:attributes]; @@ -372,7 +377,7 @@ - (void) recordHandledExceptionWithStackTrace:(NSDictionary*)exceptionDictionary report->setAttributeNoValidation("timeSinceLoad", [[[NSDate new] autorelease] timeIntervalSinceDate:self.sessionStartDate]); [self checkOffline:report]; - NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[BlockAttributeValidator attributeValidator]] autorelease]; + NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease]; if (exceptionDictionary != nil) { [contextAdapter addAttributesNewValidation:exceptionDictionary]; @@ -389,7 +394,7 @@ - (void) recordHandledExceptionWithStackTrace:(NSDictionary*)exceptionDictionary report->setAttribute("timeSinceLoad", [[[NSDate new] autorelease] timeIntervalSinceDate:self.sessionStartDate]); [self checkOffline:report]; - NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:[BlockAttributeValidator attributeValidator]] autorelease]; + NRMAExceptionReportAdaptor* contextAdapter = [[[NRMAExceptionReportAdaptor alloc] initWithReport:report attributeValidator:_attributeValidator] autorelease]; if (exceptionDictionary != nil) { [contextAdapter addAttributes:exceptionDictionary];