From 2a322645331532e22def160677a345854f00b7e2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Nov 2023 20:50:05 -0700 Subject: [PATCH] package fetching: catch relative paths that resolve into cache dir The logic here already caught the case when a dependency path tried to escape out of the zig cache directory using up directories. However, it did not catch the case when the relative path tried to reach into a different path within the zig-cache. For example, if it asked for "../../../blah" then it would be caught, but if it asked for "../blah" then it would try to resolve as "zig-cache/p/blah" and probably result in file-not-found, or perhaps resolve to a different package if someone inadvertently used a valid package hash instead of "blah". Now it correctly gives a "dependency path outside project" error, however, still allows relative paths with up-dirs that were not fetched via URL. --- src/Package/Fetch.zig | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 241d24a414a5..514d967a4854 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -261,16 +261,22 @@ pub fn run(f: *Fetch) RunError!void { f.hash_tok, try eb.addString("path-based dependencies are not hashed"), ); - if ((std.mem.startsWith(u8, pkg_root.sub_path, "../") or - std.mem.eql(u8, pkg_root.sub_path, "..")) and - pkg_root.root_dir.eql(cache_root)) - { - return f.fail( - f.location_tok, - try eb.printString("dependency path outside project: '{}{s}'", .{ - pkg_root.root_dir, pkg_root.sub_path, - }), - ); + // Packages fetched by URL may not use relative paths to escape outside the + // fetched package directory from within the package cache. + if (pkg_root.root_dir.eql(cache_root)) { + // `parent_package_root.sub_path` contains a path like this: + // "p/$hash", or + // "p/$hash/foo", with possibly more directories after "foo". + // We want to fail unless the resolved relative path has a + // prefix of "p/$hash/". + const digest_len = @typeInfo(Manifest.MultiHashHexDigest).Array.len; + const expected_prefix = f.parent_package_root.sub_path[0 .. "p/".len + digest_len]; + if (!std.mem.startsWith(u8, pkg_root.sub_path, expected_prefix)) { + return f.fail( + f.location_tok, + try eb.printString("dependency path outside project: '{}'", .{pkg_root}), + ); + } } f.package_root = pkg_root; try loadManifest(f, pkg_root);