Fix EDOHostNamingService registry poisoning vulnerability using a Read-Only Proxy.

PiperOrigin-RevId: 963507498
diff --git a/Service/Sources/EDOHostNamingService.m b/Service/Sources/EDOHostNamingService.m
index 0ba45ab..8e28cd3 100644
--- a/Service/Sources/EDOHostNamingService.m
+++ b/Service/Sources/EDOHostNamingService.m
@@ -22,6 +22,36 @@
 #import "Service/Sources/EDOHostService.h"
 #import "Service/Sources/EDOServicePort.h"
 
+@interface EDOHostNamingServiceProxy : NSObject
+- (instancetype)initWithNamingService:(EDOHostNamingService *)namingService;
+@end
+
+@implementation EDOHostNamingServiceProxy {
+  __weak EDOHostNamingService *_namingService;
+}
+
++ (BOOL)accessInstanceVariablesDirectly {
+  return NO;
+}
+
+- (instancetype)initWithNamingService:(EDOHostNamingService *)namingService {
+  self = [super init];
+  if (self) {
+    _namingService = namingService;
+  }
+  return self;
+}
+
+- (UInt16)portForServiceWithName:(NSString *)name {
+  return [_namingService portForServiceWithName:name];
+}
+
+- (UInt16)serviceConnectionPort {
+  return _namingService.serviceConnectionPort;
+}
+
+@end
+
 @implementation EDOHostNamingService {
   // The mapping from service name to host service port.
   NSMutableDictionary<NSString *, EDOServicePort *> *_servicePortsInfo;
@@ -84,8 +114,10 @@
     if (self->_service) {
       return;
     }
+    EDOHostNamingServiceProxy *proxy =
+        [[EDOHostNamingServiceProxy alloc] initWithNamingService:self];
     self->_service = [EDOHostService serviceWithPort:EDOHostNamingService.namingServerPort
-                                          rootObject:self
+                                          rootObject:proxy
                                                queue:self->_namingServiceEventQueue];
     result = self->_service.port.hostPort.port != 0;
   });
diff --git a/Service/Tests/UnitTests/EDOHostNamingServiceTest.m b/Service/Tests/UnitTests/EDOHostNamingServiceTest.m
index b37ddf6..dbb6669 100644
--- a/Service/Tests/UnitTests/EDOHostNamingServiceTest.m
+++ b/Service/Tests/UnitTests/EDOHostNamingServiceTest.m
@@ -55,6 +55,37 @@
   XCTAssertEqual([namingService portForServiceWithName:kDummyServiceName], kDummyServicePort);
 }
 
+/** Verifies that remote invocation of private mutators throws an exception. */
+- (void)testRemoteInvocationOfPrivateMutatorThrowsException {
+  [EDOHostNamingService.sharedService start];
+  id remoteNamingService =
+      [EDOClientService rootObjectWithPort:EDOHostNamingService.namingServerPort];
+
+  // addServicePort: is a private mutator and should not be available remotely when using proxy.
+  EDOServicePort *dummyPort = [EDOServicePort servicePortWithPort:12345
+                                                      serviceName:@"com.google.poison"];
+  XCTAssertThrows([remoteNamingService addServicePort:dummyPort]);
+}
+
+/** Verifies that remote invocation of stop throws an exception. */
+- (void)testRemoteInvocationOfStopThrowsException {
+  [EDOHostNamingService.sharedService start];
+  id remoteNamingService =
+      [EDOClientService rootObjectWithPort:EDOHostNamingService.namingServerPort];
+
+  // stop is a lifecycle method and should not be available remotely when using proxy.
+  XCTAssertThrows([remoteNamingService stop]);
+}
+
+/** Verifies that remote invocation of serviceConnectionPort works. */
+- (void)testRemoteInvocationOfServiceConnectionPort {
+  [EDOHostNamingService.sharedService start];
+  id remoteNamingService =
+      [EDOClientService rootObjectWithPort:EDOHostNamingService.namingServerPort];
+
+  XCTAssertNotEqual([remoteNamingService serviceConnectionPort], 0);
+}
+
 /**
  * Tests sending object request to the naming service after stopping it, and verifies that
  * exception happens.