Source
Filed from a downstream consumer (Straton-Labs-LLC/stratton-internal, vendoring WebDriverAgentLib.xcframework).
Tracking ID in our audit: ocr-prelim-04297 (atomic/retry-safe subset) and ocr-prelim-06939 (arbitrary-message subset — see also the related note at the bottom).
Affected file
WebDriverAgentLib/RodmanRunnerLib/XCDebugLogDelegate-Protocol.h (vendored header at native/runner/Frameworks/WebDriverAgentLib.xcframework/ios-arm64/RodmanRunnerLib.framework/Headers/XCDebugLogDelegate-Protocol.h)
Current declaration (verbatim)
//
// Generated by class-dump 3.5 (64 bit).
//
@class NSString;
@protocol XCDebugLogDelegate <NSObject>
- (void)logDebugMessage:(NSString *)arg1;
@end
Defects
- Header is class-dump output, not source-of-truth. The header carries a
class-dump 3.5 (64 bit) banner and lacks any nullability annotation, import, or framework guards. The same _XCTestLogDelegate symbol is also declared by the XCTest private interface; consumers that import both will get duplicate-protocol warnings under -Wprotocol.
arg1 is implicitly nonnull under NS_ASSUME_NONNULL_BEGIN. There is no NS_ASSUME_NONNULL_BEGIN in this file, but consumers that import this header inside an NS_ASSUME_NONNULL_BEGIN block will inherit that assumption. XCTest's logger calls this method with whatever it has; in practice the parameter can be nil (we have observed this on Xcode 15.x teardown hooks). The current contract makes the receiver crash if it derefs the string before nil-checking.
- No retry/atomicity contract. XCTest may invoke
logDebugMessage: from any thread (notification observers on NSLog-style callbacks). The protocol does not state whether the implementation must be re-entrant, queue-confined, or thread-safe. Downstream decorators (e.g. RRLogDecorator) wrap XCTestLogger and assume single-thread delivery, which is not actually true.
Suggested fix
Replace the class-dump header with a hand-written one:
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
Implemented by objects that want to observe debug messages emitted by
XCTest internals.
@param message The debug message. May be nil. Implementations must tolerate
a nil argument without crashing.
@discussion The XCTest framework may invoke this method from any thread.
Implementations are responsible for their own thread safety.
*/
@protocol XCDebugLogDelegate <NSObject>
- (void)logDebugMessage:(nullable NSString *)message;
@end
NS_ASSUME_NONNULL_END
Add a corresponding note to RRLogDecorator (the only known implementor in the vendored framework) so it serializes its console writes through a private serial queue.
Related: ocr-prelim-06939
The protocol takes NSString * with no contract on what constitutes a "debug message" — XCTest's internal callers pass things like class names, error descriptions, accessibility tree dumps, and partially-formatted progress text. A consumer that wants to redact secrets (passwords typed during a UI test) has no hook to inspect, classify, or skip a particular message. Suggestion: split into logDebugMessage: (free-form, opaque) and add logDebugRecord: (structured) so consumers can branch on category without parsing strings.
Source
Filed from a downstream consumer (
Straton-Labs-LLC/stratton-internal, vendoringWebDriverAgentLib.xcframework).Tracking ID in our audit:
ocr-prelim-04297(atomic/retry-safe subset) andocr-prelim-06939(arbitrary-message subset — see also the related note at the bottom).Affected file
WebDriverAgentLib/RodmanRunnerLib/XCDebugLogDelegate-Protocol.h(vendored header atnative/runner/Frameworks/WebDriverAgentLib.xcframework/ios-arm64/RodmanRunnerLib.framework/Headers/XCDebugLogDelegate-Protocol.h)Current declaration (verbatim)
Defects
class-dump 3.5 (64 bit)banner and lacks any nullability annotation, import, or framework guards. The same_XCTestLogDelegatesymbol is also declared by theXCTestprivate interface; consumers that import both will get duplicate-protocol warnings under-Wprotocol.arg1is implicitly nonnull underNS_ASSUME_NONNULL_BEGIN. There is noNS_ASSUME_NONNULL_BEGINin this file, but consumers that import this header inside anNS_ASSUME_NONNULL_BEGINblock will inherit that assumption. XCTest's logger calls this method with whatever it has; in practice the parameter can benil(we have observed this on Xcode 15.x teardown hooks). The current contract makes the receiver crash if it derefs the string before nil-checking.logDebugMessage:from any thread (notification observers onNSLog-style callbacks). The protocol does not state whether the implementation must be re-entrant, queue-confined, or thread-safe. Downstream decorators (e.g.RRLogDecorator) wrap XCTestLogger and assume single-thread delivery, which is not actually true.Suggested fix
Replace the class-dump header with a hand-written one:
Add a corresponding note to
RRLogDecorator(the only known implementor in the vendored framework) so it serializes its console writes through a private serial queue.Related: ocr-prelim-06939
The protocol takes
NSString *with no contract on what constitutes a "debug message" — XCTest's internal callers pass things like class names, error descriptions, accessibility tree dumps, and partially-formatted progress text. A consumer that wants to redact secrets (passwords typed during a UI test) has no hook to inspect, classify, or skip a particular message. Suggestion: split intologDebugMessage:(free-form, opaque) and addlogDebugRecord:(structured) so consumers can branch on category without parsing strings.