Skip to content

[vendored] XCDebugLogDelegate-Protocol.h should declare its parameter nullable and document the sink contract #1185

Description

@dariusta

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

  1. 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.
  2. 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.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions