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:
@@ -32,7 +32,7 @@ void main() {
|
||||
String? loginPath;
|
||||
var processPath = '/usr/bin:/bin';
|
||||
|
||||
void wire({String? cwd = '/repo', bool withStore = true}) {
|
||||
void wire({String? cwd = '/repo', bool withStore = true, String? home = '/home/u'}) {
|
||||
store = _FakeStore();
|
||||
published = [];
|
||||
existingDirs = {};
|
||||
@@ -45,7 +45,7 @@ void main() {
|
||||
publisher: () =>
|
||||
(p, c, data) => published.add((publisher: p, channel: c, data: data)),
|
||||
workspaceCwd: () => cwd,
|
||||
home: () => '/home/u',
|
||||
home: () => home,
|
||||
dirExists: (dir) => existingDirs.contains(dir),
|
||||
loginPath: () => loginPath,
|
||||
processPath: () => processPath,
|
||||
@@ -91,6 +91,26 @@ void main() {
|
||||
expect(empty.error?.hint, contains('clear'));
|
||||
});
|
||||
|
||||
test('add and remove apply the same guards as set (relative + separator)', () async {
|
||||
wire();
|
||||
for (final action in ['set', 'add', 'remove']) {
|
||||
final rel = await run([action, 'go/bin']);
|
||||
expect(rel.ok, isFalse, reason: '$action relative');
|
||||
expect(rel.error?.message, contains('not an absolute path'));
|
||||
final sep = await run([action, '/a:']);
|
||||
expect(sep.ok, isFalse, reason: '$action separator');
|
||||
expect(sep.error?.message, contains('PATH separator'));
|
||||
}
|
||||
expect(store.byCwd, isEmpty, reason: 'nothing was written');
|
||||
});
|
||||
|
||||
test('~ with no HOME errors instead of storing a broken entry', () async {
|
||||
wire(home: null);
|
||||
final r = await run(['set', '~/go/bin']);
|
||||
expect(r.ok, isFalse);
|
||||
expect(r.error?.message, contains('cannot expand'));
|
||||
});
|
||||
|
||||
test('a leading-dash entry is rejected by the schema (T-104 guard)', () async {
|
||||
wire();
|
||||
final r = await d.dispatch(
|
||||
|
||||
Reference in New Issue
Block a user