Skip to content

Commit 8ba8ed5

Browse files
peffjrn
authored andcommitted
credential: refuse to operate when missing host or protocol
The credential helper protocol was designed to be very flexible: the fields it takes as input are treated as a pattern, and any missing fields are taken as wildcards. This allows unusual things like: echo protocol=https | git credential reject to delete all stored https credentials (assuming the helpers themselves treat the input that way). But when helpers are invoked automatically by Git, this flexibility works against us. If for whatever reason we don't have a "host" field, then we'd match _any_ host. When you're filling a credential to send to a remote server, this is almost certainly not what you want. Prevent this at the layer that writes to the credential helper. Add a check to the credential API that the host and protocol are always passed in, and add an assertion to the credential_write function that speaks credential helper protocol to be doubly sure. There are a few ways this can be triggered in practice: - the "git credential" command passes along arbitrary credential parameters it reads from stdin. - until the previous patch, when the host field of a URL is empty, we would leave it unset (rather than setting it to the empty string) - a URL like "example.com/foo.git" is treated by curl as if "http://" was present, but our parser sees it as a non-URL and leaves all fields unset - the recent fix for URLs with embedded newlines blanks the URL but otherwise continues. Rather than having the desired effect of looking up no credential at all, many helpers will return _any_ credential Our earlier test for an embedded newline didn't catch this because it only checked that the credential was cleared, but didn't configure an actual helper. Configuring the "verbatim" helper in the test would show that it is invoked (it's obviously a silly helper which doesn't look at its input, but the point is that it shouldn't be run at all). Since we're switching this case to die(), we don't need to bother with a helper. We can see the new behavior just by checking that the operation fails. We'll add new tests covering partial input as well (these can be triggered through various means with url-parsing, but it's simpler to just check them directly, as we know we are covered even if the url parser changes behavior in the future). [jn: changed to die() instead of logging and showing a manual username/password prompt] Reported-by: Carlo Arenas <[email protected]> Signed-off-by: Jeff King <[email protected]> Signed-off-by: Jonathan Nieder <[email protected]>
1 parent 2403668 commit 8ba8ed5

File tree

2 files changed

+40
-14
lines changed

2 files changed

+40
-14
lines changed

credential.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ static int proto_is_http(const char *s)
8888

8989
static void credential_apply_config(struct credential *c)
9090
{
91+
if (!c->host)
92+
die(_("refusing to work with credential missing host field"));
93+
if (!c->protocol)
94+
die(_("refusing to work with credential missing protocol field"));
95+
9196
if (c->configured)
9297
return;
9398
git_config(credential_config_callback, c);
@@ -190,8 +195,11 @@ int credential_read(struct credential *c, FILE *fp)
190195
return 0;
191196
}
192197

193-
static void credential_write_item(FILE *fp, const char *key, const char *value)
198+
static void credential_write_item(FILE *fp, const char *key, const char *value,
199+
int required)
194200
{
201+
if (!value && required)
202+
BUG("credential value for %s is missing", key);
195203
if (!value)
196204
return;
197205
if (strchr(value, '\n'))
@@ -201,11 +209,11 @@ static void credential_write_item(FILE *fp, const char *key, const char *value)
201209

202210
void credential_write(const struct credential *c, FILE *fp)
203211
{
204-
credential_write_item(fp, "protocol", c->protocol);
205-
credential_write_item(fp, "host", c->host);
206-
credential_write_item(fp, "path", c->path);
207-
credential_write_item(fp, "username", c->username);
208-
credential_write_item(fp, "password", c->password);
212+
credential_write_item(fp, "protocol", c->protocol, 1);
213+
credential_write_item(fp, "host", c->host, 1);
214+
credential_write_item(fp, "path", c->path, 0);
215+
credential_write_item(fp, "username", c->username, 0);
216+
credential_write_item(fp, "password", c->password, 0);
209217
}
210218

211219
static int run_credential_helper(struct credential *c,

t/t0300-credentials.sh

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -400,18 +400,16 @@ test_expect_success 'empty helper spec resets helper list' '
400400
EOF
401401
'
402402

403-
test_expect_success 'url parser ignores embedded newlines' '
404-
check fill <<-EOF
403+
test_expect_success 'url parser rejects embedded newlines' '
404+
test_must_fail git credential fill 2>stderr <<-\EOF &&
405405
url=https://one.example.com?%0ahost=two.example.com/
406-
--
407-
username=askpass-username
408-
password=askpass-password
409-
--
406+
EOF
407+
cat >expect <<-\EOF &&
410408
warning: url contains a newline in its host component: https://one.example.com?%0ahost=two.example.com/
411409
warning: skipping credential lookup for url: https://one.example.com?%0ahost=two.example.com/
412-
askpass: Username:
413-
askpass: Password:
410+
fatal: refusing to work with credential missing host field
414411
EOF
412+
test_i18ncmp expect stderr
415413
'
416414

417415
test_expect_success 'host-less URLs are parsed as empty host' '
@@ -431,4 +429,24 @@ test_expect_success 'host-less URLs are parsed as empty host' '
431429
EOF
432430
'
433431

432+
test_expect_success 'credential system refuses to work with missing host' '
433+
test_must_fail git credential fill 2>stderr <<-\EOF &&
434+
protocol=http
435+
EOF
436+
cat >expect <<-\EOF &&
437+
fatal: refusing to work with credential missing host field
438+
EOF
439+
test_i18ncmp expect stderr
440+
'
441+
442+
test_expect_success 'credential system refuses to work with missing protocol' '
443+
test_must_fail git credential fill 2>stderr <<-\EOF &&
444+
host=example.com
445+
EOF
446+
cat >expect <<-\EOF &&
447+
fatal: refusing to work with credential missing protocol field
448+
EOF
449+
test_i18ncmp expect stderr
450+
'
451+
434452
test_done

0 commit comments

Comments
 (0)