git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] remote.c: Fix overtight refspec validation

From
Daniel Barkalow <barkalow@iabervon.org>
Date
Mar 21, 2008, 23:12 UTC
Message-ID
<alpine.LNX.1.00.0803211840480.19665@iabervon.org>
In-Reply-To
<7vlk4boh6v.fsf@gitster.siamese.dyndns.org>
On Fri, 21 Mar 2008, Junio C Hamano wrote:
Show 24 quoted lines
> We tightened the refspec validation code in an earlier ef00d15 (Tighten
> refspec processing, 2008-03-17) per my suggestion, but the suggestion was
> misguided to begin with and it broke this usage:
> 
>     $ git push origin HEAD~12:master
> 
> The syntax of push refspecs and fetch refspecs are similar in that they
> are both colon separated LHS and RHS (possibly prefixed with a + to
> force), but the similarity ends there.  For example, LHS in a push refspec
> can be anything that evaluates to a valid object name at runtime (except
> when colon and RHS is missing, or it is a glob), while it must be a
> valid-looking refname in a fetch refspec.  To validate them correctly, the
> caller needs to be able to say which kind of refspecs they are.  It is
> unreasonable to keep a single interface that cannot tell which kind it is
> dealing with, and ask it to behave sensibly.
> 
> This commit separates the parsing of the two into different functions, and
> clarifies the code to implement the parsing proper (i.e. splitting into
> two parts, making sure both sides are wildcard or neither side is).
> 
> This happens to also allow pushing a commit named with the esoteric "look
> for that string" syntax:
> 
>     $ git push ../test.git ':/remote.c: Fix overtight refspec:master'
Show 152 quoted lines
> 
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
> 
>   Daniel Barkalow <barkalow@iabervon.org> writes:
> 
>   > ... (although the 
>   > patch text made it hard to see; we really need some magic to make diff not 
>   > bother to save 4 identifier-free lines and just show it as a complete 
>   > replacement).
> 
>   Yeah, I tend to agree and some people seem to favor "diff -c" for this
>   reason.
> 
>   In any case, here is an updated patch with a better description and
>   necessary tests.  The "validly looking extended SHA-1 expression" check
>   is wholly outside the scope of 1.5.5 regression fix so I did not even
>   attempt to address it, but what this commit fixes needs to be addressed
>   before -rc1.
> 
>  builtin-fetch.c     |    3 +-
>  builtin-send-pack.c |    2 +-
>  remote.c            |  122 ++++++++++++++++++++++++++++++++++-----------------
>  remote.h            |    3 +-
>  t/t5511-refspec.sh  |   67 ++++++++++++++++++++++++++++
>  5 files changed, 153 insertions(+), 44 deletions(-)
>  create mode 100755 t/t5511-refspec.sh
> 
> diff --git a/builtin-fetch.c b/builtin-fetch.c
> index b2b9935..a11548c 100644
> --- a/builtin-fetch.c
> +++ b/builtin-fetch.c
> @@ -652,5 +652,6 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
>  
>  	signal(SIGINT, unlock_pack_on_signal);
>  	atexit(unlock_pack);
> -	return do_fetch(transport, parse_ref_spec(ref_nr, refs), ref_nr);
> +	return do_fetch(transport,
> +			parse_fetch_refspec(ref_nr, refs), ref_nr);
>  }
> diff --git a/builtin-send-pack.c b/builtin-send-pack.c
> index 930e0fb..bb9c33a 100644
> --- a/builtin-send-pack.c
> +++ b/builtin-send-pack.c
> @@ -537,7 +537,7 @@ static void verify_remote_names(int nr_heads, const char **heads)
>  	int i;
>  
>  	for (i = 0; i < nr_heads; i++) {
> -		const char *remote = strchr(heads[i], ':');
> +		const char *remote = strrchr(heads[i], ':');
>  
>  		remote = remote ? (remote + 1) : heads[i];
>  		switch (check_ref_format(remote)) {
> diff --git a/remote.c b/remote.c
> index 9700a33..4117bfc 100644
> --- a/remote.c
> +++ b/remote.c
> @@ -393,58 +393,98 @@ static void read_config(void)
>  	alias_all_urls();
>  }
>  
> -struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
> +static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
>  {
>  	int i;
>  	int st;
>  	struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
> +
>  	for (i = 0; i < nr_refspec; i++) {
> -		const char *sp, *ep, *gp;
> -		sp = refspec[i];
> -		if (*sp == '+') {
> +		size_t llen, rlen;
> +		int is_glob;
> +		const char *lhs, *rhs;
> +
> +		llen = rlen = is_glob = 0;
> +
> +		lhs = refspec[i];
> +		if (*lhs == '+') {
>  			rs[i].force = 1;
> -			sp++;
> +			lhs++;
>  		}
> -		gp = strstr(sp, "/*");
> -		ep = strchr(sp, ':');
> -		if (gp && ep && gp > ep)
> -			gp = NULL;
> -		if (ep) {
> -			if (ep[1]) {
> -				const char *glob = strstr(ep + 1, "/*");
> -				if (glob && glob[2])
> -					glob = NULL;
> -				if (!glob)
> -					gp = NULL;
> -				if (gp)
> -					rs[i].dst = xstrndup(ep + 1,
> -							     glob - ep - 1);
> -				else
> -					rs[i].dst = xstrdup(ep + 1);
> -			}
> -		} else {
> -			ep = sp + strlen(sp);
> +
> +		rhs = strrchr(lhs, ':');
> +		if (rhs) {
> +			rhs++;
> +			rlen = strlen(rhs);
> +			is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
> +			rs[i].dst = xstrndup(rhs, rlen - is_glob * 2);
>  		}
> -		if (gp && gp + 2 != ep)
> -			gp = NULL;
> -		if (gp) {
> -			rs[i].pattern = 1;
> -			ep = gp;
> +
> +		llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
> +		if (is_glob != (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)))
> +			goto invalid;
> +
> +		if (is_glob) {
> +			llen -= 2;
> +			rlen -= 2;
>  		}
> -		rs[i].src = xstrndup(sp, ep - sp);
> +		rs[i].pattern = is_glob;
> +		rs[i].src = xstrndup(lhs, llen);
>  
> -		if (*rs[i].src) {
> +		/*
> +		 * Do we want to validate LHS?
> +		 *
> +		 * - Fetch refspec must have LHS that names a valid
> +		 *   looking ref (unless it is empty, which defaults to
> +		 *   HEAD).
> +		 *
> +		 * - Push refspec "LHS" without ":RHS" is a synonym for
> +		 *   "LHS:LHS", and LHS must be a valid looking ref
> +		 *   (cannot be empty).
> +		 *
> +		 * - Push refspec "LHS:RHS" that is a glob must be
> +		 *   a valid looking ref (unless it is empty to remove)
> +		 *
> +		 * Hence we check non-empty LHS for fetch, and
> +		 * colonless or glob LHS for push here.
> +		 *
> +		 * Note that push refspec "LHS:RHS" that is not a glob
> +		 * can have any valid extended SHA-1 expression (or
> +		 * "empty" for removal) in LHS, and we cannot check
> +		 * for error until it actually gets used.
> +		 */

Wouldn't this be clearer and not meaningfully harder in parse_fetch_refspec and parse_push_refspec? There are, between the two types of checking and the two sides to check, a total of 6 expressions used to determine whether something has to be valid as a head, and only one pair of them are the same (rs[i].dst), and that's trivial (you don't check the part after the colon if there's no colon).

> +		if (fetch ? (*rs[i].src) : (!rhs || is_glob)) {
This is an odd combination of locals and struct members.
                                         : (!rs[i].dst || rs[i].pattern) {
Show 25 quoted lines
>  			st = check_ref_format(rs[i].src);
>  			if (st && st != CHECK_REF_FORMAT_ONELEVEL)
> -				die("Invalid refspec '%s'", refspec[i]);
> +				goto invalid;
>  		}
> -		if (rs[i].dst && *rs[i].dst) {
> +
> +		/*
> +		 * RHS: both push destination and fetch tracking ref
> +		 * must name a valid looking ref, but in fetch, colon
> +		 * followed by emptiness is the same as not having the
> +		 * colon nor RHS.
> +		 */
> +		if (rs[i].dst && (!fetch || *rs[i].dst)) {
>  			st = check_ref_format(rs[i].dst);
>  			if (st && st != CHECK_REF_FORMAT_ONELEVEL)
> -				die("Invalid refspec '%s'", refspec[i]);
> +				goto invalid;
>  		}
>  	}
>  	return rs;
> +
> + invalid:
> +	die("Invalid refspec '%s'", refspec[i]);
> +}
Previous: Junio C HamanoNext: Junio C Hamano
Message 10 of 20 in “Permit refspec source side to parse as a sha1”
  1. Permit refspec source side to parse as a sha1Daniel Barkalow, Mar 21, 2008
  2. Junio C HamanoMar 21, 2008
  3. Junio C HamanoMar 21, 2008
  4. Daniel BarkalowMar 21, 2008
  5. Junio C HamanoMar 21, 2008
  6. Daniel BarkalowMar 21, 2008
  7. Junio C HamanoMar 21, 2008
  8. Daniel BarkalowMar 21, 2008
  9. remote.c: Fix overtight refspec validationJunio C Hamano, Mar 21, 2008
  10. Daniel BarkalowMar 21, 2008
  11. Junio C HamanoMar 21, 2008
  12. Daniel BarkalowMar 22, 2008
  13. Junio C HamanoMar 22, 2008
  14. Daniel BarkalowMar 22, 2008
  15. Linus TorvaldsMar 26, 2008
  16. Junio C HamanoMar 26, 2008
  17. Junio C HamanoMar 26, 2008
  18. Daniel BarkalowMar 26, 2008
  19. Junio C HamanoMar 26, 2008
  20. Jeff KingMar 26, 2008

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.