diff mbox series

[v3] git-credential-store: skip empty lines and comments from store

Message ID 20200427125915.88667-1-carenas@gmail.com (mailing list archive)
State New, archived
Headers show
Series [v3] git-credential-store: skip empty lines and comments from store | expand

Commit Message

Carlo Marcelo Arenas Belón April 27, 2020, 12:59 p.m. UTC
with the added checks for invalid URLs in credentials, any locally
modified store files which might have empty lines or even comments
were reported[1] failing to parse as valid credentials.

using the store file in this manner wasn't intended by the original
code and it had latent issues which the new code dutifully prevented
but since the strings used wouldn't had been valid credentials anyway
we could instead detect them and skip the matching logic and therefore
formalize this "feature".

trim all lines as they are being read from the store file and skip the
ones that will be otherwise empty or that start with "#" (therefore
assuming them to be comments)

[1] https://stackoverflow.com/a/61420852/5005936

Reported-by: Dirk <dirk@ed4u.de>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
v3:
* avoid using q_to_cr as suggested by Peff
* a more verbose commit message and slightly more complete documentation 
v2:
* use a here-doc for clarity as suggested by Eric
* improve commit message and include documentation

 Documentation/git-credential-store.txt |  7 +++++++
 credential-store.c                     |  3 +++
 t/t0302-credential-store.sh            | 15 +++++++++++++++
 3 files changed, 25 insertions(+)

Comments

Philip Oakley April 27, 2020, 1:48 p.m. UTC | #1
Hi Carlo,
On 27/04/2020 13:59, Carlo Marcelo Arenas Belón wrote:
> with the added checks for invalid URLs in credentials, any locally
> modified store files which might have empty lines or even comments
> were reported[1] failing to parse as valid credentials.
>
> using the store file in this manner wasn't intended by the original
> code and it had latent issues which the new code dutifully prevented
> but since the strings used wouldn't had been valid credentials anyway
> we could instead detect them and skip the matching logic and therefore
> formalize this "feature".
>
> trim all lines as they are being read from the store file and skip the

Does trimming affect any credentials that may have spaces either end?
I've not looked at the format.
> ones that will be otherwise empty or that start with "#" (therefore
> assuming them to be comments)
>
> [1] https://stackoverflow.com/a/61420852/5005936
>
> Reported-by: Dirk <dirk@ed4u.de>
> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> ---
> v3:
> * avoid using q_to_cr as suggested by Peff
> * a more verbose commit message and slightly more complete documentation 
> v2:
> * use a here-doc for clarity as suggested by Eric
> * improve commit message and include documentation
>
>  Documentation/git-credential-store.txt |  7 +++++++
>  credential-store.c                     |  3 +++
>  t/t0302-credential-store.sh            | 15 +++++++++++++++
>  3 files changed, 25 insertions(+)
>
> diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
> index 693dd9d9d7..48ab4b13e5 100644
> --- a/Documentation/git-credential-store.txt
> +++ b/Documentation/git-credential-store.txt
> @@ -101,6 +101,13 @@ username (if we already have one) match, then the password is returned
>  to Git. See the discussion of configuration in linkgit:gitcredentials[7]
>  for more information.
>  
> +Note that the file used is not a configuration file and should be ideally
> +managed only through git, as any manually introduced typos will compromise
> +the validation of credentials.
> +
> +The parser will ignore any lines starting with the '#' character during
> +the processing of credentials for read, though.
Should the trimming of leading/trailing spaces be mentioned?

