Unescape url-encoded usernames and passwords
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
diff --git a/src/netops.c b/src/netops.c
index 9653344..72172f3 100644
--- a/src/netops.c
+++ b/src/netops.c
@@ -658,6 +658,29 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d)
git__free(d->pass); d->pass = NULL;
}
+static char unescape_hex(char *x)
+{
+ char digit;
+ digit = ((x[0] >= 'A') ? ((x[0] & 0xdf) - 'A')+10 : (x[0] - '0'));
+ digit *= 16;
+ digit += ((x[1] >= 'A') ? ((x[1] & 0xdf) - 'A')+10 : (x[1] - '0'));
+ return digit;
+}
+
+static char* unescape(char *str)
+{
+ int x, y;
+
+ for (x=y=0; str[x]; ++x, ++y) {
+ if ((str[x] = str[y]) == '%') {
+ str[x] = unescape_hex(str+y+1);
+ y += 2;
+ }
+ }
+ str[x] = '\0';
+ return str;
+}
+
int gitno_extract_url_parts(
char **host,
char **port,
@@ -699,13 +722,14 @@ int gitno_extract_url_parts(
if (u.field_data[UF_USERINFO].len) {
const char *colon = strchr(_userinfo, ':');
if (colon && (colon - _userinfo) < u.field_data[UF_USERINFO].len) {
- *username = git__substrdup(_userinfo, colon - _userinfo);
- *password = git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo));
+ *username = unescape(git__substrdup(_userinfo, colon - _userinfo));
+ *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo)));
GITERR_CHECK_ALLOC(*password);
} else {
*username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len);
}
GITERR_CHECK_ALLOC(*username);
+
}
return 0;
diff --git a/tests-clar/network/urlparse.c b/tests-clar/network/urlparse.c
index 4babb0f..3ec3a51 100644
--- a/tests-clar/network/urlparse.c
+++ b/tests-clar/network/urlparse.c
@@ -33,7 +33,7 @@ void test_network_urlparse__trivial(void)
cl_assert_equal_p(pass, NULL);
}
-void test_network_urlparse__weird_url(void)
+void test_network_urlparse__encoded_password(void)
{
cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass,
"https://user:pass%2fis%40bad@hostname.com:1234/", "1"));
@@ -41,7 +41,7 @@ void test_network_urlparse__weird_url(void)
cl_assert_equal_s(port, "1234");
cl_assert_equal_s(path, "/");
cl_assert_equal_s(user, "user");
- cl_assert_equal_s(pass, "pass%2fis%40bad");
+ cl_assert_equal_s(pass, "pass/is@bad");
}
void test_network_urlparse__user(void)
@@ -127,6 +127,18 @@ void test_network_urlparse__connection_data_ssl(void)
cl_assert_equal_i(conndata.use_ssl, true);
}
+void test_network_urlparse__encoded_username_password(void)
+{
+ cl_git_pass(gitno_connection_data_from_url(&conndata,
+ "https://user%2fname:pass%40word@example.com/foo/bar/baz", "bar/baz"));
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_s(conndata.user, "user/name");
+ cl_assert_equal_s(conndata.pass, "pass@word");
+ cl_assert_equal_i(conndata.use_ssl, true);
+}
+
void test_network_urlparse__connection_data_cross_host_redirect(void)
{
conndata.host = git__strdup("bar.com");