Merge branch 'jk/http-walker-limit-redirect-2.9'
Transport with dumb http can be fooled into following foreign URLs that the end user does not intend to, especially with the server side redirects and http-alternates mechanism, which can lead to security issues. Tighten the redirection and make it more obvious to the end user when it happens. * jk/http-walker-limit-redirect-2.9: http: treat http-alternates like redirects http: make redirects more obvious remote-curl: rename shadowed options variable http: always update the base URL for redirects http: simplify update_url_from_redirect
This commit is contained in:
@ -1891,6 +1891,16 @@ http.userAgent::
|
|||||||
of common USER_AGENT strings (but not including those like git/1.7.1).
|
of common USER_AGENT strings (but not including those like git/1.7.1).
|
||||||
Can be overridden by the `GIT_HTTP_USER_AGENT` environment variable.
|
Can be overridden by the `GIT_HTTP_USER_AGENT` environment variable.
|
||||||
|
|
||||||
|
http.followRedirects::
|
||||||
|
Whether git should follow HTTP redirects. If set to `true`, git
|
||||||
|
will transparently follow any redirect issued by a server it
|
||||||
|
encounters. If set to `false`, git will treat all redirects as
|
||||||
|
errors. If set to `initial`, git will follow redirects only for
|
||||||
|
the initial request to a remote, but not for subsequent
|
||||||
|
follow-up HTTP requests. Since git uses the redirected URL as
|
||||||
|
the base for the follow-up requests, this is generally
|
||||||
|
sufficient. The default is `initial`.
|
||||||
|
|
||||||
http.<url>.*::
|
http.<url>.*::
|
||||||
Any of the http.* options above can be applied selectively to some URLs.
|
Any of the http.* options above can be applied selectively to some URLs.
|
||||||
For a config key to match a URL, each element of the config key is
|
For a config key to match a URL, each element of the config key is
|
||||||
|
@ -274,8 +274,7 @@ static void process_alternates_response(void *callback_data)
|
|||||||
struct strbuf target = STRBUF_INIT;
|
struct strbuf target = STRBUF_INIT;
|
||||||
strbuf_add(&target, base, serverlen);
|
strbuf_add(&target, base, serverlen);
|
||||||
strbuf_add(&target, data + i, posn - i - 7);
|
strbuf_add(&target, data + i, posn - i - 7);
|
||||||
if (walker->get_verbosely)
|
warning("adding alternate object store: %s",
|
||||||
fprintf(stderr, "Also look at %s\n",
|
|
||||||
target.buf);
|
target.buf);
|
||||||
newalt = xmalloc(sizeof(*newalt));
|
newalt = xmalloc(sizeof(*newalt));
|
||||||
newalt->next = NULL;
|
newalt->next = NULL;
|
||||||
@ -302,6 +301,9 @@ static void fetch_alternates(struct walker *walker, const char *base)
|
|||||||
struct alternates_request alt_req;
|
struct alternates_request alt_req;
|
||||||
struct walker_data *cdata = walker->data;
|
struct walker_data *cdata = walker->data;
|
||||||
|
|
||||||
|
if (http_follow_config != HTTP_FOLLOW_ALWAYS)
|
||||||
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If another request has already started fetching alternates,
|
* If another request has already started fetching alternates,
|
||||||
* wait for them to arrive and return to processing this request's
|
* wait for them to arrive and return to processing this request's
|
||||||
|
54
http.c
54
http.c
@ -111,6 +111,8 @@ static int http_proactive_auth;
|
|||||||
static const char *user_agent;
|
static const char *user_agent;
|
||||||
static int curl_empty_auth;
|
static int curl_empty_auth;
|
||||||
|
|
||||||
|
enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
|
||||||
|
|
||||||
#if LIBCURL_VERSION_NUM >= 0x071700
|
#if LIBCURL_VERSION_NUM >= 0x071700
|
||||||
/* Use CURLOPT_KEYPASSWD as is */
|
/* Use CURLOPT_KEYPASSWD as is */
|
||||||
#elif LIBCURL_VERSION_NUM >= 0x070903
|
#elif LIBCURL_VERSION_NUM >= 0x070903
|
||||||
@ -366,6 +368,16 @@ static int http_options(const char *var, const char *value, void *cb)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strcmp("http.followredirects", var)) {
|
||||||
|
if (value && !strcmp(value, "initial"))
|
||||||
|
http_follow_config = HTTP_FOLLOW_INITIAL;
|
||||||
|
else if (git_config_bool(var, value))
|
||||||
|
http_follow_config = HTTP_FOLLOW_ALWAYS;
|
||||||
|
else
|
||||||
|
http_follow_config = HTTP_FOLLOW_NONE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* Fall back on the default ones */
|
/* Fall back on the default ones */
|
||||||
return git_default_config(var, value, cb);
|
return git_default_config(var, value, cb);
|
||||||
}
|
}
|
||||||
@ -717,7 +729,6 @@ static CURL *get_curl_handle(void)
|
|||||||
curl_low_speed_time);
|
curl_low_speed_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_setopt(result, CURLOPT_FOLLOWLOCATION, 1);
|
|
||||||
curl_easy_setopt(result, CURLOPT_MAXREDIRS, 20);
|
curl_easy_setopt(result, CURLOPT_MAXREDIRS, 20);
|
||||||
#if LIBCURL_VERSION_NUM >= 0x071301
|
#if LIBCURL_VERSION_NUM >= 0x071301
|
||||||
curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
|
curl_easy_setopt(result, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
|
||||||
@ -734,6 +745,7 @@ static CURL *get_curl_handle(void)
|
|||||||
if (is_transport_allowed("ftps"))
|
if (is_transport_allowed("ftps"))
|
||||||
allowed_protocols |= CURLPROTO_FTPS;
|
allowed_protocols |= CURLPROTO_FTPS;
|
||||||
curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS, allowed_protocols);
|
curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS, allowed_protocols);
|
||||||
|
curl_easy_setopt(result, CURLOPT_PROTOCOLS, allowed_protocols);
|
||||||
#else
|
#else
|
||||||
if (transport_restrict_protocols())
|
if (transport_restrict_protocols())
|
||||||
warning("protocol restrictions not applied to curl redirects because\n"
|
warning("protocol restrictions not applied to curl redirects because\n"
|
||||||
@ -1044,6 +1056,16 @@ struct active_request_slot *get_active_slot(void)
|
|||||||
curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
|
curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
|
||||||
curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
|
curl_easy_setopt(slot->curl, CURLOPT_RANGE, NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default following to off unless "ALWAYS" is configured; this gives
|
||||||
|
* callers a sane starting point, and they can tweak for individual
|
||||||
|
* HTTP_FOLLOW_* cases themselves.
|
||||||
|
*/
|
||||||
|
if (http_follow_config == HTTP_FOLLOW_ALWAYS)
|
||||||
|
curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||||
|
else
|
||||||
|
curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 0);
|
||||||
|
|
||||||
#if LIBCURL_VERSION_NUM >= 0x070a08
|
#if LIBCURL_VERSION_NUM >= 0x070a08
|
||||||
curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
|
curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
|
||||||
#endif
|
#endif
|
||||||
@ -1286,9 +1308,12 @@ static int handle_curl_result(struct slot_results *results)
|
|||||||
* If we see a failing http code with CURLE_OK, we have turned off
|
* If we see a failing http code with CURLE_OK, we have turned off
|
||||||
* FAILONERROR (to keep the server's custom error response), and should
|
* FAILONERROR (to keep the server's custom error response), and should
|
||||||
* translate the code into failure here.
|
* translate the code into failure here.
|
||||||
|
*
|
||||||
|
* Likewise, if we see a redirect (30x code), that means we turned off
|
||||||
|
* redirect-following, and we should treat the result as an error.
|
||||||
*/
|
*/
|
||||||
if (results->curl_result == CURLE_OK &&
|
if (results->curl_result == CURLE_OK &&
|
||||||
results->http_code >= 400) {
|
results->http_code >= 300) {
|
||||||
results->curl_result = CURLE_HTTP_RETURNED_ERROR;
|
results->curl_result = CURLE_HTTP_RETURNED_ERROR;
|
||||||
/*
|
/*
|
||||||
* Normally curl will already have put the "reason phrase"
|
* Normally curl will already have put the "reason phrase"
|
||||||
@ -1607,6 +1632,9 @@ static int http_request(const char *url,
|
|||||||
strbuf_addstr(&buf, " no-cache");
|
strbuf_addstr(&buf, " no-cache");
|
||||||
if (options && options->keep_error)
|
if (options && options->keep_error)
|
||||||
curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
|
curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
|
||||||
|
if (options && options->initial_request &&
|
||||||
|
http_follow_config == HTTP_FOLLOW_INITIAL)
|
||||||
|
curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
|
||||||
|
|
||||||
headers = curl_slist_append(headers, buf.buf);
|
headers = curl_slist_append(headers, buf.buf);
|
||||||
|
|
||||||
@ -1655,16 +1683,16 @@ static int http_request(const char *url,
|
|||||||
*
|
*
|
||||||
* Note that this assumes a sane redirect scheme. It's entirely possible
|
* Note that this assumes a sane redirect scheme. It's entirely possible
|
||||||
* in the example above to end up at a URL that does not even end in
|
* in the example above to end up at a URL that does not even end in
|
||||||
* "info/refs". In such a case we simply punt, as there is not much we can
|
* "info/refs". In such a case we die. There's not much we can do, such a
|
||||||
* do (and such a scheme is unlikely to represent a real git repository,
|
* scheme is unlikely to represent a real git repository, and failing to
|
||||||
* which means we are likely about to abort anyway).
|
* rewrite the base opens options for malicious redirects to do funny things.
|
||||||
*/
|
*/
|
||||||
static int update_url_from_redirect(struct strbuf *base,
|
static int update_url_from_redirect(struct strbuf *base,
|
||||||
const char *asked,
|
const char *asked,
|
||||||
const struct strbuf *got)
|
const struct strbuf *got)
|
||||||
{
|
{
|
||||||
const char *tail;
|
const char *tail;
|
||||||
size_t tail_len;
|
size_t new_len;
|
||||||
|
|
||||||
if (!strcmp(asked, got->buf))
|
if (!strcmp(asked, got->buf))
|
||||||
return 0;
|
return 0;
|
||||||
@ -1673,14 +1701,16 @@ static int update_url_from_redirect(struct strbuf *base,
|
|||||||
die("BUG: update_url_from_redirect: %s is not a superset of %s",
|
die("BUG: update_url_from_redirect: %s is not a superset of %s",
|
||||||
asked, base->buf);
|
asked, base->buf);
|
||||||
|
|
||||||
tail_len = strlen(tail);
|
new_len = got->len;
|
||||||
|
if (!strip_suffix_mem(got->buf, &new_len, tail))
|
||||||
if (got->len < tail_len ||
|
die(_("unable to update url base from redirection:\n"
|
||||||
strcmp(tail, got->buf + got->len - tail_len))
|
" asked for: %s\n"
|
||||||
return 0; /* insane redirect scheme */
|
" redirect: %s"),
|
||||||
|
asked, got->buf);
|
||||||
|
|
||||||
strbuf_reset(base);
|
strbuf_reset(base);
|
||||||
strbuf_add(base, got->buf, got->len - tail_len);
|
strbuf_add(base, got->buf, new_len);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
http.h
10
http.h
@ -116,6 +116,13 @@ extern struct credential http_auth;
|
|||||||
|
|
||||||
extern char curl_errorstr[CURL_ERROR_SIZE];
|
extern char curl_errorstr[CURL_ERROR_SIZE];
|
||||||
|
|
||||||
|
enum http_follow_config {
|
||||||
|
HTTP_FOLLOW_NONE,
|
||||||
|
HTTP_FOLLOW_ALWAYS,
|
||||||
|
HTTP_FOLLOW_INITIAL
|
||||||
|
};
|
||||||
|
extern enum http_follow_config http_follow_config;
|
||||||
|
|
||||||
static inline int missing__target(int code, int result)
|
static inline int missing__target(int code, int result)
|
||||||
{
|
{
|
||||||
return /* file:// URL -- do we ever use one??? */
|
return /* file:// URL -- do we ever use one??? */
|
||||||
@ -139,7 +146,8 @@ extern char *get_remote_object_url(const char *url, const char *hex,
|
|||||||
/* Options for http_get_*() */
|
/* Options for http_get_*() */
|
||||||
struct http_get_options {
|
struct http_get_options {
|
||||||
unsigned no_cache:1,
|
unsigned no_cache:1,
|
||||||
keep_error:1;
|
keep_error:1,
|
||||||
|
initial_request:1;
|
||||||
|
|
||||||
/* If non-NULL, returns the content-type of the response. */
|
/* If non-NULL, returns the content-type of the response. */
|
||||||
struct strbuf *content_type;
|
struct strbuf *content_type;
|
||||||
|
@ -274,7 +274,7 @@ static struct discovery *discover_refs(const char *service, int for_push)
|
|||||||
struct strbuf effective_url = STRBUF_INIT;
|
struct strbuf effective_url = STRBUF_INIT;
|
||||||
struct discovery *last = last_discovery;
|
struct discovery *last = last_discovery;
|
||||||
int http_ret, maybe_smart = 0;
|
int http_ret, maybe_smart = 0;
|
||||||
struct http_get_options options;
|
struct http_get_options http_options;
|
||||||
|
|
||||||
if (last && !strcmp(service, last->service))
|
if (last && !strcmp(service, last->service))
|
||||||
return last;
|
return last;
|
||||||
@ -291,15 +291,16 @@ static struct discovery *discover_refs(const char *service, int for_push)
|
|||||||
strbuf_addf(&refs_url, "service=%s", service);
|
strbuf_addf(&refs_url, "service=%s", service);
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&options, 0, sizeof(options));
|
memset(&http_options, 0, sizeof(http_options));
|
||||||
options.content_type = &type;
|
http_options.content_type = &type;
|
||||||
options.charset = &charset;
|
http_options.charset = &charset;
|
||||||
options.effective_url = &effective_url;
|
http_options.effective_url = &effective_url;
|
||||||
options.base_url = &url;
|
http_options.base_url = &url;
|
||||||
options.no_cache = 1;
|
http_options.initial_request = 1;
|
||||||
options.keep_error = 1;
|
http_options.no_cache = 1;
|
||||||
|
http_options.keep_error = 1;
|
||||||
|
|
||||||
http_ret = http_get_strbuf(refs_url.buf, &buffer, &options);
|
http_ret = http_get_strbuf(refs_url.buf, &buffer, &http_options);
|
||||||
switch (http_ret) {
|
switch (http_ret) {
|
||||||
case HTTP_OK:
|
case HTTP_OK:
|
||||||
break;
|
break;
|
||||||
@ -314,6 +315,9 @@ static struct discovery *discover_refs(const char *service, int for_push)
|
|||||||
die("unable to access '%s': %s", url.buf, curl_errorstr);
|
die("unable to access '%s': %s", url.buf, curl_errorstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.verbosity && !starts_with(refs_url.buf, url.buf))
|
||||||
|
warning(_("redirecting to %s"), url.buf);
|
||||||
|
|
||||||
last= xcalloc(1, sizeof(*last_discovery));
|
last= xcalloc(1, sizeof(*last_discovery));
|
||||||
last->service = service;
|
last->service = service;
|
||||||
last->buf_alloc = strbuf_detach(&buffer, &last->len);
|
last->buf_alloc = strbuf_detach(&buffer, &last->len);
|
||||||
|
@ -123,6 +123,7 @@ ScriptAlias /error/ error.sh/
|
|||||||
</Files>
|
</Files>
|
||||||
|
|
||||||
RewriteEngine on
|
RewriteEngine on
|
||||||
|
RewriteRule ^/dumb-redir/(.*)$ /dumb/$1 [R=301]
|
||||||
RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
|
RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
|
||||||
RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
|
RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
|
||||||
RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301]
|
RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301]
|
||||||
@ -132,6 +133,19 @@ RewriteRule ^/ftp-redir/(.*)$ ftp://localhost:1000/$1 [R=302]
|
|||||||
RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
|
RewriteRule ^/loop-redir/x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-x-(.*) /$1 [R=302]
|
||||||
RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
|
RewriteRule ^/loop-redir/(.*)$ /loop-redir/x-$1 [R=302]
|
||||||
|
|
||||||
|
# The first rule issues a client-side redirect to something
|
||||||
|
# that _doesn't_ look like a git repo. The second rule is a
|
||||||
|
# server-side rewrite, so that it turns out the odd-looking
|
||||||
|
# thing _is_ a git repo. The "[PT]" tells Apache to match
|
||||||
|
# the usual ScriptAlias rules for /smart.
|
||||||
|
RewriteRule ^/insane-redir/(.*)$ /intern-redir/$1/foo [R=301]
|
||||||
|
RewriteRule ^/intern-redir/(.*)/foo$ /smart/$1 [PT]
|
||||||
|
|
||||||
|
# Serve info/refs internally without redirecting, but
|
||||||
|
# issue a redirect for any object requests.
|
||||||
|
RewriteRule ^/redir-objects/(.*/info/refs)$ /dumb/$1 [PT]
|
||||||
|
RewriteRule ^/redir-objects/(.*/objects/.*)$ /dumb/$1 [R=301]
|
||||||
|
|
||||||
# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
|
# Apache 2.2 does not understand <RequireAll>, so we use RewriteCond.
|
||||||
# And as RewriteCond does not allow testing for non-matches, we match
|
# And as RewriteCond does not allow testing for non-matches, we match
|
||||||
# the desired case first (one has abra, two has cadabra), and let it
|
# the desired case first (one has abra, two has cadabra), and let it
|
||||||
|
@ -307,5 +307,66 @@ test_expect_success 'remote-http complains cleanly about malformed urls' '
|
|||||||
test_must_fail git remote-http http::/example.com/repo.git
|
test_must_fail git remote-http http::/example.com/repo.git
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'redirects can be forbidden/allowed' '
|
||||||
|
test_must_fail git -c http.followRedirects=false \
|
||||||
|
clone $HTTPD_URL/dumb-redir/repo.git dumb-redir &&
|
||||||
|
git -c http.followRedirects=true \
|
||||||
|
clone $HTTPD_URL/dumb-redir/repo.git dumb-redir 2>stderr
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'redirects are reported to stderr' '
|
||||||
|
# just look for a snippet of the redirected-to URL
|
||||||
|
test_i18ngrep /dumb/ stderr
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'non-initial redirects can be forbidden' '
|
||||||
|
test_must_fail git -c http.followRedirects=initial \
|
||||||
|
clone $HTTPD_URL/redir-objects/repo.git redir-objects &&
|
||||||
|
git -c http.followRedirects=true \
|
||||||
|
clone $HTTPD_URL/redir-objects/repo.git redir-objects
|
||||||
|
'
|
||||||
|
|
||||||
|
test_expect_success 'http.followRedirects defaults to "initial"' '
|
||||||
|
test_must_fail git clone $HTTPD_URL/redir-objects/repo.git default
|
||||||
|
'
|
||||||
|
|
||||||
|
# The goal is for a clone of the "evil" repository, which has no objects
|
||||||
|
# itself, to cause the client to fetch objects from the "victim" repository.
|
||||||
|
test_expect_success 'set up evil alternates scheme' '
|
||||||
|
victim=$HTTPD_DOCUMENT_ROOT_PATH/victim.git &&
|
||||||
|
git init --bare "$victim" &&
|
||||||
|
git -C "$victim" --work-tree=. commit --allow-empty -m secret &&
|
||||||
|
git -C "$victim" repack -ad &&
|
||||||
|
git -C "$victim" update-server-info &&
|
||||||
|
sha1=$(git -C "$victim" rev-parse HEAD) &&
|
||||||
|
|
||||||
|
evil=$HTTPD_DOCUMENT_ROOT_PATH/evil.git &&
|
||||||
|
git init --bare "$evil" &&
|
||||||
|
# do this by hand to avoid object existence check
|
||||||
|
printf "%s\\t%s\\n" $sha1 refs/heads/master >"$evil/info/refs"
|
||||||
|
'
|
||||||
|
|
||||||
|
# Here we'll just redirect via HTTP. In a real-world attack these would be on
|
||||||
|
# different servers, but we should reject it either way.
|
||||||
|
test_expect_success 'http-alternates is a non-initial redirect' '
|
||||||
|
echo "$HTTPD_URL/dumb/victim.git/objects" \
|
||||||
|
>"$evil/objects/info/http-alternates" &&
|
||||||
|
test_must_fail git -c http.followRedirects=initial \
|
||||||
|
clone $HTTPD_URL/dumb/evil.git evil-initial &&
|
||||||
|
git -c http.followRedirects=true \
|
||||||
|
clone $HTTPD_URL/dumb/evil.git evil-initial
|
||||||
|
'
|
||||||
|
|
||||||
|
# Curl supports a lot of protocols that we'd prefer not to allow
|
||||||
|
# http-alternates to use, but it's hard to test whether curl has
|
||||||
|
# accessed, say, the SMTP protocol, because we are not running an SMTP server.
|
||||||
|
# But we can check that it does not allow access to file://, which would
|
||||||
|
# otherwise allow this clone to complete.
|
||||||
|
test_expect_success 'http-alternates cannot point at funny protocols' '
|
||||||
|
echo "file://$victim/objects" >"$evil/objects/info/http-alternates" &&
|
||||||
|
test_must_fail git -c http.followRedirects=true \
|
||||||
|
clone "$HTTPD_URL/dumb/evil.git" evil-file
|
||||||
|
'
|
||||||
|
|
||||||
stop_httpd
|
stop_httpd
|
||||||
test_done
|
test_done
|
||||||
|
@ -119,6 +119,10 @@ test_expect_success 'redirects re-root further requests' '
|
|||||||
git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
|
git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 're-rooting dies on insane schemes' '
|
||||||
|
test_must_fail git clone $HTTPD_URL/insane-redir/repo.git insane
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'clone from password-protected repository' '
|
test_expect_success 'clone from password-protected repository' '
|
||||||
echo two >expect &&
|
echo two >expect &&
|
||||||
set_askpass user@host pass@host &&
|
set_askpass user@host pass@host &&
|
||||||
|
@ -18,6 +18,7 @@ test_proto "smart http" http "$HTTPD_URL/smart/repo.git"
|
|||||||
|
|
||||||
test_expect_success 'curl redirects respect whitelist' '
|
test_expect_success 'curl redirects respect whitelist' '
|
||||||
test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
|
test_must_fail env GIT_ALLOW_PROTOCOL=http:https \
|
||||||
|
GIT_SMART_HTTP=0 \
|
||||||
git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
|
git clone "$HTTPD_URL/ftp-redir/repo.git" 2>stderr &&
|
||||||
{
|
{
|
||||||
test_i18ngrep "ftp.*disabled" stderr ||
|
test_i18ngrep "ftp.*disabled" stderr ||
|
||||||
|
Reference in New Issue
Block a user