Add --unrestricted to DTD command.

NOTE: the tests view better if you turn off whitespaces on the diff.

This command will start a DTD instance that has the FileSystem service restrictions disabled. Which can be used in development environments.

Bug: https://github.com/dart-lang/sdk/issues/54762
Change-Id: I829f83d7c7afc7dd83732fc8a600cb41ffe36f63
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/352921
Reviewed-by: Kenzie Davisson <kenzieschmoll@google.com>
Commit-Queue: Dan Chevalier <danchevalier@google.com>
diff --git a/pkg/dtd/test/file_system_service_test.dart b/pkg/dtd/test/file_system_service_test.dart
index b3a9c83..500c340 100644
--- a/pkg/dtd/test/file_system_service_test.dart
+++ b/pkg/dtd/test/file_system_service_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:io';
+import 'dart:math';
 
 import 'package:dtd/dtd.dart';
 import 'package:json_rpc_2/json_rpc_2.dart';
@@ -29,17 +30,7 @@
   late DTDConnection client;
   late String dtdSecret;
   late Uri dtdUri;
-
   setUp(() async {
-    toolingDaemonProcess = ToolingDaemonTestProcess();
-    await toolingDaemonProcess.start();
-    dtdUri = toolingDaemonProcess.uri;
-    dtdSecret = toolingDaemonProcess.trustedSecret;
-
-    client = await DartToolingDaemon.connect(
-      dtdUri,
-    );
-
     tmpDirectory = await Directory.systemTemp.createTemp();
 
     // Setup foo dir
@@ -89,296 +80,321 @@
     toolingDaemonProcess.kill();
   });
 