Also the git-credential page mentions that the credential must end with
a blank line ("don't forget.."). Should that be mentioned here, or have
I misunderstood?
> +
>  GIT
>  ---
>  Part of the linkgit:git[1] suite
> diff --git a/credential-store.c b/credential-store.c
> index c010497cb2..b2f160890d 100644
> --- a/credential-store.c
> +++ b/credential-store.c
> @@ -24,6 +24,9 @@ static int parse_credential_file(const char *fn,
>  	}
>  
>  	while (strbuf_getline_lf(&line, fh) != EOF) {
> +		strbuf_trim(&line);
> +		if (line.len == 0 || *line.buf == '#')
> +			continue;
>  		credential_from_url(&entry, line.buf);
>  		if (entry.username && entry.password &&
>  		    credential_match(c, &entry)) {
> diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
> index d6b54e8c65..5e6ace3a06 100755
> --- a/t/t0302-credential-store.sh
> +++ b/t/t0302-credential-store.sh
> @@ -120,4 +120,19 @@ test_expect_success 'erase: erase matching credentials from both xdg and home fi
>  	test_must_be_empty "$HOME/.config/git/credentials"
>  '
>  
> +test_expect_success 'get: allow for empty lines or comments in store file' '
> +	test_write_lines "#comment" " " "" \
> +		 https://user:pass@example.com >"$HOME/.git-credentials" &&
> +	check fill store <<-\EOF
> +	protocol=https
> +	host=example.com
> +	--
> +	protocol=https
> +	host=example.com
> +	username=user
> +	password=pass
> +	--
> +	EOF
> +'
> +
>  test_done
Philip
Dirk April 27, 2020, 3:39 p.m. UTC | #2
Thank you very much. That's correct and ideal in my eyes.

Regarding the comments, this is a new feature, of course. I think it's a worthless discussion about the question if the correct handling of empty lines are a bugfix or a new feature. In fact empty lines were handled correcty (ignored). So this might be considered a feature. But it wasn't documented, so it's a bugfix...

Anyway. Thank you all.

Dirk


Am 27.04.20 um 14:59 schrieb Carlo Marcelo Arenas Belón:
> with the added checks for invalid URLs in credentials, any locally
> modified store files which might have empty lines or even comments
> were reported[1] failing to parse as valid credentials.
>
> using the store file in this manner wasn't intended by the original
> code and it had latent issues which the new code dutifully prevented
> but since the strings used wouldn't had been valid credentials anyway
> we could instead detect them and skip the matching logic and therefore
> formalize this "feature".
>
> trim all lines as they are being read from the store file and skip the
> ones that will be otherwise empty or that start with "#" (therefore
> assuming them to be comments)
>
> [1] https://stackoverflow.com/a/61420852/5005936
>
> Reported-by: Dirk <dirk@ed4u.de>
> Helped-by: Eric Sunshine <sunshine@sunshineco.com>
> Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
> ---
> v3:
> * avoid using q_to_cr as suggested by Peff
> * a more verbose commit message and slightly more complete documentation 
> v2:
> * use a here-doc for clarity as suggested by Eric
> * improve commit message and include documentation
>
>  Documentation/git-credential-store.txt |  7 +++++++
>  credential-store.c                     |  3 +++
>  t/t0302-credential-store.sh            | 15 +++++++++++++++
>  3 files changed, 25 insertions(+)
>
> diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
> index 693dd9d9d7..48ab4b13e5 100644
> --- a/Documentation/git-credential-store.txt
> +++ b/Documentation/git-credential-store.txt
> @@ -101,6 +101,13 @@ username (if we already have one) match, then the password is returned
>  to Git. See the discussion of configuration in linkgit:gitcredentials[7]
>  for more information.
>  
> +Note that the file used is not a configuration file and should be ideally
> +managed only through git, as any manually introduced typos will compromise
> +the validation of credentials.
> +
> +The parser will ignore any lines starting with the '#' character during
> +the processing of credentials for read, though.
> +
>  GIT
>  ---
>  Part of the linkgit:git[1] suite
> diff --git a/credential-store.c b/credential-store.c
> index c010497cb2..b2f160890d 100644
> --- a/credential-store.c
> +++ b/credential-store.c
> @@ -24,6 +24,9 @@ static int parse_credential_file(const char *fn,
>  	}
>  
>  	while (strbuf_getline_lf(&line, fh) != EOF) {
> +		strbuf_trim(&line);
> +		if (line.len == 0 || *line.buf == '#')
> +			continue;
>  		credential_from_url(&entry, line.buf);
>  		if (entry.username && entry.password &&
>  		    credential_match(c, &entry)) {
> diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
> index d6b54e8c65..5e6ace3a06 100755
> --- a/t/t0302-credential-store.sh
> +++ b/t/t0302-credential-store.sh
> @@ -120,4 +120,19 @@ test_expect_success 'erase: erase matching credentials from both xdg and home fi
>  	test_must_be_empty "$HOME/.config/git/credentials"
>  '
>  
> +test_expect_success 'get: allow for empty lines or comments in store file' '
> +	test_write_lines "#comment" " " "" \
> +		 https://user:pass@example.com >"$HOME/.git-credentials" &&
> +	check fill store <<-\EOF
> +	protocol=https
> +	host=example.com
> +	--
> +	protocol=https
> +	host=example.com
> +	username=user
> +	password=pass
> +	--
> +	EOF
> +'
> +
>  test_done
Junio C Hamano April 27, 2020, 6:09 p.m. UTC | #3
Carlo Marcelo Arenas Belón  <carenas@gmail.com> writes:

> with the added checks for invalid URLs in credentials, any locally

s/with/With/

> modified store files which might have empty lines or even comments
> were reported[1] failing to parse as valid credentials.

These files are not supposed to be viewed or edited without the help
of the credential helpers.  Do these blank lines and comments even
survive when a new credential is approved, or do we just overwrite
and lose them?

I'd rather not to do either, if we did not have to, but if it were
necessary for us to do something, I am OK to ignore empty lines.
But I'd prefer not to mix the new "# comment" feature in, if we did
not have to.

Also, triming the lines that are not empty is unwarranted.  IIUC,
what the "store" action writes encodes whitespaces, so as soon as
you see whitespace on either end, (or anywhere on the line for that
matter), it is a hand-edited cruft in the file.  If you ignore
comments, you probably should ignore those lines, too.
Jeff King April 27, 2020, 7:18 p.m. UTC | #4
On Mon, Apr 27, 2020 at 11:09:34AM -0700, Junio C Hamano wrote:

> > modified store files which might have empty lines or even comments
> > were reported[1] failing to parse as valid credentials.
> 
> These files are not supposed to be viewed or edited without the help
> of the credential helpers.  Do these blank lines and comments even
> survive when a new credential is approved, or do we just overwrite
> and lose them?

That's a good question. In the older code we do save them, because
credential-store passes through lines which don't match the credential
we're operating on.

But in Carlo's patch, the immediate "continue" means we wouldn't ever
call "other_cb", which is what does that pass-through.

> I'd rather not to do either, if we did not have to, but if it were
> necessary for us to do something, I am OK to ignore empty lines.
> But I'd prefer not to mix the new "# comment" feature in, if we did
> not have to.
> 
> Also, triming the lines that are not empty is unwarranted.  IIUC,
> what the "store" action writes encodes whitespaces, so as soon as
> you see whitespace on either end, (or anywhere on the line for that
> matter), it is a hand-edited cruft in the file.  If you ignore
> comments, you probably should ignore those lines, too.

Yeah, all of that seems quite sensible.

-Peff
Junio C Hamano April 27, 2020, 8:43 p.m. UTC | #5
Jeff King <peff@peff.net> writes:

> On Mon, Apr 27, 2020 at 11:09:34AM -0700, Junio C Hamano wrote:
>
>> > modified store files which might have empty lines or even comments
>> > were reported[1] failing to parse as valid credentials.
>> 
>> These files are not supposed to be viewed or edited without the help
>> of the credential helpers.  Do these blank lines and comments even
>> survive when a new credential is approved, or do we just overwrite
>> and lose them?
>
> That's a good question. In the older code we do save them, because
> credential-store passes through lines which don't match the credential
> we're operating on.
>
> But in Carlo's patch, the immediate "continue" means we wouldn't ever
> call "other_cb", which is what does that pass-through.

So, does that mean the patch that started this thread will still help
users who wrote custom comments and blank lines in their credential
store by letting git-credential-store start up, but leaves a ticking
bomb for them to lose these precious comments and blanks once they
add a new site, change password, etc., at which point the user realizes
that comments and blanks are not supported after all?

>> I'd rather not to do either, if we did not have to, but if it were
>> necessary for us to do something, I am OK to ignore empty lines.
>> But I'd prefer not to mix the new "# comment" feature in, if we did
>> not have to.
>> 
>> Also, triming the lines that are not empty is unwarranted.  IIUC,
>> what the "store" action writes encodes whitespaces, so as soon as
>> you see whitespace on either end, (or anywhere on the line for that
>> matter), it is a hand-edited cruft in the file.  If you ignore
>> comments, you probably should ignore those lines, too.
>
> Yeah, all of that seems quite sensible.

I think the first patch we need is a (belated) documentation patch,
that adds to the existing "STORAGE FORMAT".  We already say "Each
credential is stored on its own line as a URL", but we do not say
anything about allowing other cruft in the file.  We probably
should.  Adding a "comment" feature, if anybody feels like it, is OK
and we can loosen the paragraph when that happens.

-- >8 --
Subject: credential-store: document the file format a bit more

Reading a malformed credential URL line and silently ignoring it
does not mean that we promise to torelate and/or keep empty lines
and "# commented" lines forever.

Some people seem to take anything that is not explicitly forbidden
as allowed, but the world does not work that way.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 Documentation/git-credential-store.txt | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
index 693dd9d9d7..76b0798856 100644
--- a/Documentation/git-credential-store.txt
+++ b/Documentation/git-credential-store.txt
@@ -94,6 +94,10 @@ stored on its own line as a URL like:
 https://user:pass@example.com
 ------------------------------
 
+No other kinds of lines (e.g. empty lines or comment lines) are
+allowed in the file, even though some may be silently ignored. Do
+not view or edit the file with editors.
+
 When Git needs authentication for a particular URL context,
 credential-store will consider that context a pattern to match against
 each entry in the credentials file.  If the protocol, hostname, and
Jeff King April 27, 2020, 9:10 p.m. UTC | #6
On Mon, Apr 27, 2020 at 01:43:38PM -0700, Junio C Hamano wrote:

> >> These files are not supposed to be viewed or edited without the help
> >> of the credential helpers.  Do these blank lines and comments even
> >> survive when a new credential is approved, or do we just overwrite
> >> and lose them?
> >
> > That's a good question. In the older code we do save them, because
> > credential-store passes through lines which don't match the credential
> > we're operating on.
> >
> > But in Carlo's patch, the immediate "continue" means we wouldn't ever
> > call "other_cb", which is what does that pass-through.
> 
> So, does that mean the patch that started this thread will still help
> users who wrote custom comments and blank lines in their credential
> store by letting git-credential-store start up, but leaves a ticking
> bomb for them to lose these precious comments and blanks once they
> add a new site, change password, etc., at which point the user realizes
> that comments and blanks are not supported after all?

Yes, exactly. If I start with:

  cat >creds <<\EOF
  # this is a comment
  https://user:pass@example.com/
  EOF

then:

  echo url=https://other:pass@other.example.com |
  git credential-store --file=creds store

with v2.26.0 results in:

  https://other:pass@other.example.com
  # this is a comment
  https://user:pass@example.com

but applying the patch on top:

  https://other:pass@other.example.com
  https://user:pass@example.com/

That raises another issue about comments, too: we make no promises about
where we write new entries. They could be inserted between a comment
line and one it is meant to annotate (that line could even be moved if
we reject and then re-approve a credential).

> I think the first patch we need is a (belated) documentation patch,
> that adds to the existing "STORAGE FORMAT".  We already say "Each
> credential is stored on its own line as a URL", but we do not say
> anything about allowing other cruft in the file.  We probably
> should.  Adding a "comment" feature, if anybody feels like it, is OK
> and we can loosen the paragraph when that happens.

The more I look into it, the more negative I am on adding such a comment
feature.

> -- >8 --
> Subject: credential-store: document the file format a bit more

Yeah, this certainly clarifies things. I suppose one could ask why we'd
bother documenting the format at all, then, though I do think it could
be helpful for debugging.

> Reading a malformed credential URL line and silently ignoring it
> does not mean that we promise to torelate and/or keep empty lines
> and "# commented" lines forever.

s/torelate/tolerate/

-Peff
Carlo Marcelo Arenas Belón April 27, 2020, 11:49 p.m. UTC | #7
On Mon, Apr 27, 2020 at 01:43:38PM -0700, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
> 
> > On Mon, Apr 27, 2020 at 11:09:34AM -0700, Junio C Hamano wrote:
> >
> >> > modified store files which might have empty lines or even comments
> >> > were reported[1] failing to parse as valid credentials.
> >> 
> >> These files are not supposed to be viewed or edited without the help
> >> of the credential helpers.  Do these blank lines and comments even
> >> survive when a new credential is approved, or do we just overwrite
> >> and lose them?
> >
> > That's a good question. In the older code we do save them, because
> > credential-store passes through lines which don't match the credential
> > we're operating on.
> >
> > But in Carlo's patch, the immediate "continue" means we wouldn't ever
> > call "other_cb", which is what does that pass-through.
> 
> So, does that mean the patch that started this thread will still help
> users who wrote custom comments and blank lines in their credential
> store by letting git-credential-store start up, but leaves a ticking
> bomb for them to lose these precious comments and blanks once they
> add a new site, change password, etc., at which point the user realizes
> that comments and blanks are not supported after all?

yes, and it also helps users that might have added spaces or tabs
around their credentials while editing them to still be able to use those
instead of just failing to match them.

IMHO the only "regression" I was fixing was the fact that the current
code will get to throw a fatal error with an unhelpful message and prevent
access to valid credentials as shown by :

$ /git credential fill
protocol=http
host=example.com

warning: url has no scheme:
fatal: credential url cannot be parsed:
Username for 'http://example.com':

> >> I'd rather not to do either, if we did not have to, but if it were
> >> necessary for us to do something, I am OK to ignore empty lines.
> >> But I'd prefer not to mix the new "# comment" feature in, if we did
> >> not have to.
> >> 
> >> Also, triming the lines that are not empty is unwarranted.  IIUC,
> >> what the "store" action writes encodes whitespaces, so as soon as
> >> you see whitespace on either end, (or anywhere on the line for that
> >> matter), it is a hand-edited cruft in the file.  If you ignore
> >> comments, you probably should ignore those lines, too.
> >
> > Yeah, all of that seems quite sensible.
> 
> I think the first patch we need is a (belated) documentation patch,
> that adds to the existing "STORAGE FORMAT".  We already say "Each
> credential is stored on its own line as a URL", but we do not say
> anything about allowing other cruft in the file.  We probably
> should.  Adding a "comment" feature, if anybody feels like it, is OK
> and we can loosen the paragraph when that happens.
> 
> -- >8 --
> Subject: credential-store: document the file format a bit more
> 
> Reading a malformed credential URL line and silently ignoring it
> does not mean that we promise to torelate and/or keep empty lines
> and "# commented" lines forever.
> 
> Some people seem to take anything that is not explicitly forbidden
> as allowed, but the world does not work that way.
> 
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  Documentation/git-credential-store.txt | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
> index 693dd9d9d7..76b0798856 100644
> --- a/Documentation/git-credential-store.txt
> +++ b/Documentation/git-credential-store.txt
> @@ -94,6 +94,10 @@ stored on its own line as a URL like:
>  https://user:pass@example.com
>  ------------------------------
>  
> +No other kinds of lines (e.g. empty lines or comment lines) are
> +allowed in the file, even though some may be silently ignored. Do
> +not view or edit the file with editors.

view should be ok; mentioning that any typos or extraneous characters
will compromise the validation of credentials like I mentioned in my
proposed documentation update probably worth doing here instead, too.

Carlo
Carlo Marcelo Arenas Belón April 28, 2020, 1:37 a.m. UTC | #8
On Mon, Apr 27, 2020 at 05:10:13PM -0400, Jeff King wrote:
> On Mon, Apr 27, 2020 at 01:43:38PM -0700, Junio C Hamano wrote:
> > >> These files are not supposed to be viewed or edited without the help
> > >> of the credential helpers.  Do these blank lines and comments even
> > >> survive when a new credential is approved, or do we just overwrite
> > >> and lose them?
> > >
> > > That's a good question. In the older code we do save them, because
> > > credential-store passes through lines which don't match the credential
> > > we're operating on.
> > >
> > > But in Carlo's patch, the immediate "continue" means we wouldn't ever
> > > call "other_cb", which is what does that pass-through.
> > 
> > So, does that mean the patch that started this thread will still help
> > users who wrote custom comments and blank lines in their credential
> > store by letting git-credential-store start up, but leaves a ticking
> > bomb for them to lose these precious comments and blanks once they
> > add a new site, change password, etc., at which point the user realizes
> > that comments and blanks are not supported after all?
> 
> The more I look into it, the more negative I am on adding such a comment
> feature.

FWIW I am not interested on implementing a comment feature (regardless of
what the subject says, and indeed was going to use quotes around it in my
original patch), but it was already too long ;)

my objective was to allow people that were not upgrading because of this
"regression" a path forward, but after all this discussion I am convinced
too that my approach is misguided.

if users are already manually editing this file, they should be able to
fix the corrupted lines themselves but the error shown needs to be improved.
and the failure mode corrected as well so they are not blocked.

this also means that my "sneaky" way to fix formatting issues is gone
and the passthrough is called so nothing will be lost, but as you also
said the location is not warranted to be kept so this comment "feature"
is as useless as intended.

would something like the following (wouldn't pass the last test yet as
it will need special handling to be able to handle the full path of the
broken file as shown in the warning sent to stderr) on top of Junio's
make more sense?

it still "silently" fixes the formatting issues where a leading tab (test
added) or spaces were added to easy the transition, but probably should
also error out as Junio suggested originally.

had also updated the commit message and what is left of the documentation.

Carlo
-- >8 --
Subject: [RFC PATCH v4] git-credential-store: skip empty lines and comments
 from store

with the added checks for invalid URLs in credentials, any locally
modified store files which might have empty lines or even comments
were reported[1] failing to parse as valid credentials.

using the store file in this manner wasn't intended by the original
code and it had latent issues which the new code dutifully prevented
but since the strings used wouldn't had been valid credentials anyway
we could instead detect them and skip the matching logic and therefore
prevent a fatal failure.

trim all lines as they are being read from the store file and skip the
ones that will be otherwise empty or that start with "#" (therefore
assuming them to be comments) but warn users so they will be make
aware of the issues introduced by their manual editing.

[1] https://stackoverflow.com/a/61420852/5005936

Reported-by: Dirk <dirk@ed4u.de>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 Documentation/git-credential-store.txt |  4 ++++
 credential-store.c                     |  7 ++++++
 t/t0302-credential-store.sh            | 31 ++++++++++++++++++++++++++
 3 files changed, 42 insertions(+)

diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
index 693dd9d9d7..e9a1993021 100644
--- a/Documentation/git-credential-store.txt
+++ b/Documentation/git-credential-store.txt
@@ -101,6 +101,10 @@ username (if we already have one) match, then the password is returned
 to Git. See the discussion of configuration in linkgit:gitcredentials[7]
 for more information.
 
+The parser will ignore any lines that are empty or starting with '#' character
+during the processing of credentials for read, but warn the user so they can
+be corrected.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/credential-store.c b/credential-store.c
index c010497cb2..f1f016fec1 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -24,6 +24,13 @@ static int parse_credential_file(const char *fn,
 	}
 
 	while (strbuf_getline_lf(&line, fh) != EOF) {
+		strbuf_trim(&line);
+		if (line.len == 0 || *line.buf == '#') {
+			warning("ignoring corrupted credential entry \"%s\" in %s", line.buf, fn);
+			if (other_cb)
+				other_cb(&line);
+			continue;
+		}
 		credential_from_url(&entry, line.buf);
 		if (entry.username && entry.password &&
 		    credential_match(c, &entry)) {
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
index d6b54e8c65..12a158c136 100755
--- a/t/t0302-credential-store.sh
+++ b/t/t0302-credential-store.sh
@@ -120,4 +120,35 @@ test_expect_success 'erase: erase matching credentials from both xdg and home fi
 	test_must_be_empty "$HOME/.config/git/credentials"
 '
 
+test_expect_success 'get: attempt to workaround formatting isssues (ex: tab)' '
+	q_to_tab >"$HOME/.git-credentials" <<-\EOF &&
+	Qhttps://user:pass@example.com
+	EOF
+	check fill store <<-\EOF
+	protocol=https
+	host=example.com
+	--
+	protocol=https
+	host=example.com
+	username=user
+	password=pass
+	--
+	EOF
+'
+
+test_expect_success 'get: skip empty lines or "comments" in store file' '
+	test_write_lines "#comment" " " "" \
+		 https://user:pass@example.com >"$HOME/.git-credentials" &&
+	check fill store <<-\EOF
+	protocol=https
+	host=example.com
+	--
+	protocol=https
+	host=example.com
+	username=user
+	password=pass
+	--
+	EOF
+'
+
 test_done
Carlo Marcelo Arenas Belón April 28, 2020, 1:49 a.m. UTC | #9
On Mon, Apr 27, 2020 at 02:48:05PM +0100, Philip Oakley wrote:
> On 27/04/2020 13:59, Carlo Marcelo Arenas Belón wrote:
> > with the added checks for invalid URLs in credentials, any locally
> > modified store files which might have empty lines or even comments
> > were reported[1] failing to parse as valid credentials.
> >
> > using the store file in this manner wasn't intended by the original
> > code and it had latent issues which the new code dutifully prevented
> > but since the strings used wouldn't had been valid credentials anyway
> > we could instead detect them and skip the matching logic and therefore
> > formalize this "feature".
> >
> > trim all lines as they are being read from the store file and skip the
> 
> Does trimming affect any credentials that may have spaces either end?

all credentials are url encoded so the only spaces that are affected are
the ones that were added by careless editing of that file.

as Eric pointed out, any tabs will be silently "cleaned" as well.

> Should the trimming of leading/trailing spaces be mentioned?

I wanted to keep that as an undocumented "feature" as it is just meant
to help people to avoid a fatal error during the transition but didn't
want to encourage people thinking it is a supported part or even worse
encourage people to start editing their files to add tabs and spaces.

Junio's suggested documentation fix[1] makes that clearer that I could

> Also the git-credential page mentions that the credential must end with
> a blank line ("don't forget.."). Should that be mentioned here, or have
> I misunderstood?

that is for the protocol part of it (which is used between git and the
credential helper), the file format for this specific helper doesn't
require that.

Carlo

[1] https://lore.kernel.org/git/xmqqv9lk7j7p.fsf@gitster.c.googlers.com/
Jonathan Nieder April 28, 2020, 5:25 a.m. UTC | #10
Junio C Hamano wrote:

> -- >8 --
> Subject: credential-store: document the file format a bit more
>
> Reading a malformed credential URL line and silently ignoring it
> does not mean that we promise to torelate and/or keep empty lines
> and "# commented" lines forever.
>
> Some people seem to take anything that is not explicitly forbidden
> as allowed, but the world does not work that way.
>
> Signed-off-by: Junio C Hamano <gitster@pobox.com>
> ---
>  Documentation/git-credential-store.txt | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
> index 693dd9d9d7..76b0798856 100644
> --- a/Documentation/git-credential-store.txt
> +++ b/Documentation/git-credential-store.txt
> @@ -94,6 +94,10 @@ stored on its own line as a URL like:
>  https://user:pass@example.com
>  ------------------------------
> 
> +No other kinds of lines (e.g. empty lines or comment lines) are
> +allowed in the file, even though some may be silently ignored. Do
> +not view or edit the file with editors.
> +
>  When Git needs authentication for a particular URL context,

I like this.

I do suspect this is easy to run into accidentally.  In $DAYJOB (in
the context of [1]) there was a service that accidentally wrote a \n\n
at the end of a line that was used by git-credential-store.  Once the
cause was tracked down, it was straightforward to fix, but I don't
like the idea that others in a similar position may end up tempted to
just not upgrade Git.

Independently, there is the thread we are replying to.

Independently, in Debian's bug tracking system, Stefan (cc-ed)
reports[2]:

| the vulnerability in CVE-2020-11008 is related to the handling
| of credential helpers in git. In Buster this has been fixed in
| 1:2.20.1-2+deb10u3. This broke my existing configuration where
| repositories have credential.helper=store set. This is
| documented in /usr/share/man/man1/git-credential-store.1.gz
| and other files from git, git-doc etc.
| I am unsure how to proceed... is this helper now unsupported?

(Stefan, do you have more details?  Did you manually populate your
credential store?  What error message do you get?)

I wonder if in addition to the above documentation change we may want
something guaranteed to catch all cases where people would have
experienced a regression, like

diff --git i/credential-store.c w/credential-store.c
index c010497cb21..294e7716815 100644
--- i/credential-store.c
+++ w/credential-store.c
@@ -24,8 +24,8 @@ static int parse_credential_file(const char *fn,
 	}
 
 	while (strbuf_getline_lf(&line, fh) != EOF) {
-		credential_from_url(&entry, line.buf);
-		if (entry.username && entry.password &&
+		if (!credential_from_url_gently(&entry, line.buf, 1) &&
+		    entry.username && entry.password &&
 		    credential_match(c, &entry)) {
 			found_credential = 1;
 			if (match_cb) {

And then we can tighten the handling of unrecognized lines to first
warn and then error out, as a controlled change that doesn't lead
people to regret updating git.

Thoughts?

Thanks,
Jonathan

[1] https://cs.opensource.google/copybara/copybara/+/master:java/com/google/copybara/git/GitOptions.java;drc=bc79a0b1ffe18f79dea0b75ba3a24b641a50a9fc;l=46
[2] https://bugs.debian.org/958929
Jeff King April 28, 2020, 5:41 a.m. UTC | #11
On Mon, Apr 27, 2020 at 10:25:10PM -0700, Jonathan Nieder wrote:

> I wonder if in addition to the above documentation change we may want
> something guaranteed to catch all cases where people would have
> experienced a regression, like
> 
> diff --git i/credential-store.c w/credential-store.c
> index c010497cb21..294e7716815 100644
> --- i/credential-store.c
> +++ w/credential-store.c
> @@ -24,8 +24,8 @@ static int parse_credential_file(const char *fn,
>  	}
>  
>  	while (strbuf_getline_lf(&line, fh) != EOF) {
> -		credential_from_url(&entry, line.buf);
> -		if (entry.username && entry.password &&
> +		if (!credential_from_url_gently(&entry, line.buf, 1) &&
> +		    entry.username && entry.password &&
>  		    credential_match(c, &entry)) {
>  			found_credential = 1;
>  			if (match_cb) {
> 
> And then we can tighten the handling of unrecognized lines to first
> warn and then error out, as a controlled change that doesn't lead
> people to regret updating git.

I like that solution, as it mostly brings us back to the original
behavior, as weird or unexpected as it was.

The only I think would be different is:

  ://user:pass@example.com

which I think the old code would have treated as matching any protocol
(and the new code will reject as a bogus URL).

I wondered if you'd need to free the partially-parsed struct fields on
failure, but the answer is no; we'd clear it the next time through the
loop, or at the very end of the function.

-Peff
Carlo Marcelo Arenas Belón April 28, 2020, 7:18 a.m. UTC | #12
On Tue, Apr 28, 2020 at 01:41:55AM -0400, Jeff King wrote:
> On Mon, Apr 27, 2020 at 10:25:10PM -0700, Jonathan Nieder wrote:
> 
> > I wonder if in addition to the above documentation change we may want
> > something guaranteed to catch all cases where people would have
> > experienced a regression, like
> > 
> > diff --git i/credential-store.c w/credential-store.c
> > index c010497cb21..294e7716815 100644
> > --- i/credential-store.c
> > +++ w/credential-store.c
> > @@ -24,8 +24,8 @@ static int parse_credential_file(const char *fn,
> >  	}
> >  
> >  	while (strbuf_getline_lf(&line, fh) != EOF) {
> > -		credential_from_url(&entry, line.buf);
> > -		if (entry.username && entry.password &&
> > +		if (!credential_from_url_gently(&entry, line.buf, 1) &&
> > +		    entry.username && entry.password &&
> >  		    credential_match(c, &entry)) {
> >  			found_credential = 1;
> >  			if (match_cb) {
> > 
> > And then we can tighten the handling of unrecognized lines to first
> > warn and then error out, as a controlled change that doesn't lead
> > people to regret updating git.
> 
> I like that solution, as it mostly brings us back to the original
> behavior, as weird or unexpected as it was.

I like this version better as well, and we could even reuse my test
case.

it wouldn't cover cases where there were leading spaces/tabs around
the credential which I have to admit I liked just because it is
more robust to bad input, and there is no sane way now to tell the
user that there is invalid data anyway, but I am ok eitherway.

Carlo
Jeff King April 28, 2020, 8:16 a.m. UTC | #13
On Tue, Apr 28, 2020 at 12:18:02AM -0700, Carlo Marcelo Arenas Belón wrote:

> > >  	while (strbuf_getline_lf(&line, fh) != EOF) {
> > > -		credential_from_url(&entry, line.buf);
> > > -		if (entry.username && entry.password &&
> > > +		if (!credential_from_url_gently(&entry, line.buf, 1) &&
> > > +		    entry.username && entry.password &&
> > >  		    credential_match(c, &entry)) {
> > >  			found_credential = 1;
> > >  			if (match_cb) {
> > > 
> > > And then we can tighten the handling of unrecognized lines to first
> > > warn and then error out, as a controlled change that doesn't lead
> > > people to regret updating git.
> > 
> > I like that solution, as it mostly brings us back to the original
> > behavior, as weird or unexpected as it was.
> 
> I like this version better as well, and we could even reuse my test
> case.
> 
> it wouldn't cover cases where there were leading spaces/tabs around
> the credential which I have to admit I liked just because it is
> more robust to bad input, and there is no sane way now to tell the
> user that there is invalid data anyway, but I am ok eitherway.

I think if we're discouraging people from hand-editing the file, then
that feature would be going in the wrong direction anyway.

Did you or Jonathan want to wrap it up with the test and commit message?

-Peff
Stefan Tauner April 28, 2020, 10:58 a.m. UTC | #14
On Mon, 27 Apr 2020 22:25:10 -0700
Jonathan Nieder <jrnieder@gmail.com> wrote:

> Independently, in Debian's bug tracking system, Stefan (cc-ed)
> reports[2]:
> 
> | the vulnerability in CVE-2020-11008 is related to the handling
> | of credential helpers in git. In Buster this has been fixed in
> | 1:2.20.1-2+deb10u3. This broke my existing configuration where
> | repositories have credential.helper=store set. This is
> | documented in /usr/share/man/man1/git-credential-store.1.gz
> | and other files from git, git-doc etc.
> | I am unsure how to proceed... is this helper now unsupported?
> 
> (Stefan, do you have more details?  Did you manually populate your
> credential store?  What error message do you get?)

I can't remember for sure - it's literally years ago - but I am pretty
sure I did not *populate* it manually initially. However, I might have
edited it to separate entries (https vs. smtp) and I might have added
additional entries by c&p & editing.

I have replaced the one instance of \n\n used for separation with a
normal single line break between entries and how it works fine. Before
that I got the following output (that's obviously not very helpful if
you don't know the whole story :)

> git pull
> warning: url has no scheme: 
> fatal: credential url cannot be parsed: 
> Already up to date.
> Current branch master is up to date.
Carlo Marcelo Arenas Belón April 28, 2020, 11:25 a.m. UTC | #15
On Tue, Apr 28, 2020 at 04:16:27AM -0400, Jeff King wrote:
> On Tue, Apr 28, 2020 at 12:18:02AM -0700, Carlo Marcelo Arenas Belón wrote:
> > 
> > it wouldn't cover cases where there were leading spaces/tabs around
> > the credential which I have to admit I liked just because it is
> > more robust to bad input, and there is no sane way now to tell the
> > user that there is invalid data anyway, but I am ok eitherway.
> 
> I think if we're discouraging people from hand-editing the file, then
> that feature would be going in the wrong direction anyway.

I think it will actually encourage them to edit the file (and complain
of a regression), since they can see the credential is correct, but
somehow git fails to work with it unlike their previous version.

remember there is no warning that could explain why that happens and
with this pach there is no error either.

> Did you or Jonathan want to wrap it up with the test and commit message?

I was going to say Jonathan will have to do it, since he was the only one
that didn't provide a patch (and therefore a sign off) but guess I can
save everyone some time and practice my git-send-email skills (which 
obviously failed me)

Jonathan,

make sure your patch[1] is complete and correct and to resend it with
a sign off, feel free to add me as Tested-by if you feel like, but I
added myself as Helped-by anyway ;)

Junio,

didn't fix the typo that Peff suggested fixed[2], because I forgot
and I wasn't sure if that also means I should SOB it, but if you
could also consider my suggestion[3] then at you get my SOB warranted ;)

Carlo

PS. guess I should had send it as an RFC instead

[1] https://lore.kernel.org/git/20200428105254.28658-3-carenas@gmail.com/
[2] https://lore.kernel.org/git/20200427211013.GB1732530@coredump.intra.peff.net/
[3] https://lore.kernel.org/git/20200427234909.GC61348@Carlos-MBP/
Junio C Hamano April 28, 2020, 4:03 p.m. UTC | #16
Jonathan Nieder <jrnieder@gmail.com> writes:

> I wonder if in addition to the above documentation change we may want
> something guaranteed to catch all cases where people would have
> experienced a regression, like
>
> diff --git i/credential-store.c w/credential-store.c
> index c010497cb21..294e7716815 100644
> --- i/credential-store.c
> +++ w/credential-store.c
> @@ -24,8 +24,8 @@ static int parse_credential_file(const char *fn,
>  	}
>  
>  	while (strbuf_getline_lf(&line, fh) != EOF) {
> -		credential_from_url(&entry, line.buf);
> -		if (entry.username && entry.password &&
> +		if (!credential_from_url_gently(&entry, line.buf, 1) &&
> +		    entry.username && entry.password &&
>  		    credential_match(c, &entry)) {
>  			found_credential = 1;
>  			if (match_cb) {

Hmph, so the idea is, instead of ignoring the potential error
detected by credential_from_url() and using credential when it is
available, we loudly attempt to parse and give warning on malformed
entries when we discard a line?

I think that is an excellent idea.

It would be nicer if we can somehow add where we found the offending
line, e.g.

    /home/me/.gitcredential:396: url has no scheme: ://u:p@host/path

Do we feel it a bit disturbing that u:p is shown in the error
message, without redacting, by the way?
Carlo Marcelo Arenas Belón April 28, 2020, 9:14 p.m. UTC | #17
On Tue, Apr 28, 2020 at 09:03:36AM -0700, Junio C Hamano wrote:
> Jonathan Nieder <jrnieder@gmail.com> writes:
> 
> > I wonder if in addition to the above documentation change we may want
> > something guaranteed to catch all cases where people would have
> > experienced a regression, like
> >
> > diff --git i/credential-store.c w/credential-store.c
> > index c010497cb21..294e7716815 100644
> > --- i/credential-store.c
> > +++ w/credential-store.c
> > @@ -24,8 +24,8 @@ static int parse_credential_file(const char *fn,
> >  	}
> >  
> >  	while (strbuf_getline_lf(&line, fh) != EOF) {
> > -		credential_from_url(&entry, line.buf);
> > -		if (entry.username && entry.password &&
> > +		if (!credential_from_url_gently(&entry, line.buf, 1) &&
> > +		    entry.username && entry.password &&
> >  		    credential_match(c, &entry)) {
> >  			found_credential = 1;
> >  			if (match_cb) {
> 
> Hmph, so the idea is, instead of ignoring the potential error
> detected by credential_from_url() and using credential when it is
> available, we loudly attempt to parse and give warning on malformed
> entries when we discard a line?

the idea was to silently ignore the line (notice quiet = 1), which
is the "original behaviour" as Peff pointed out in his reply in
20200428054155.GB2376380@coredump.intra.peff.net[1]

> I think that is an excellent idea.
> 
> It would be nicer if we can somehow add where we found the offending
> line, e.g.
> 
>     /home/me/.gitcredential:396: url has no scheme: ://u:p@host/path
> 
> Do we feel it a bit disturbing that u:p is shown in the error
> message, without redacting, by the way?

we could do so with the following on top as a 5/4.

most of the test changes would go away in a reroll, and are similar
to what would have been needed for my original suggested patch[2] with
the exception that in this series we won't support silently fixing
credentials which is something you mentioned a preference on.

Carlo

[1] https://lore.kernel.org/git/20200428054155.GB2376380@coredump.intra.peff.net/ 
[2] https://lore.kernel.org/git/20200428013726.GD61348@Carlos-MBP/
-- >8 --
Subject: [RFC PATCH] credential-store: SQUASH!!

add warnings

Suggested-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>
---
 credential-store.c          | 55 +++++++++++++++++++++++++++++++------
 t/t0302-credential-store.sh | 42 ++++++++++++++++------------
 2 files changed, 70 insertions(+), 27 deletions(-)

diff --git a/credential-store.c b/credential-store.c
index 294e771681..f76292df20 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -6,6 +6,32 @@
 
 static struct lock_file credential_lock;
 
+static char *redact_credential(const struct strbuf *line)
+{
+	struct strbuf redacted_line = STRBUF_INIT;
+	char *at = strchr(line->buf, '@');
+	char *colon;
+	int redacted = 0;
+
+	if (at) {
+		strbuf_addf(&redacted_line, "%.*s",
+			(int)(at - line->buf), line->buf);
+		colon = strrchr(redacted_line.buf, ':');
+		if (colon && *(colon + 1) != '/' && colon > redacted_line.buf) {
+			redacted_line.len = colon - redacted_line.buf + 1;
+			strbuf_addstr(&redacted_line, "<redacted>");
+			strbuf_addstr(&redacted_line, at);
+			redacted = 1;
+		}
+		else
+			strbuf_reset(&redacted_line);
+	}
+	if (!redacted)
+		strbuf_addbuf(&redacted_line, line);
+
+	return redacted_line.buf;
+}
+
 static int parse_credential_file(const char *fn,
 				  struct credential *c,
 				  void (*match_cb)(struct credential *),
@@ -15,6 +41,7 @@ static int parse_credential_file(const char *fn,
 	struct strbuf line = STRBUF_INIT;
 	struct credential entry = CREDENTIAL_INIT;
 	int found_credential = 0;
+	int lineno = 0;
 
 	fh = fopen(fn, "r");
 	if (!fh) {
@@ -24,17 +51,27 @@ static int parse_credential_file(const char *fn,
 	}
 
 	while (strbuf_getline_lf(&line, fh) != EOF) {
-		if (!credential_from_url_gently(&entry, line.buf, 1) &&
-		    entry.username && entry.password &&
-		    credential_match(c, &entry)) {
-			found_credential = 1;
-			if (match_cb) {
-				match_cb(&entry);
-				break;
+		lineno++;
+		if (!credential_from_url_gently(&entry, line.buf, 1)) {
+			if (entry.username && entry.password &&
+				credential_match(c, &entry)) {
+				found_credential = 1;
+				if (match_cb) {
+					match_cb(&entry);
+					break;
+				}
 			}
+			else if (other_cb)
+				other_cb(&line);
+		}
+		else {
+			if (other_cb)
+				other_cb(&line);
+			char *redacted = redact_credential(&line);
+			warning("%s:%d ignoring invalid credential: %s",
+				fn, lineno, redacted);
+			free(redacted);
 		}
-		else if (other_cb)
-			other_cb(&line);
 	}
 
 	credential_clear(&entry);
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
index 7c7a48303f..597a75903a 100755
--- a/t/t0302-credential-store.sh
+++ b/t/t0302-credential-store.sh
@@ -120,36 +120,42 @@ test_expect_success 'erase: erase matching credentials from both xdg and home fi
 	test_must_be_empty "$HOME/.config/git/credentials"
 '
 
-test_expect_success 'get: store file can contain empty/bogus lines' '
-	test_write_lines "#comment" " " "" \
-		 https://user:pass@example.com >"$HOME/.git-credentials" &&
-	check fill store <<-\EOF
+test_expect_success 'get: credentials without scheme are invalid' '
+	echo "://user:pass@example.com" >"$HOME/.git-credentials" &&
+	cat >expect-stdout <<-\STDOUT &&
 	protocol=https
 	host=example.com
-	--
+	username=askpass-username
+	password=askpass-password
+	STDOUT
+	test_config credential.helper store &&
+	git credential fill <<-\EOF >stdout 2>stderr &&
 	protocol=https
 	host=example.com
-	username=user
-	password=pass
-	--
 	EOF
+	test_cmp expect-stdout stdout &&
+	grep "askpass: Username for '\''https://example.com'\'':" stderr &&
+	grep "askpass: Password for '\''https://askpass-username@example.com'\'':" stderr &&
+	test_i18ngrep "ignoring invalid credential" stderr
 '
 
-test_expect_success 'get: ignore credentials without scheme' '
-	echo "://user:pass@example.com" >"$HOME/.git-credentials" &&
-	check fill store <<-\EOF
+test_expect_success 'get: store file can contain empty/bogus lines' '
+	test_write_lines "#comment" " " "" \
+		 https://user:pass@example.com >"$HOME/.git-credentials" &&
+	cat >expect-stdout <<-\STDOUT &&
 	protocol=https
 	host=example.com
-	--
+	username=user
+	password=pass
+	STDOUT
+	test_config credential.helper store &&
+	git credential fill <<-\EOF >stdout 2>stderr &&
 	protocol=https
 	host=example.com
-	username=askpass-username
-	password=askpass-password
-	--
-	askpass: Username for '\''https://example.com'\'':
-	askpass: Password for '\''https://askpass-username@example.com'\'':
-	--
 	EOF
+	test_cmp expect-stdout stdout &&
+	test_i18ngrep "ignoring invalid credential" stderr &&
+	test_line_count = 3 stderr
 '
 
 test_done
Junio C Hamano April 28, 2020, 9:17 p.m. UTC | #18
Carlo Marcelo Arenas Belón <carenas@gmail.com> writes:

>> > +		if (!credential_from_url_gently(&entry, line.buf, 1) &&
>> > +		    entry.username && entry.password &&
>> >  		    credential_match(c, &entry)) {
>> >  			found_credential = 1;
>> >  			if (match_cb) {
>> 
>> Hmph, so the idea is, instead of ignoring the potential error
>> detected by credential_from_url() and using credential when it is
>> available, we loudly attempt to parse and give warning on malformed
>> entries when we discard a line?
>
> the idea was to silently ignore the line (notice quiet = 1), which

Ah, sorry, I apparently did not read the patch carefully enough.

Thanks for a correction.
Philip Oakley April 29, 2020, 10:09 a.m. UTC | #19
Hi Carlo,
Thanks for the clarification.
Philip

On 28/04/2020 02:49, Carlo Marcelo Arenas Belón wrote:
> On Mon, Apr 27, 2020 at 02:48:05PM +0100, Philip Oakley wrote:
>> On 27/04/2020 13:59, Carlo Marcelo Arenas Belón wrote:
>>> with the added checks for invalid URLs in credentials, any locally
>>> modified store files which might have empty lines or even comments
>>> were reported[1] failing to parse as valid credentials.
>>>
>>> using the store file in this manner wasn't intended by the original
>>> code and it had latent issues which the new code dutifully prevented
>>> but since the strings used wouldn't had been valid credentials anyway
>>> we could instead detect them and skip the matching logic and therefore
>>> formalize this "feature".
>>>
>>> trim all lines as they are being read from the store file and skip the
>> Does trimming affect any credentials that may have spaces either end?
> all credentials are url encoded so the only spaces that are affected are
> the ones that were added by careless editing of that file.
>
> as Eric pointed out, any tabs will be silently "cleaned" as well.
>
>> Should the trimming of leading/trailing spaces be mentioned?
> I wanted to keep that as an undocumented "feature" as it is just meant
> to help people to avoid a fatal error during the transition but didn't
> want to encourage people thinking it is a supported part or even worse
> encourage people to start editing their files to add tabs and spaces.
>
> Junio's suggested documentation fix[1] makes that clearer that I could
>
>> Also the git-credential page mentions that the credential must end with
>> a blank line ("don't forget.."). Should that be mentioned here, or have
>> I misunderstood?
> that is for the protocol part of it (which is used between git and the
> credential helper), the file format for this specific helper doesn't
> require that.
>
> Carlo
>
> [1] https://lore.kernel.org/git/xmqqv9lk7j7p.fsf@gitster.c.googlers.com/
diff mbox series

Patch

diff --git a/Documentation/git-credential-store.txt b/Documentation/git-credential-store.txt
index 693dd9d9d7..48ab4b13e5 100644
--- a/Documentation/git-credential-store.txt
+++ b/Documentation/git-credential-store.txt
@@ -101,6 +101,13 @@  username (if we already have one) match, then the password is returned
 to Git. See the discussion of configuration in linkgit:gitcredentials[7]
 for more information.
 
+Note that the file used is not a configuration file and should be ideally
+managed only through git, as any manually introduced typos will compromise
+the validation of credentials.
+
+The parser will ignore any lines starting with the '#' character during
+the processing of credentials for read, though.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
diff --git a/credential-store.c b/credential-store.c
index c010497cb2..b2f160890d 100644
--- a/credential-store.c
+++ b/credential-store.c
@@ -24,6 +24,9 @@  static int parse_credential_file(const char *fn,
 	}
 
 	while (strbuf_getline_lf(&line, fh) != EOF) {
+		strbuf_trim(&line);
+		if (line.len == 0 || *line.buf == '#')
+			continue;
 		credential_from_url(&entry, line.buf);
 		if (entry.username && entry.password &&
 		    credential_match(c, &entry)) {
diff --git a/t/t0302-credential-store.sh b/t/t0302-credential-store.sh
index d6b54e8c65..5e6ace3a06 100755
--- a/t/t0302-credential-store.sh
+++ b/t/t0302-credential-store.sh
@@ -120,4 +120,19 @@  test_expect_success 'erase: erase matching credentials from both xdg and home fi
 	test_must_be_empty "$HOME/.config/git/credentials"
 '
 
+test_expect_success 'get: allow for empty lines or comments in store file' '
+	test_write_lines "#comment" " " "" \
+		 https://user:pass@example.com >"$HOME/.git-credentials" &&
+	check fill store <<-\EOF
+	protocol=https
+	host=example.com
+	--
+	protocol=https
+	host=example.com
+	username=user
+	password=pass
+	--
+	EOF
+'
+
 test_done