threads / patch / 16099

patchIntroduce receive.denyDeletes

Subject: [PATCH] Introduce receive.denyDeletes

## tl;dr

8 messages between Oct 30, 2008 and Nov 1, 2008. Diffs are folded; open one to read it.

replies: 7people: 4as markdown or json

Jan Krüger· Oct 30, 2008, 18:11 UTC · lore

Occasionally, it may be useful to prevent branches from getting deleted from a centralized repository, particularly when no administrative access to the server is available to undo it via reflog. It also makes receive.denyNonFastForwards more useful if it is used for access control, since it prevents force-updating refs by deleting and re-creating a ref.

Signed-off-by: Jan Krüger <jk@jk.gs>
---
Fairly low invasiveness. Includes documentation and test case. I have run all
parts of the test suite that use receive-pack, send-pack and friends.
 Documentation/config.txt |    4 ++++
 builtin-receive-pack.c   |   12 ++++++++++++
 t/t5400-send-pack.sh     |   11 +++++++++++
 3 files changed, 27 insertions(+), 0 deletions(-)
Show changes to 3 files +27 −0

Documentation/config.txt, builtin-receive-pack.c, t/t5400-send-pack.sh

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 29369d0..965ed74 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1188,6 +1188,10 @@ receive.unpackLimit::
 	especially on slow filesystems.  If not set, the value of
 	`transfer.unpackLimit` is used instead.
 
+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.
+
 receive.denyNonFastForwards::
 	If set to true, git-receive-pack will deny a ref update which is
 	not a fast forward. Use this to prevent such an update via a push,
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index 9f60f31..2c0225c 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -11,6 +11,7 @@
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
+static int deny_deletes = 0;
 static int deny_non_fast_forwards = 0;
 static int receive_fsck_objects;
 static int receive_unpack_limit = -1;
