Merge branch 'ds/credentials-in-url'
The "fetch.credentialsInUrl" configuration variable controls what happens when a URL with embedded login credential is used. * ds/credentials-in-url: remote: create fetch.credentialsInUrl configmaint
commit
11698e551c
|
@ -96,3 +96,17 @@ fetch.writeCommitGraph::
|
|||
merge and the write may take longer. Having an updated commit-graph
|
||||
file helps performance of many Git commands, including `git merge-base`,
|
||||
`git push -f`, and `git log --graph`. Defaults to false.
|
||||
|
||||
fetch.credentialsInUrl::
|
||||
A URL can contain plaintext credentials in the form
|
||||
`<protocol>://<user>:<password>@<domain>/<path>`. Using such URLs
|
||||
is not recommended as it exposes the password in multiple ways,
|
||||
including Git storing the URL as plaintext in the repository config.
|
||||
The `fetch.credentialsInUrl` option provides instruction for how Git
|
||||
should react to seeing such a URL, with these values:
|
||||
+
|
||||
* `allow` (default): Git will proceed with its activity without warning.
|
||||
* `warn`: Git will write a warning message to `stderr` when parsing a URL
|
||||
with a plaintext credential.
|
||||
* `die`: Git will write a failure message to `stderr` when parsing a URL
|
||||
with a plaintext credential.
|
||||
|
|
48
remote.c
48
remote.c
|
@ -1,6 +1,7 @@
|
|||
#include "cache.h"
|
||||
#include "config.h"
|
||||
#include "remote.h"
|
||||
#include "urlmatch.h"
|
||||
#include "refs.h"
|
||||
#include "refspec.h"
|
||||
#include "object-store.h"
|
||||
|
@ -617,6 +618,50 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
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))
|
||||
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 = { 0 };
|
||||
|
||||
if (!url_normalize(remote->url[i], &url_info) ||
|
||||
!url_info.passwd_off)
|
||||
goto loop_cleanup;
|
||||
|
||||
strbuf_reset(&redacted);
|
||||
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 (warn_not_die)
|
||||
warning(_("URL '%s' uses plaintext credentials"), redacted.buf);
|
||||
else
|
||||
die(_("URL '%s' uses plaintext credentials"), redacted.buf);
|
||||
|
||||
loop_cleanup:
|
||||
free(url_info.url);
|
||||
}
|
||||
|
||||
strbuf_release(&redacted);
|
||||
}
|
||||
|
||||
static struct remote *
|
||||
remotes_remote_get_1(struct remote_state *remote_state, const char *name,
|
||||
const char *(*get_default)(struct remote_state *,
|
||||
|
@ -642,6 +687,9 @@ remotes_remote_get_1(struct remote_state *remote_state, const char *name,
|
|||
add_url_alias(remote_state, ret, name);
|
||||
if (!valid_remote(ret))
|
||||
return NULL;
|
||||
|
||||
validate_remote_url(ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ This test checks the following functionality:
|
|||
* --porcelain output format
|
||||
* hiderefs
|
||||
* reflogs
|
||||
* URL validation
|
||||
'
|
||||
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||
|
@ -1833,4 +1834,35 @@ test_expect_success 'refuse to push a hidden ref, and make sure do not pollute t
|
|||
test_dir_is_empty testrepo/.git/objects/pack
|
||||
'
|
||||
|
||||
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 &&
|
||||
|
||||
test_must_fail git -c fetch.credentialsInUrl=die fetch https://username:@localhost 2>err &&
|
||||
grep "fatal: $message" err >warnings &&
|
||||
test_line_count = 1 warnings
|
||||
'
|
||||
|
||||
|
||||
test_expect_success 'push warns or fails when using username:password' '
|
||||
message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
|
||||
test_must_fail git -c fetch.credentialsInUrl=allow push https://username:password@localhost 2>err &&
|
||||
! grep "$message" err &&
|
||||
|
||||
test_must_fail git -c fetch.credentialsInUrl=warn push https://username:password@localhost 2>err &&
|
||||
grep "warning: $message" err >warnings &&
|
||||
test_must_fail git -c fetch.credentialsInUrl=die push https://username:password@localhost 2>err &&
|
||||
grep "fatal: $message" err >warnings &&
|
||||
test_line_count = 1 warnings
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -71,6 +71,29 @@ test_expect_success 'clone respects GIT_WORK_TREE' '
|
|||
|
||||
'
|
||||
|
||||
test_expect_success 'clone warns or fails when using username:password' '
|
||||
message="URL '\''https://username:<redacted>@localhost/'\'' uses plaintext credentials" &&
|
||||
test_must_fail git -c fetch.credentialsInUrl=allow clone https://username:password@localhost attempt1 2>err &&
|
||||
! grep "$message" err &&
|
||||
|
||||
test_must_fail git -c fetch.credentialsInUrl=warn clone https://username:password@localhost attempt2 2>err &&
|
||||
grep "warning: $message" err >warnings &&
|
||||
test_line_count = 2 warnings &&
|
||||
|
||||
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
|
||||
'
|
||||
|
||||
test_expect_success 'clone does not detect username:password when it is https://username@domain:port/' '
|
||||
test_must_fail git -c fetch.credentialsInUrl=warn clone https://username@localhost:8080 attempt3 2>err &&
|
||||
! grep "uses plaintext credentials" err
|
||||
'
|
||||
|
||||
test_expect_success 'clone from hooks' '
|
||||
|
||||
test_create_repo r0 &&
|
||||
|
|
Loading…
Reference in New Issue