-  group('FileSystem', () {
-    group('setIDEWorkspaceRoots', () {
-      test('wrong secret is unauthorized', () {
-        expect(
-          () => client.setIDEWorkspaceRoots('abc123', [Uri.directory('/')]),
-          throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
-        );
-        expect(
-          () => client.readFileAsString(
-            aFile.uri,
-          ),
-          throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
-        );
-      });
+  group('restricted', () {
+    setUp(() async {
+      toolingDaemonProcess = ToolingDaemonTestProcess(unrestricted: false);
+      await toolingDaemonProcess.start();
+      dtdUri = toolingDaemonProcess.uri;
+      dtdSecret = toolingDaemonProcess.trustedSecret!;
 
-      test('root must have file scheme', () {
-        expect(
-          () => client.setIDEWorkspaceRoots(
-            dtdSecret,
-            [Uri.parse('/some/path/')],
-          ),
-          throwsAnRpcError(RpcErrorCodes.kExpectsUriParamWithFileScheme),
-        );
-      });
-
-      test('no IDE workspace roots', () {
-        final file = File(p.join(fooDirectory.path, 'newfile.txt'));
-        expect(
-          () => client.listDirectoryContents(fooDirectory.uri),
-          throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
-        );
-        expect(
-          () => client.readFileAsString(
-            file.uri,
-          ),
-          throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
-        );
-        expect(
-          () => client.writeFileAsString(
-            file.uri,
-            'this should not be written',
-          ),
-          throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
-        );
-        expect(file.existsSync(), false);
-      });
-
-      test('one IDE workspace root', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-
-        final fileContents = 'New file contents';
-        final listResult = await client.listDirectoryContents(
-          fooDirectory.uri,
-        );
-        expect(
-          listResult.uris,
-          containsAll(fooDirContents),
-        );
-
-        await client.writeFileAsString(aFile.uri, fileContents);
-
-        final readResult = await client.readFileAsString(
-          aFile.uri,
-        );
-        expect(readResult.content, fileContents);
-      });
-
-      test('multiple IDE workspace roots', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [
-          fooDirectory.uri,
-          barDirectory.uri,
-        ]);
-
-        // Operate in foo
-        final newAFileContents = 'New afile contents';
-        final fooListResult = await client.listDirectoryContents(
-          fooDirectory.uri,
-        );
-        expect(
-          fooListResult.uris,
-          containsAll(fooDirContents),
-        );
-
-        await client.writeFileAsString(aFile.uri, newAFileContents);
-
-        final readResult = await client.readFileAsString(
-          aFile.uri,
-        );
-        expect(readResult.content, newAFileContents);
-
-        // Operate in bar
-        final newEFileContents = 'New efile contents';
-        final barListResult = await client.listDirectoryContents(
-          fooDirectory.uri,
-        );
-        expect(
-          barListResult.uris,
-          containsAll(fooDirContents),
-        );
-
-        await client.writeFileAsString(aFile.uri, newEFileContents);
-
-        final eReadResult = await client.readFileAsString(
-          aFile.uri,
-        );
-        expect(eReadResult.content, newEFileContents);
-      });
-
-      test('remove an IDE workspace root', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [
-          fooDirectory.uri,
-          barDirectory.uri,
-        ]);
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-
-        final fileContents = 'New file contents';
-        final listResult = await client.listDirectoryContents(
-          fooDirectory.uri,
-        );
-        expect(
-          listResult.uris,
-          containsAll(fooDirContents),
-        );
-
-        await client.writeFileAsString(aFile.uri, fileContents);
-
-        final readResult = await client.readFileAsString(
-          aFile.uri,
-        );
-        expect(readResult.content, fileContents);
-
-        expect(
-          () => client.listDirectoryContents(barDirectory.uri),
-          throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
-        );
-        expect(
-          () => client.readFileAsString(eFile.uri),
-          throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
-        );
-        expect(
-          () => client.writeFileAsString(eFile.uri, fileContents),
-          throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
-        );
-      });
+      client = await DartToolingDaemon.connect(dtdUri);
     });
 
-    group('getIDEWorkspaceRoots', () {
-      test('empty IDE workspace roots', () async {
-        final roots = await client.getIDEWorkspaceRoots();
-        expect(roots.ideWorkspaceRoots, isEmpty);
-      });
+    group('FileSystem', () {
+      group('setIDEWorkspaceRoots', () {
+        test('wrong secret is unauthorized', () {
+          expect(
+            () => client.setIDEWorkspaceRoots('abc123', [Uri.directory('/')]),
+            throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
+          );
+          expect(
+            () => client.readFileAsString(aFile.uri),
+            throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
+          );
+        });
 
-      test('multiple IDE workspace roots', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [
-          fooDirectory.uri,
-          barDirectory.uri,
-        ]);
-        final roots = await client.getIDEWorkspaceRoots();
-        expect(
-          roots.ideWorkspaceRoots,
-          containsAll([
+        test('root must have file scheme', () {
+          expect(
+            () => client
+                .setIDEWorkspaceRoots(dtdSecret, [Uri.parse('/some/path/')]),
+            throwsAnRpcError(RpcErrorCodes.kExpectsUriParamWithFileScheme),
+          );
+        });
+
+        test('no IDE workspace roots', () {
+          final file = File(p.join(fooDirectory.path, 'newfile.txt'));
+          expect(
+            () => client.listDirectoryContents(fooDirectory.uri),
+            throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
+          );
+          expect(
+            () => client.readFileAsString(
+              file.uri,
+            ),
+            throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
+          );
+          expect(
+            () => client.writeFileAsString(
+              file.uri,
+              'this should not be written',
+            ),
+            throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
+          );
+          expect(file.existsSync(), false);
+        });
+
+        test('one IDE workspace root', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+
+          final fileContents = 'New file contents';
+          final listResult = await client.listDirectoryContents(
+            fooDirectory.uri,
+          );
+          expect(listResult.uris, containsAll(fooDirContents));
+
+          await client.writeFileAsString(aFile.uri, fileContents);
+
+          final readResult = await client.readFileAsString(aFile.uri);
+          expect(readResult.content, fileContents);
+        });
+
+        test('multiple IDE workspace roots', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [
             fooDirectory.uri,
             barDirectory.uri,
-          ]),
-        );
+          ]);
+
+          // Operate in foo
+          final newAFileContents = 'New afile contents';
+          final fooListResult = await client.listDirectoryContents(
+            fooDirectory.uri,
+          );
+          expect(fooListResult.uris, containsAll(fooDirContents));
+
+          await client.writeFileAsString(aFile.uri, newAFileContents);
+
+          final readResult = await client.readFileAsString(aFile.uri);
+          expect(readResult.content, newAFileContents);
+
+          // Operate in bar
+          final newEFileContents = 'New efile contents';
+          final barListResult = await client.listDirectoryContents(
+            fooDirectory.uri,
+          );
+          expect(barListResult.uris, containsAll(fooDirContents));
+
+          await client.writeFileAsString(aFile.uri, newEFileContents);
+
+          final eReadResult = await client.readFileAsString(aFile.uri);
+          expect(eReadResult.content, newEFileContents);
+        });
+
+        test('remove an IDE workspace root', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [
+            fooDirectory.uri,
+            barDirectory.uri,
+          ]);
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+
+          final fileContents = 'New file contents';
+          final listResult = await client.listDirectoryContents(
+            fooDirectory.uri,
+          );
+          expect(
+            listResult.uris,
+            containsAll(fooDirContents),
+          );
+
+          await client.writeFileAsString(aFile.uri, fileContents);
+
+          final readResult = await client.readFileAsString(aFile.uri);
+          expect(readResult.content, fileContents);
+
+          expect(
+            () => client.listDirectoryContents(barDirectory.uri),
+            throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
+          );
+          expect(
+            () => client.readFileAsString(eFile.uri),
+            throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
+          );
+          expect(
+            () => client.writeFileAsString(eFile.uri, fileContents),
+            throwsAnRpcError(RpcErrorCodes.kPermissionDenied),
+          );
+        });
+      });
+
+      group('getIDEWorkspaceRoots', () {
+        test('empty IDE workspace roots', () async {
+          final roots = await client.getIDEWorkspaceRoots();
+          expect(roots.ideWorkspaceRoots, isEmpty);
+        });
+
+        test('multiple IDE workspace roots', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [
+            fooDirectory.uri,
+            barDirectory.uri,
+          ]);
+          final roots = await client.getIDEWorkspaceRoots();
+          expect(
+            roots.ideWorkspaceRoots,
+            containsAll([fooDirectory.uri, barDirectory.uri]),
+          );
+        });
+      });
+
+      group('listDirectoryContents', () {
+        test('listing a file should fail', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+          expect(
+            () => client.listDirectoryContents(aFile.uri),
+            throwsAnRpcError(RpcErrorCodes.kDirectoryDoesNotExist),
+          );
+        });
+
+        test('non-existent directory should fail', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+          expect(
+            () => client.listDirectoryContents(
+              Uri.directory(p.join(fooDirectory.path, 'A')),
+            ),
+            throwsAnRpcError(RpcErrorCodes.kDirectoryDoesNotExist),
+          );
+        });
+
+        test('should work for an empty directory', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+          final emptyFooDir = Directory(p.join(fooDirectory.path, 'emptyDir'));
+          emptyFooDir.createSync();
+
+          final listResult =
+              await client.listDirectoryContents(emptyFooDir.uri);
+          expect(listResult.uris, isEmpty);
+        });
+
+        test('must have a file scheme', () {
+          expect(
+            () => client.listDirectoryContents(
+              Uri.parse('/some/path/'),
+            ),
+            throwsAnRpcError(RpcErrorCodes.kExpectsUriParamWithFileScheme),
+          );
+        });
+      });
+
+      group('readFileAsString', () {
+        test('fails on a non-existent file', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+          expect(
+            () => client.readFileAsString(
+              File(p.join(fooDirectory.path, 'nonExistentFile.txt')).uri,
+            ),
+            throwsAnRpcError(RpcErrorCodes.kFileDoesNotExist),
+          );
+        });
+
+        test('must have a file scheme', () {
+          expect(
+            () => client.readFileAsString(Uri.parse('/some/path/')),
+            throwsAnRpcError(RpcErrorCodes.kExpectsUriParamWithFileScheme),
+          );
+        });
+      });
+
+      group('writeFileAsString', () {
+        final newFileContents = 'Some new file contents';
+
+        test('can overwrite an existing file', () async {
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+
+          expect(aFile.readAsStringSync(), aFileContents);
+
+          await client.writeFileAsString(aFile.uri, newFileContents);
+
+          expect(aFile.readAsStringSync(), newFileContents);
+        });
+
+        test('creates the file if it does not exist', () async {
+          final nonExistentFile = File(
+            p.join(fooDirectory.path, 'nonExistentFile.txt'),
+          );
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+          expect(nonExistentFile.existsSync(), false);
+          await client.writeFileAsString(nonExistentFile.uri, newFileContents);
+          expect(nonExistentFile.existsSync(), true);
+          expect(nonExistentFile.readAsStringSync(), newFileContents);
+        });
+
+        test('creates sub directories if they don\'t exist', () async {
+          final fileInNonExistentDirectory = File(
+            p.join(
+              fooDirectory.path,
+              'a',
+              'b',
+              'c',
+              'nonExistentFile.txt',
+            ),
+          );
+          await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
+          expect(fileInNonExistentDirectory.existsSync(), false);
+          await client.writeFileAsString(
+            fileInNonExistentDirectory.uri,
+            newFileContents,
+          );
+          expect(fileInNonExistentDirectory.existsSync(), true);
+          expect(
+            fileInNonExistentDirectory.readAsStringSync(),
+            newFileContents,
+          );
+        });
+
+        test('must have a file scheme', () {
+          expect(
+            () => client.writeFileAsString(
+              Uri.parse('/some/path/'),
+              'some contents',
+            ),
+            throwsAnRpcError(RpcErrorCodes.kExpectsUriParamWithFileScheme),
+          );
+        });
       });
     });
