fix(env): harden the PATH preset per review (T-511)
Three holes from the T-511 adversarial review pass: - An entry containing the PATH separator smuggled extra tokens into the joined PATH — a stray trailing ':' yields an EMPTY token, which POSIX shells resolve as CWD (the dot-in-PATH hazard). The CLI verb and the settings control now reject such entries, and applyPathPreset skips malformed stored values that predate the check. - The gitdir pointer a worktree resolution follows is repo-controlled text; the resolved main root is now validated (must hold a real .git directory) before its preset key is trusted, so a crafted pointer can't alias an arbitrary path's preset. - A pane spawned with a cwd below the workspace root hashed the subdirectory and silently missed the workspace preset; the lookup now keys any in-workspace cwd to the workspace root (presetLookupRoot). Also: the Add button pairs buttonBackground with its own buttonHoverBackground token instead of borrowing the list-item hover token, and the hosted-Claude leg gains an end-to-end orchestrator test (preset lookup → spawn env). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,19 @@ void main() {
|
||||
expect(spawnedArgs.single, isNot(contains('--effort')));
|
||||
});
|
||||
|
||||
test('the workspace PATH preset reaches the spawned session env (D-106)', () async {
|
||||
final envs = <Map<String, String>?>[];
|
||||
final preset = ClaudeSessionOrchestrator(
|
||||
processFactory: ({required sessionArgs, required cwd, env}) async {
|
||||
envs.add(env);
|
||||
return _FakeProc();
|
||||
},
|
||||
pathPresetFor: (cwd) => cwd == '/repo' ? const ['/opt/go/bin'] : const [],
|
||||
);
|
||||
await preset.spawn(SpawnSpec(id: 'p1', role: 'primary', sessionId: 'p1-uuid', cwd: '/repo'));
|
||||
expect(envs.single?['PATH'], startsWith('/opt/go/bin:'), reason: 'preset dirs lead the delta PATH');
|
||||
});
|
||||
|
||||
test('a fresh session gets the skills nudge; resume + fork do not (T-490)', () async {
|
||||
String appendPrompt(List<String> args) {
|
||||
final i = args.indexOf('--append-system-prompt');
|
||||
|
||||
@@ -81,6 +81,29 @@ void main() {
|
||||
expect(dirs(f.tempDir.path), isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('an entry containing a PATH separator is rejected (CWD-token guard)', (tester) async {
|
||||
await tester.runAsync(() => f.services.settings.setProjectDir(f.tempDir));
|
||||
await pump(tester);
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byType(EditableText), '/opt/go/bin:');
|
||||
await tester.tap(find.text('Add entry'));
|
||||
await tester.pump();
|
||||
expect(find.textContaining('absolute path'), findsOneWidget);
|
||||
expect(dirs(f.tempDir.path), isEmpty);
|
||||
});
|
||||
|
||||
testWidgets('~/ expands against HOME and Enter submits (parity with the CLI verb)', (tester) async {
|
||||
final home = Platform.environment['HOME'];
|
||||
if (home == null || home.isEmpty) return; // no HOME in this environment — the guard path is CLI-tested
|
||||
await tester.runAsync(() => f.services.settings.setProjectDir(f.tempDir));
|
||||
await pump(tester);
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byType(EditableText), '~/go/bin/');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
expect(dirs(f.tempDir.path), ['$home/go/bin']);
|
||||
});
|
||||
|
||||
testWidgets('an existing dir renders without the missing tag', (tester) async {
|
||||
await tester.runAsync(() async {
|
||||
await f.services.settings.setProjectDir(f.tempDir);
|
||||
|
||||
Reference in New Issue
Block a user