Commit 2a32264533

Andrew Kelley <andrew@ziglang.org>
2023-11-28 04:50:05
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.
1 parent a98d4a6
Changed files (1)
src
Package
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);