diff --git a/CHANGELOG.md b/CHANGELOG.md index e51f7627140..a4903d39247 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,50 @@ +v0.26.7 +------- + +This is a security release fixing the following list of issues: + +- Submodule URLs and paths with a leading "-" are now ignored. + This is due to the recently discovered CVE-2018-17456, which + can lead to arbitrary code execution in upstream git. While + libgit2 itself is not vulnerable, it can be used to inject + options in an implementation which performs a recursive clone + by executing an external command. + +- When running repack while doing repo writes, + `packfile_load__cb()` could see some temporary files in the + directory that were bigger than the usual, and makes `memcmp` + overflow on the `p->pack_name` string. This issue was reported + and fixed by bisho. + +- The configuration file parser used unbounded recursion to parse + multiline variables, which could lead to a stack overflow. The + issue was reported by the oss-fuzz project, issue 10048 and + fixed by Nelson Elhage. + +- The fix to the unbounded recursion introduced a memory leak in + the config parser. While this leak was never in a public + release, the oss-fuzz project reported this as issue 10127. The + fix was implemented by Nelson Elhage and Patrick Steinhardt. + +- When parsing "ok" packets received via the smart protocol, our + parsing code did not correctly verify the bounds of the + packets, which could result in a heap-buffer overflow. The + issue was reported by the oss-fuzz project, issue 9749 and + fixed by Patrick Steinhardt. + +- The parsing code for the smart protocol has been tightened in + general, fixing heap-buffer overflows when parsing the packet + type as well as for "ACK" and "unpack" packets. The issue was + discovered and fixed by Patrick Steinhardt. + +- Fixed potential integer overflows on platforms with 16 bit + integers when parsing packets for the smart protocol. The issue + was discovered and fixed by Patrick Steinhardt. + +- Fixed potential NULL pointer dereference when parsing + configuration files which have "include.path" statements + without a value. + v0.26.6 ------- diff --git a/include/git2/version.h b/include/git2/version.h index f16bb911726..520726ec551 100644 --- a/include/git2/version.h +++ b/include/git2/version.h @@ -7,10 +7,10 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "0.26.6" +#define LIBGIT2_VERSION "0.26.7" #define LIBGIT2_VER_MAJOR 0 #define LIBGIT2_VER_MINOR 26 -#define LIBGIT2_VER_REVISION 6 +#define LIBGIT2_VER_REVISION 7 #define LIBGIT2_VER_PATCH 0 #define LIBGIT2_SOVERSION 26 diff --git a/src/config_file.c b/src/config_file.c index e15d57bbba5..721dbef32cb 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1347,48 +1347,50 @@ static int unescape_line( static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes) { - char *line = NULL, *proc_line = NULL; int quote_count; - bool multiline; + bool multiline = true; + + while (multiline) { + char *line = NULL, *proc_line = NULL; + int error; + + /* Check that the next line exists */ + line = reader_readline(reader, false); + GITERR_CHECK_ALLOC(line); + + /* + * We've reached the end of the file, there is no continuation. + * (this is not an error). + */ + if (line[0] == '\0') { + error = 0; + goto out; + } - /* Check that the next line exists */ - line = reader_readline(reader, false); - if (line == NULL) - return -1; + /* If it was just a comment, pretend it didn't exist */ + quote_count = strip_comments(line, !!in_quotes); + if (line[0] == '\0') + goto next; - /* We've reached the end of the file, there is no continuation. - * (this is not an error). - */ - if (line[0] == '\0') { - git__free(line); - return 0; - } + if ((error = unescape_line(&proc_line, &multiline, + line, in_quotes)) < 0) + goto out; - quote_count = strip_comments(line, !!in_quotes); + /* Add this line to the multiline var */ + if ((error = git_buf_puts(value, proc_line)) < 0) + goto out; - /* If it was just a comment, pretend it didn't exist */ - if (line[0] == '\0') { +next: git__free(line); - return parse_multiline_variable(reader, value, quote_count); - /* TODO: unbounded recursion. This **could** be exploitable */ - } + git__free(proc_line); + in_quotes = quote_count; + continue; - if (unescape_line(&proc_line, &multiline, line, in_quotes) < 0) { +out: git__free(line); - return -1; + git__free(proc_line); + return error; } - /* add this line to the multiline var */ - - git_buf_puts(value, proc_line); - git__free(line); - git__free(proc_line); - - /* - * If we need to continue reading the next line, let's just - * keep putting stuff in the buffer - */ - if (multiline) - return parse_multiline_variable(reader, value, quote_count); return 0; } @@ -1596,7 +1598,7 @@ static int read_on_variable( result = 0; /* Add or append the new config option */ - if (!git__strcmp(var->entry->name, "include.path")) { + if (!git__strcmp(var->entry->name, "include.path") && var->entry->value) { struct reader *r; git_buf path = GIT_BUF_INIT; char *dir; diff --git a/src/odb_pack.c b/src/odb_pack.c index 51770a88e21..d5120797541 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -209,7 +209,7 @@ static int packfile_load__cb(void *data, git_buf *path) for (i = 0; i < backend->packs.length; ++i) { struct git_pack_file *p = git_vector_get(&backend->packs, i); - if (memcmp(p->pack_name, path_str, cmp_len) == 0) + if (strncmp(p->pack_name, path_str, cmp_len) == 0) return 0; } diff --git a/src/submodule.c b/src/submodule.c index 96c8e667569..74008dcf632 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -1792,6 +1792,14 @@ static int get_value(const char **out, git_config *cfg, git_buf *buf, const char return error; } +static bool looks_like_command_line_option(const char *s) +{ + if (s && s[0] == '-') + return true; + + return false; +} + static int submodule_read_config(git_submodule *sm, git_config *cfg) { git_buf key = GIT_BUF_INIT; @@ -1805,24 +1813,31 @@ static int submodule_read_config(git_submodule *sm, git_config *cfg) if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) { in_config = 1; + /* We would warn here if we had that API */ + if (!looks_like_command_line_option(value)) { /* * TODO: if case insensitive filesystem, then the following strcmp * should be strcasecmp */ - if (strcmp(sm->name, value) != 0) { - if (sm->path != sm->name) - git__free(sm->path); - sm->path = git__strdup(value); - GITERR_CHECK_ALLOC(sm->path); + if (strcmp(sm->name, value) != 0) { + if (sm->path != sm->name) + git__free(sm->path); + sm->path = git__strdup(value); + GITERR_CHECK_ALLOC(sm->path); + } + } } else if (error != GIT_ENOTFOUND) { goto cleanup; } if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) { - in_config = 1; - sm->url = git__strdup(value); - GITERR_CHECK_ALLOC(sm->url); + /* We would warn here if we had that API */ + if (!looks_like_command_line_option(value)) { + in_config = 1; + sm->url = git__strdup(value); + GITERR_CHECK_ALLOC(sm->url); + } } else if (error != GIT_ENOTFOUND) { goto cleanup; } diff --git a/src/transports/smart.h b/src/transports/smart.h index b47001fe004..ee1f4c2a8d2 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -28,14 +28,14 @@ extern bool git_smart__ofs_delta_enabled; -enum git_pkt_type { +typedef enum { GIT_PKT_CMD, GIT_PKT_FLUSH, GIT_PKT_REF, GIT_PKT_HAVE, GIT_PKT_ACK, GIT_PKT_NAK, - GIT_PKT_PACK, + GIT_PKT_PACK__UNUSED, GIT_PKT_COMMENT, GIT_PKT_ERR, GIT_PKT_DATA, @@ -43,7 +43,7 @@ enum git_pkt_type { GIT_PKT_OK, GIT_PKT_NG, GIT_PKT_UNPACK, -}; +} git_pkt_type; /* Used for multi_ack and mutli_ack_detailed */ enum git_ack_status { @@ -55,11 +55,11 @@ enum git_ack_status { /* This would be a flush pkt */ typedef struct { - enum git_pkt_type type; + git_pkt_type type; } git_pkt; struct git_pkt_cmd { - enum git_pkt_type type; + git_pkt_type type; char *cmd; char *path; char *host; @@ -67,50 +67,50 @@ struct git_pkt_cmd { /* This is a pkt-line with some info in it */ typedef struct { - enum git_pkt_type type; + git_pkt_type type; git_remote_head head; char *capabilities; } git_pkt_ref; /* Useful later */ typedef struct { - enum git_pkt_type type; + git_pkt_type type; git_oid oid; enum git_ack_status status; } git_pkt_ack; typedef struct { - enum git_pkt_type type; + git_pkt_type type; char comment[GIT_FLEX_ARRAY]; } git_pkt_comment; typedef struct { - enum git_pkt_type type; - int len; + git_pkt_type type; + size_t len; char data[GIT_FLEX_ARRAY]; } git_pkt_data; typedef git_pkt_data git_pkt_progress; typedef struct { - enum git_pkt_type type; - int len; + git_pkt_type type; + size_t len; char error[GIT_FLEX_ARRAY]; } git_pkt_err; typedef struct { - enum git_pkt_type type; + git_pkt_type type; char *ref; } git_pkt_ok; typedef struct { - enum git_pkt_type type; + git_pkt_type type; char *ref; char *msg; } git_pkt_ng; typedef struct { - enum git_pkt_type type; + git_pkt_type type; int unpack_ok; } git_pkt_unpack; @@ -184,7 +184,7 @@ int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream int git_smart__update_heads(transport_smart *t, git_vector *symrefs); /* smart_pkt.c */ -int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); +int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen); int git_pkt_buffer_flush(git_buf *buf); int git_pkt_send_flush(GIT_SOCKET s); int git_pkt_buffer_done(git_buf *buf); diff --git a/src/transports/smart_pkt.c b/src/transports/smart_pkt.c index d10d6c68fae..e726d077731 100644 --- a/src/transports/smart_pkt.c +++ b/src/transports/smart_pkt.c @@ -43,34 +43,43 @@ static int flush_pkt(git_pkt **out) static int ack_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ack *pkt; - GIT_UNUSED(line); - GIT_UNUSED(len); pkt = git__calloc(1, sizeof(git_pkt_ack)); GITERR_CHECK_ALLOC(pkt); - pkt->type = GIT_PKT_ACK; - line += 3; - len -= 3; - if (len >= GIT_OID_HEXSZ) { - git_oid_fromstr(&pkt->oid, line + 1); - line += GIT_OID_HEXSZ + 1; - len -= GIT_OID_HEXSZ + 1; - } + if (git__prefixncmp(line, len, "ACK ")) + goto out_err; + line += 4; + len -= 4; + + if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->oid, line) < 0) + goto out_err; + line += GIT_OID_HEXSZ; + len -= GIT_OID_HEXSZ; - if (len >= 7) { - if (!git__prefixcmp(line + 1, "continue")) + if (len && line[0] == ' ') { + line++; + len--; + + if (!git__prefixncmp(line, len, "continue")) pkt->status = GIT_ACK_CONTINUE; - if (!git__prefixcmp(line + 1, "common")) + else if (!git__prefixncmp(line, len, "common")) pkt->status = GIT_ACK_COMMON; - if (!git__prefixcmp(line + 1, "ready")) + else if (!git__prefixncmp(line, len, "ready")) pkt->status = GIT_ACK_READY; + else + goto out_err; } *out = (git_pkt *) pkt; return 0; + +out_err: + giterr_set(GITERR_NET, "error parsing ACK pkt-line"); + git__free(pkt); + return -1; } static int nak_pkt(git_pkt **out) @@ -86,19 +95,6 @@ static int nak_pkt(git_pkt **out) return 0; } -static int pack_pkt(git_pkt **out) -{ - git_pkt *pkt; - - pkt = git__malloc(sizeof(git_pkt)); - GITERR_CHECK_ALLOC(pkt); - - pkt->type = GIT_PKT_PACK; - *out = pkt; - - return 0; -} - static int comment_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_comment *pkt; @@ -120,10 +116,12 @@ static int comment_pkt(git_pkt **out, const char *line, size_t len) static int err_pkt(git_pkt **out, const char *line, size_t len) { - git_pkt_err *pkt; + git_pkt_err *pkt = NULL; size_t alloclen; /* Remove "ERR " from the line */ + if (git__prefixncmp(line, len, "ERR ")) + goto out_err; line += 4; len -= 4; @@ -131,15 +129,20 @@ static int err_pkt(git_pkt **out, const char *line, size_t len) GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1); pkt = git__malloc(alloclen); GITERR_CHECK_ALLOC(pkt); - pkt->type = GIT_PKT_ERR; - pkt->len = (int)len; + pkt->len = len; + memcpy(pkt->error, line, len); pkt->error[len] = '\0'; *out = (git_pkt *) pkt; return 0; + +out_err: + giterr_set(GITERR_NET, "error parsing ERR pkt-line"); + git__free(pkt); + return -1; } static int data_pkt(git_pkt **out, const char *line, size_t len) @@ -155,7 +158,7 @@ static int data_pkt(git_pkt **out, const char *line, size_t len) GITERR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_DATA; - pkt->len = (int) len; + pkt->len = len; memcpy(pkt->data, line, len); *out = (git_pkt *) pkt; @@ -176,7 +179,7 @@ static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len) GITERR_CHECK_ALLOC(pkt); pkt->type = GIT_PKT_PROGRESS; - pkt->len = (int) len; + pkt->len = len; memcpy(pkt->data, line, len); *out = (git_pkt *) pkt; @@ -212,28 +215,25 @@ static int sideband_error_pkt(git_pkt **out, const char *line, size_t len) */ static int ref_pkt(git_pkt **out, const char *line, size_t len) { - int error; git_pkt_ref *pkt; size_t alloclen; - pkt = git__malloc(sizeof(git_pkt_ref)); + pkt = git__calloc(1, sizeof(git_pkt_ref)); GITERR_CHECK_ALLOC(pkt); - - memset(pkt, 0x0, sizeof(git_pkt_ref)); pkt->type = GIT_PKT_REF; - if ((error = git_oid_fromstr(&pkt->head.oid, line)) < 0) - goto error_out; - - /* Check for a bit of consistency */ - if (line[GIT_OID_HEXSZ] != ' ') { - giterr_set(GITERR_NET, "error parsing pkt-line"); - error = -1; - goto error_out; - } - /* Jump from the name */ - line += GIT_OID_HEXSZ + 1; - len -= (GIT_OID_HEXSZ + 1); + if (len < GIT_OID_HEXSZ || git_oid_fromstr(&pkt->head.oid, line) < 0) + goto out_err; + line += GIT_OID_HEXSZ; + len -= GIT_OID_HEXSZ; + + if (git__prefixncmp(line, len, " ")) + goto out_err; + line++; + len--; + + if (!len) + goto out_err; if (line[len - 1] == '\n') --len; @@ -245,36 +245,36 @@ static int ref_pkt(git_pkt **out, const char *line, size_t len) memcpy(pkt->head.name, line, len); pkt->head.name[len] = '\0'; - if (strlen(pkt->head.name) < len) { + if (strlen(pkt->head.name) < len) pkt->capabilities = strchr(pkt->head.name, '\0') + 1; - } *out = (git_pkt *)pkt; return 0; -error_out: +out_err: + giterr_set(GITERR_NET, "error parsing REF pkt-line"); + if (pkt) + git__free(pkt->head.name); git__free(pkt); - return error; + return -1; } static int ok_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ok *pkt; - const char *ptr; size_t alloc_len; pkt = git__malloc(sizeof(*pkt)); GITERR_CHECK_ALLOC(pkt); - pkt->type = GIT_PKT_OK; - line += 3; /* skip "ok " */ - if (!(ptr = strchr(line, '\n'))) { - giterr_set(GITERR_NET, "invalid packet line"); - git__free(pkt); - return -1; - } - len = ptr - line; + if (git__prefixncmp(line, len, "ok ")) + goto out_err; + line += 3; + len -= 3; + + if (line[len - 1] == '\n') + --len; GITERR_CHECK_ALLOC_ADD(&alloc_len, len, 1); pkt->ref = git__malloc(alloc_len); @@ -285,12 +285,17 @@ static int ok_pkt(git_pkt **out, const char *line, size_t len) *out = (git_pkt *)pkt; return 0; + +out_err: + giterr_set(GITERR_NET, "error parsing OK pkt-line"); + git__free(pkt); + return -1; } static int ng_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_ng *pkt; - const char *ptr; + const char *ptr, *eol; size_t alloclen; pkt = git__malloc(sizeof(*pkt)); @@ -299,11 +304,13 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) pkt->ref = NULL; pkt->type = GIT_PKT_NG; - if (len < 3) + eol = line + len; + + if (git__prefixncmp(line, len, "ng ")) goto out_err; - line += 3; /* skip "ng " */ - len -= 3; - if (!(ptr = memchr(line, ' ', len))) + line += 3; + + if (!(ptr = memchr(line, ' ', eol - line))) goto out_err; len = ptr - line; @@ -314,11 +321,11 @@ static int ng_pkt(git_pkt **out, const char *line, size_t len) memcpy(pkt->ref, line, len); pkt->ref[len] = '\0'; - if (len < 1) - goto out_err; line = ptr + 1; - len -= 1; - if (!(ptr = memchr(line, '\n', len))) + if (line >= eol) + goto out_err; + + if (!(ptr = memchr(line, '\n', eol - line))) goto out_err; len = ptr - line; @@ -343,13 +350,11 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_unpack *pkt; - GIT_UNUSED(len); - pkt = git__malloc(sizeof(*pkt)); GITERR_CHECK_ALLOC(pkt); - pkt->type = GIT_PKT_UNPACK; - if (!git__prefixcmp(line, "unpack ok")) + + if (!git__prefixncmp(line, len, "unpack ok")) pkt->unpack_ok = 1; else pkt->unpack_ok = 0; @@ -358,13 +363,17 @@ static int unpack_pkt(git_pkt **out, const char *line, size_t len) return 0; } -static int32_t parse_len(const char *line) +static int parse_len(size_t *out, const char *line, size_t linelen) { char num[PKT_LEN_SIZE + 1]; int i, k, error; int32_t len; const char *num_end; + /* Not even enough for the length */ + if (linelen < PKT_LEN_SIZE) + return GIT_EBUFS; + memcpy(num, line, PKT_LEN_SIZE); num[PKT_LEN_SIZE] = '\0'; @@ -376,7 +385,7 @@ static int32_t parse_len(const char *line) num[k] = '.'; } } - + giterr_set(GITERR_NET, "invalid hex digit in length: '%s'", num); return -1; } @@ -385,7 +394,11 @@ static int32_t parse_len(const char *line) if ((error = git__strtol32(&len, num, &num_end, 16)) < 0) return error; - return len; + if (len < 0) + return -1; + + *out = (size_t) len; + return 0; } /* @@ -402,35 +415,32 @@ static int32_t parse_len(const char *line) */ int git_pkt_parse_line( - git_pkt **head, const char *line, const char **out, size_t bufflen) + git_pkt **pkt, const char **endptr, const char *line, size_t linelen) { - int ret; - int32_t len; - - /* Not even enough for the length */ - if (bufflen > 0 && bufflen < PKT_LEN_SIZE) - return GIT_EBUFS; + int error; + size_t len; - len = parse_len(line); - if (len < 0) { + if ((error = parse_len(&len, line, linelen)) < 0) { /* - * If we fail to parse the length, it might be because the - * server is trying to send us the packfile already. + * If we fail to parse the length, it might be + * because the server is trying to send us the + * packfile already or because we do not yet have + * enough data. */ - if (bufflen >= 4 && !git__prefixcmp(line, "PACK")) { - giterr_clear(); - *out = line; - return pack_pkt(head); - } - - return (int)len; + if (error == GIT_EBUFS) + ; + else if (!git__prefixncmp(line, linelen, "PACK")) + giterr_set(GITERR_NET, "unexpected pack file"); + else + giterr_set(GITERR_NET, "bad packet length"); + return error; } /* - * If we were given a buffer length, then make sure there is - * enough in the buffer to satisfy this line + * Make sure there is enough in the buffer to satisfy + * this line. */ - if (bufflen > 0 && bufflen < (size_t)len) + if (linelen < len) return GIT_EBUFS; /* @@ -453,38 +463,38 @@ int git_pkt_parse_line( } if (len == 0) { /* Flush pkt */ - *out = line; - return flush_pkt(head); + *endptr = line; + return flush_pkt(pkt); } len -= PKT_LEN_SIZE; /* the encoded length includes its own size */ if (*line == GIT_SIDE_BAND_DATA) - ret = data_pkt(head, line, len); + error = data_pkt(pkt, line, len); else if (*line == GIT_SIDE_BAND_PROGRESS) - ret = sideband_progress_pkt(head, line, len); + error = sideband_progress_pkt(pkt, line, len); else if (*line == GIT_SIDE_BAND_ERROR) - ret = sideband_error_pkt(head, line, len); - else if (!git__prefixcmp(line, "ACK")) - ret = ack_pkt(head, line, len); - else if (!git__prefixcmp(line, "NAK")) - ret = nak_pkt(head); - else if (!git__prefixcmp(line, "ERR ")) - ret = err_pkt(head, line, len); + error = sideband_error_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "ACK")) + error = ack_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "NAK")) + error = nak_pkt(pkt); + else if (!git__prefixncmp(line, len, "ERR")) + error = err_pkt(pkt, line, len); else if (*line == '#') - ret = comment_pkt(head, line, len); - else if (!git__prefixcmp(line, "ok")) - ret = ok_pkt(head, line, len); - else if (!git__prefixcmp(line, "ng")) - ret = ng_pkt(head, line, len); - else if (!git__prefixcmp(line, "unpack")) - ret = unpack_pkt(head, line, len); + error = comment_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "ok")) + error = ok_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "ng")) + error = ng_pkt(pkt, line, len); + else if (!git__prefixncmp(line, len, "unpack")) + error = unpack_pkt(pkt, line, len); else - ret = ref_pkt(head, line, len); + error = ref_pkt(pkt, line, len); - *out = line + len; + *endptr = line + len; - return ret; + return error; } void git_pkt_free(git_pkt *pkt) diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index eab10aac69e..9283a6fe2cd 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -41,7 +41,7 @@ int git_smart__store_refs(transport_smart *t, int flushes) do { if (buf->offset > 0) - error = git_pkt_parse_line(&pkt, buf->data, &line_end, buf->offset); + error = git_pkt_parse_line(&pkt, &line_end, buf->data, buf->offset); else error = GIT_EBUFS; @@ -206,15 +206,15 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vec return 0; } -static int recv_pkt(git_pkt **out, gitno_buffer *buf) +static int recv_pkt(git_pkt **out, git_pkt_type *pkt_type, gitno_buffer *buf) { const char *ptr = buf->data, *line_end = ptr; git_pkt *pkt = NULL; - int pkt_type, error = 0, ret; + int error = 0, ret; do { if (buf->offset > 0) - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); + error = git_pkt_parse_line(&pkt, &line_end, ptr, buf->offset); else error = GIT_EBUFS; @@ -233,13 +233,14 @@ static int recv_pkt(git_pkt **out, gitno_buffer *buf) } while (error); gitno_consume(buf, line_end); - pkt_type = pkt->type; + if (pkt_type) + *pkt_type = pkt->type; if (out != NULL) *out = pkt; else git__free(pkt); - return pkt_type; + return error; } static int store_common(transport_smart *t) @@ -249,7 +250,7 @@ static int store_common(transport_smart *t) int error; do { - if ((error = recv_pkt(&pkt, buf)) < 0) + if ((error = recv_pkt(&pkt, NULL, buf)) < 0) return error; if (pkt->type == GIT_PKT_ACK) { @@ -317,7 +318,7 @@ static int wait_while_ack(gitno_buffer *buf) while (1) { git__free(pkt); - if ((error = recv_pkt((git_pkt **)&pkt, buf)) < 0) + if ((error = recv_pkt((git_pkt **)&pkt, NULL, buf)) < 0) return error; if (pkt->type == GIT_PKT_NAK) @@ -342,7 +343,8 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c gitno_buffer *buf = &t->buffer; git_buf data = GIT_BUF_INIT; git_revwalk *walk = NULL; - int error = -1, pkt_type; + int error = -1; + git_pkt_type pkt_type; unsigned int i; git_oid oid; @@ -392,16 +394,13 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c if ((error = store_common(t)) < 0) goto on_error; } else { - pkt_type = recv_pkt(NULL, buf); - - if (pkt_type == GIT_PKT_ACK) { + error = recv_pkt(NULL, &pkt_type, buf); + if (error < 0) { + goto on_error; + } else if (pkt_type == GIT_PKT_ACK) { break; } else if (pkt_type == GIT_PKT_NAK) { continue; - } else if (pkt_type < 0) { - /* recv_pkt returned an error */ - error = pkt_type; - goto on_error; } else { giterr_set(GITERR_NET, "Unexpected pkt type"); error = -1; @@ -467,10 +466,10 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c /* Now let's eat up whatever the server gives us */ if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) { - pkt_type = recv_pkt(NULL, buf); + error = recv_pkt(NULL, &pkt_type, buf); - if (pkt_type < 0) { - return pkt_type; + if (error < 0) { + return error; } else if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) { giterr_set(GITERR_NET, "Unexpected pkt type"); return -1; @@ -591,7 +590,7 @@ int git_smart__download_pack( goto done; } - if ((error = recv_pkt(&pkt, buf)) >= 0) { + if ((error = recv_pkt(&pkt, NULL, buf)) >= 0) { /* Check cancellation after network call */ if (t->cancelled.val) { giterr_clear(); @@ -749,7 +748,7 @@ static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, } while (line_len > 0) { - error = git_pkt_parse_line(&pkt, line, &line_end, line_len); + error = git_pkt_parse_line(&pkt, &line_end, line, line_len); if (error == GIT_EBUFS) { /* Buffer the data when the inner packet is split @@ -792,8 +791,8 @@ static int parse_report(transport_smart *transport, git_push *push) for (;;) { if (buf->offset > 0) - error = git_pkt_parse_line(&pkt, buf->data, - &line_end, buf->offset); + error = git_pkt_parse_line(&pkt, &line_end, + buf->data, buf->offset); else error = GIT_EBUFS; diff --git a/src/util.c b/src/util.c index a44f4c9acfc..964b0ab6a64 100644 --- a/src/util.c +++ b/src/util.c @@ -250,35 +250,47 @@ void git__strtolower(char *str) git__strntolower(str, strlen(str)); } -int git__prefixcmp(const char *str, const char *prefix) +GIT_INLINE(int) prefixcmp(const char *str, size_t str_n, const char *prefix, bool icase) { - for (;;) { - unsigned char p = *(prefix++), s; + int s, p; + + while (str_n--) { + s = (unsigned char)*str++; + p = (unsigned char)*prefix++; + + if (icase) { + s = git__tolower(s); + p = git__tolower(p); + } + if (!p) return 0; - if ((s = *(str++)) != p) + + if (s != p) return s - p; } + + return (0 - *prefix); } -int git__prefixcmp_icase(const char *str, const char *prefix) +int git__prefixcmp(const char *str, const char *prefix) { - return strncasecmp(str, prefix, strlen(prefix)); + return prefixcmp(str, SIZE_MAX, prefix, false); } -int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix) +int git__prefixncmp(const char *str, size_t str_n, const char *prefix) { - int s, p; - - while(str_n--) { - s = (unsigned char)git__tolower(*str++); - p = (unsigned char)git__tolower(*prefix++); + return prefixcmp(str, str_n, prefix, false); +} - if (s != p) - return s - p; - } +int git__prefixcmp_icase(const char *str, const char *prefix) +{ + return prefixcmp(str, SIZE_MAX, prefix, true); +} - return (0 - *prefix); +int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix) +{ + return prefixcmp(str, str_n, prefix, true); } int git__suffixcmp(const char *str, const char *suffix) diff --git a/src/util.h b/src/util.h index eb15250d8dd..8e666f9de2d 100644 --- a/src/util.h +++ b/src/util.h @@ -254,6 +254,7 @@ GIT_INLINE(void) git__free(void *ptr) extern int git__prefixcmp(const char *str, const char *prefix); extern int git__prefixcmp_icase(const char *str, const char *prefix); +extern int git__prefixncmp(const char *str, size_t str_n, const char *prefix); extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix); extern int git__suffixcmp(const char *str, const char *suffix); diff --git a/tests/config/include.c b/tests/config/include.c index 0a07c9b8514..36fc59b753e 100644 --- a/tests/config/include.c +++ b/tests/config/include.c @@ -32,6 +32,7 @@ void test_config_include__absolute(void) git_buf_free(&buf); git_config_free(cfg); + cl_git_pass(p_unlink("config-include-absolute")); } void test_config_include__homedir(void) @@ -51,6 +52,8 @@ void test_config_include__homedir(void) git_config_free(cfg); cl_sandbox_set_search_path_defaults(); + + cl_git_pass(p_unlink("config-include-homedir")); } /* We need to pretend that the variables were defined where the file was included */ @@ -75,6 +78,8 @@ void test_config_include__ordering(void) git_buf_free(&buf); git_config_free(cfg); + cl_git_pass(p_unlink("included")); + cl_git_pass(p_unlink("including")); } /* We need to pretend that the variables were defined where the file was included */ @@ -87,8 +92,23 @@ void test_config_include__depth(void) cl_git_fail(git_config_open_ondisk(&cfg, "a")); - p_unlink("a"); - p_unlink("b"); + cl_git_pass(p_unlink("a")); + cl_git_pass(p_unlink("b")); +} + +void test_config_include__empty_path_sanely_handled(void) +{ + git_config *cfg; + git_buf buf = GIT_BUF_INIT; + + cl_git_mkfile("a", "[include]\npath"); + cl_git_pass(git_config_open_ondisk(&cfg, "a")); + cl_git_pass(git_config_get_string_buf(&buf, cfg, "include.path")); + cl_assert_equal_s("", git_buf_cstr(&buf)); + + git_buf_free(&buf); + git_config_free(cfg); + cl_git_pass(p_unlink("a")); } void test_config_include__missing(void) @@ -106,6 +126,7 @@ void test_config_include__missing(void) git_buf_free(&buf); git_config_free(cfg); + cl_git_pass(p_unlink("including")); } void test_config_include__missing_homedir(void) @@ -126,6 +147,7 @@ void test_config_include__missing_homedir(void) git_config_free(cfg); cl_sandbox_set_search_path_defaults(); + cl_git_pass(p_unlink("including")); } #define replicate10(s) s s s s s s s s s s @@ -150,4 +172,8 @@ void test_config_include__depth2(void) git_buf_free(&buf); git_config_free(cfg); + + cl_git_pass(p_unlink("top-level")); + cl_git_pass(p_unlink("middle")); + cl_git_pass(p_unlink("bottom")); } diff --git a/tests/core/string.c b/tests/core/string.c index 90e8fa027cf..85db0c66230 100644 --- a/tests/core/string.c +++ b/tests/core/string.c @@ -40,6 +40,48 @@ void test_core_string__2(void) cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0); } +/* compare prefixes with len */ +void test_core_string__prefixncmp(void) +{ + cl_assert(git__prefixncmp("", 0, "") == 0); + cl_assert(git__prefixncmp("a", 1, "") == 0); + cl_assert(git__prefixncmp("", 0, "a") < 0); + cl_assert(git__prefixncmp("a", 1, "b") < 0); + cl_assert(git__prefixncmp("b", 1, "a") > 0); + cl_assert(git__prefixncmp("ab", 2, "a") == 0); + cl_assert(git__prefixncmp("ab", 1, "a") == 0); + cl_assert(git__prefixncmp("ab", 2, "ac") < 0); + cl_assert(git__prefixncmp("a", 1, "ac") < 0); + cl_assert(git__prefixncmp("ab", 1, "ac") < 0); + cl_assert(git__prefixncmp("ab", 2, "aa") > 0); + cl_assert(git__prefixncmp("ab", 1, "aa") < 0); +} + +/* compare prefixes with len */ +void test_core_string__prefixncmp_icase(void) +{ + cl_assert(git__prefixncmp_icase("", 0, "") == 0); + cl_assert(git__prefixncmp_icase("a", 1, "") == 0); + cl_assert(git__prefixncmp_icase("", 0, "a") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "b") < 0); + cl_assert(git__prefixncmp_icase("A", 1, "b") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "B") < 0); + cl_assert(git__prefixncmp_icase("b", 1, "a") > 0); + cl_assert(git__prefixncmp_icase("B", 1, "a") > 0); + cl_assert(git__prefixncmp_icase("b", 1, "A") > 0); + cl_assert(git__prefixncmp_icase("ab", 2, "a") == 0); + cl_assert(git__prefixncmp_icase("Ab", 2, "a") == 0); + cl_assert(git__prefixncmp_icase("ab", 2, "A") == 0); + cl_assert(git__prefixncmp_icase("ab", 1, "a") == 0); + cl_assert(git__prefixncmp_icase("ab", 2, "ac") < 0); + cl_assert(git__prefixncmp_icase("Ab", 2, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 2, "Ac") < 0); + cl_assert(git__prefixncmp_icase("a", 1, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 1, "ac") < 0); + cl_assert(git__prefixncmp_icase("ab", 2, "aa") > 0); + cl_assert(git__prefixncmp_icase("ab", 1, "aa") < 0); +} + void test_core_string__strcmp(void) { cl_assert(git__strcmp("", "") == 0); diff --git a/tests/submodule/inject_option.c b/tests/submodule/inject_option.c new file mode 100644 index 00000000000..c13e12cf6de --- /dev/null +++ b/tests/submodule/inject_option.c @@ -0,0 +1,80 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "path.h" +#include "submodule_helpers.h" +#include "fileops.h" +#include "repository.h" + +static git_repository *g_repo = NULL; + +void test_submodule_inject_option__initialize(void) +{ + g_repo = setup_fixture_submodule_simple(); +} + +void test_submodule_inject_option__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static int find_naughty(git_submodule *sm, const char *name, void *payload) +{ + int *foundit = (int *) payload; + + GIT_UNUSED(sm); + + if (!git__strcmp("naughty", name)) + *foundit = true; + + return 0; +} + +void test_submodule_inject_option__url(void) +{ + int foundit; + git_submodule *sm; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules")); + cl_git_rewritefile(buf.ptr, + "[submodule \"naughty\"]\n" + " path = testrepo\n" + " url = -u./payload\n"); + git_buf_free(&buf); + + /* We do want to find it, but with the appropriate field empty */ + foundit = 0; + cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit)); + cl_assert_equal_i(1, foundit); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty")); + cl_assert_equal_s("testrepo", git_submodule_path(sm)); + cl_assert_equal_p(NULL, git_submodule_url(sm)); + + git_submodule_free(sm); +} + +void test_submodule_inject_option__path(void) +{ + int foundit; + git_submodule *sm; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules")); + cl_git_rewritefile(buf.ptr, + "[submodule \"naughty\"]\n" + " path = --something\n" + " url = blah.git\n"); + git_buf_free(&buf); + + /* We do want to find it, but with the appropriate field empty */ + foundit = 0; + cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit)); + cl_assert_equal_i(1, foundit); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty")); + cl_assert_equal_s("naughty", git_submodule_path(sm)); + cl_assert_equal_s("blah.git", git_submodule_url(sm)); + + git_submodule_free(sm); +} diff --git a/tests/transports/smart/packet.c b/tests/transports/smart/packet.c new file mode 100644 index 00000000000..5b623a378b9 --- /dev/null +++ b/tests/transports/smart/packet.c @@ -0,0 +1,340 @@ +#include "clar_libgit2.h" +#include "transports/smart.h" + +enum expected_status { + PARSE_SUCCESS, + PARSE_FAILURE +}; + +static void assert_flush_parses(const char *line) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_FLUSH); + cl_assert_equal_strn(endptr, line + 4, linelen - 4); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_data_pkt_parses(const char *line, const char *expected_data, size_t expected_len) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_data *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_DATA); + cl_assert_equal_i(pkt->len, expected_len); + cl_assert_equal_strn(pkt->data, expected_data, expected_len); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_sideband_progress_parses(const char *line, const char *expected_data, size_t expected_len) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_progress *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_PROGRESS); + cl_assert_equal_i(pkt->len, expected_len); + cl_assert_equal_strn(pkt->data, expected_data, expected_len); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_error_parses(const char *line, const char *expected_error, size_t expected_len) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_err *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_ERR); + cl_assert_equal_i(pkt->len, expected_len); + cl_assert_equal_strn(pkt->error, expected_error, expected_len); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_ack_parses(const char *line, const char *expected_oid, enum git_ack_status expected_status) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_ack *pkt; + git_oid oid; + + cl_git_pass(git_oid_fromstr(&oid, expected_oid)); + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_ACK); + cl_assert_equal_oid(&pkt->oid, &oid); + cl_assert_equal_i(pkt->status, expected_status); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_nak_parses(const char *line) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_NAK); + cl_assert_equal_strn(endptr, line + 7, linelen - 7); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_comment_parses(const char *line, const char *expected_comment) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_comment *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_COMMENT); + cl_assert_equal_strn(pkt->comment, expected_comment, strlen(expected_comment)); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_ok_parses(const char *line, const char *expected_ref) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_ok *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_OK); + cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref)); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_unpack_parses(const char *line, bool ok) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_unpack *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_UNPACK); + cl_assert_equal_i(pkt->unpack_ok, ok); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_ng_parses(const char *line, const char *expected_ref, const char *expected_msg) +{ + size_t linelen = strlen(line) + 1; + const char *endptr; + git_pkt_ng *pkt; + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_NG); + cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref)); + cl_assert_equal_strn(pkt->msg, expected_msg, strlen(expected_msg)); + + git_pkt_free((git_pkt *) pkt); +} + +#define assert_ref_parses(line, expected_oid, expected_ref, expected_capabilities) \ + assert_ref_parses_(line, sizeof(line), expected_oid, expected_ref, expected_capabilities) + +static void assert_ref_parses_(const char *line, size_t linelen, const char *expected_oid, + const char *expected_ref, const char *expected_capabilities) +{ + const char *endptr; + git_pkt_ref *pkt; + git_oid oid; + + cl_git_pass(git_oid_fromstr(&oid, expected_oid)); + + cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen)); + cl_assert_equal_i(pkt->type, GIT_PKT_REF); + cl_assert_equal_oid(&pkt->head.oid, &oid); + cl_assert_equal_strn(pkt->head.name, expected_ref, strlen(expected_ref)); + if (expected_capabilities) + cl_assert_equal_strn(pkt->capabilities, expected_capabilities, strlen(expected_capabilities)); + else + cl_assert_equal_p(NULL, pkt->capabilities); + + git_pkt_free((git_pkt *) pkt); +} + +static void assert_pkt_fails(const char *line) +{ + const char *endptr; + git_pkt *pkt; + cl_git_fail(git_pkt_parse_line(&pkt, &endptr, line, strlen(line) + 1)); +} + +void test_transports_smart_packet__parsing_garbage_fails(void) +{ + assert_pkt_fails("0foobar"); + assert_pkt_fails("00foobar"); + assert_pkt_fails("000foobar"); + assert_pkt_fails("0001"); + assert_pkt_fails(""); + assert_pkt_fails("0"); + assert_pkt_fails("0i00"); + assert_pkt_fails("f"); +} + +void test_transports_smart_packet__flush_parses(void) +{ + assert_flush_parses("0000"); + assert_flush_parses("0000foobar"); +} + +void test_transports_smart_packet__data_pkt(void) +{ + assert_pkt_fails("000foobar"); + assert_pkt_fails("0001o"); + assert_pkt_fails("0001\1"); + assert_data_pkt_parses("0005\1", "", 0); + assert_pkt_fails("0009\1o"); + assert_data_pkt_parses("0009\1data", "data", 4); + assert_data_pkt_parses("000a\1data", "data", 5); +} + +void test_transports_smart_packet__sideband_progress_pkt(void) +{ + assert_pkt_fails("0001\2"); + assert_sideband_progress_parses("0005\2", "", 0); + assert_pkt_fails("0009\2o"); + assert_sideband_progress_parses("0009\2data", "data", 4); + assert_sideband_progress_parses("000a\2data", "data", 5); +} + +void test_transports_smart_packet__sideband_err_pkt(void) +{ + assert_pkt_fails("0001\3"); + assert_error_parses("0005\3", "", 0); + assert_pkt_fails("0009\3o"); + assert_error_parses("0009\3data", "data", 4); + assert_error_parses("000a\3data", "data", 5); +} + +void test_transports_smart_packet__ack_pkt(void) +{ + assert_ack_parses("0030ACK 0000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000", 0); + assert_ack_parses("0039ACK 0000000000000000000000000000000000000000 continue", + "0000000000000000000000000000000000000000", + GIT_ACK_CONTINUE); + assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 common", + "0000000000000000000000000000000000000000", + GIT_ACK_COMMON); + assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 ready", + "0000000000000000000000000000000000000000", + GIT_ACK_READY); + + /* these should fail as they don't have OIDs */ + assert_pkt_fails("0007ACK"); + assert_pkt_fails("0008ACK "); + + /* this one is missing a space and should thus fail */ + assert_pkt_fails("0036ACK00000000000000000x0000000000000000000000 ready"); + + /* the following ones have invalid OIDs and should thus fail */ + assert_pkt_fails("0037ACK 00000000000000000x0000000000000000000000 ready"); + assert_pkt_fails("0036ACK 000000000000000000000000000000000000000 ready"); + assert_pkt_fails("0036ACK 00000000000000000x0000000000000000000000ready"); + + /* this one has an invalid status and should thus fail */ + assert_pkt_fails("0036ACK 0000000000000000000000000000000000000000 read"); +} + +void test_transports_smart_packet__nak_pkt(void) +{ + assert_nak_parses("0007NAK"); + assert_pkt_fails("0007NaK"); + assert_pkt_fails("0007nak"); + assert_nak_parses("0007NAKfoobar"); + assert_pkt_fails("0007nakfoobar"); + assert_pkt_fails("0007 NAK"); +} + +void test_transports_smart_packet__error_pkt(void) +{ + assert_pkt_fails("0007ERR"); + assert_pkt_fails("0008ERRx"); + assert_error_parses("0008ERR ", "", 0); + assert_error_parses("000EERR ERRMSG", "ERRMSG", 6); +} + +void test_transports_smart_packet__comment_pkt(void) +{ + assert_comment_parses("0005#", ""); + assert_comment_parses("000B#foobar", "#fooba"); + assert_comment_parses("000C#foobar", "#foobar"); + assert_comment_parses("001A#this is a comment\nfoo", "#this is a comment\nfoo"); +} + +void test_transports_smart_packet__ok_pkt(void) +{ + assert_pkt_fails("0007ok\n"); + assert_ok_parses("0007ok ", ""); + assert_ok_parses("0008ok \n", ""); + assert_ok_parses("0008ok x", "x"); + assert_ok_parses("0009ok x\n", "x"); + assert_pkt_fails("001OK ref/foo/bar"); + assert_ok_parses("0012ok ref/foo/bar", "ref/foo/bar"); + assert_pkt_fails("0013OK ref/foo/bar\n"); + assert_ok_parses("0013ok ref/foo/bar\n", "ref/foo/bar"); +} + +void test_transports_smart_packet__ng_pkt(void) +{ + /* TODO: same as for ok pkt */ + assert_pkt_fails("0007ng\n"); + assert_pkt_fails("0008ng \n"); + assert_pkt_fails("000Bng ref\n"); + assert_pkt_fails("000Bng ref\n"); + /* TODO: is this a valid packet line? Probably not. */ + assert_ng_parses("000Ang x\n", "", "x"); + assert_ng_parses("000Fng ref msg\n", "ref", "msg"); + assert_ng_parses("000Fng ref msg\n", "ref", "msg"); +} + +void test_transports_smart_packet__unpack_pkt(void) +{ + assert_unpack_parses("000Dunpack ok", 1); + assert_unpack_parses("000Dunpack ng error-msg", 0); + /* TODO: the following tests should fail */ + assert_unpack_parses("000Aunpack", 0); + assert_unpack_parses("0011unpack foobar", 0); + assert_unpack_parses("0010unpack ng ok", 0); + assert_unpack_parses("0010unpack okfoo", 1); +} + +void test_transports_smart_packet__ref_pkt(void) +{ + assert_pkt_fails("002C0000000000000000000000000000000000000000"); + assert_pkt_fails("002D0000000000000000000000000000000000000000\n"); + assert_pkt_fails("00300000000000000000000000000000000000000000HEAD"); + assert_pkt_fails("004800000000x0000000000000000000000000000000 refs/heads/master\0multi_ack"); + assert_ref_parses( + "003F0000000000000000000000000000000000000000 refs/heads/master\0", + "0000000000000000000000000000000000000000", "refs/heads/master", ""); + assert_ref_parses( + "00480000000000000000000000000000000000000000 refs/heads/master\0multi_ack", + "0000000000000000000000000000000000000000", "refs/heads/master", "multi_ack"); + assert_ref_parses( + "00460000000000000000000000000000000000000000 refs/heads/master\0one two", + "0000000000000000000000000000000000000000", "refs/heads/master", "one two"); + assert_ref_parses( + "00310000000000000000000000000000000000000000 HEAD", + "0000000000000000000000000000000000000000", "HEAD", NULL); + assert_pkt_fails("0031000000000000000000000000000000000000000 HEAD"); + assert_ref_parses( + "00360000000000000000000000000000000000000000 HEAD HEAD", + "0000000000000000000000000000000000000000", "HEAD HEAD", NULL); +}