From: Jonathan Nieder Date: Sat, 24 Apr 2010 21:10:42 GMT Subject: [PATCH 2/2] fast-import: validate entire ident string Message-ID: <20100424211042.GC24948@progeny.tock> In-Reply-To: <20100424203827.GA24948@progeny.tock> The author, committer, and tagger name and email should not include any embedded <, >, or newline characters. The format of the identification string is ('author'|'committer'|'tagger') sp name sp < email > sp date If an object has no name attached, then git expects to find two spaces in a row. Helped-by: Mark Lodato Signed-off-by: Jonathan Nieder --- For malformed input, the parser in pretty.c and ‘git commit --amend’ tend to end up with different ideas of who the author is. A lot of the time, commit --amend gives up with "fatal: invalid commit". Documentation/git-fast-import.txt | 9 ++-- fast-import.c | 54 ++++++++++++++++---------- t/t9300-fast-import.sh | 75 +++++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 25 deletions(-) diff --git a/Documentation/git-fast-import.txt b/Documentation/git-fast-import.txt index 19082b0..ee725c6 100644 --- a/Documentation/git-fast-import.txt +++ b/Documentation/git-fast-import.txt @@ -337,8 +337,8 @@ change to the project. .... 'commit' SP LF mark? - ('author' (SP )? SP LT GT SP LF)? - 'committer' (SP )? SP LT GT SP LF + ('author' SP ? SP LT GT SP LF)? + 'committer' SP ? SP LT GT SP LF data ('from' SP LF)? ('merge' SP LF)? @@ -393,8 +393,9 @@ Here `` is the person's display name (for example (``cm@example.com''). `LT` and `GT` are the literal less-than (\x3c) and greater-than (\x3e) symbols. These are required to delimit the email address from the other fields in the line. Note that -`` is free-form and may contain any sequence of bytes, except -`LT` and `LF`. It is typically UTF-8 encoded. +`` and `` are free-form and may contain any sequence +of bytes that are not `LT`, `GT`, or `LF`. Both are typically UTF-8 +encoded. The time of the change is specified by `` using the date format that was selected by the \--date-format= command line option. diff --git a/fast-import.c b/fast-import.c index 1701cf1..d919168 100644 --- a/fast-import.c +++ b/fast-import.c @@ -19,8 +19,8 @@ Format of STDIN stream: new_commit ::= 'commit' sp ref_str lf mark? - ('author' (sp name)? sp '<' email '>' sp when lf)? - 'committer' (sp name)? sp '<' email '>' sp when lf + ('author' sp name? sp '<' email '>' sp when lf)? + 'committer' sp name? sp '<' email '>' sp when lf commit_msg ('from' sp committish lf)? ('merge' sp committish lf)* @@ -47,7 +47,7 @@ Format of STDIN stream: new_tag ::= 'tag' sp tag_str lf 'from' sp committish lf - ('tagger' (sp name)? sp '<' email '>' sp when lf)? + ('tagger' sp name? sp '<' email '>' sp when lf)? tag_msg; tag_msg ::= data; @@ -123,9 +123,8 @@ Format of STDIN stream: sha1exp ::= # Any valid GIT SHA1 expression; hexsha1 ::= # SHA1 in hexadecimal format; - # note: name and email are UTF8 strings, however name must not - # contain '<' or lf and email must not contain any of the - # following: '<', '>', lf. + # note: name and email are UTF8 strings, however name and email + # must not contain any of the following: '<', '>', lf. # name ::= # valid GIT author/committer name; email ::= # valid GIT author/committer email; @@ -1929,34 +1928,47 @@ static int validate_raw_date(const char *src, char *result, int maxlen) return 0; } -static char *parse_ident(const char *buf) +static size_t parse_name_and_email(const char *src, char **result, size_t extra) { - const char *gt; + const char *lt, *gt; size_t name_len; - char *ident; - gt = strrchr(buf, '>'); - if (!gt) - die("Missing > in ident string: %s", buf); + lt = src + strcspn(src, "<>\n"); + if (lt == src || lt[-1] != ' ' || *lt != '<') + die("Invalid name in ident string: %s", src); + gt = lt + 1 + strcspn(lt + 1, "<>\n"); + if (*gt != '>') + die("Invalid email in ident string: %s", src); gt++; if (*gt != ' ') - die("Missing space after > in ident string: %s", buf); + die("Missing space after > in ident string: %s", src); gt++; - name_len = gt - buf; - ident = xmalloc(name_len + 24); - strncpy(ident, buf, name_len); + name_len = gt - src; + *result = xmalloc(name_len + extra); + memcpy(*result, src, name_len); + return name_len; +} + +static char *parse_ident(const char *buf) +{ + const char *date; + size_t name_len; + char *ident; + + name_len = parse_name_and_email(buf, &ident, 24); + date = buf + name_len; switch (whenspec) { case WHENSPEC_RAW: - if (validate_raw_date(gt, ident + name_len, 24) < 0) - die("Invalid raw date \"%s\" in ident: %s", gt, buf); + if (validate_raw_date(date, ident + name_len, 24) < 0) + die("Invalid raw date \"%s\" in ident: %s", date, buf); break; case WHENSPEC_RFC2822: - if (parse_date(gt, ident + name_len, 24) < 0) - die("Invalid rfc2822 date \"%s\" in ident: %s", gt, buf); + if (parse_date(date, ident + name_len, 24) < 0) + die("Invalid rfc2822 date \"%s\" in ident: %s", date, buf); break; case WHENSPEC_NOW: - if (strcmp("now", gt)) + if (strcmp("now", date)) die("Date in ident must be 'now': %s", buf); datestamp(ident + name_len, 24); break; diff --git a/t/t9300-fast-import.sh b/t/t9300-fast-import.sh index ed653a7..a7e379f 100755 --- a/t/t9300-fast-import.sh +++ b/t/t9300-fast-import.sh @@ -348,6 +348,81 @@ test_expect_success \ cat >input < Sat, 24 Apr 2010 14:49:52 -0500 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> Tue Feb 6 12:35:01 2007 -0500 +data <input < Sat, 24 Apr 2010 14:49:52 -0500 +committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> Tue Feb 6 12:35:01 2007 -0500 +data <input < Sat, 24 Apr 2010 14:49:52 -0500 +committer C O >Mitter <$GIT_COMMITTER_EMAIL> Tue Feb 6 12:35:01 2007 -0500 +data <input < Sat, 24 Apr 2010 15:05:27 -0500 +committer $GIT_COMMITTER_NAME Tue Feb 6 12:35:01 2007 -0500 +data <input < Sat, 24 Apr 2010 15:05:27 -0500 +committer $GIT_COMMITTER_NAME <äggh!some!other!machine!example> Tue Feb 6 12:35:01 2007 -0500 +data <input < 1170783301 - 0500 committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 117078330 -0500 data <