+  });
 
-    group('listDirectoryContents', () {
-      test('listing a file should fail', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-        expect(
-          () => client.listDirectoryContents(aFile.uri),
-          throwsAnRpcError(RpcErrorCodes.kDirectoryDoesNotExist),
-        );
-      });
+  group('unrestricted', () {
+    setUp(() async {
+      toolingDaemonProcess = ToolingDaemonTestProcess(unrestricted: true);
+      await toolingDaemonProcess.start();
+      dtdUri = toolingDaemonProcess.uri;
 
-      test('non-existent directory should fail', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-        expect(
-          () => client.listDirectoryContents(
-            Uri.directory(p.join(fooDirectory.path, 'A')),
-          ),
-          throwsAnRpcError(RpcErrorCodes.kDirectoryDoesNotExist),
-        );
-      });
-
-      test('should work for an empty directory', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-        final emptyFooDir = Directory(p.join(fooDirectory.path, 'emptyDir'));
-        emptyFooDir.createSync();
-
-        final listResult = await client.listDirectoryContents(emptyFooDir.uri);
-        expect(listResult.uris, isEmpty);
-      });
-
-      test('must have a file scheme', () {
-        expect(
-          () => client.listDirectoryContents(
-            Uri.parse('/some/path/'),
-          ),
-          throwsAnRpcError(RpcErrorCodes.kExpectsUriParamWithFileScheme),
-        );
-      });
+      client = await DartToolingDaemon.connect(dtdUri);
     });
 
-    group('readFileAsString', () {
-      test('fails on a non-existent file', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-        expect(
-          () => client.readFileAsString(
-            File(p.join(fooDirectory.path, 'nonExistentFile.txt')).uri,
-          ),
-          throwsAnRpcError(RpcErrorCodes.kFileDoesNotExist),
-        );
-      });
+    test('works when no roots set', () async {
+      final fileContents = 'New file contents';
 
-      test('must have a file scheme', () {
-        expect(
-          () => client.readFileAsString(
-            Uri.parse('/some/path/'),
-          ),
-          throwsAnRpcError(RpcErrorCodes.kExpectsUriParamWithFileScheme),
-        );
-      });
+      expect((await client.getIDEWorkspaceRoots()).ideWorkspaceRoots, isEmpty);
+
+      final listResult = await client.listDirectoryContents(fooDirectory.uri);
+      expect(listResult.uris, containsAll(fooDirContents));
+
+      await client.writeFileAsString(aFile.uri, fileContents);
+
+      final readResult = await client.readFileAsString(aFile.uri);
+      expect(readResult.content, fileContents);
     });
 
-    group('writeFileAsString', () {
-      final newFileContents = 'Some new file contents';
-
-      test('can overwrite an existing file', () async {
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-
-        expect(aFile.readAsStringSync(), aFileContents);
-
-        await client.writeFileAsString(
-          aFile.uri,
-          newFileContents,
+    test(
+      'works when ide workspace roots set to a different directory',
+      () async {
+        await client.setIDEWorkspaceRoots(
+          Random().nextInt(10000).toString(),
+          [barDirectory.uri],
         );
+        final fileContents = 'New file contents';
+        final listResult = await client.listDirectoryContents(fooDirectory.uri);
+        expect(listResult.uris, containsAll(fooDirContents));
 
-        expect(aFile.readAsStringSync(), newFileContents);
-      });
+        await client.writeFileAsString(aFile.uri, fileContents);
 
-      test('creates the file if it does not exist', () async {
-        final nonExistentFile = File(
-          p.join(
-            fooDirectory.path,
-            'nonExistentFile.txt',
-          ),
-        );
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-        expect(nonExistentFile.existsSync(), false);
-        await client.writeFileAsString(
-          nonExistentFile.uri,
-          newFileContents,
-        );
-        expect(nonExistentFile.existsSync(), true);
-        expect(nonExistentFile.readAsStringSync(), newFileContents);
-      });
-
-      test('creates sub directories if they don\'t exist', () async {
-        final fileInNonExistentDirectory = File(
-          p.join(
-            fooDirectory.path,
-            'a',
-            'b',
-            'c',
-            'nonExistentFile.txt',
-          ),
-        );
-        await client.setIDEWorkspaceRoots(dtdSecret, [fooDirectory.uri]);
-        expect(fileInNonExistentDirectory.existsSync(), false);
-        await client.writeFileAsString(
-          fileInNonExistentDirectory.uri,
-          newFileContents,
-        );
-        expect(fileInNonExistentDirectory.existsSync(), true);
-        expect(fileInNonExistentDirectory.readAsStringSync(), newFileContents);
-      });
-
-      test('must have a file scheme', () {
-        expect(
-          () => client.writeFileAsString(
-            Uri.parse('/some/path/'),
-            'some contents',
-          ),
-          throwsAnRpcError(RpcErrorCodes.kExpectsUriParamWithFileScheme),
-        );
-      });
-    });
+        final readResult = await client.readFileAsString(aFile.uri);
+        expect(readResult.content, fileContents);
+        expect((await client.getIDEWorkspaceRoots()).ideWorkspaceRoots, [
+          barDirectory.uri,
+        ]);
+      },
+    );
   });
 }
 