@@ -23,6 +24,11 @@ static int capabilities_sent;
 
 static int receive_pack_config(const char *var, const char *value, void *cb)
 {
+	if (strcmp(var, "receive.denydeletes") == 0) {
+		deny_deletes = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (strcmp(var, "receive.denynonfastforwards") == 0) {
 		deny_non_fast_forwards = git_config_bool(var, value);
 		return 0;
@@ -185,6 +191,12 @@ static const char *update(struct command *cmd)
 		      "but I can't find it!", sha1_to_hex(new_sha1));
 		return "bad pack";
 	}
+	if (deny_deletes && is_null_sha1(new_sha1) &&
+	    !is_null_sha1(old_sha1) &&
+	    !prefixcmp(name, "refs/heads/")) {
+		error("denying ref deletion for %s", name);
+		return "deletion prohibited";
+	}
 	if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
 	    !is_null_sha1(old_sha1) &&
 	    !prefixcmp(name, "refs/heads/")) {
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 544771d..6db9e18 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -104,6 +104,17 @@ HOME=`pwd`/no-such-directory
 export HOME ;# this way we force the victim/.git/config to be used.
 
 test_expect_success \
+	'pushing a delete should be denied with denyDeletes' '
+	cd victim &&
+	git config receive.denyDeletes true &&
+	git branch extra master &&
+	cd .. &&
+	test -f victim/.git/refs/heads/extra &&
+	git send-pack ./victim/.git/ :extra master && return 1
+	rm -f victim/.git/refs/heads/extra
+'
+
+test_expect_success \
         'pushing with --force should be denied with denyNonFastforwards' '
 	cd victim &&
 	git config receive.denyNonFastforwards true &&
-- 
1.6.0.3.523.g304d0.dirty
Shawn O. Pearce· Oct 30, 2008, 18:32 UTC · re: Jan Krüger · lore

Re: [PATCH] Introduce receive.denyDeletes

Jan Krrrger <jk@jk.gs> wrote:
Show 5 quoted lines
> Occasionally, it may be useful to prevent branches from getting deleted from
> a centralized repository, particularly when no administrative access to the
> server is available to undo it via reflog. It also makes
> receive.denyNonFastForwards more useful if it is used for access control, since
> it prevents force-updating refs by deleting and re-creating a ref.
...
Show 8 quoted lines
>  test_expect_success \
> +	'pushing a delete should be denied with denyDeletes' '
> +	cd victim &&
> +	git config receive.denyDeletes true &&
> +	git branch extra master &&
> +	cd .. &&
> +	test -f victim/.git/refs/heads/extra &&
> +	git send-pack ./victim/.git/ :extra master && return 1
Hmm, that should be:
  test_must_fail git send-pack ./victim/.git/ :extra master
-- 
Shawn.
Jan Krüger· Oct 30, 2008, 18:45 UTC · re: Shawn O. Pearce · lore

Re: [PATCH] Introduce receive.denyDeletes

On Thu, 30 Oct 2008 11:32:10 -0700, "Shawn O. Pearce" <spearce@spearce.org> wrote:

Show 7 quoted lines
> >  test_expect_success \
> > [snip]
> > +	git send-pack ./victim/.git/ :extra master && return 1
> 
> Hmm, that should be:
> 
>   test_must_fail git send-pack ./victim/.git/ :extra master

Can I then delete the branch afterwards without lots of juggling (in case the test fails due to a random other reason that the branch accidentally getting deleted by receive-pack)? I'd expect I'd have to save the exit code to a temporary variable and that's just as ugly.

Disclaimer: I sort of just hacked the test case together by stringing
together pieces of existing test code, so I don't presume full
understanding of the test framework.
By the way, I love what your mail client did to my name.
-Jan
Junio C Hamano· Oct 31, 2008, 08:04 UTC · re: Jan Krüger · lore

Re: [PATCH] Introduce receive.denyDeletes

"Jan Krüger" <jk@jk.gs> writes:
> Can I then delete the branch afterwards without lots of juggling (in
> case the test fails due to a random other reason that the branch
> accidentally getting deleted by receive-pack)? I'd expect I'd have to
> save the exit code to a temporary variable and that's just as ugly.

Although I agree that your attempt to allow the test continue even when this test fails is a very good practice, I personally do not find the alternative you mention ugly at all. I actually find that "return 1" uglier because it feels like it knows too much about how test_expect_success is implemented.

Shawn O. Pearce· Oct 31, 2008, 14:30 UTC · re: Junio C Hamano · lore

Re: [PATCH] Introduce receive.denyDeletes

Junio C Hamano <gitster@pobox.com> wrote:
Show 6 quoted lines
> "Jan Krüger" <jk@jk.gs> writes:
> 
> > Can I then delete the branch afterwards without lots of juggling (in
> > case the test fails due to a random other reason that the branch
> > accidentally getting deleted by receive-pack)? I'd expect I'd have to
> > save the exit code to a temporary variable and that's just as ugly.
If you want to delete the branch after the test is done, do it
outside of the test_expect_success's 3rd argument.  Then it will
run the branch deletion whether or not the test was successful.
 
Show 5 quoted lines
> Although I agree that your attempt to allow the test continue even when
> this test fails is a very good practice, I personally do not find the
> alternative you mention ugly at all.  I actually find that "return 1"
> uglier because it feels like it knows too much about how
> test_expect_success is implemented.

Yea, I also found the "return 1" to be horribly difficult to read, and knowing far too much about the test suite.

-- 
Shawn.
Jan Krüger· Nov 1, 2008, 14:42 UTC · re: Shawn O. Pearce · lore

[PATCH v2] Introduce receive.denyDeletes

Occasionally, it may be useful to prevent branches from getting deleted from a centralized repository, particularly when no administrative access to the server is available to undo it via reflog. It also makes receive.denyNonFastForwards more useful if it is used for access control since it prevents force-updating by deleting and re-creating a ref.

Signed-off-by: Jan Krüger <jk@jk.gs>
---
Like this, then?
 Documentation/config.txt |    4 ++++
 builtin-receive-pack.c   |   12 ++++++++++++
 t/t5400-send-pack.sh     |   11 +++++++++++
 3 files changed, 27 insertions(+), 0 deletions(-)
Show changes to 3 files +27 −0

Documentation/config.txt, builtin-receive-pack.c, t/t5400-send-pack.sh

diff --git a/Documentation/config.txt b/Documentation/config.txt
index 29369d0..965ed74 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1188,6 +1188,10 @@ receive.unpackLimit::
 	especially on slow filesystems.  If not set, the value of
 	`transfer.unpackLimit` is used instead.
 
+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.
+
 receive.denyNonFastForwards::
 	If set to true, git-receive-pack will deny a ref update which is
 	not a fast forward. Use this to prevent such an update via a push,
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index 9f60f31..2c0225c 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -11,6 +11,7 @@
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
+static int deny_deletes = 0;
 static int deny_non_fast_forwards = 0;
 static int receive_fsck_objects;
 static int receive_unpack_limit = -1;
@@ -23,6 +24,11 @@ static int capabilities_sent;
 
 static int receive_pack_config(const char *var, const char *value, void *cb)
 {
+	if (strcmp(var, "receive.denydeletes") == 0) {
+		deny_deletes = git_config_bool(var, value);
+		return 0;
+	}
+
 	if (strcmp(var, "receive.denynonfastforwards") == 0) {
 		deny_non_fast_forwards = git_config_bool(var, value);
 		return 0;
@@ -185,6 +191,12 @@ static const char *update(struct command *cmd)
 		      "but I can't find it!", sha1_to_hex(new_sha1));
 		return "bad pack";
 	}
+	if (deny_deletes && is_null_sha1(new_sha1) &&
+	    !is_null_sha1(old_sha1) &&
+	    !prefixcmp(name, "refs/heads/")) {
+		error("denying ref deletion for %s", name);
+		return "deletion prohibited";
+	}
 	if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
 	    !is_null_sha1(old_sha1) &&
 	    !prefixcmp(name, "refs/heads/")) {
diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
index 544771d..6fe2f87 100755
--- a/t/t5400-send-pack.sh
+++ b/t/t5400-send-pack.sh
@@ -103,6 +103,17 @@ unset GIT_CONFIG GIT_CONFIG_LOCAL
 HOME=`pwd`/no-such-directory
 export HOME ;# this way we force the victim/.git/config to be used.
 
+test_expect_failure \
+	'pushing a delete should be denied with denyDeletes' '
+	cd victim &&
+	git config receive.denyDeletes true &&
+	git branch extra master &&
+	cd .. &&
+	test -f victim/.git/refs/heads/extra &&
+	test_must_fail git send-pack ./victim/.git/ :extra master
+'
+rm -f victim/.git/refs/heads/extra
+
 test_expect_success \
         'pushing with --force should be denied with denyNonFastforwards' '
 	cd victim &&
-- 
1.6.0.3.524.g86cf.dirty
Shawn O. Pearce· Nov 1, 2008, 18:07 UTC · re: Jan Krüger · lore

Re: [PATCH v2] Introduce receive.denyDeletes

Jan Krrrger <jk@jk.gs> wrote:
Show 9 quoted lines
> Occasionally, it may be useful to prevent branches from getting deleted from
> a centralized repository, particularly when no administrative access to the
> server is available to undo it via reflog. It also makes
> receive.denyNonFastForwards more useful if it is used for access control
> since it prevents force-updating by deleting and re-creating a ref.
> 
> Signed-off-by: Jan Krüger <jk@jk.gs>
> ---
> Like this, then?
Acked-by: Shawn O. Pearce <spearce@spearce.org>
 
Show 81 quoted lines
>  Documentation/config.txt |    4 ++++
>  builtin-receive-pack.c   |   12 ++++++++++++
>  t/t5400-send-pack.sh     |   11 +++++++++++
>  3 files changed, 27 insertions(+), 0 deletions(-)
> 
> diff --git a/Documentation/config.txt b/Documentation/config.txt
> index 29369d0..965ed74 100644
> --- a/Documentation/config.txt
> +++ b/Documentation/config.txt
> @@ -1188,6 +1188,10 @@ receive.unpackLimit::
>  	especially on slow filesystems.  If not set, the value of
>  	`transfer.unpackLimit` is used instead.
>  
> +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.
> +
>  receive.denyNonFastForwards::
>  	If set to true, git-receive-pack will deny a ref update which is
>  	not a fast forward. Use this to prevent such an update via a push,
> diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
> index 9f60f31..2c0225c 100644
> --- a/builtin-receive-pack.c
> +++ b/builtin-receive-pack.c
> @@ -11,6 +11,7 @@
>  
>  static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
>  
> +static int deny_deletes = 0;
>  static int deny_non_fast_forwards = 0;
>  static int receive_fsck_objects;
>  static int receive_unpack_limit = -1;
> @@ -23,6 +24,11 @@ static int capabilities_sent;
>  
>  static int receive_pack_config(const char *var, const char *value, void *cb)
>  {
> +	if (strcmp(var, "receive.denydeletes") == 0) {
> +		deny_deletes = git_config_bool(var, value);
> +		return 0;
> +	}
> +
>  	if (strcmp(var, "receive.denynonfastforwards") == 0) {
>  		deny_non_fast_forwards = git_config_bool(var, value);
>  		return 0;
> @@ -185,6 +191,12 @@ static const char *update(struct command *cmd)
>  		      "but I can't find it!", sha1_to_hex(new_sha1));
>  		return "bad pack";
>  	}
> +	if (deny_deletes && is_null_sha1(new_sha1) &&
> +	    !is_null_sha1(old_sha1) &&
> +	    !prefixcmp(name, "refs/heads/")) {
> +		error("denying ref deletion for %s", name);
> +		return "deletion prohibited";
> +	}
>  	if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
>  	    !is_null_sha1(old_sha1) &&
>  	    !prefixcmp(name, "refs/heads/")) {
> diff --git a/t/t5400-send-pack.sh b/t/t5400-send-pack.sh
> index 544771d..6fe2f87 100755
> --- a/t/t5400-send-pack.sh
> +++ b/t/t5400-send-pack.sh
> @@ -103,6 +103,17 @@ unset GIT_CONFIG GIT_CONFIG_LOCAL
>  HOME=`pwd`/no-such-directory
>  export HOME ;# this way we force the victim/.git/config to be used.
>  
> +test_expect_failure \
> +	'pushing a delete should be denied with denyDeletes' '
> +	cd victim &&
> +	git config receive.denyDeletes true &&
> +	git branch extra master &&
> +	cd .. &&
> +	test -f victim/.git/refs/heads/extra &&
> +	test_must_fail git send-pack ./victim/.git/ :extra master
> +'
> +rm -f victim/.git/refs/heads/extra
> +
>  test_expect_success \
>          'pushing with --force should be denied with denyNonFastforwards' '
>  	cd victim &&
> -- 
> 1.6.0.3.524.g86cf.dirty
-- 
Shawn.

← back to recent threads