diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice.mm b/src/darwin/Framework/CHIP/MTRBaseDevice.mm index 2f75038eda97a2..3868060175bb1b 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice.mm +++ b/src/darwin/Framework/CHIP/MTRBaseDevice.mm @@ -23,6 +23,7 @@ #import "MTRCluster.h" #import "MTRClusterStateCacheContainer_Internal.h" #import "MTRCluster_Internal.h" +#import "MTRDeviceDataValidation.h" #import "MTRDevice_Internal.h" #import "MTRError_Internal.h" #import "MTREventTLVValueDecoder_Internal.h" diff --git a/src/darwin/Framework/CHIP/MTRBaseDevice_Internal.h b/src/darwin/Framework/CHIP/MTRBaseDevice_Internal.h index 075ee99da20990..fa766463e3e025 100644 --- a/src/darwin/Framework/CHIP/MTRBaseDevice_Internal.h +++ b/src/darwin/Framework/CHIP/MTRBaseDevice_Internal.h @@ -18,7 +18,7 @@ #import "MTRBaseDevice.h" #import -#import "MTRDeviceDataValueDictionary.h" +#import "MTRDefines_Internal.h" #include #include diff --git a/src/darwin/Framework/CHIP/MTRDefines_Internal.h b/src/darwin/Framework/CHIP/MTRDefines_Internal.h index 71a03aa09184c5..ba7d6be51d61f5 100644 --- a/src/darwin/Framework/CHIP/MTRDefines_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDefines_Internal.h @@ -65,6 +65,11 @@ typedef struct {} variable_hidden_by_mtr_hide; // Default timed interaction timeout, in ms, if another one is not provided. #define MTR_DEFAULT_TIMED_INTERACTION_TIMEOUT_MS 10000 +// Useful building block for type-checking machinery. Uses C-style cast so it +// can be used in .m files as well. +#define MTR_SAFE_CAST(object, classname) \ + ([object isKindOfClass:[classname class]] ? (classname *) (object) : nil) + #pragma mark - XPC Defines #define MTR_SIMPLE_REMOTE_XPC_GETTER(XPC_CONNECTION, NAME, TYPE, DEFAULT_VALUE, GETTER_NAME, PREFIX) \ @@ -179,3 +184,15 @@ typedef struct {} variable_hidden_by_mtr_hide; #define MTR_ABSTRACT_METHOD() \ _MTR_ABSTRACT_METHOD_IMPL("%@ or some ancestor must implement %@", self.class, NSStringFromSelector(_cmd)) + +#pragma mark - Typedefs for some commonly used types. + +/** + * A data-value as defined in MTRBaseDevice.h. + */ +typedef NSDictionary * MTRDeviceDataValueDictionary; + +/** + * A response-value as defined in MTRBaseDevice.h. + */ +typedef NSDictionary * MTRDeviceResponseValueDictionary; diff --git a/src/darwin/Framework/CHIP/MTRDevice.mm b/src/darwin/Framework/CHIP/MTRDevice.mm index e887e360e57196..2337d0eae625f5 100644 --- a/src/darwin/Framework/CHIP/MTRDevice.mm +++ b/src/darwin/Framework/CHIP/MTRDevice.mm @@ -25,7 +25,6 @@ #import "MTRConversion.h" #import "MTRDefines_Internal.h" #import "MTRDeviceController_Internal.h" -#import "MTRDeviceDataValueDictionary.h" #import "MTRDevice_Internal.h" #import "MTRError_Internal.h" #import "MTRLogging_Internal.h" @@ -759,248 +758,3 @@ - (void)invokeCommandWithEndpointID:(NSNumber *)endpointID } @end - -static BOOL MTRArrayIsStructurallyValid(NSArray * input, NSArray * structure) -{ - if (structure.count != 1) { - // Unexpected structure. Just claim NO; later we might give meaning to - // such things. - MTR_LOG_ERROR("Don't know how to make sense of array structure with more than one element: %@", structure); - return NO; - } - - id elementStructure = structure[0]; - for (id inputElement in input) { - if (!MTRInputIsStructurallyValid(inputElement, elementStructure)) { - MTR_LOG_ERROR("Array element not structurally valid: %@, %@", inputElement, elementStructure); - return NO; - } - } - return YES; -} - -static BOOL MTRDictionaryIsStructurallyValid(NSDictionary * input, NSDictionary * structure) -{ - for (id key in structure) { - id inputValue = input[key]; - if (!inputValue) { - MTR_LOG_ERROR("Input does not have key %@: %@, %@", key, input, structure); - return NO; - } - id valueStructure = structure[key]; - if (!MTRInputIsStructurallyValid(inputValue, valueStructure)) { - MTR_LOG_ERROR("Dictionary value not structurally valid: %@, %@", inputValue, valueStructure); - return NO; - } - } - - return YES; -} - -static BOOL MTRAttributeReportIsStructurallyValid(id input) -{ - // Input is an array of values, but they could have slightly different - // structures, so we can't do a single MTRInputIsStructurallyValid to check - // that. Check the items one at a time. - if (![input isKindOfClass:NSArray.class]) { - MTR_LOG_ERROR("Attribute report is not an array: %@", input); - return NO; - } - - NSArray * inputArray = input; - for (id item in inputArray) { - // item can be a value report or an error report. - if (!MTRInputIsStructurallyValid(item, @ { - MTRAttributePathKey : MTRAttributePath.class, - MTRDataKey : MTRInputStructureDataValue, - }) - && !MTRInputIsStructurallyValid(item, @ { - MTRAttributePathKey : MTRAttributePath.class, - MTRErrorKey : NSError.class, - })) { - MTR_LOG_ERROR("Attribute report contains a weird entry: %@", item); - return NO; - } - - // Now we know item is in fact a dictionary, and it has at least one of MTRDataKey and MTRErrorKey. Make sure it's - // not claiming both, which could confuse code that examines it. - NSDictionary * itemDictionary = item; - if (itemDictionary[MTRDataKey] != nil && itemDictionary[MTRErrorKey] != nil) { - MTR_LOG_ERROR("Attribute report contains an entry that claims to be both data and error: %@", item); - return NO; - } - } - - return YES; -} - -static BOOL MTREventReportIsStructurallyValid(id input) -{ - // Input is an array of values, but they could have slightly different - // structures, so we can't do a single MTRInputIsStructurallyValid to check - // that. Check the items one at a time. - if (![input isKindOfClass:NSArray.class]) { - MTR_LOG_ERROR("Event report is not an array: %@", input); - return NO; - } - - NSArray * inputArray = input; - for (id item in inputArray) { - // item can be a value report or an error report. - // MTREventIsHistoricalKey is claimed to be present no matter what, as - // long as MTREventPathKey is present. - if (!MTRInputIsStructurallyValid(item, @ { - MTREventPathKey : MTREventPath.class, - MTRDataKey : MTRInputStructureDataValue, - MTREventIsHistoricalKey : NSNumber.class, - }) - && !MTRInputIsStructurallyValid(item, @ { - MTREventPathKey : MTREventPath.class, - MTRErrorKey : NSError.class, - MTREventIsHistoricalKey : NSNumber.class, - })) { - MTR_LOG_ERROR("Event report contains a weird entry: %@", item); - return NO; - } - - // Now we know item is in fact a dictionary, and it has at least one of MTRDataKey and MTRErrorKey. Make sure it's - // not claiming both, which could confuse code that examines it. - NSDictionary * itemDictionary = item; - if (itemDictionary[MTRDataKey] != nil && itemDictionary[MTRErrorKey] != nil) { - MTR_LOG_ERROR("Event report contains an entry that claims to be both data and error: %@", item); - return NO; - } - - if (itemDictionary[MTRDataKey]) { - // In this case, the structure is supposed to contain some more - // values. - if (!MTRInputIsStructurallyValid(itemDictionary, @ { - MTREventNumberKey : NSNumber.class, - MTREventPriorityKey : NSNumber.class, - MTREventTimeTypeKey : NSNumber.class, - })) { - MTR_LOG_ERROR("Event report fields that depend on MTRDataKey failed validation: %@", itemDictionary); - return NO; - } - - // And check well-formedness of our timestamps. - uint64_t eventTimeType = [itemDictionary[MTREventTimeTypeKey] unsignedLongLongValue]; - switch (eventTimeType) { - case MTREventTimeTypeSystemUpTime: { - if (!MTRInputIsStructurallyValid(itemDictionary, @ { MTREventSystemUpTimeKey : NSNumber.class })) { - MTR_LOG_ERROR("Event report claims system uptime timing but does not have the time: %@", itemDictionary); - return NO; - } - break; - } - case MTREventTimeTypeTimestampDate: { - if (!MTRInputIsStructurallyValid(itemDictionary, @ { MTREventTimestampDateKey : NSDate.class })) { - MTR_LOG_ERROR("Event report claims epoch timing but does not have the time: %@", itemDictionary); - return NO; - } - break; - } - default: - MTR_LOG_ERROR("Uknown time type for event report: %@", itemDictionary); - return NO; - } - } - } - - return YES; -} - -static BOOL MTRInvokeResponseIsStructurallyValid(id input) -{ - // Input is an array with a single value that must have MTRCommandPathKey. - if (!MTRInputIsStructurallyValid(input, @[ @ { MTRCommandPathKey : MTRCommandPath.class } ])) { - MTR_LOG_ERROR("Invoke response is not an array with the right things in it: %@", input); - return NO; - } - - NSArray * inputArray = input; - if (inputArray.count != 1) { - MTR_LOG_ERROR("Invoke response is not an array with exactly one entry: %@", input); - return NO; - } - - // Note that we have already checked the one entry is a dictionary that has - // MTRCommandPathKey. - NSDictionary * responseValue = inputArray[0]; - id data = responseValue[MTRDataKey]; - id error = responseValue[MTRErrorKey]; - - if (data != nil && error != nil) { - MTR_LOG_ERROR("Invoke response claims to have both data and error: %@", responseValue); - return NO; - } - - if (error != nil) { - return MTRInputIsStructurallyValid(error, NSError.class); - } - - if (data == nil) { - // This is valid: indicates a success status response. - return YES; - } - - if (!MTRInputIsStructurallyValid(data, MTRInputStructureDataValue)) { - MTR_LOG_ERROR("Invoke response claims to have data that is not a data-value: %@", data); - return NO; - } - - // Now we know data is a dictionary (in fact a data-value). The only thing - // we promise about it is that it has type MTRStructureValueType. - NSDictionary * dataDictionary = data; - if (dataDictionary[MTRTypeKey] != MTRStructureValueType) { - MTR_LOG_ERROR("Invoke response data is not of structure type: %@", data); - return NO; - } - - return YES; -} - -BOOL MTRInputIsStructurallyValid(id input, id structure) -{ - if ([structure isEqual:MTRInputStructureDataValue]) { - return MTRDataValueDictionaryIsWellFormed(input); - } - - if ([structure isEqual:MTRInputStructureAttributeReport]) { - return MTRAttributeReportIsStructurallyValid(input); - } - - if ([structure isEqual:MTRInputStructureEventReport]) { - return MTREventReportIsStructurallyValid(input); - } - - if ([structure isEqual:MTRInputStructureInvokeResponse]) { - return MTRInvokeResponseIsStructurallyValid(input); - } - - // Literal dictionaries and arrays actually have classes that are not - // exactly NSArray.class and NSDictionary.class, so checking isKindOfClass - // against [structure class] needs to be avoided. - if ([structure isKindOfClass:NSDictionary.class] && - [input isKindOfClass:NSDictionary.class]) { - return MTRDictionaryIsStructurallyValid(input, structure); - } - - if ([structure isKindOfClass:NSArray.class] && - [input isKindOfClass:NSArray.class]) { - return MTRArrayIsStructurallyValid(input, structure); - } - - if ([structure class] != structure) { - // Not a class object, not sure what to do with it. - MTR_LOG_ERROR("Unknown structure value: %@", structure); - return NO; - } - - if (![input isKindOfClass:structure]) { - MTR_LOG_ERROR("Input not of the right class: %@, %@", input, structure); - return NO; - } - - return YES; -} diff --git a/src/darwin/Framework/CHIP/MTRDeviceClusterData.h b/src/darwin/Framework/CHIP/MTRDeviceClusterData.h index d34ada90e9a7f2..2abdc6113b44af 100644 --- a/src/darwin/Framework/CHIP/MTRDeviceClusterData.h +++ b/src/darwin/Framework/CHIP/MTRDeviceClusterData.h @@ -17,7 +17,6 @@ #import #import "MTRDefines_Internal.h" -#import "MTRDeviceDataValueDictionary.h" NS_ASSUME_NONNULL_BEGIN diff --git a/src/darwin/Framework/CHIP/MTRDeviceDataValidation.h b/src/darwin/Framework/CHIP/MTRDeviceDataValidation.h new file mode 100644 index 00000000000000..5d448d8aa4fcba --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRDeviceDataValidation.h @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import + +#import "MTRDefines_Internal.h" + +NS_ASSUME_NONNULL_BEGIN + +// Returns whether a data-value dictionary is well-formed (in the sense that all +// the types of the objects inside are as expected, so it's actually a valid +// representation of some TLV). Implemented in MTRBaseDevice.mm because that's +// where the pieces needed to implement it are, but declared here so our tests +// can see it. +MTR_EXTERN MTR_TESTABLE BOOL MTRDataValueDictionaryIsWellFormed(MTRDeviceDataValueDictionary value); + +// Returns whether the provided attribute report actually has the right sorts of +// objects in the right places. +MTR_EXTERN MTR_TESTABLE BOOL MTRAttributeReportIsWellFormed(NSArray * report); + +// Returns whether the provided event report actually has the right sorts of +// objects in the right places. +MTR_EXTERN MTR_TESTABLE BOOL MTREventReportIsWellFormed(NSArray * report); + +// Returns whether the provided invoke response actually has the right sorts of +// objects in the right places. +MTR_EXTERN MTR_TESTABLE BOOL MTRInvokeResponseIsWellFormed(NSArray * response); + +NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDeviceDataValidation.mm b/src/darwin/Framework/CHIP/MTRDeviceDataValidation.mm new file mode 100644 index 00000000000000..1b5fab06f1a002 --- /dev/null +++ b/src/darwin/Framework/CHIP/MTRDeviceDataValidation.mm @@ -0,0 +1,216 @@ +/** + * Copyright (c) 2024 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#import "MTRDeviceDataValidation.h" + +#import + +#import "MTRLogging_Internal.h" + +// MTRDataValueDictionaryIsWellFormed lives in MTRBaseDevice.mm, because it uses +// static functions defined in that file. + +#pragma mark - Helpers used by multiple validators + +#define MTR_CHECK_CLASS(className) \ + ^(className * arg) { return MTR_SAFE_CAST(arg, className) != nil; } + +// input is not known to be an NSDictionary yet on entry. +// +// expectedShape maps keys to value validator blocks. +static BOOL MTRDictionaryHasExpectedShape(NSDictionary * input, NSDictionary * expectedShape) +{ + if (!MTR_SAFE_CAST(input, NSDictionary)) { + return NO; + } + + for (id key in expectedShape) { + id value = input[key]; + if (!value) { + return NO; + } + auto validator = static_cast(expectedShape[key]); + if (!validator(value)) { + return NO; + } + } + + return YES; +} + +#pragma mark - Attribute report validation + +static const auto sAttributeDataShape = @{ + MTRAttributePathKey : MTR_CHECK_CLASS(MTRAttributePath), + MTRDataKey : (^(MTRDeviceDataValueDictionary arg) { + return MTRDataValueDictionaryIsWellFormed(arg); + }), +}; + +static const auto sAttributeErrorShape = @{ + MTRAttributePathKey : MTR_CHECK_CLASS(MTRAttributePath), + MTRErrorKey : MTR_CHECK_CLASS(NSError), +}; + +BOOL MTRAttributeReportIsWellFormed(NSArray * report) +{ + if (!MTR_SAFE_CAST(report, NSArray)) { + MTR_LOG_ERROR("Attribute report is not an array: %@", report); + return NO; + } + + for (MTRDeviceResponseValueDictionary item in report) { + // item can be a value report or an error report. + if (!MTRDictionaryHasExpectedShape(item, sAttributeDataShape) && !MTRDictionaryHasExpectedShape(item, sAttributeErrorShape)) { + MTR_LOG_ERROR("Attribute report contains a weird entry: %@", item); + return NO; + } + + // Now we know item is in fact a dictionary, and it has at least one of MTRDataKey and MTRErrorKey. Make sure it's + // not claiming both, which could confuse code that examines it. + if (item[MTRDataKey] != nil && item[MTRErrorKey] != nil) { + MTR_LOG_ERROR("Attribute report contains an entry that claims to be both data and error: %@", item); + return NO; + } + } + + return YES; +} + +#pragma mark - Event report validation + +// MTREventIsHistoricalKey is claimed to be present no matter what, as +// long as MTREventPathKey is present. +static const auto sEventDataShape = @{ + MTREventPathKey : MTR_CHECK_CLASS(MTREventPath), + MTRDataKey : (^(MTRDeviceDataValueDictionary arg) { + return MTRDataValueDictionaryIsWellFormed(arg); + }), + MTREventIsHistoricalKey : MTR_CHECK_CLASS(NSNumber), + MTREventNumberKey : MTR_CHECK_CLASS(NSNumber), + MTREventPriorityKey : MTR_CHECK_CLASS(NSNumber), + MTREventTimeTypeKey : MTR_CHECK_CLASS(NSNumber), +}; + +static const auto sEventErrorShape = @{ + MTREventPathKey : MTR_CHECK_CLASS(MTREventPath), + MTRErrorKey : MTR_CHECK_CLASS(NSError), + MTREventIsHistoricalKey : MTR_CHECK_CLASS(NSNumber), +}; + +BOOL MTREventReportIsWellFormed(NSArray * report) +{ + if (!MTR_SAFE_CAST(report, NSArray)) { + MTR_LOG_ERROR("Event report is not an array: %@", report); + return NO; + } + + for (MTRDeviceResponseValueDictionary item in report) { + // item can be a value report or an error report. + if (!MTRDictionaryHasExpectedShape(item, sEventDataShape) && !MTRDictionaryHasExpectedShape(item, sEventErrorShape)) { + MTR_LOG_ERROR("Event report contains a weird entry: %@", item); + return NO; + } + + // Now we know item is in fact a dictionary, and it has at least one of MTRDataKey and MTRErrorKey. Make sure it's + // not claiming both, which could confuse code that examines it. + if (item[MTRDataKey] != nil && item[MTRErrorKey] != nil) { + MTR_LOG_ERROR("Event report contains an entry that claims to be both data and error: %@", item); + return NO; + } + + if (item[MTRDataKey]) { + // Check well-formedness of our timestamps. Note that we have + // already validated the type of item[MTREventTimeTypeKey]. + uint64_t eventTimeType = [item[MTREventTimeTypeKey] unsignedLongLongValue]; + switch (eventTimeType) { + case MTREventTimeTypeSystemUpTime: { + if (!MTR_SAFE_CAST(item[MTREventSystemUpTimeKey], NSNumber)) { + MTR_LOG_ERROR("Event report claims system uptime timing but does not have the time: %@", item); + return NO; + } + break; + } + case MTREventTimeTypeTimestampDate: { + if (!MTR_SAFE_CAST(item[MTREventTimestampDateKey], NSDate)) { + MTR_LOG_ERROR("Event report claims epoch timing but does not have the time: %@", item); + return NO; + } + break; + } + default: + MTR_LOG_ERROR("Uknown time type for event report: %@", item); + return NO; + } + } + } + + return YES; +} + +#pragma mark - Invoke response validation + +BOOL MTRInvokeResponseIsWellFormed(NSArray * response) +{ + if (!MTR_SAFE_CAST(response, NSArray)) { + MTR_LOG_ERROR("Invoke response is not an array: %@", response); + return NO; + } + + // Input is an array with a single value that must have MTRCommandPathKey. + if (response.count != 1) { + MTR_LOG_ERROR("Invoke response is not an array with exactly one entry: %@", response); + return NO; + } + + MTRDeviceResponseValueDictionary responseValue = response[0]; + + if (!MTR_SAFE_CAST(responseValue, NSDictionary) || !MTR_SAFE_CAST(responseValue[MTRCommandPathKey], MTRCommandPath)) { + MTR_LOG_ERROR("Invoke response is not an array with the right things in it: %@", response); + return NO; + } + + MTRDeviceDataValueDictionary _Nullable data = responseValue[MTRDataKey]; + NSError * _Nullable error = responseValue[MTRErrorKey]; + + if (data != nil && error != nil) { + MTR_LOG_ERROR("Invoke response claims to have both data and error: %@", responseValue); + return NO; + } + + if (error != nil) { + return MTR_SAFE_CAST(error, NSError) != nil; + } + + if (data == nil) { + // This is valid: indicates a success status response. + return YES; + } + + if (!MTRDataValueDictionaryIsWellFormed(data)) { + MTR_LOG_ERROR("Invoke response claims to have data that is not a data-value: %@", data); + return NO; + } + + // Now we know data is a dictionary (in fact a data-value). The only thing + // we promise about it is that it has type MTRStructureValueType. + if (data[MTRTypeKey] != MTRStructureValueType) { + MTR_LOG_ERROR("Invoke response data is not of structure type: %@", data); + return NO; + } + + return YES; +} diff --git a/src/darwin/Framework/CHIP/MTRDeviceDataValueDictionary.h b/src/darwin/Framework/CHIP/MTRDeviceDataValueDictionary.h deleted file mode 100644 index 53a6b2e6f914b7..00000000000000 --- a/src/darwin/Framework/CHIP/MTRDeviceDataValueDictionary.h +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Copyright (c) 2024 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#import - -NS_ASSUME_NONNULL_BEGIN - -/** - * A data-value as defined in MTRBaseDevice.h. - */ -typedef NSDictionary * MTRDeviceDataValueDictionary; - -NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm index 7e698f0df5dcfb..712d0cff1e17ed 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_Concrete.mm @@ -31,7 +31,7 @@ #import "MTRDeviceConnectivityMonitor.h" #import "MTRDeviceControllerOverXPC.h" #import "MTRDeviceController_Internal.h" -#import "MTRDeviceDataValueDictionary.h" +#import "MTRDeviceDataValidation.h" #import "MTRDevice_Concrete.h" #import "MTRDevice_Internal.h" #import "MTRError_Internal.h" @@ -1887,9 +1887,10 @@ - (void)_handleAttributeReport:(NSArray *> *)attrib } // BEGIN DRAGON: This is used by the XPC Server to inject reports into local cache and broadcast them -- (void)_injectAttributeReport:(NSArray *> *)attributeReport fromSubscription:(BOOL)isFromSubscription +- (void)_injectAttributeReport:(NSArray *)attributeReport fromSubscription:(BOOL)isFromSubscription { - if (!MTRInputIsStructurallyValid(attributeReport, @"attribute-report")) { + if (!MTRAttributeReportIsWellFormed(attributeReport)) { + MTR_LOG_ERROR("%@ injected attribute report is not well-formed: %@", self, attributeReport); return; } @@ -1903,9 +1904,10 @@ - (void)_injectAttributeReport:(NSArray *> *)attrib } errorHandler:nil]; } -- (void)_injectEventReport:(NSArray *> *)eventReport +- (void)_injectEventReport:(NSArray *)eventReport { - if (!MTRInputIsStructurallyValid(eventReport, @"event-report")) { + if (!MTREventReportIsWellFormed(eventReport)) { + MTR_LOG_ERROR("%@ injected event report is not well-formed: %@", self, eventReport); return; } diff --git a/src/darwin/Framework/CHIP/MTRDevice_Internal.h b/src/darwin/Framework/CHIP/MTRDevice_Internal.h index a1ba023c1b5f1c..35cc25e26949da 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_Internal.h +++ b/src/darwin/Framework/CHIP/MTRDevice_Internal.h @@ -22,7 +22,6 @@ #import "MTRAsyncWorkQueue.h" #import "MTRDefines_Internal.h" -#import "MTRDeviceDataValueDictionary.h" #import "MTRDeviceStorageBehaviorConfiguration_Internal.h" NS_ASSUME_NONNULL_BEGIN @@ -176,38 +175,6 @@ MTR_DIRECT_MEMBERS - (void)devicePrivateInternalStateChanged:(MTRDevice *)device internalState:(NSDictionary *)state; @end -// Returns whether a data-value dictionary is well-formed (in the sense that all -// the types of the objects inside are as expected, so it's actually a valid -// representation of some TLV). Implemented in MTRBaseDevice.mm because that's -// where the pieces needed to implement it are, but declared here so our tests -// can see it. -MTR_EXTERN MTR_TESTABLE BOOL MTRDataValueDictionaryIsWellFormed(MTRDeviceDataValueDictionary value); - -// Checks whether the input's structure corresponds to the expected structure, -// using the following rules: -// -// 1) If structure is MTRInputStructureDataValue, checks that input is a -// well-formed data-value. -// 2) If structure is the MTRInputStructureAttributeReport, checks that input is -// a well-formed array of response-values representing attribute reports. -// 3) If structure is MTRInputStructureEventReport, checks that input is a -// well-formed array of response-values representing attribute reports. -// 4) If the structure is MTRInputStructureInvokeResponse, checks that the input is -// a well-formed array of a single response-value representing an invoke response. -// 5) If structure is a dictionary, checks that its keys are all present in -// the input, and have values that match the structure of the provided values. -// 6) If structure is an array, that array is expected to contain a single -// element, which is the structure expected from the array elements. Using -// multiple elements in structure will return NO, to allow adding new -// functionality here in the future as needed. -// 7) If structure is a Class object, checks that the input's class matches that. -// 8) In all other cases returns NO for now, until we decide on what -// semantics those structures should have. -// -// For example a structure of @{ @"abc": NSNumber.class } will check that input is -// a dictionary, that it has "abc" as a key, and that the value is an NSNumber. -MTR_EXTERN MTR_TESTABLE BOOL MTRInputIsStructurallyValid(id input, id structure); - #pragma mark - Constants static NSString * const kDefaultSubscriptionPoolSizeOverrideKey = @"subscriptionPoolSizeOverride"; @@ -225,10 +192,4 @@ static NSString * const kMTRDeviceInternalPropertyLastSubscriptionAttemptWait = static NSString * const kMTRDeviceInternalPropertyMostRecentReportTime = @"MTRDeviceInternalPropertyMostRecentReportTime"; static NSString * const kMTRDeviceInternalPropertyLastSubscriptionFailureTime = @"MTRDeviceInternalPropertyLastSubscriptionFailureTime"; -// Constants used by MTRInputIsStructurallyValid -static NSString * const MTRInputStructureDataValue = @"data-value"; -static NSString * const MTRInputStructureAttributeReport = @"attribute-report"; -static NSString * const MTRInputStructureEventReport = @"event-report"; -static NSString * const MTRInputStructureInvokeResponse = @"invoke-response"; - NS_ASSUME_NONNULL_END diff --git a/src/darwin/Framework/CHIP/MTRDevice_XPC.mm b/src/darwin/Framework/CHIP/MTRDevice_XPC.mm index f24b1ae0bf0cfb..8fd09f672dc90e 100644 --- a/src/darwin/Framework/CHIP/MTRDevice_XPC.mm +++ b/src/darwin/Framework/CHIP/MTRDevice_XPC.mm @@ -41,7 +41,7 @@ #import "MTRDeviceControllerStartupParams_Internal.h" #import "MTRDeviceController_Concrete.h" #import "MTRDeviceController_XPC.h" -#import "MTRDeviceDataValueDictionary.h" +#import "MTRDeviceDataValidation.h" #import "MTRDevice_Concrete.h" #import "MTRDevice_Internal.h" #import "MTRDevice_XPC_Internal.h" @@ -136,7 +136,8 @@ - (NSString *)description // required methods for MTRDeviceDelegates - (oneway void)device:(NSNumber *)nodeID stateChanged:(MTRDeviceState)state { - if (!MTRInputIsStructurallyValid(nodeID, NSNumber.class)) { + if (!MTR_SAFE_CAST(nodeID, NSNumber)) { + MTR_LOG_ERROR("%@ invalid device:stateChanged: nodeID: %@", self, nodeID); return; } @@ -146,9 +147,15 @@ - (oneway void)device:(NSNumber *)nodeID stateChanged:(MTRDeviceState)state }]; } -- (oneway void)device:(NSNumber *)nodeID receivedAttributeReport:(NSArray *> *)attributeReport +- (oneway void)device:(NSNumber *)nodeID receivedAttributeReport:(NSArray *)attributeReport { - if (!MTRInputIsStructurallyValid(nodeID, NSNumber.class) || !MTRInputIsStructurallyValid(attributeReport, MTRInputStructureAttributeReport)) { + if (!MTR_SAFE_CAST(nodeID, NSNumber)) { + MTR_LOG_ERROR("%@ invalid device:receivedAttributeReport: nodeID: %@", self, nodeID); + return; + } + + if (!MTRAttributeReportIsWellFormed(attributeReport)) { + MTR_LOG_ERROR("%@ invalid device:receivedAttributeReport: attributeReport: %@", self, attributeReport); return; } @@ -158,9 +165,15 @@ - (oneway void)device:(NSNumber *)nodeID receivedAttributeReport:(NSArray *> *)eventReport +- (oneway void)device:(NSNumber *)nodeID receivedEventReport:(NSArray *)eventReport { - if (!MTRInputIsStructurallyValid(nodeID, NSNumber.class) || !MTRInputIsStructurallyValid(eventReport, MTRInputStructureEventReport)) { + if (!MTR_SAFE_CAST(nodeID, NSNumber)) { + MTR_LOG_ERROR("%@ invalid device:receivedEventReport: nodeID: %@", self, nodeID); + return; + } + + if (!MTREventReportIsWellFormed(eventReport)) { + MTR_LOG_ERROR("%@ invalid device:receivedEventReport: eventReport: %@", self, eventReport); return; } @@ -173,7 +186,8 @@ - (oneway void)device:(NSNumber *)nodeID receivedEventReport:(NSArray *)keys valueRequired:(BOOL)required +{ + // All the keys are NSNumber-valued. + for (NSString * key in keys) { id value = dictionary[key]; if (!value) { + if (required) { + MTR_LOG_ERROR("%@ device:internalStateUpdated: handed state with no value for \"%@\": %@", self, key, value); + return NO; + } + continue; } - if (!MTRInputIsStructurallyValid(value, NSNumber.class)) { + if (!MTR_SAFE_CAST(value, NSNumber)) { MTR_LOG_ERROR("%@ device:internalStateUpdated: handed state with invalid value for \"%@\": %@", self, key, value); - return; + return NO; } } + return YES; +} + +- (oneway void)device:(NSNumber *)nodeID internalStateUpdated:(NSDictionary *)dictionary +{ + if (!MTR_SAFE_CAST(nodeID, NSNumber)) { + MTR_LOG_ERROR("%@ invalid device:internalStateUpdated: nodeID: %@", self, nodeID); + return; + } + + if (!MTR_SAFE_CAST(dictionary, NSDictionary)) { + MTR_LOG_ERROR("%@ invalid device:internalStateUpdated dictionary: %@", self, dictionary); + return; + } + + VerifyOrReturn([self _internalState:dictionary hasValidValuesForKeys:requiredInternalStateKeys valueRequired:YES]); + VerifyOrReturn([self _internalState:dictionary hasValidValuesForKeys:optionalInternalStateKeys valueRequired:NO]); + [self _setInternalState:dictionary]; MTR_LOG("%@ internal state updated", self); } @@ -318,13 +351,13 @@ - (void)_invokeCommandWithEndpointID:(NSNumber *)endpointID return; } - if (error != nil && !MTRInputIsStructurallyValid(error, NSError.class)) { + if (error != nil && !MTR_SAFE_CAST(error, NSError)) { MTR_LOG_ERROR("%@ got invoke response for (%@, %@, %@) that has invalid error object: %@", self, endpointID, clusterID, commandID, error); completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT]); return; } - if (values != nil && !MTRInputIsStructurallyValid(values, MTRInputStructureInvokeResponse)) { + if (values != nil && !MTRInvokeResponseIsWellFormed(values)) { MTR_LOG_ERROR("%@ got invoke response for (%@, %@, %@) that has invalid data: %@", self, clusterID, commandID, values, values); completion(nil, [MTRError errorForCHIPErrorCode:CHIP_ERROR_INVALID_ARGUMENT]); return; diff --git a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m index 486c58dcb38c1b..b3d605a70d7772 100644 --- a/src/darwin/Framework/CHIPTests/MTRDeviceTests.m +++ b/src/darwin/Framework/CHIPTests/MTRDeviceTests.m @@ -27,6 +27,7 @@ #import "MTRCommandPayloadExtensions_Internal.h" #import "MTRDeviceClusterData.h" #import "MTRDeviceControllerLocalTestStorage.h" +#import "MTRDeviceDataValidation.h" #import "MTRDeviceStorageBehaviorConfiguration.h" #import "MTRDeviceTestDelegate.h" #import "MTRDevice_Internal.h" @@ -5187,112 +5188,13 @@ - (void)test041_AttributeDataValueValidation } } -- (void)test042_StructuralValidityChecker +- (void)test042_AttributeReportWellFormedness { __auto_type * testData = @[ @{ - @"input" : @ { - MTRTypeKey : MTRSignedIntegerValueType, - MTRValueKey : @(-5), - }, - @"structure" : MTRInputStructureDataValue, - @"valid" : @(YES), - }, - @{ - @"input" : @(5), - @"structure" : NSNumber.class, - @"valid" : @(YES), - }, - @{ - @"input" : @("abc"), - @"structure" : NSNumber.class, - @"valid" : @(NO), - }, - @{ - @"input" : @("abc"), - @"structure" : NSString.class, - @"valid" : @(YES), - }, - @{ - @"input" : @[ - @("abc"), - @("def"), - ], - @"structure" : @[ NSString.class ], + @"input" : @[], @"valid" : @(YES), }, - @{ - @"input" : @[ - @("abc"), - @(5), - ], - @"structure" : @[ NSString.class ], - @"valid" : @(NO), - }, - @{ - @"input" : @ { - @"a" : @(5), - @"b" : @("def"), - }, - @"structure" : @ { - @"a" : NSNumber.class, - @"b" : NSString.class, - }, - @"valid" : @(YES), - }, - @{ - @"input" : @ { - @"a" : @(5), - }, - @"structure" : @ { - @"a" : NSNumber.class, - @"b" : NSString.class, - }, - // Validation for "b" fails. - @"valid" : @(NO), - }, - @{ - @"input" : @ { - @"a" : @(5), - @"b" : @("def"), - @"c" : [NSData data], - }, - @"structure" : @ { - @"a" : NSNumber.class, - @"b" : NSString.class, - }, - // Extra keys in input are OK, if we are not planning to touch them. - @"valid" : @(YES), - }, - @{ - @"input" : @ { - MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(0) clusterID:@(6) attributeID:@(0)], - MTRDataKey : @ { - MTRTypeKey : MTRBooleanValueType, - MTRValueKey : @(YES), - }, - }, - @"structure" : @ { - MTRAttributePathKey : MTRAttributePath.class, - MTRDataKey : MTRInputStructureDataValue - }, - @"valid" : @(YES), - }, - @{ - @"input" : @ { - MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(0) clusterID:@(6) attributeID:@(0)], - MTRDataKey : @ { - MTRTypeKey : MTRBooleanValueType, - MTRValueKey : @(YES), - }, - }, - @"structure" : @ { - MTREventPathKey : MTREventPath.class, - MTRDataKey : MTRInputStructureDataValue - }, - // No MTREventPathKey in input. - @"valid" : @(NO), - }, @{ @"input" : @[ @{ @@ -5303,7 +5205,6 @@ - (void)test042_StructuralValidityChecker }, }, ], - @"structure" : MTRInputStructureAttributeReport, @"valid" : @(YES), }, @{ @@ -5320,7 +5221,6 @@ - (void)test042_StructuralValidityChecker MTRErrorKey : [NSError errorWithDomain:MTRErrorDomain code:0 userInfo:nil], }, ], - @"structure" : MTRInputStructureAttributeReport, @"valid" : @(YES), }, @{ @@ -5333,10 +5233,38 @@ - (void)test042_StructuralValidityChecker MTRErrorKey : [NSError errorWithDomain:MTRErrorDomain code:0 userInfo:nil], }, ], - @"structure" : MTRInputStructureAttributeReport, - // Missing error or data + // Missing both error and data + @"valid" : @(NO), + }, + @{ + @"input" : @[ + @{ + MTRAttributePathKey : [MTRAttributePath attributePathWithEndpointID:@(0) clusterID:@(6) attributeID:@(0)], + MTRDataKey : @ { + MTRTypeKey : MTRBooleanValueType, + MTRValueKey : @("abc"), + }, + }, + ], + // Data dictionary is broken. + @"valid" : @(NO), + }, + @{ + @"input" : @ {}, + // Input is not an array. @"valid" : @(NO), }, + ]; + + for (NSDictionary * test in testData) { + XCTAssertEqual(MTRAttributeReportIsWellFormed(test[@"input"]), [test[@"valid"] boolValue], + "input: %@", test[@"input"]); + } +} + +- (void)test043_EventReportWellFormedness +{ + __auto_type * testData = @[ @{ @"input" : @[ @{ @@ -5345,7 +5273,6 @@ - (void)test042_StructuralValidityChecker MTREventIsHistoricalKey : @(NO), }, ], - @"structure" : MTRInputStructureEventReport, @"valid" : @(YES), }, @{ @@ -5363,7 +5290,6 @@ - (void)test042_StructuralValidityChecker MTREventIsHistoricalKey : @(NO), }, ], - @"structure" : MTRInputStructureEventReport, @"valid" : @(YES), }, @{ @@ -5381,7 +5307,6 @@ - (void)test042_StructuralValidityChecker MTREventIsHistoricalKey : @(NO), }, ], - @"structure" : MTRInputStructureEventReport, @"valid" : @(YES), }, @{ @@ -5399,7 +5324,6 @@ - (void)test042_StructuralValidityChecker MTREventIsHistoricalKey : @(NO), }, ], - @"structure" : MTRInputStructureEventReport, // Wrong date type @"valid" : @(NO), }, @@ -5418,7 +5342,6 @@ - (void)test042_StructuralValidityChecker MTREventIsHistoricalKey : @(NO), }, ], - @"structure" : MTRInputStructureEventReport, // Wrong type of EventNumber @"valid" : @(NO), }, @@ -5437,7 +5360,6 @@ - (void)test042_StructuralValidityChecker MTREventIsHistoricalKey : @(NO), }, ], - @"structure" : MTRInputStructureEventReport, // Wrong type of EventPriority @"valid" : @(NO), }, @@ -5456,23 +5378,36 @@ - (void)test042_StructuralValidityChecker MTREventIsHistoricalKey : @(NO), }, ], - @"structure" : MTRInputStructureEventReport, // Wrong type of EventTimeType @"valid" : @(NO), }, @{ @"input" : @[ @(5) ], - @"structure" : MTRInputStructureEventReport, // Wrong type of data entirely. @"valid" : @(NO), }, + @{ + @"input" : @ {}, + // Not even an array. + @"valid" : @(NO), + }, + ]; + + for (NSDictionary * test in testData) { + XCTAssertEqual(MTREventReportIsWellFormed(test[@"input"]), [test[@"valid"] boolValue], + "input: %@", test[@"input"]); + } +} + +- (void)test044_InvokeResponseWellFormedness +{ + __auto_type * testData = @[ @{ @"input" : @[ @{ MTRCommandPathKey : [MTRCommandPath commandPathWithEndpointID:@(0) clusterID:@(6) commandID:@(0)], }, ], - @"structure" : MTRInputStructureInvokeResponse, @"valid" : @(YES), }, @{ @@ -5484,7 +5419,6 @@ - (void)test042_StructuralValidityChecker MTRCommandPathKey : [MTRCommandPath commandPathWithEndpointID:@(0) clusterID:@(6) commandID:@(0)], }, ], - @"structure" : MTRInputStructureInvokeResponse, // Multiple responses @"valid" : @(NO), }, @@ -5495,7 +5429,6 @@ - (void)test042_StructuralValidityChecker MTRErrorKey : [NSError errorWithDomain:MTRErrorDomain code:0 userInfo:nil], }, ], - @"structure" : MTRInputStructureInvokeResponse, @"valid" : @(YES), }, @{ @@ -5508,7 +5441,6 @@ - (void)test042_StructuralValidityChecker }, }, ], - @"structure" : MTRInputStructureInvokeResponse, @"valid" : @(YES), }, @{ @@ -5522,7 +5454,6 @@ - (void)test042_StructuralValidityChecker MTRErrorKey : [NSError errorWithDomain:MTRErrorDomain code:0 userInfo:nil], }, ], - @"structure" : MTRInputStructureInvokeResponse, // Having both data and error not valid. @"valid" : @(NO), }, @@ -5536,7 +5467,6 @@ - (void)test042_StructuralValidityChecker }, }, ], - @"structure" : MTRInputStructureInvokeResponse, // Data is not a struct. @"valid" : @(NO), }, @@ -5547,15 +5477,14 @@ - (void)test042_StructuralValidityChecker MTRDataKey : @(6), }, ], - @"structure" : MTRInputStructureInvokeResponse, // Data is not a data-value at all.. @"valid" : @(NO), }, ]; for (NSDictionary * test in testData) { - XCTAssertEqual(MTRInputIsStructurallyValid(test[@"input"], test[@"structure"]), [test[@"valid"] boolValue], - "input: %@, structure: %@", test[@"input"], test[@"structure"]); + XCTAssertEqual(MTRInvokeResponseIsWellFormed(test[@"input"]), [test[@"valid"] boolValue], + "input: %@", test[@"input"]); } } diff --git a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m index 721cb09b994e25..071566381cc69c 100644 --- a/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m +++ b/src/darwin/Framework/CHIPTests/MTRPerControllerStorageTests.m @@ -19,9 +19,9 @@ #import #import +#import "MTRDefines_Internal.h" #import "MTRDeviceClusterData.h" #import "MTRDeviceControllerLocalTestStorage.h" -#import "MTRDeviceDataValueDictionary.h" #import "MTRDeviceStorageBehaviorConfiguration.h" #import "MTRDeviceTestDelegate.h" #import "MTRDevice_Internal.h" diff --git a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h index b00507d665a9f0..591110b34ea712 100644 --- a/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h +++ b/src/darwin/Framework/CHIPTests/TestHelpers/MTRTestDeclarations.h @@ -18,8 +18,8 @@ #import #import +#import "MTRDefines_Internal.h" #import "MTRDeviceClusterData.h" -#import "MTRDeviceDataValueDictionary.h" #import "MTRDevice_Internal.h" NS_ASSUME_NONNULL_BEGIN diff --git a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj index bd05397c05c67e..a666b8830654db 100644 --- a/src/darwin/Framework/Matter.xcodeproj/project.pbxproj +++ b/src/darwin/Framework/Matter.xcodeproj/project.pbxproj @@ -134,7 +134,8 @@ 5109E9B72CB8B83D0006884B /* MTRDeviceTypeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5109E9B62CB8B83D0006884B /* MTRDeviceTypeTests.m */; }; 5109E9BA2CC1F23E0006884B /* MTRDeviceClusterData.h in Headers */ = {isa = PBXBuildFile; fileRef = 5109E9B82CC1F23E0006884B /* MTRDeviceClusterData.h */; }; 5109E9BB2CC1F23E0006884B /* MTRDeviceClusterData.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5109E9B92CC1F23E0006884B /* MTRDeviceClusterData.mm */; }; - 5109E9BD2CC1F25C0006884B /* MTRDeviceDataValueDictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 5109E9BC2CC1F25C0006884B /* MTRDeviceDataValueDictionary.h */; }; + 5109E9C02CCAD64F0006884B /* MTRDeviceDataValidation.h in Headers */ = {isa = PBXBuildFile; fileRef = 5109E9BE2CCAD64F0006884B /* MTRDeviceDataValidation.h */; }; + 5109E9C12CCAD64F0006884B /* MTRDeviceDataValidation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5109E9BF2CCAD64F0006884B /* MTRDeviceDataValidation.mm */; }; 510A07492A685D3900A9241C /* Matter.apinotes in Headers */ = {isa = PBXBuildFile; fileRef = 510A07482A685D3900A9241C /* Matter.apinotes */; settings = {ATTRIBUTES = (Public, ); }; }; 510CECA8297F72970064E0B3 /* MTROperationalCertificateIssuerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 510CECA6297F72470064E0B3 /* MTROperationalCertificateIssuerTests.m */; }; 5117DD3829A931AE00FFA1AA /* MTROperationalBrowser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5117DD3629A931AD00FFA1AA /* MTROperationalBrowser.mm */; }; @@ -589,7 +590,8 @@ 5109E9B62CB8B83D0006884B /* MTRDeviceTypeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MTRDeviceTypeTests.m; sourceTree = ""; }; 5109E9B82CC1F23E0006884B /* MTRDeviceClusterData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRDeviceClusterData.h; sourceTree = ""; }; 5109E9B92CC1F23E0006884B /* MTRDeviceClusterData.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDeviceClusterData.mm; sourceTree = ""; }; - 5109E9BC2CC1F25C0006884B /* MTRDeviceDataValueDictionary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRDeviceDataValueDictionary.h; sourceTree = ""; }; + 5109E9BE2CCAD64F0006884B /* MTRDeviceDataValidation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MTRDeviceDataValidation.h; sourceTree = ""; }; + 5109E9BF2CCAD64F0006884B /* MTRDeviceDataValidation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = MTRDeviceDataValidation.mm; sourceTree = ""; }; 510A07482A685D3900A9241C /* Matter.apinotes */ = {isa = PBXFileReference; lastKnownFileType = text.apinotes; name = Matter.apinotes; path = CHIP/Matter.apinotes; sourceTree = ""; }; 510CECA6297F72470064E0B3 /* MTROperationalCertificateIssuerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MTROperationalCertificateIssuerTests.m; sourceTree = ""; }; 5117DD3629A931AD00FFA1AA /* MTROperationalBrowser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MTROperationalBrowser.mm; sourceTree = ""; }; @@ -1425,7 +1427,8 @@ 51565CB32A7AD78D00469F18 /* MTRDeviceControllerStorageDelegate.h */, 5A6FEC9427B5976200F25F42 /* MTRDeviceControllerXPCConnection.h */, 5A6FEC9527B5983000F25F42 /* MTRDeviceControllerXPCConnection.mm */, - 5109E9BC2CC1F25C0006884B /* MTRDeviceDataValueDictionary.h */, + 5109E9BE2CCAD64F0006884B /* MTRDeviceDataValidation.h */, + 5109E9BF2CCAD64F0006884B /* MTRDeviceDataValidation.mm */, 5A6FEC8B27B5609C00F25F42 /* MTRDeviceOverXPC.h */, 5A6FEC9727B5C6AF00F25F42 /* MTRDeviceOverXPC.mm */, 754784632BFE65B70089C372 /* MTRDeviceStorageBehaviorConfiguration.h */, @@ -1707,7 +1710,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - 5109E9BD2CC1F25C0006884B /* MTRDeviceDataValueDictionary.h in Headers */, 51D0B1282B617246006E3511 /* MTRServerEndpoint.h in Headers */, 51565CB62A7B0D6600469F18 /* MTRDeviceControllerParameters.h in Headers */, 51565CB42A7AD78D00469F18 /* MTRDeviceControllerStorageDelegate.h in Headers */, @@ -1767,6 +1769,7 @@ 516415FD2B6ACA8300D5CE11 /* MTRServerAccessControl.h in Headers */, 3CF134AB289D8DF70017A19E /* MTRDeviceAttestationInfo.h in Headers */, B2E0D7B2245B0B5C003C5B48 /* MTRManualSetupPayloadParser.h in Headers */, + 5109E9C02CCAD64F0006884B /* MTRDeviceDataValidation.h in Headers */, 3CF134A7289D8ADA0017A19E /* MTRCSRInfo.h in Headers */, 88E07D612B9A89A4005FD53E /* MTRMetricKeys.h in Headers */, 3D4733B32BE2D1DA003DC19B /* MTRUtilities.h in Headers */, @@ -2157,6 +2160,7 @@ 7596A85528788557004DAE0E /* MTRClusters.mm in Sources */, 88EBF8CF27FABDD500686BC1 /* MTRDeviceAttestationDelegateBridge.mm in Sources */, 5A6FEC9827B5C6AF00F25F42 /* MTRDeviceOverXPC.mm in Sources */, + 5109E9C12CCAD64F0006884B /* MTRDeviceDataValidation.mm in Sources */, 9BDA2A062C5D9AF800A32BDD /* MTRDevice_Concrete.mm in Sources */, 514654492A72F9DF00904E61 /* MTRDemuxingStorage.mm in Sources */, 1E4D655229C30A8700BC3478 /* MTRCommissionableBrowser.mm in Sources */,