summaryrefslogtreecommitdiff
path: root/src/apprt
diff options
context:
space:
mode:
authorMarco Trevisan (TreviƱo) <mail@3v1n0.net>2025-09-19 06:40:11 +0200
committerMitchell Hashimoto <m@mitchellh.com>2025-09-19 15:54:52 -0700
commitd55f3e5c4171efcdc0619a961fdbaea3f541c262 (patch)
tree7c4417840be6fbc2a91f08c50ecc4dbc822cab75 /src/apprt
parent2de105e0353093b8dc966c244c281c77fdb97ce4 (diff)
gtk/surface: Filter out the SNAP variables by their contents
When running in a snap context we need to filtering out all the SNAP_* variables out there, but this is not enough, because it's also needed to sanitize them by ensuring that no variable containing a path pointing to a $SNAP folder is leaked there. Otherwise we might have (for example) XDG_RUNTIME_DIRS containing a "private" snap path, and that will be exposed to all the applications that will be started from ghostty
Diffstat (limited to 'src/apprt')
-rw-r--r--src/apprt/gtk/class/surface.zig101
1 files changed, 89 insertions, 12 deletions
diff --git a/src/apprt/gtk/class/surface.zig b/src/apprt/gtk/class/surface.zig
index c26d0c1ef..fb2f2bd47 100644
--- a/src/apprt/gtk/class/surface.zig
+++ b/src/apprt/gtk/class/surface.zig
@@ -1228,18 +1228,7 @@ pub const Surface = extern struct {
// Unset environment varies set by snaps if we're running in a snap.
// This allows Ghostty to further launch additional snaps.
if (env.get("SNAP")) |_| {
- env.remove("SNAP");
- env.remove("DRIRC_CONFIGDIR");
- env.remove("__EGL_EXTERNAL_PLATFORM_CONFIG_DIRS");
- env.remove("__EGL_VENDOR_LIBRARY_DIRS");
- env.remove("LD_LIBRARY_PATH");
- env.remove("LIBGL_DRIVERS_PATH");
- env.remove("LIBVA_DRIVERS_PATH");
- env.remove("VK_LAYER_PATH");
- env.remove("XLOCALEDIR");
- env.remove("GDK_PIXBUF_MODULEDIR");
- env.remove("GDK_PIXBUF_MODULE_FILE");
- env.remove("GTK_PATH");
+ try filterSnapPaths(alloc, &env);
}
// This is a hack because it ties ourselves (optionally) to the
@@ -1253,6 +1242,94 @@ pub const Surface = extern struct {
return env;
}
+ /// Filter out environment variables that start with forbidden prefixes
+ fn filterSnapPaths(allocator: std.mem.Allocator, env_map: *std.process.EnvMap) !void {
+ const snap_paths_vars = [_][]const u8{
+ "SNAP",
+ "SNAP_USER_COMMON",
+ "SNAP_USER_DATA",
+ "SNAP_DATA",
+ "SNAP_COMMON",
+ };
+
+ var env_to_remove = std.ArrayList([]const u8).init(allocator);
+ defer env_to_remove.deinit();
+
+ var env_to_update = std.ArrayList(struct {
+ key: []const u8,
+ value: []const u8,
+ }).init(allocator);
+ defer {
+ for (env_to_update.items) |item| {
+ allocator.free(item.value);
+ }
+ env_to_update.deinit();
+ }
+
+ var it = env_map.iterator();
+ while (it.next()) |entry| {
+ const key = entry.key_ptr.*;
+ const value = entry.value_ptr.*;
+
+ if (std.mem.eql(u8, key, "TERMINFO")) {
+ continue;
+ }
+
+ if (std.mem.startsWith(u8, key, "GHOSTTY")) {
+ continue;
+ }
+
+ if (std.mem.startsWith(u8, key, "SNAP_")) {
+ try env_to_remove.append(key);
+ continue;
+ }
+
+ var paths = std.mem.splitAny(u8, value, ":");
+ var filtered_paths = std.ArrayList([]const u8).init(allocator);
+ defer filtered_paths.deinit();
+
+ var modified = false;
+ while (paths.next()) |path| {
+ var include = true;
+ for (snap_paths_vars) |snap_path_var| {
+ const snap_path_val = env_map.get(snap_path_var);
+
+ if (snap_path_val) |snap_path| {
+ if (snap_path.len == 0) {
+ continue;
+ }
+
+ if (std.mem.startsWith(u8, path, snap_path)) {
+ include = false;
+ modified = true;
+ break;
+ }
+ }
+ }
+ if (include) {
+ try filtered_paths.append(path);
+ }
+ }
+
+ if (modified) {
+ if (filtered_paths.items.len > 0) {
+ const new_value = try std.mem.join(allocator, ":", filtered_paths.items);
+ try env_to_update.append(.{ .key = key, .value = new_value });
+ } else {
+ try env_to_remove.append(key);
+ }
+ }
+ }
+
+ for (env_to_update.items) |item| {
+ try env_map.put(item.key, item.value);
+ }
+
+ for (env_to_remove.items) |key| {
+ _ = env_map.remove(key);
+ }
+ }
+
pub fn clipboardRequest(
self: *Self,
clipboard_type: apprt.Clipboard,