diff --git a/pkg/dtd/test/utils.dart b/pkg/dtd/test/utils.dart
index e8d9ee5..57d62cc 100644
--- a/pkg/dtd/test/utils.dart
+++ b/pkg/dtd/test/utils.dart
@@ -7,15 +7,21 @@
 import 'dart:io';
 
 class ToolingDaemonTestProcess {
-  late final String trustedSecret;
+  ToolingDaemonTestProcess({this.unrestricted = false});
+  late final String? trustedSecret;
   late final Uri uri;
   late final Process? process;
+  final bool unrestricted;
 
   Future<Process> start() async {
     final completer = Completer<void>();
     process = await Process.start(
       Platform.resolvedExecutable,
-      ['tooling-daemon', '--machine'],
+      [
+        'tooling-daemon',
+        '--machine',
+        if (unrestricted) '--unrestricted',
+      ],
     );
     process!.stdout.transform(utf8.decoder).listen((line) {
       stderr.write('DTD stdout: $line');
@@ -23,7 +29,8 @@
         final json = jsonDecode(line) as Map<String, Object?>;
         final toolingDaemonDetails =
             json['tooling_daemon_details'] as Map<String, dynamic>;
-        trustedSecret = toolingDaemonDetails['trusted_client_secret'] as String;
+        trustedSecret =
+            toolingDaemonDetails['trusted_client_secret'] as String?;
         uri = Uri.parse(toolingDaemonDetails['uri'] as String);
         completer.complete();
       } catch (e) {
diff --git a/pkg/dtd_impl/lib/dart_tooling_daemon.dart b/pkg/dtd_impl/lib/dart_tooling_daemon.dart
index 36af61c..9336b94 100644
--- a/pkg/dtd_impl/lib/dart_tooling_daemon.dart
+++ b/pkg/dtd_impl/lib/dart_tooling_daemon.dart
@@ -29,6 +29,11 @@
     isFlag: true,
     negatable: false,
     help: 'Sets output format to JSON for consumption in tools.',
+  ),
+  unrestricted(
+    isFlag: true,
+    negatable: false,
+    help: 'Disables restrictions on services registered by DTD.',
   );
 
   const DartToolingDaemonOptions({
@@ -70,13 +75,17 @@
 class DartToolingDaemon {
   DartToolingDaemon._({
     required this.secret,
+    required bool unrestrictedMode,
     bool ipv6 = false,
     bool shouldLogRequests = false,
   })  : _ipv6 = ipv6,
         _shouldLogRequests = shouldLogRequests {
     streamManager = DTDStreamManager(this);
     clientManager = DTDClientManager();
-    fileSystemService = FileSystemService(secret: secret);
+    fileSystemService = FileSystemService(
+      secret: secret,
+      unrestrictedMode: unrestrictedMode,
+    );
   }
   static const _kSseHandlerPath = '\$debugHandler';
 
@@ -158,9 +167,13 @@
       return null;
     }
     final machineMode = parsedArgs[DartToolingDaemonOptions.machine.name];
+    final unrestrictedMode =
+        parsedArgs[DartToolingDaemonOptions.unrestricted.name];
+
     final secret = _generateSecret();
     final dtd = DartToolingDaemon._(
       secret: secret,
+      unrestrictedMode: unrestrictedMode,
       ipv6: ipv6,
       shouldLogRequests: shouldLogRequests,
     );
@@ -170,7 +183,7 @@
         jsonEncode({
           'tooling_daemon_details': {
             'uri': dtd.uri.toString(),
-            'trusted_client_secret': secret,
+            ...(!unrestrictedMode ? {'trusted_client_secret': secret} : {}),
           },
         }),
       );
@@ -179,7 +192,10 @@
         'The Dart Tooling Daemon is listening on '
         '${dtd.uri.toString()}',
       );
-      print('Trusted Client Secret: $secret');
+
+      if (!unrestrictedMode) {
+        print('Trusted Client Secret: $secret');
+      }
     }
     return dtd;
   }
diff --git a/pkg/dtd_impl/lib/src/file_system_service.dart b/pkg/dtd_impl/lib/src/file_system_service.dart
index 68789bc..b71bc99 100644
--- a/pkg/dtd_impl/lib/src/file_system_service.dart
+++ b/pkg/dtd_impl/lib/src/file_system_service.dart
@@ -12,11 +12,10 @@
 import 'dtd_client.dart';
 
 class FileSystemService {
-  FileSystemService({
-    required String secret,
-  }) : _secret = secret;
+  FileSystemService({required this.secret, required this.unrestrictedMode});
 
-  final String _secret;
+  final String secret;
+  final bool unrestrictedMode;
   final List<Uri> _ideWorkspaceRoots = [];
 
   static const String _serviceName = 'FileSystem';
@@ -51,6 +50,9 @@
   }
 
   void _ensureIDEWorkspaceRootsContainUri(Uri uri) {
+    // If in unrestricted mode, no need to do these checks.
+    if (unrestrictedMode) return;
+
     for (final root in _ideWorkspaceRoots) {
       if (uri.path.startsWith(root.path)) {
         return;
@@ -64,7 +66,7 @@
   Map<String, Object?> _setIDEWorkspaceRoots(Parameters parameters) {
     final incomingSecret = parameters['secret'].asString;
 
-    if (_secret != incomingSecret) {
+    if (!unrestrictedMode && secret != incomingSecret) {
       throw RpcErrorCodes.buildRpcException(
         RpcErrorCodes.kPermissionDenied,
       );
@@ -91,9 +93,7 @@
     return IDEWorkspaceRoots(ideWorkspaceRoots: _ideWorkspaceRoots).toJson();
   }
 
-  Future<Map<String, Object?>> _readFileAsString(
-    Parameters parameters,
-  ) async {
+  Future<Map<String, Object?>> _readFileAsString(Parameters parameters) async {
     final uri = _extractUri(parameters);
     _ensureIDEWorkspaceRootsContainUri(uri);
     final file = File.fromUri(uri);
@@ -120,9 +120,7 @@
     return uri;
   }
 
-  Future<Map<String, Object?>> _writeFileAsString(
-    Parameters parameters,
-  ) async {
+  Future<Map<String, Object?>> _writeFileAsString(Parameters parameters) async {
     final uri = _extractUri(parameters);
     final contents = parameters['contents'].asString;
     final encoding = Encoding.getByName(