credential: handle `credential.<partial-URL>.<key>` again
In the patches for CVE-2020-11008, the ability to specify credential settings in the config for partial URLs got lost. For example, it used to be possible to specify a credential helper for a specific protocol: [credential "https://"] helper = my-https-helper Likewise, it used to be possible to configure settings for a specific host, e.g.: [credential "dev.azure.com"] useHTTPPath = true Let's reinstate this behavior. While at it, increase the test coverage to document and verify the behavior with a couple other categories of partial URLs. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Johannes Schindelin committed
Apr 24, 2020 at 22:35 UTC
12294990c90e043862be9eb7eb22c3784b526340
4 files changed
+77
-3
credential.c
+27
@@ -37,6 +37,10 @@ int credential_match(const struct credential *want,
37
#undef CHECK
38
}
39
40
+
41
+static int credential_from_potentially_partial_url(struct credential *c,
42
+ const char *url);
43
+
44
static int credential_config_callback(const char *var, const char *value,
45
void *data)
46
{
@@ -82,6 +86,22 @@ static int select_all(const struct urlmatch_item *a,
86
return 0;
87
}
88
89
+static int match_partial_url(const char *url, void *cb)
90
+{
91
+ struct credential *c = cb;
92
+ struct credential want = CREDENTIAL_INIT;
93
+ int matches = 0;
94
+
95
+ if (credential_from_potentially_partial_url(&want, url) < 0)
96
+ warning(_("skipping credential lookup for key: credential.%s"),
97
+ url);
98
+ else
99
+ matches = credential_match(&want, c);
100
+ credential_clear(&want);
101
+
102
+ return matches;
103
+}
104
+
105
static void credential_apply_config(struct credential *c)
106
{
107
char *normalized_url;
@@ -101,6 +121,7 @@ static void credential_apply_config(struct credential *c)
121
config.collect_fn = credential_config_callback;
122
config.cascade_fn = NULL;
123
config.select_fn = select_all;
124
+ config.fallback_match_fn = match_partial_url;
125
config.cb = c;
126
127
credential_format(c, &url);
@@ -468,6 +489,12 @@ static int credential_from_url_1(struct credential *c, const char *url,
489
return 0;
490
}
491
492
+static int credential_from_potentially_partial_url(struct credential *c,
493
+ const char *url)
494
+{
495
+ return credential_from_url_1(c, url, 1, 0);
496
+}
497
+
498
int credential_from_url_gently(struct credential *c, const char *url, int quiet)
499
{
500
return credential_from_url_1(c, url, 0, quiet);
t/t0300-credentials.sh
+38
@@ -575,4 +575,42 @@ test_expect_success 'credential system refuses to work with missing protocol' '
575
test_i18ncmp expect stderr
576
'
577
578
+test_expect_success 'credential config with partial URLs' '
579
+ echo "echo password=yep" | write_script git-credential-yep &&
580
+ test_write_lines url=https://user@example.com/repo.git >stdin &&
581
+ for partial in \
582
+ example.com \
583
+ user@example.com \
584
+ https:// \
585
+ https://example.com \
586
+ https://example.com/ \
587
+ https://user@example.com \
588
+ https://user@example.com/ \
589
+ https://example.com/repo.git \
590
+ https://user@example.com/repo.git \
591
+ /repo.git
592
+ do
593
+ git -c credential.$partial.helper=yep \
594
+ credential fill <stdin >stdout &&
595
+ grep yep stdout ||
596
+ return 1
597
+ done &&
598
+
599
+ for partial in \
600
+ dont.use.this \
601
+ http:// \
602
+ /repo
603
+ do
604
+ git -c credential.$partial.helper=yep \
605
+ credential fill <stdin >stdout &&
606
+ ! grep yep stdout ||
607
+ return 1
608
+ done &&
609
+
610
+ git -c credential.$partial.helper=yep \
611
+ -c credential.with%0anewline.username=uh-oh \
612
+ credential fill <stdin >stdout 2>stderr &&
613
+ test_i18ngrep "skipping credential lookup for key" stderr
614
+'
615
+
616
test_done
urlmatch.c
+7
-3
@@ -572,10 +572,14 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
572
573
config_url = xmemdupz(key, dot - key);
574
norm_url = url_normalize_1(config_url, &norm_info, 1);
575
+ if (norm_url)
576
+ retval = match_urls(url, &norm_info, &matched);
577
+ else if (collect->fallback_match_fn)
578
+ retval = collect->fallback_match_fn(config_url,
579
+ collect->cb);
580
+ else
581
+ retval = 0;
582
free(config_url);
576
- if (!norm_url)
577
- return 0;
578
- retval = match_urls(url, &norm_info, &matched);
583
free(norm_url);
584
if (!retval)
585
return 0;
urlmatch.h
+5
@@ -59,6 +59,11 @@ struct urlmatch_config {
59
* specificity rules) than existing.
60
*/
61
int (*select_fn)(const struct urlmatch_item *found, const struct urlmatch_item *existing);
62
+ /*
63
+ * An optional callback to allow e.g. for partial URLs; it shall
64
+ * return 1 or 0 depending whether `url` matches or not.
65
+ */
66
+ int (*fallback_match_fn)(const char *url, void *cb);
67
};
68
69
int urlmatch_config_entry(const char *var, const char *value, void *cb);