authorgravatar for 132888838+arhxam@users.noreply.github.comArham Amin <132888838+arhxam@users.noreply.github.com> 2026-08-04 19:05:10+05:30
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-04 09:35:10-04:00
logc30a6d9b9943cfbf2fd47efc9de6eb9675457d52
tree5727cf57422667b961ba66178290444595fdfb5e
parent37ae1abbe947f0e002d008276225e4af2248a969
signature Signed by PGP key B5690EEEBB952194

fix(server): scrub AppImage XDG_DATA_DIRS and GSETTINGS_SCHEMA_DIR from terminals (#5075)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

2 files changed, 20 insertions(+), 4 deletions(-)

apps/server/src/terminal/Manager.test.ts+7
...@@ -1400,6 +1400,8 @@ it.layer(...@@ -1400,6 +1400,8 @@ it.layer(
1400 OWD: "/home/user/project",1400 OWD: "/home/user/project",
1401 PATH: `${appDir}/usr/bin:${appDir}:/usr/local/bin:/usr/bin:/bin`,1401 PATH: `${appDir}/usr/bin:${appDir}:/usr/local/bin:/usr/bin:/bin`,
1402 LD_LIBRARY_PATH: `${appDir}/usr/lib:/home/user/.local/lib`,1402 LD_LIBRARY_PATH: `${appDir}/usr/lib:/home/user/.local/lib`,
1403 XDG_DATA_DIRS: `${appDir}/usr/share:/usr/local/share:/usr/share`,
1404 GSETTINGS_SCHEMA_DIR: `${appDir}/usr/share/glib-2.0/schemas`,
1403 TEST_TERMINAL_KEEP: "keep-me",1405 TEST_TERMINAL_KEEP: "keep-me",
1404 },1406 },
1405 });1407 });
...@@ -1419,6 +1421,11 @@ it.layer(...@@ -1419,6 +1421,11 @@ it.layer(
1419 // mount segments that the runtime prepended.1421 // mount segments that the runtime prepended.
1420 expect(spawnInput.env.PATH).toBe("/usr/local/bin:/usr/bin:/bin");1422 expect(spawnInput.env.PATH).toBe("/usr/local/bin:/usr/bin:/bin");
1421 expect(spawnInput.env.LD_LIBRARY_PATH).toBe("/home/user/.local/lib");1423 expect(spawnInput.env.LD_LIBRARY_PATH).toBe("/home/user/.local/lib");
1424 // XDG_DATA_DIRS keeps the host entries but drops the AppImage share dir.
1425 expect(spawnInput.env.XDG_DATA_DIRS).toBe("/usr/local/share:/usr/share");
1426 // GSETTINGS_SCHEMA_DIR pointed only at the mount, so it is removed and
1427 // gsettings falls back to the host schema location.
1428 expect(spawnInput.env.GSETTINGS_SCHEMA_DIR).toBeUndefined();
1422 // Unrelated host vars still pass through untouched.1429 // Unrelated host vars still pass through untouched.
1423 expect(spawnInput.env.TEST_TERMINAL_KEEP).toBe("keep-me");1430 expect(spawnInput.env.TEST_TERMINAL_KEEP).toBe("keep-me");
1424 }),1431 }),
apps/server/src/terminal/Manager.ts+13-4
...@@ -1097,10 +1097,19 @@ function shouldExcludeTerminalEnvKey(key: string): boolean {...@@ -1097,10 +1097,19 @@ function shouldExcludeTerminalEnvKey(key: string): boolean {
1097// They describe the AppImage itself, not the user's session, so terminals must1097// They describe the AppImage itself, not the user's session, so terminals must
1098// not inherit them.1098// not inherit them.
1099const APPIMAGE_RUNTIME_ENV_KEYS = ["APPIMAGE", "APPDIR", "ARGV0", "OWD"] as const;1099const APPIMAGE_RUNTIME_ENV_KEYS = ["APPIMAGE", "APPDIR", "ARGV0", "OWD"] as const;
1100// PATH-style variables the AppImage runtime prepends with its temporary mount1100// Colon-separated search-path variables the AppImage runtime points at its
1101// (e.g. /tmp/.mount_T3-XXXX/usr/bin). Only the mount segments are dropped; the1101// temporary mount (e.g. /tmp/.mount_T3-XXXX/usr/bin, the bundled glib schemas,
1102// user's real entries are preserved.1102// and an $APPDIR/usr/share XDG data entry). Only the mount segments are
1103const APPIMAGE_PATH_LIKE_ENV_KEYS = ["PATH", "LD_LIBRARY_PATH"] as const;1103// dropped; the user's real entries are preserved. When nothing but mount
1104// segments remain the variable is removed entirely so consumers fall back to
1105// their platform default (e.g. gsettings finds the host schemas instead of
1106// reporting "No schemas installed"). See issues #1699 and #5059.
1107const APPIMAGE_PATH_LIKE_ENV_KEYS = [
1108 "PATH",
1109 "LD_LIBRARY_PATH",
1110 "XDG_DATA_DIRS",
1111 "GSETTINGS_SCHEMA_DIR",
1112] as const;
11041113
1105function isPathSegmentUnderAppDir(segment: string, appDir: string): boolean {1114function isPathSegmentUnderAppDir(segment: string, appDir: string): boolean {
1106 return segment === appDir || segment.startsWith(`${appDir}/`);1115 return segment === appDir || segment.startsWith(`${appDir}/`);