credential_format(): also encode <host>[:<port>]

An upcoming change wants to sanitize the credential password prompt where a URL is displayed that may potentially come from a `.gitmodules` file. To this end, the `credential_format()` function is employed. To sanitize the host name (and optional port) part of the URL, we need a new mode of the `strbuf_add_percentencode()` function because the current mode is both too strict and too lenient: too strict because it encodes `:`, `[` and `]` (which should be left unencoded in `<host>:<port>` and in IPv6 addresses), and too lenient because it does not encode invalid host name characters `/`, `_` and `~`. So let's introduce and use a new mode specifically to encode the host name and optional port part of a URI, leaving alpha-numerical characters, periods, colons and brackets alone and encoding all others. This only leads to a change of behavior for URLs that contain invalid host names. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>

Johannes Schindelin committed Nov 7, 2024 at 08:57 UTC c903985bf7e772e2d08275c1a95c8a55ab011577
4 files changed +19 -2
credential.c
+2 -1
@@ -164,7 +164,8 @@ static void credential_format(struct credential *c, struct strbuf *out)
164 strbuf_addch(out, '@');
165 }
166 if (c->host)
167 - strbuf_addstr(out, c->host);
167 + strbuf_add_percentencode(out, c->host,
168 + STRBUF_ENCODE_HOST_AND_PORT);
169 if (c->path) {
170 strbuf_addch(out, '/');
171 strbuf_add_percentencode(out, c->path, 0);
strbuf.c
+3 -1
@@ -492,7 +492,9 @@ void strbuf_add_percentencode(struct strbuf *dst, const char *src, int flags)
492 unsigned char ch = src[i];
493 if (ch <= 0x1F || ch >= 0x7F ||
494 (ch == '/' && (flags & STRBUF_ENCODE_SLASH)) ||
495 - strchr(URL_UNSAFE_CHARS, ch))
495 + ((flags & STRBUF_ENCODE_HOST_AND_PORT) ?
496 + !isalnum(ch) && !strchr("-.:[]", ch) :
497 + !!strchr(URL_UNSAFE_CHARS, ch)))
498 strbuf_addf(dst, "%%%02X", (unsigned char)ch);
499 else
500 strbuf_addch(dst, ch);
strbuf.h
+1
@@ -380,6 +380,7 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb,
380 void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
381
382 #define STRBUF_ENCODE_SLASH 1
383 +#define STRBUF_ENCODE_HOST_AND_PORT 2
384
385 /**
386 * Append the contents of a string to a strbuf, percent-encoding any characters
t/t0300-credentials.sh
+13
@@ -514,6 +514,19 @@ test_expect_success 'match percent-encoded values in username' '
514 EOF
515 '
516
517 +test_expect_success 'match percent-encoded values in hostname' '
518 + test_config "credential.https://a%20b%20c/.helper" "$HELPER" &&
519 + check fill <<-\EOF
520 + url=https://a b c/
521 + --
522 + protocol=https
523 + host=a b c
524 + username=foo
525 + password=bar
526 + --
527 + EOF
528 +'
529 +
530 test_expect_success 'fetch with multiple path components' '
531 test_unconfig credential.helper &&
532 test_config credential.https://example.com/foo/repo.git.helper "verbatim foo bar" &&