All of lore.kernel.org
 help / color / mirror / Atom feed
From: Junio C Hamano <gitster@pobox.com>
To: "Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com>
Cc: git@vger.kernel.org, peff@peff.net, me@ttaylorr.com,
	avarab@gmail.com, christian.couder@gmail.com,
	johannes.schindelin@gmx.de, jrnieder@gmail.com,
	"brian m. carlson" <sandals@crustytoothpaste.net>,
	Robert Coup <robert.coup@koordinates.com>,
	Derrick Stolee <derrickstolee@github.com>
Subject: Re: [PATCH v4] remote: create fetch.credentialsInUrl config
Date: Thu, 02 Jun 2022 14:20:59 -0700	[thread overview]
Message-ID: <xmqq35gmkbic.fsf@gitster.g> (raw)
In-Reply-To: <pull.1237.v4.git.1654190434908.gitgitgadget@gmail.com> (Derrick Stolee via GitGitGadget's message of "Thu, 02 Jun 2022 17:20:34 +0000")

"Derrick Stolee via GitGitGadget" <gitgitgadget@gmail.com> writes:

> +static void validate_remote_url(struct remote *remote)
> +{
> +	int i;
> +	const char *value;
> +	struct strbuf redacted = STRBUF_INIT;
> +
> +	if (git_config_get_string_tmp("fetch.credentialsinurl", &value) ||
> +	    !strcmp("allow", value))
> +		return;
> +
> +	for (i = 0; i < remote->url_nr; i++) {
> +		struct url_info url_info = { NULL };

The initializer should be "= { 0 }" not "= { NULL }".  In other
words, we shouldn't have to care if the first member in the struct
happens to be of a pointer type, and we shouldn't have to change
between 0 and NULL whenever the type of the first member changes.

Even though it frowns upon assigning 0 to a pointer variable or a
pointer member in a struct, sparse knows that such an initializer is
OK.

cf. https://lore.kernel.org/git/YVJSwuqjolz28+mG@coredump.intra.peff.net/

Please have a blank line after the variable decl. 

> +		url_normalize(remote->url[i], &url_info);

url_normalize() returns "char *", and you get NULL when parsing
fails; out_info->err may also help when it happens, but I think we
will call url_normalize() again later in the existing caller, and
it will give whatever error message we need to give appropriately,
so it is OK to silently jump to the loop_cleanup label from here.

> +		if (!url_info.passwd_len)
> +			goto loop_cleanup;

I wonder what should happen to "https://username:@localhost" (i.e.
an empty string is used as a password).  I am fine if we allow it as
an obvious "this is like an anonymous ftp; anybody can connect" use
case, but I do not know how useful it would be in practice.  

I am also fine if certain authentication scheme needs username and
password, the latter of which is never used because the "real thing"
like Kerberos kicks in when the real authentication happens but
becasue something needs to be there to "trigger" the authentication,
and that is why we deliberately allow an empty string case unwarned.
But if this is a deliberate thing, we'd need to caution future
developers about it.

I do not think passwd_ofs can ever be 0 if we have an embedded
password in the URL, so checking it may be a better approach, if we
care about an empty-string case.  I can be persuaded either way.

> +
> +		strbuf_add(&redacted, url_info.url, url_info.passwd_off);
> +		strbuf_addstr(&redacted, "<redacted>");
> +		strbuf_addstr(&redacted, url_info.url + url_info.passwd_off + url_info.passwd_len);
> +
> +		if (!strcmp("warn", value))
> +			warning(_("URL '%s' uses plaintext credentials"), redacted.buf);
> +		if (!strcmp("die", value))
> +			die(_("URL '%s' uses plaintext credentials"), redacted.buf);

We obviously could introduce another local variable that is set
based on the "value" before we enter the loop to "optimize", but
this is an error codepath, so I do not mind repeated strcmp() on the
constant value in the loop.

I do have to wonder what we should do when value is none of the
three we know about.  Right now, it makes the function an expensive
noop, so upfront at the beginning of the function, we might need
something like

	int to_warn_not_die;

	if (git_config_get_string_tmp(..."))
 		return;
	if (!strcmp("warn", value))
		to_warn_not_die = 1;
	else if (!strcmp("die", value))
		to_warn_not_die = 0;
	else
		return;

anyway, in which case we would also do

	if (to_warn_not_die)
		warning(...);
	else
		die(...);

in the loop, perhaps?  I dunno.

I wonder if die_message() may want to report all the offending URL
for a given remote (with a concluding die() after the loop).  I am
OK with dying at the first offence, though.  In the worst case, the
end user experience would be:

    $ git fetch there
    die() about the first URL for the nickname
    $ edit .git/config
    $ git fetch there
    die() about the second URL for the nickname

The user will learn after getting the same error message twice and
scan through the other URLs when editing .git/config for the second
time to fix the second URL.  If I were writing this patch, I would
probably play lazy and die on the first offender.

> +test_expect_success 'fetch warns or fails when using username:password' '
> +	message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
> +	test_must_fail git -c fetch.credentialsInUrl=allow fetch https://username:password@localhost 2>err &&
> +	! grep "$message" err &&
> +
> +	test_must_fail git -c fetch.credentialsInUrl=warn fetch https://username:password@localhost 2>err &&
> +	grep "warning: $message" err >warnings &&
> +	test_line_count = 3 warnings &&
> +
> +	test_must_fail git -c fetch.credentialsInUrl=die fetch https://username:password@localhost 2>err &&
> +	grep "fatal: $message" err >warnings &&
> +	test_line_count = 1 warnings

Reusing warnings file for die messages is probably OK ;-)

An extra test with an empty string as a password would have caught
the differences between using passwd_off and passwd_len to detect
the presence of a password here.

Taking all together, I'll queue the following on top as a separate
fix-up patch, but I may well be giving (some) bad pieces of advice,
so I will wait for others to comment.

Thanks.

 remote.c              | 26 ++++++++++++++++++--------
 t/t5516-fetch-push.sh |  4 ++++
 t/t5601-clone.sh      |  4 ++++
 3 files changed, 26 insertions(+), 8 deletions(-)

diff --git c/remote.c w/remote.c
index 59b6839445..2cdc064fa8 100644
--- c/remote.c
+++ w/remote.c
@@ -619,25 +619,35 @@ static void validate_remote_url(struct remote *remote)
 	int i;
 	const char *value;
 	struct strbuf redacted = STRBUF_INIT;
+	int warn_not_die;
 
-	if (git_config_get_string_tmp("fetch.credentialsinurl", &value) ||
-	    !strcmp("allow", value))
+	if (git_config_get_string_tmp("fetch.credentialsinurl", &value))
 		return;
 
+	if (!strcmp("warn", value))
+		warn_not_die = 1;
+	else if (!strcmp("die", value))
+		warn_not_die = 0;
+	else if (!strcmp("allow", value))
+		return;
+	else
+		die(_("unrecognized value fetch.credentialsInURL: '%s'"), value);
+
 	for (i = 0; i < remote->url_nr; i++) {
-		struct url_info url_info = { NULL };
-		url_normalize(remote->url[i], &url_info);
+		struct url_info url_info = { 0 };
 
-		if (!url_info.passwd_len)
+		if (!url_normalize(remote->url[i], &url_info) ||
+		    !url_info.passwd_off)
 			goto loop_cleanup;
 
 		strbuf_add(&redacted, url_info.url, url_info.passwd_off);
 		strbuf_addstr(&redacted, "<redacted>");
-		strbuf_addstr(&redacted, url_info.url + url_info.passwd_off + url_info.passwd_len);
+		strbuf_addstr(&redacted,
+			      url_info.url + url_info.passwd_off + url_info.passwd_len);
 
-		if (!strcmp("warn", value))
+		if (warn_not_die)
 			warning(_("URL '%s' uses plaintext credentials"), redacted.buf);
-		if (!strcmp("die", value))
+		else
 			die(_("URL '%s' uses plaintext credentials"), redacted.buf);
 
 loop_cleanup:
diff --git c/t/t5516-fetch-push.sh w/t/t5516-fetch-push.sh
index afb9236bee..a67acc3263 100755
--- c/t/t5516-fetch-push.sh
+++ w/t/t5516-fetch-push.sh
@@ -1821,6 +1821,10 @@ test_expect_success 'fetch warns or fails when using username:password' '
 
 	test_must_fail git -c fetch.credentialsInUrl=die fetch https://username:password@localhost 2>err &&
 	grep "fatal: $message" err >warnings &&
+	test_line_count = 1 warnings &&
+
+	test_must_fail git -c fetch.credentialsInUrl=die fetch https://username:@localhost 2>err &&
+	grep "fatal: $message" err >warnings &&
 	test_line_count = 1 warnings
 '
 
diff --git c/t/t5601-clone.sh w/t/t5601-clone.sh
index ddc4cc7ec2..cf0a3ef3f4 100755
--- c/t/t5601-clone.sh
+++ w/t/t5601-clone.sh
@@ -82,6 +82,10 @@ test_expect_success 'clone warns or fails when using username:password' '
 
 	test_must_fail git -c fetch.credentialsInUrl=die clone https://username:password@localhost attempt3 2>err &&
 	grep "fatal: $message" err >warnings &&
+	test_line_count = 1 warnings &&
+
+	test_must_fail git -c fetch.credentialsInUrl=die clone https://username:@localhost attempt3 2>err &&
+	grep "fatal: $message" err >warnings &&
 	test_line_count = 1 warnings
 '
 

  reply	other threads:[~2022-06-02 21:21 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-23 18:04 [PATCH] urlmatch: create fetch.credentialsInUrl config Derrick Stolee via GitGitGadget
2022-05-23 19:06 ` Junio C Hamano
2022-05-23 20:31   ` Derrick Stolee
2022-05-23 21:14     ` Junio C Hamano
2022-05-24 11:46     ` Johannes Schindelin
2022-05-24 20:14       ` Derrick Stolee
2022-05-23 20:37   ` Junio C Hamano
2022-05-24 11:51   ` Johannes Schindelin
2022-05-24  8:18 ` Ævar Arnfjörð Bjarmason
2022-05-24 13:50   ` Derrick Stolee
2022-05-24 21:01     ` Ævar Arnfjörð Bjarmason
2022-05-25 14:03       ` Derrick Stolee
2022-05-24 11:42 ` Johannes Schindelin
2022-05-24 20:16   ` Derrick Stolee
2022-05-27 13:27 ` [PATCH v2] " Derrick Stolee via GitGitGadget
2022-05-27 14:22   ` Ævar Arnfjörð Bjarmason
2022-05-27 14:43     ` Derrick Stolee
2022-05-27 18:09   ` Junio C Hamano
2022-05-27 18:40     ` Junio C Hamano
2022-05-30  0:16   ` Junio C Hamano
2022-05-31 13:32     ` Derrick Stolee
2022-06-01  1:16   ` [PATCH v3 0/2] fetch: " Derrick Stolee via GitGitGadget
2022-06-01  1:16     ` [PATCH v3 1/2] remote: " Derrick Stolee via GitGitGadget
2022-06-01 19:19       ` Ævar Arnfjörð Bjarmason
2022-06-02 13:38         ` Derrick Stolee
2022-06-01  1:16     ` [PATCH v3 2/2] usage: add warn_once() helper for repeated warnings Derrick Stolee via GitGitGadget
2022-06-01 12:29       ` Ævar Arnfjörð Bjarmason
2022-06-01 18:42         ` Derrick Stolee
2022-06-01 19:33           ` Ævar Arnfjörð Bjarmason
2022-06-02 13:43             ` Derrick Stolee
2022-06-01 20:21           ` Junio C Hamano
2022-06-02 14:24             ` Derrick Stolee
2022-06-02 17:53               ` Junio C Hamano
2022-06-01 20:40       ` Junio C Hamano
2022-06-02 17:20     ` [PATCH v4] remote: create fetch.credentialsInUrl config Derrick Stolee via GitGitGadget
2022-06-02 21:20       ` Junio C Hamano [this message]
2022-06-03 12:54         ` Derrick Stolee
2022-06-06 15:37           ` Junio C Hamano
2022-06-06 14:36       ` [PATCH v5] " Derrick Stolee via GitGitGadget
2022-06-06 16:34         ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xmqq35gmkbic.fsf@gitster.g \
    --to=gitster@pobox.com \
    --cc=avarab@gmail.com \
    --cc=christian.couder@gmail.com \
    --cc=derrickstolee@github.com \
    --cc=git@vger.kernel.org \
    --cc=gitgitgadget@gmail.com \
    --cc=johannes.schindelin@gmx.de \
    --cc=jrnieder@gmail.com \
    --cc=me@ttaylorr.com \
    --cc=peff@peff.net \
    --cc=robert.coup@koordinates.com \
    --cc=sandals@crustytoothpaste.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.