The new receive.objectCountLimit config variable defines an upper limit on the number of objects to accept in a single push. If the number of objects in a push exceeds this limit, the entire push is discarded without storing the pushed objects on the server at all. This is meant to prevent an unintended large push (typically a result of the user not being aware of exactly what is being pushed, e.g. pushing a large rewritten history) from entering the repo. Usually, this kind of limit could be imposed by a pre-receive or update hook, but both of those run _after_ the pack has been received and stored by receive-pack, so they cannot prevent the pack from being stored on the server. Documentation and tests are included. Improved-by: Junio C Hamano <gitster@xxxxxxxxx> Signed-off-by: Johan Herland <johan@xxxxxxxxxxx> --- Here is the re-roll with "receive-in-core-and-discard", plus the config variable rename. Have fun! :) ...Johan Documentation/config.txt | 9 +++++++++ builtin/receive-pack.c | 14 +++++++++++++- t/t5400-send-pack.sh | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 285c7f7..b356956 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -1591,6 +1591,15 @@ receive.unpackLimit:: especially on slow filesystems. If not set, the value of `transfer.unpackLimit` is used instead. +receive.objectCountLimit:: + If the number of objects received in a push exceeds this limit, + then the entire push will be refused. This is meant to prevent + an unintended large push (typically a result of the user not + being aware of exactly what is being pushed, e.g. pushing a + large rewritten history) from entering the repo. If not set, + there is no upper limit on the number of objects transferred + in a single push. + receive.denyDeletes:: If set to true, git-receive-pack will deny a ref update that deletes the ref. Use this to prevent such a ref deletion via a push. diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c index e1ba4dc..f521c83 100644 --- a/builtin/receive-pack.c +++ b/builtin/receive-pack.c @@ -25,6 +25,7 @@ static int deny_non_fast_forwards; static enum deny_action deny_current_branch = DENY_UNCONFIGURED; static enum deny_action deny_delete_current = DENY_UNCONFIGURED; static int receive_fsck_objects; +static int receive_object_count_limit; static int receive_unpack_limit = -1; static int transfer_unpack_limit = -1; static int unpack_limit = 100; @@ -63,6 +64,11 @@ static int receive_pack_config(const char *var, const char *value, void *cb) return 0; } + if (strcmp(var, "receive.objectcountlimit") == 0) { + receive_object_count_limit = git_config_int(var, value); + return 0; + } + if (strcmp(var, "receive.unpacklimit") == 0) { receive_unpack_limit = git_config_int(var, value); return 0; @@ -648,7 +654,13 @@ static const char *unpack(void) "--pack_header=%"PRIu32",%"PRIu32, ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries)); - if (ntohl(hdr.hdr_entries) < unpack_limit) { + if (receive_object_count_limit > 0 && + ntohl(hdr.hdr_entries) > receive_object_count_limit) { + char buf[1024]; + while (xread(0, buf, 1024) > 0) + ; /* nothing */ + return "received pack exceeds configured receive.objectCountLimit"; + } else if (ntohl(hdr.hdr_entries) < unpack_limit) { int code, i = 0; const char *unpacker[4]; unpacker[i++] = "unpack-objects"; diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh index 0eace37..9198019 100755 --- a/t/t5400-send-pack.sh +++ b/t/t5400-send-pack.sh @@ -222,4 +222,48 @@ test_expect_success 'deny pushing to delete current branch' ' ) ' +test_expect_success 'deny pushing when receive.objectCountLimit is exceeded' ' + rewound_push_setup && + ( + cd parent && + git config receive.objectCountLimit 1 + ) && + ( + cd child && + git reset --hard origin/master && + echo three > file && git commit -a -m three && + test_must_fail git send-pack ../parent master 2>errs && + grep -q "receive\\.objectCountLimit" errs + ) && + parent_head=$(cd parent && git rev-parse --verify master) && + child_head=$(cd child && git rev-parse --verify master) && + test "$parent_head" != "$child_head" +' + +test_expect_success 'repeated push failure proves that objects were not stored remotely' ' + ( + cd child && + test_must_fail git send-pack ../parent master 2>errs && + grep -q "receive\\.objectCountLimit" errs + ) && + parent_head=$(cd parent && git rev-parse --verify master) && + child_head=$(cd child && git rev-parse --verify master) && + test "$parent_head" != "$child_head" +' + +test_expect_success 'increasing receive.objectCountLimit allows the push' ' + ( + cd parent && + git config receive.objectCountLimit 10 + ) && + ( + cd child && + git send-pack ../parent master 2>errs && + test_must_fail grep -q "receive\\.objectCountLimit" errs + ) && + parent_head=$(cd parent && git rev-parse --verify master) && + child_head=$(cd child && git rev-parse --verify master) && + test "$parent_head" = "$child_head" +' + test_done -- 1.7.5.rc1.3.g4d7b -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html