Merge branch 'rs/apply-validate-input' into maint
Tighten error checks for invalid "git apply" input. * rs/apply-validate-input: apply: check git diffs for mutually exclusive header lines apply: check git diffs for invalid file modes apply: check git diffs for missing old filenames
This commit is contained in:
commit
33c3c2d368
35
apply.c
35
apply.c
@ -210,6 +210,7 @@ struct patch {
|
|||||||
unsigned ws_rule;
|
unsigned ws_rule;
|
||||||
int lines_added, lines_deleted;
|
int lines_added, lines_deleted;
|
||||||
int score;
|
int score;
|
||||||
|
int extension_linenr; /* first line specifying delete/new/rename/copy */
|
||||||
unsigned int is_toplevel_relative:1;
|
unsigned int is_toplevel_relative:1;
|
||||||
unsigned int inaccurate_eof:1;
|
unsigned int inaccurate_eof:1;
|
||||||
unsigned int is_binary:1;
|
unsigned int is_binary:1;
|
||||||
@ -1011,20 +1012,27 @@ static int gitdiff_newname(struct apply_state *state,
|
|||||||
DIFF_NEW_NAME);
|
DIFF_NEW_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int parse_mode_line(const char *line, int linenr, unsigned int *mode)
|
||||||
|
{
|
||||||
|
char *end;
|
||||||
|
*mode = strtoul(line, &end, 8);
|
||||||
|
if (end == line || !isspace(*end))
|
||||||
|
return error(_("invalid mode on line %d: %s"), linenr, line);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int gitdiff_oldmode(struct apply_state *state,
|
static int gitdiff_oldmode(struct apply_state *state,
|
||||||
const char *line,
|
const char *line,
|
||||||
struct patch *patch)
|
struct patch *patch)
|
||||||
{
|
{
|
||||||
patch->old_mode = strtoul(line, NULL, 8);
|
return parse_mode_line(line, state->linenr, &patch->old_mode);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gitdiff_newmode(struct apply_state *state,
|
static int gitdiff_newmode(struct apply_state *state,
|
||||||
const char *line,
|
const char *line,
|
||||||
struct patch *patch)
|
struct patch *patch)
|
||||||
{
|
{
|
||||||
patch->new_mode = strtoul(line, NULL, 8);
|
return parse_mode_line(line, state->linenr, &patch->new_mode);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gitdiff_delete(struct apply_state *state,
|
static int gitdiff_delete(struct apply_state *state,
|
||||||
@ -1138,7 +1146,7 @@ static int gitdiff_index(struct apply_state *state,
|
|||||||
memcpy(patch->new_sha1_prefix, line, len);
|
memcpy(patch->new_sha1_prefix, line, len);
|
||||||
patch->new_sha1_prefix[len] = 0;
|
patch->new_sha1_prefix[len] = 0;
|
||||||
if (*ptr == ' ')
|
if (*ptr == ' ')
|
||||||
patch->old_mode = strtoul(ptr+1, NULL, 8);
|
return gitdiff_oldmode(state, ptr + 1, patch);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1322,6 +1330,18 @@ static char *git_header_name(struct apply_state *state,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int check_header_line(struct apply_state *state, struct patch *patch)
|
||||||
|
{
|
||||||
|
int extensions = (patch->is_delete == 1) + (patch->is_new == 1) +
|
||||||
|
(patch->is_rename == 1) + (patch->is_copy == 1);
|
||||||
|
if (extensions > 1)
|
||||||
|
return error(_("inconsistent header lines %d and %d"),
|
||||||
|
patch->extension_linenr, state->linenr);
|
||||||
|
if (extensions && !patch->extension_linenr)
|
||||||
|
patch->extension_linenr = state->linenr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Verify that we recognize the lines following a git header */
|
/* Verify that we recognize the lines following a git header */
|
||||||
static int parse_git_header(struct apply_state *state,
|
static int parse_git_header(struct apply_state *state,
|
||||||
const char *line,
|
const char *line,
|
||||||
@ -1388,6 +1408,8 @@ static int parse_git_header(struct apply_state *state,
|
|||||||
res = p->fn(state, line + oplen, patch);
|
res = p->fn(state, line + oplen, patch);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
if (check_header_line(state, patch))
|
||||||
|
return -1;
|
||||||
if (res > 0)
|
if (res > 0)
|
||||||
return offset;
|
return offset;
|
||||||
break;
|
break;
|
||||||
@ -1585,7 +1607,8 @@ static int find_header(struct apply_state *state,
|
|||||||
patch->old_name = xstrdup(patch->def_name);
|
patch->old_name = xstrdup(patch->def_name);
|
||||||
patch->new_name = xstrdup(patch->def_name);
|
patch->new_name = xstrdup(patch->def_name);
|
||||||
}
|
}
|
||||||
if (!patch->is_delete && !patch->new_name) {
|
if ((!patch->new_name && !patch->is_delete) ||
|
||||||
|
(!patch->old_name && !patch->is_new)) {
|
||||||
error(_("git diff header lacks filename information "
|
error(_("git diff header lacks filename information "
|
||||||
"(line %d)"), state->linenr);
|
"(line %d)"), state->linenr);
|
||||||
return -128;
|
return -128;
|
||||||
|
@ -13,7 +13,9 @@ test_expect_success setup '
|
|||||||
echo modified >file &&
|
echo modified >file &&
|
||||||
git diff --stat -p >patch-0.txt &&
|
git diff --stat -p >patch-0.txt &&
|
||||||
chmod +x file &&
|
chmod +x file &&
|
||||||
git diff --stat -p >patch-1.txt
|
git diff --stat -p >patch-1.txt &&
|
||||||
|
sed "s/^\(new mode \).*/\1/" <patch-1.txt >patch-empty-mode.txt &&
|
||||||
|
sed "s/^\(new mode \).*/\1garbage/" <patch-1.txt >patch-bogus-mode.txt
|
||||||
'
|
'
|
||||||
|
|
||||||
test_expect_success FILEMODE 'same mode (no index)' '
|
test_expect_success FILEMODE 'same mode (no index)' '
|
||||||
@ -59,4 +61,16 @@ test_expect_success FILEMODE 'mode update (index only)' '
|
|||||||
git ls-files -s file | grep "^100755"
|
git ls-files -s file | grep "^100755"
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success FILEMODE 'empty mode is rejected' '
|
||||||
|
git reset --hard &&
|
||||||
|
test_must_fail git apply patch-empty-mode.txt 2>err &&
|
||||||
|
test_i18ngrep "invalid mode" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success FILEMODE 'bogus mode is rejected' '
|
||||||
|
git reset --hard &&
|
||||||
|
test_must_fail git apply patch-bogus-mode.txt 2>err &&
|
||||||
|
test_i18ngrep "invalid mode" err
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -35,4 +35,28 @@ test_expect_success 'apply diff with inconsistent filenames in headers' '
|
|||||||
test_i18ngrep "inconsistent old filename" err
|
test_i18ngrep "inconsistent old filename" err
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'apply diff with new filename missing from headers' '
|
||||||
|
cat >missing_new_filename.diff <<-\EOF &&
|
||||||
|
diff --git a/f b/f
|
||||||
|
index 0000000..d00491f
|
||||||
|
--- a/f
|
||||||
|
@@ -0,0 +1 @@
|
||||||
|
+1
|
||||||
|
EOF
|
||||||
|
test_must_fail git apply missing_new_filename.diff 2>err &&
|
||||||
|
test_i18ngrep "lacks filename information" err
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'apply diff with old filename missing from headers' '
|
||||||
|
cat >missing_old_filename.diff <<-\EOF &&
|
||||||
|
diff --git a/f b/f
|
||||||
|
index d00491f..0000000
|
||||||
|
+++ b/f
|
||||||
|
@@ -1 +0,0 @@
|
||||||
|
-1
|
||||||
|
EOF
|
||||||
|
test_must_fail git apply missing_old_filename.diff 2>err &&
|
||||||
|
test_i18ngrep "lacks filename information" err
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
@ -29,4 +29,22 @@ test_expect_success 'apply exits non-zero with no-op patch' '
|
|||||||
test_must_fail git apply --check input
|
test_must_fail git apply --check input
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'invalid combination: create and copy' '
|
||||||
|
test_must_fail git apply --check - <<-\EOF
|
||||||
|
diff --git a/1 b/2
|
||||||
|
new file mode 100644
|
||||||
|
copy from 1
|
||||||
|
copy to 2
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'invalid combination: create and rename' '
|
||||||
|
test_must_fail git apply --check - <<-\EOF
|
||||||
|
diff --git a/1 b/2
|
||||||
|
new file mode 100644
|
||||||
|
rename from 1
|
||||||
|
rename to 2
|
||||||
|
EOF
|
||||||
|
'
|
||||||
|
|
||||||
test_done
|
test_done
|
||||||
|
Loading…
Reference in New Issue
Block a user