apply: do not read from beyond a symbolic link
[gitweb.git] / builtin / apply.c
index ef32e4f62415956aa8e2366589d4cd6200d3bf37..422e4ce7aada87216f6ce0f69754e527d6583cca 100644 (file)
@@ -50,6 +50,7 @@ static int apply_verbosely;
 static int allow_overlap;
 static int no_add;
 static int threeway;
+static int unsafe_paths;
 static const char *fake_ancestor;
 static int line_termination = '\n';
 static unsigned int p_context = UINT_MAX;
@@ -3135,7 +3136,7 @@ static int load_patch_target(struct strbuf *buf,
                             const char *name,
                             unsigned expected_mode)
 {
-       if (cached) {
+       if (cached || check_index) {
                if (read_file_or_gitlink(ce, buf))
                        return error(_("read of %s failed"), name);
        } else if (name) {
@@ -3144,6 +3145,8 @@ static int load_patch_target(struct strbuf *buf,
                                return read_file_or_gitlink(ce, buf);
                        else
                                return SUBMODULE_PATCH_WITHOUT_INDEX;
+               } else if (has_symlink_leading_path(name, strlen(name))) {
+                       return error(_("reading from '%s' beyond a symbolic link"), name);
                } else {
                        if (read_old_data(st, name, buf))
                                return error(_("read of %s failed"), name);
@@ -3483,6 +3486,23 @@ static int check_to_create(const char *new_name, int ok_if_exists)
        return 0;
 }
 
+static void die_on_unsafe_path(struct patch *patch)
+{
+       const char *old_name = NULL;
+       const char *new_name = NULL;
+       if (patch->is_delete)
+               old_name = patch->old_name;
+       else if (!patch->is_new && !patch->is_copy)
+               old_name = patch->old_name;
+       if (!patch->is_delete)
+               new_name = patch->new_name;
+
+       if (old_name && !verify_path(old_name))
+               die(_("invalid path '%s'"), old_name);
+       if (new_name && !verify_path(new_name))
+               die(_("invalid path '%s'"), new_name);
+}
+
 /*
  * Check and apply the patch in-core; leave the result in patch->result
  * for the caller to write it out to the final destination.
@@ -3570,6 +3590,9 @@ static int check_patch(struct patch *patch)
                }
        }
 
+       if (!unsafe_paths)
+               die_on_unsafe_path(patch);
+
        if (apply_data(patch, &st, ce) < 0)
                return error(_("%s: patch does not apply"), name);
        patch->rejected = 0;
@@ -4379,6 +4402,8 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                        N_("make sure the patch is applicable to the current index")),
                OPT_BOOL(0, "cached", &cached,
                        N_("apply a patch without touching the working tree")),
+               OPT_BOOL(0, "unsafe-paths", &unsafe_paths,
+                       N_("accept a patch that touches outside the working area")),
                OPT_BOOL(0, "apply", &force_apply,
                        N_("also apply the patch (use with --stat/--summary/--check)")),
                OPT_BOOL('3', "3way", &threeway,
@@ -4451,6 +4476,9 @@ int cmd_apply(int argc, const char **argv, const char *prefix_)
                        die(_("--cached outside a repository"));
                check_index = 1;
        }
+       if (check_index)
+               unsafe_paths = 0;
+
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
                int fd;