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 config
This commit is contained in:
commit
11698e551c
@ -96,3 +96,17 @@ fetch.writeCommitGraph::
|
|||||||
merge and the write may take longer. Having an updated commit-graph
|
merge and the write may take longer. Having an updated commit-graph
|
||||||
file helps performance of many Git commands, including `git merge-base`,
|
file helps performance of many Git commands, including `git merge-base`,
|
||||||
`git push -f`, and `git log --graph`. Defaults to false.
|
`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 "cache.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "remote.h"
|
#include "remote.h"
|
||||||
|
#include "urlmatch.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "refspec.h"
|
#include "refspec.h"
|
||||||
#include "object-store.h"
|
#include "object-store.h"
|
||||||
@ -617,6 +618,50 @@ const char *remote_ref_for_branch(struct branch *branch, int for_push)
|
|||||||
return NULL;
|
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 *
|
static struct remote *
|
||||||
remotes_remote_get_1(struct remote_state *remote_state, const char *name,
|
remotes_remote_get_1(struct remote_state *remote_state, const char *name,
|
||||||
const char *(*get_default)(struct remote_state *,
|
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);
|
add_url_alias(remote_state, ret, name);
|
||||||
if (!valid_remote(ret))
|
if (!valid_remote(ret))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
validate_remote_url(ret);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ This test checks the following functionality:
|
|||||||
* --porcelain output format
|
* --porcelain output format
|
||||||
* hiderefs
|
* hiderefs
|
||||||
* reflogs
|
* reflogs
|
||||||
|
* URL validation
|
||||||
'
|
'
|
||||||
|
|
||||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
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_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
|
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_expect_success 'clone from hooks' '
|
||||||
|
|
||||||
test_create_repo r0 &&
|
test_create_repo r0 &&
|
||||||
|
Loading…
x
Reference in New Issue
Block a user