Commit 0cf6b2f2 by Vicent Martí

Merge pull request #805 from nulltoken/fix/revwalk-email-parsing

Fix revwalk email parsing
parents db2d4061 8aedf1d5
...@@ -23,6 +23,9 @@ GIT_BEGIN_DECL ...@@ -23,6 +23,9 @@ GIT_BEGIN_DECL
* Create a new action signature. The signature must be freed * Create a new action signature. The signature must be freed
* manually or using git_signature_free * manually or using git_signature_free
* *
* Note: angle brackets ('<' and '>') characters are not allowed
* to be used in either the `name` or the `email` parameter.
*
* @param sig_out new signature, in case of error NULL * @param sig_out new signature, in case of error NULL
* @param name name of the person * @param name name of the person
* @param email email of the person * @param email email of the person
......
...@@ -188,7 +188,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo ...@@ -188,7 +188,7 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1; const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
unsigned char *buffer = raw->data; unsigned char *buffer = raw->data;
unsigned char *buffer_end = buffer + raw->len; unsigned char *buffer_end = buffer + raw->len;
unsigned char *parents_start; unsigned char *parents_start, *committer_start;
int i, parents = 0; int i, parents = 0;
int commit_time; int commit_time;
...@@ -219,17 +219,34 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo ...@@ -219,17 +219,34 @@ static int commit_quick_parse(git_revwalk *walk, commit_object *commit, git_rawo
commit->out_degree = (unsigned short)parents; commit->out_degree = (unsigned short)parents;
if ((committer_start = buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
return commit_error(commit, "object is corrupted");
buffer++;
if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL) if ((buffer = memchr(buffer, '\n', buffer_end - buffer)) == NULL)
return commit_error(commit, "object is corrupted"); return commit_error(commit, "object is corrupted");
if ((buffer = memchr(buffer, '<', buffer_end - buffer)) == NULL || /* Skip trailing spaces */
(buffer = memchr(buffer, '>', buffer_end - buffer)) == NULL) while (buffer > committer_start && git__isspace(*buffer))
return commit_error(commit, "malformed author information"); buffer--;
while (*buffer == '>' || git__isspace(*buffer)) /* Seek for the begining of the pack of digits */
buffer++; while (buffer > committer_start && git__isdigit(*buffer))
buffer--;
/* Skip potential timezone offset */
if ((buffer > committer_start) && (*buffer == '+' || *buffer == '-')) {
buffer--;
while (buffer > committer_start && git__isspace(*buffer))
buffer--;
while (buffer > committer_start && git__isdigit(*buffer))
buffer--;
}
if (git__strtol32(&commit_time, (char *)buffer, NULL, 10) < 0) if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
return commit_error(commit, "cannot parse commit time"); return commit_error(commit, "cannot parse commit time");
commit->time = (time_t)commit_time; commit->time = (time_t)commit_time;
......
...@@ -40,7 +40,7 @@ static const char *skip_trailing_spaces(const char *buffer_start, const char *bu ...@@ -40,7 +40,7 @@ static const char *skip_trailing_spaces(const char *buffer_start, const char *bu
static int signature_error(const char *msg) static int signature_error(const char *msg)
{ {
giterr_set(GITERR_INVALID, "Failed to parse signature - %s", msg); giterr_set(GITERR_INVALID, "Failed to process signature - %s", msg);
return -1; return -1;
} }
...@@ -72,9 +72,16 @@ static int process_trimming(const char *input, char **storage, const char *input ...@@ -72,9 +72,16 @@ static int process_trimming(const char *input, char **storage, const char *input
return 0; return 0;
} }
static bool contains_angle_brackets(const char *input)
{
if (strchr(input, '<') != NULL)
return true;
return strchr(input, '>') != NULL;
}
int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset) int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
{ {
int error;
git_signature *p = NULL; git_signature *p = NULL;
assert(name && email); assert(name && email);
...@@ -84,11 +91,18 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema ...@@ -84,11 +91,18 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema
p = git__calloc(1, sizeof(git_signature)); p = git__calloc(1, sizeof(git_signature));
GITERR_CHECK_ALLOC(p); GITERR_CHECK_ALLOC(p);
if ((error = process_trimming(name, &p->name, name + strlen(name), 1)) < 0 || if (process_trimming(name, &p->name, name + strlen(name), 1) < 0 ||
(error = process_trimming(email, &p->email, email + strlen(email), 1)) < 0) process_trimming(email, &p->email, email + strlen(email), 1) < 0)
{ {
git_signature_free(p); git_signature_free(p);
return error; return -1;
}
if (contains_angle_brackets(p->email) ||
contains_angle_brackets(p->name))
{
git_signature_free(p);
return signature_error("Neither `name` nor `email` should contain angle brackets chars.");
} }
p->when.time = time; p->when.time = time;
......
...@@ -13,17 +13,39 @@ static int try_build_signature(const char *name, const char *email, git_time_t t ...@@ -13,17 +13,39 @@ static int try_build_signature(const char *name, const char *email, git_time_t t
return error; return error;
} }
static void assert_name_and_email(
void test_commit_signature__create_trim(void) const char *expected_name,
const char *expected_email,
const char *name,
const char *email)
{ {
// creating a signature trims leading and trailing spaces
git_signature *sign; git_signature *sign;
cl_git_pass(git_signature_new(&sign, " nulltoken ", " emeric.fermas@gmail.com ", 1234567890, 60));
cl_assert(strcmp(sign->name, "nulltoken") == 0); cl_git_pass(git_signature_new(&sign, name, email, 1234567890, 60));
cl_assert(strcmp(sign->email, "emeric.fermas@gmail.com") == 0); cl_assert_equal_s(expected_name, sign->name);
git_signature_free((git_signature *)sign); cl_assert_equal_s(expected_email, sign->email);
git_signature_free(sign);
}
void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void)
{
assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com ");
}
void test_commit_signature__angle_brackets_in_names_are_not_supported(void)
{
cl_git_fail(try_build_signature("<Phil Haack", "phil@haack", 1234567890, 60));
cl_git_fail(try_build_signature("Phil>Haack", "phil@haack", 1234567890, 60));
cl_git_fail(try_build_signature("<Phil Haack>", "phil@haack", 1234567890, 60));
} }
void test_commit_signature__angle_brackets_in_email_are_not_supported(void)
{
cl_git_fail(try_build_signature("Phil Haack", ">phil@haack", 1234567890, 60));
cl_git_fail(try_build_signature("Phil Haack", "phil@>haack", 1234567890, 60));
cl_git_fail(try_build_signature("Phil Haack", "<phil@haack>", 1234567890, 60));
}
void test_commit_signature__create_empties(void) void test_commit_signature__create_empties(void)
{ {
...@@ -39,21 +61,13 @@ void test_commit_signature__create_empties(void) ...@@ -39,21 +61,13 @@ void test_commit_signature__create_empties(void)
void test_commit_signature__create_one_char(void) void test_commit_signature__create_one_char(void)
{ {
// creating a one character signature // creating a one character signature
git_signature *sign; assert_name_and_email("x", "foo@bar.baz", "x", "foo@bar.baz");
cl_git_pass(git_signature_new(&sign, "x", "foo@bar.baz", 1234567890, 60));
cl_assert(strcmp(sign->name, "x") == 0);
cl_assert(strcmp(sign->email, "foo@bar.baz") == 0);
git_signature_free((git_signature *)sign);
} }
void test_commit_signature__create_two_char(void) void test_commit_signature__create_two_char(void)
{ {
// creating a two character signature // creating a two character signature
git_signature *sign; assert_name_and_email("xx", "foo@bar.baz", "xx", "foo@bar.baz");
cl_git_pass(git_signature_new(&sign, "xx", "x@y.z", 1234567890, 60));
cl_assert(strcmp(sign->name, "xx") == 0);
cl_assert(strcmp(sign->email, "x@y.z") == 0);
git_signature_free((git_signature *)sign);
} }
void test_commit_signature__create_zero_char(void) void test_commit_signature__create_zero_char(void)
......
...@@ -107,7 +107,7 @@ void test_network_remotelocal__retrieve_advertised_references(void) ...@@ -107,7 +107,7 @@ void test_network_remotelocal__retrieve_advertised_references(void)
cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
cl_assert_equal_i(how_many_refs, 21); cl_assert_equal_i(how_many_refs, 22);
} }
void test_network_remotelocal__retrieve_advertised_references_from_spaced_repository(void) void test_network_remotelocal__retrieve_advertised_references_from_spaced_repository(void)
...@@ -121,7 +121,7 @@ void test_network_remotelocal__retrieve_advertised_references_from_spaced_reposi ...@@ -121,7 +121,7 @@ void test_network_remotelocal__retrieve_advertised_references_from_spaced_reposi
cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs));
cl_assert_equal_i(how_many_refs, 21); cl_assert_equal_i(how_many_refs, 22);
git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */ git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */
remote = NULL; remote = NULL;
......
...@@ -48,7 +48,7 @@ static void assert_retrieval(unsigned int flags, unsigned int expected_count) ...@@ -48,7 +48,7 @@ static void assert_retrieval(unsigned int flags, unsigned int expected_count)
void test_refs_branches_foreach__retrieve_all_branches(void) void test_refs_branches_foreach__retrieve_all_branches(void)
{ {
assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 9); assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 10);
} }
void test_refs_branches_foreach__retrieve_remote_branches(void) void test_refs_branches_foreach__retrieve_remote_branches(void)
...@@ -58,7 +58,7 @@ void test_refs_branches_foreach__retrieve_remote_branches(void) ...@@ -58,7 +58,7 @@ void test_refs_branches_foreach__retrieve_remote_branches(void)
void test_refs_branches_foreach__retrieve_local_branches(void) void test_refs_branches_foreach__retrieve_local_branches(void)
{ {
assert_retrieval(GIT_BRANCH_LOCAL, 7); assert_retrieval(GIT_BRANCH_LOCAL, 8);
} }
struct expectations { struct expectations {
......
...@@ -46,7 +46,7 @@ static void assert_retrieval(const char *glob, unsigned int flags, int expected_ ...@@ -46,7 +46,7 @@ static void assert_retrieval(const char *glob, unsigned int flags, int expected_
void test_refs_foreachglob__retrieve_all_refs(void) void test_refs_foreachglob__retrieve_all_refs(void)
{ {
/* 7 heads (including one packed head) + 1 note + 2 remotes + 6 tags */ /* 7 heads (including one packed head) + 1 note + 2 remotes + 6 tags */
assert_retrieval("*", GIT_REF_LISTALL, 16); assert_retrieval("*", GIT_REF_LISTALL, 17);
} }
void test_refs_foreachglob__retrieve_remote_branches(void) void test_refs_foreachglob__retrieve_remote_branches(void)
...@@ -56,7 +56,7 @@ void test_refs_foreachglob__retrieve_remote_branches(void) ...@@ -56,7 +56,7 @@ void test_refs_foreachglob__retrieve_remote_branches(void)
void test_refs_foreachglob__retrieve_local_branches(void) void test_refs_foreachglob__retrieve_local_branches(void)
{ {
assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 7); assert_retrieval("refs/heads/*", GIT_REF_LISTALL, 8);
} }
void test_refs_foreachglob__retrieve_partially_named_references(void) void test_refs_foreachglob__retrieve_partially_named_references(void)
......
258f0e2a959a364e40ed6603d5d44fbb24765b10
...@@ -129,8 +129,8 @@ void test_revwalk_basic__glob_heads(void) ...@@ -129,8 +129,8 @@ void test_revwalk_basic__glob_heads(void)
i++; i++;
} }
/* git log --branches --oneline | wc -l => 13 */ /* git log --branches --oneline | wc -l => 14 */
cl_assert(i == 13); cl_assert(i == 14);
} }
void test_revwalk_basic__push_head(void) void test_revwalk_basic__push_head(void)
......
#include "clar_libgit2.h"
static git_repository *_repo;
static git_revwalk *_walk;
void test_revwalk_signatureparsing__initialize(void)
{
cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
cl_git_pass(git_revwalk_new(&_walk, _repo));
}
void test_revwalk_signatureparsing__cleanup(void)
{
git_revwalk_free(_walk);
git_repository_free(_repo);
}
void test_revwalk_signatureparsing__do_not_choke_when_name_contains_angle_brackets(void)
{
git_reference *ref;
git_oid commit_oid;
git_commit *commit;
const git_signature *signature;
/*
* The branch below points at a commit with angle brackets in the committer/author name
* committer <Yu V. Bin Haacked> <foo@example.com> 1323847743 +0100
*/
cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/haacked"));
git_revwalk_push(_walk, git_reference_oid(ref));
cl_git_pass(git_revwalk_next(&commit_oid, _walk));
cl_git_pass(git_commit_lookup(&commit, _repo, git_reference_oid(ref)));
signature = git_commit_committer(commit);
cl_assert_equal_s("Yu V. Bin Haacked", signature->email);
cl_assert_equal_s("", signature->name);
cl_assert_equal_i(1323847743, (int)signature->when.time);
cl_assert_equal_i(60, signature->when.offset);
git_commit_free(commit);
git_reference_free(ref);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment