lib/sandbox/src/cwd.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

 1 const std = @import("std");
 2 const sys = @import("sys");
 3 
 4 const resolve = @import("resolve.zig");
 5 
 6 pub fn validate(path: []const u8) !void {
 7     if (path.len == 0) return error.InvalidCwd;
 8     if (std.fs.path.isAbsolute(path)) return error.AbsoluteCwd;
 9     var iterator = std.fs.path.componentIterator(path);
10     while (iterator.next()) |component| {
11         if (std.mem.eql(u8, component.name, "..")) return error.CwdEscapesLayer;
12     }
13 }
14 
15 pub fn ensureInside(root: sys.fs.Dir, child: sys.fs.Dir) !void {
16     var root_buffer: [std.fs.max_path_bytes]u8 = undefined;
17     const root_path = try resolve.dirPath(root, &root_buffer);
18     var child_buffer: [std.fs.max_path_bytes]u8 = undefined;
19     const child_path = try resolve.dirPath(child, &child_buffer);
20     if (!contains(root_path, child_path)) return error.CwdEscapesLayer;
21 }
22 
23 fn contains(root: []const u8, child: []const u8) bool {
24     if (std.mem.eql(u8, root, child)) return true;
25     if (root.len == 1 and root[0] == std.fs.path.sep) return std.fs.path.isAbsolute(child);
26     if (!std.mem.startsWith(u8, child, root)) return false;
27     if (child.len <= root.len) return false;
28     return child[root.len] == std.fs.path.sep;
29 }
30 
31 test "cwd rejects absolute and parent paths" {
32     try std.testing.expectError(error.AbsoluteCwd, validate("/tmp"));
33     try std.testing.expectError(error.CwdEscapesLayer, validate("../tmp"));
34     try std.testing.expectError(error.CwdEscapesLayer, validate("sub/../tmp"));
35     try validate(".");
36     try validate("sub");
37 }