mirror of
https://github.com/git/git.git
synced 2024-06-02 11:46:11 +02:00
4c5971e18a
It's unusual to see: https://example.com?query-parameters without an intervening slash, like: https://example.com/some-path?query-parameters or even: https://example.com/?query-parameters but it is a valid end to the hostname (actually "authority component") according to RFC 3986. Likewise for "#". And curl will parse the URL according to the standard, meaning it will contact example.com, but our credential code would ask about a bogus hostname with a "?" in it. Let's make sure we follow the standard, and more importantly ask about the same hosts that curl will be talking to. It would be nice if we could just ask curl to parse the URL for us. But it didn't grow a URL-parsing API until 7.62, so we'd be stuck with fallback code either way. Plus we'd need this code in the main Git binary, where we've tried to avoid having a link dependency on libcurl. But let's at least fix our parser. Moving to curl's parser would prevent other potential discrepancies, but this gives us immediate relief for the known problem, and would help our fallback code if we eventually use curl. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
488 lines
9.8 KiB
Bash
Executable File
488 lines
9.8 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
test_description='basic credential helper tests'
|
|
. ./test-lib.sh
|
|
. "$TEST_DIRECTORY"/lib-credential.sh
|
|
|
|
test_expect_success 'setup helper scripts' '
|
|
cat >dump <<-\EOF &&
|
|
whoami=$(echo $0 | sed s/.*git-credential-//)
|
|
echo >&2 "$whoami: $*"
|
|
OIFS=$IFS
|
|
IFS==
|
|
while read key value; do
|
|
echo >&2 "$whoami: $key=$value"
|
|
eval "$key=$value"
|
|
done
|
|
IFS=$OIFS
|
|
EOF
|
|
|
|
write_script git-credential-useless <<-\EOF &&
|
|
. ./dump
|
|
exit 0
|
|
EOF
|
|
|
|
write_script git-credential-verbatim <<-\EOF &&
|
|
user=$1; shift
|
|
pass=$1; shift
|
|
. ./dump
|
|
test -z "$user" || echo username=$user
|
|
test -z "$pass" || echo password=$pass
|
|
EOF
|
|
|
|
PATH="$PWD:$PATH"
|
|
'
|
|
|
|
test_expect_success 'credential_fill invokes helper' '
|
|
check fill "verbatim foo bar" <<-\EOF
|
|
--
|
|
username=foo
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'credential_fill invokes multiple helpers' '
|
|
check fill useless "verbatim foo bar" <<-\EOF
|
|
--
|
|
username=foo
|
|
password=bar
|
|
--
|
|
useless: get
|
|
verbatim: get
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'credential_fill stops when we get a full response' '
|
|
check fill "verbatim one two" "verbatim three four" <<-\EOF
|
|
--
|
|
username=one
|
|
password=two
|
|
--
|
|
verbatim: get
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'credential_fill continues through partial response' '
|
|
check fill "verbatim one \"\"" "verbatim two three" <<-\EOF
|
|
--
|
|
username=two
|
|
password=three
|
|
--
|
|
verbatim: get
|
|
verbatim: get
|
|
verbatim: username=one
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'credential_fill passes along metadata' '
|
|
check fill "verbatim one two" <<-\EOF
|
|
protocol=ftp
|
|
host=example.com
|
|
path=foo.git
|
|
--
|
|
protocol=ftp
|
|
host=example.com
|
|
path=foo.git
|
|
username=one
|
|
password=two
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=ftp
|
|
verbatim: host=example.com
|
|
verbatim: path=foo.git
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'credential_approve calls all helpers' '
|
|
check approve useless "verbatim one two" <<-\EOF
|
|
username=foo
|
|
password=bar
|
|
--
|
|
--
|
|
useless: store
|
|
useless: username=foo
|
|
useless: password=bar
|
|
verbatim: store
|
|
verbatim: username=foo
|
|
verbatim: password=bar
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'do not bother storing password-less credential' '
|
|
check approve useless <<-\EOF
|
|
username=foo
|
|
--
|
|
--
|
|
EOF
|
|
'
|
|
|
|
|
|
test_expect_success 'credential_reject calls all helpers' '
|
|
check reject useless "verbatim one two" <<-\EOF
|
|
username=foo
|
|
password=bar
|
|
--
|
|
--
|
|
useless: erase
|
|
useless: username=foo
|
|
useless: password=bar
|
|
verbatim: erase
|
|
verbatim: username=foo
|
|
verbatim: password=bar
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'usernames can be preserved' '
|
|
check fill "verbatim \"\" three" <<-\EOF
|
|
username=one
|
|
--
|
|
username=one
|
|
password=three
|
|
--
|
|
verbatim: get
|
|
verbatim: username=one
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'usernames can be overridden' '
|
|
check fill "verbatim two three" <<-\EOF
|
|
username=one
|
|
--
|
|
username=two
|
|
password=three
|
|
--
|
|
verbatim: get
|
|
verbatim: username=one
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'do not bother completing already-full credential' '
|
|
check fill "verbatim three four" <<-\EOF
|
|
username=one
|
|
password=two
|
|
--
|
|
username=one
|
|
password=two
|
|
--
|
|
EOF
|
|
'
|
|
|
|
# We can't test the basic terminal password prompt here because
|
|
# getpass() tries too hard to find the real terminal. But if our
|
|
# askpass helper is run, we know the internal getpass is working.
|
|
test_expect_success 'empty helper list falls back to internal getpass' '
|
|
check fill <<-\EOF
|
|
--
|
|
username=askpass-username
|
|
password=askpass-password
|
|
--
|
|
askpass: Username:
|
|
askpass: Password:
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'internal getpass does not ask for known username' '
|
|
check fill <<-\EOF
|
|
username=foo
|
|
--
|
|
username=foo
|
|
password=askpass-password
|
|
--
|
|
askpass: Password:
|
|
EOF
|
|
'
|
|
|
|
HELPER="!f() {
|
|
cat >/dev/null
|
|
echo username=foo
|
|
echo password=bar
|
|
}; f"
|
|
test_expect_success 'respect configured credentials' '
|
|
test_config credential.helper "$HELPER" &&
|
|
check fill <<-\EOF
|
|
--
|
|
username=foo
|
|
password=bar
|
|
--
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'match configured credential' '
|
|
test_config credential.https://example.com.helper "$HELPER" &&
|
|
check fill <<-\EOF
|
|
protocol=https
|
|
host=example.com
|
|
path=repo.git
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=foo
|
|
password=bar
|
|
--
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'do not match configured credential' '
|
|
test_config credential.https://foo.helper "$HELPER" &&
|
|
check fill <<-\EOF
|
|
protocol=https
|
|
host=bar
|
|
--
|
|
protocol=https
|
|
host=bar
|
|
username=askpass-username
|
|
password=askpass-password
|
|
--
|
|
askpass: Username for '\''https://bar'\'':
|
|
askpass: Password for '\''https://askpass-username@bar'\'':
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'match multiple configured helpers' '
|
|
test_config credential.helper "verbatim \"\" \"\"" &&
|
|
test_config credential.https://example.com.helper "$HELPER" &&
|
|
check fill <<-\EOF
|
|
protocol=https
|
|
host=example.com
|
|
path=repo.git
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=foo
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=example.com
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'match multiple configured helpers with URLs' '
|
|
test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
|
|
test_config credential.https://example.com.helper "$HELPER" &&
|
|
check fill <<-\EOF
|
|
protocol=https
|
|
host=example.com
|
|
path=repo.git
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=foo
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=example.com
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'match percent-encoded values' '
|
|
test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
|
|
check fill <<-\EOF
|
|
url=https://example.com/%2566.git
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=foo
|
|
password=bar
|
|
--
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'pull username from config' '
|
|
test_config credential.https://example.com.username foo &&
|
|
check fill <<-\EOF
|
|
protocol=https
|
|
host=example.com
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=foo
|
|
password=askpass-password
|
|
--
|
|
askpass: Password for '\''https://foo@example.com'\'':
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'honors username from URL over helper (URL)' '
|
|
test_config credential.https://example.com.username bob &&
|
|
test_config credential.https://example.com.helper "verbatim \"\" bar" &&
|
|
check fill <<-\EOF
|
|
url=https://alice@example.com
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=alice
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=example.com
|
|
verbatim: username=alice
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'honors username from URL over helper (components)' '
|
|
test_config credential.https://example.com.username bob &&
|
|
test_config credential.https://example.com.helper "verbatim \"\" bar" &&
|
|
check fill <<-\EOF
|
|
protocol=https
|
|
host=example.com
|
|
username=alice
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=alice
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=example.com
|
|
verbatim: username=alice
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'last matching username wins' '
|
|
test_config credential.https://example.com/path.git.username bob &&
|
|
test_config credential.https://example.com.username alice &&
|
|
test_config credential.https://example.com.helper "verbatim \"\" bar" &&
|
|
check fill <<-\EOF
|
|
url=https://example.com/path.git
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=alice
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=example.com
|
|
verbatim: username=alice
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'http paths can be part of context' '
|
|
check fill "verbatim foo bar" <<-\EOF &&
|
|
protocol=https
|
|
host=example.com
|
|
path=foo.git
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
username=foo
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=example.com
|
|
EOF
|
|
test_config credential.https://example.com.useHttpPath true &&
|
|
check fill "verbatim foo bar" <<-\EOF
|
|
protocol=https
|
|
host=example.com
|
|
path=foo.git
|
|
--
|
|
protocol=https
|
|
host=example.com
|
|
path=foo.git
|
|
username=foo
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=example.com
|
|
verbatim: path=foo.git
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'context uses urlmatch' '
|
|
test_config "credential.https://*.org.useHttpPath" true &&
|
|
check fill "verbatim foo bar" <<-\EOF
|
|
protocol=https
|
|
host=example.org
|
|
path=foo.git
|
|
--
|
|
protocol=https
|
|
host=example.org
|
|
path=foo.git
|
|
username=foo
|
|
password=bar
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=example.org
|
|
verbatim: path=foo.git
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'helpers can abort the process' '
|
|
test_must_fail git \
|
|
-c credential.helper="!f() { echo quit=1; }; f" \
|
|
-c credential.helper="verbatim foo bar" \
|
|
credential fill >stdout &&
|
|
test_must_be_empty stdout
|
|
'
|
|
|
|
test_expect_success 'empty helper spec resets helper list' '
|
|
test_config credential.helper "verbatim file file" &&
|
|
check fill "" "verbatim cmdline cmdline" <<-\EOF
|
|
--
|
|
username=cmdline
|
|
password=cmdline
|
|
--
|
|
verbatim: get
|
|
EOF
|
|
'
|
|
|
|
test_expect_success 'url parser ignores embedded newlines' '
|
|
check fill <<-EOF
|
|
url=https://one.example.com?%0ahost=two.example.com/
|
|
--
|
|
username=askpass-username
|
|
password=askpass-password
|
|
--
|
|
warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
|
|
warning: skipping credential lookup for url: https://one.example.com?%0ahost=two.example.com/
|
|
askpass: Username:
|
|
askpass: Password:
|
|
EOF
|
|
'
|
|
|
|
# usage: check_host_and_path <url> <expected-host> <expected-path>
|
|
check_host_and_path () {
|
|
# we always parse the path component, but we need this to make sure it
|
|
# is passed to the helper
|
|
test_config credential.useHTTPPath true &&
|
|
check fill "verbatim user pass" <<-EOF
|
|
url=$1
|
|
--
|
|
protocol=https
|
|
host=$2
|
|
path=$3
|
|
username=user
|
|
password=pass
|
|
--
|
|
verbatim: get
|
|
verbatim: protocol=https
|
|
verbatim: host=$2
|
|
verbatim: path=$3
|
|
EOF
|
|
}
|
|
|
|
test_expect_success 'url parser handles bare query marker' '
|
|
check_host_and_path https://example.com?foo.git example.com ?foo.git
|
|
'
|
|
|
|
test_expect_success 'url parser handles bare fragment marker' '
|
|
check_host_and_path https://example.com#foo.git example.com "#foo.git"
|
|
'
|
|
|
|
test_expect_success 'url parser not confused by encoded markers' '
|
|
check_host_and_path https://example.com%23%3f%2f/foo.git \
|
|
"example.com#?/" foo.git
|
|
'
|
|
|
|
test_done
|