confine editor.open/editor.save to the workspace (T-363)
The editor registry resolved buffer paths with a string join that passed absolute paths through verbatim and never normalized `..` — an unconfined read and write primitive over IPC while files.read was carefully guarded. Buffer paths now resolve through resolveUnderRootFollowingSymlinks: traversal, absolute escapes, and symlinks-out are rejected at open, and re-checked at save so a symlink swapped in under an open buffer's path can't redirect the write. D-80's extra read roots deliberately do not apply — a buffer is a write surface. Handlers map PathOutsideRoot to the same error files.read uses. Also merges a duplicate Added heading that had crept into the Unreleased changelog section. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,7 @@ library;
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import '../files/path_safety.dart';
|
||||
import '../ipc/envelope.dart';
|
||||
import '../panes/event_sink.dart';
|
||||
import 'buffer.dart';
|
||||
@@ -212,10 +213,13 @@ class EditorRegistry {
|
||||
events.emit(IpcEvent(subsystem: 'editor', kind: kind, timestamp: DateTime.now().toUtc(), data: data));
|
||||
}
|
||||
|
||||
/// Resolve a buffer path to disk under the workspace root, with the
|
||||
/// same traversal/symlink containment as files.* (T-363). A buffer is
|
||||
/// a WRITE surface (save), so the D-80 extra read roots do not apply —
|
||||
/// strictly workspace-confined. Throws [PathOutsideRoot] on escape.
|
||||
String _absolutePathOf(String repoRelative) {
|
||||
if (repoRelative.startsWith('/')) return repoRelative;
|
||||
final sep = Platform.pathSeparator;
|
||||
return '${workspaceRoot.absolute.path}$sep${repoRelative.replaceAll('/', sep)}';
|
||||
return resolveUnderRootFollowingSymlinks(workspaceRoot, repoRelative.replaceAll('/', sep));
|
||||
}
|
||||
|
||||
// Support JSON decode of Selection from IPC args.
|
||||
|
||||
Reference in New Issue
Block a user