← Commit list

32a513e180

Stefan Eissing
Stefan Eissing
2026-04-16
SUCCESS Duration: 245s

Subject

urlapi: same origin tests

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

lib/http2.c+12 -13
@@ -718,8 +718,9 @@ static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
return second;
}
-static int set_transfer_url(struct Curl_easy *data, bool via_ssl_conn,
- struct curl_pushheaders *hp)
+static int set_transfer_url(struct Curl_easy *newhandle,
+ struct curl_pushheaders *hp,
+ struct Curl_easy *data)
{
const char *v;
CURLUcode uc;
@@ -732,14 +733,6 @@ static int set_transfer_url(struct Curl_easy *data, bool via_ssl_conn,
v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME);
if(v) {
- if(!via_ssl_conn) {
- /* PUSH over an insecure connection, accept only insecure schemes. */
- const struct Curl_scheme *scheme = Curl_get_scheme(v);
- if(!scheme || (scheme->flags & PROTOPT_SSL)) {
- rc = 1;
- goto fail;
- }
- }
uc = curl_url_set(u, CURLUPART_SCHEME, v, 0);
if(uc) {
rc = 1;
@@ -765,6 +758,13 @@ static int set_transfer_url(struct Curl_easy *data, bool via_ssl_conn,
}
}
+ /* We can only allow PUSH of resource from the same origin, e.g.
+ * scheme + hostname + port */
+ if(!Curl_url_same_origin(data->state.uh, u)) {
+ rc = 1;
+ goto fail;
+ }
+
uc = curl_url_get(u, CURLUPART_URL, &url, 0);
if(uc)
rc = 4;
@@ -773,7 +773,7 @@ fail:
if(rc)
return rc;
- Curl_bufref_set(&data->state.url, url, 0, curl_free);
+ Curl_bufref_set(&newhandle->state.url, url, 0, curl_free);
return 0;
}
@@ -819,8 +819,7 @@ static int push_promise(struct Curl_cfilter *cf,
heads.stream = stream;
heads.frame = frame;
- rv = set_transfer_url(newhandle,
- Curl_conn_is_ssl(cf->conn, cf->sockindex), &heads);
+ rv = set_transfer_url(newhandle, &heads, data);
if(rv) {
CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d",
frame->promised_stream_id, rv);
lib/urlapi-int.h+2 -0
@@ -63,4 +63,6 @@ CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace);
#define U_CURLU_URLDECODE (unsigned int)CURLU_URLDECODE
#define U_CURLU_PATH_AS_IS (unsigned int)CURLU_PATH_AS_IS
+bool Curl_url_same_origin(CURLU *base, CURLU *href);
+
#endif /* HEADER_CURL_URLAPI_INT_H */
lib/urlapi.c+33 -0
@@ -1991,3 +1991,36 @@ nomem:
}
return CURLUE_OK;
}
+
+bool Curl_url_same_origin(CURLU *base, CURLU *href)
+{
+ const struct Curl_scheme *s = NULL;
+
+ /* base must be an absolute URL */
+ if(!base->scheme || !base->host)
+ return FALSE;
+ if(href->scheme && !curl_strequal(base->scheme, href->scheme))
+ return FALSE;
+ if(href->host) {
+ if(!curl_strequal(base->host, href->host))
+ return FALSE;
+ if(!curl_strequal(base->port, href->port)) {
+ /* This may still match if only one has an explicit port
+ * and it is the default for the scheme. */
+ if(base->port && href->port)
+ return FALSE;
+
+ s = Curl_get_scheme(base->scheme);
+ if(!s) /* Cannot match default port for unknown scheme */
+ return FALSE;
+
+ /* The port which is set must be the default one */
+ if((base->port && (base->portnum != s->defport)) ||
+ (href->port && (href->portnum != s->defport)))
+ return FALSE;
+ }
+ }
+ else if(href->port) /* no host in href, then there must be no port */
+ return FALSE;
+ return TRUE;
+}
tests/unit/unit1675.c+79 -0
@@ -301,5 +301,84 @@ static CURLcode test_unit1675(const char *arg)
abort_if(fails, "parse_file tests failed");
}
+ /* Test same origin check. For now, we can only do that when
+ * schemes are supported by libcurl. */
+#ifndef CURL_DISABLE_HTTP
+ {
+ CURLU *base, *href;
+ int fails = 0;
+ unsigned int i;
+ bool match;
+ struct origin_test {
+ const char *base;
+ const char *scheme;
+ const char *host;
+ const char *port;
+ const char *path;
+ bool expect_match;
+ };
+ const struct origin_test tests[] = {
+ {"http://host:123/x", "http", "host", "123", "/y", TRUE},
+ {"http://host:123/x", NULL, "host", "123", "/y", TRUE},
+ {"http://host:123/x", NULL, NULL, NULL, "/y", TRUE},
+ {"http://host:80/x", "http", "host", "123", "/y", FALSE},
+ {"http://host:80/x", "http", "host", NULL, "/y", TRUE},
+ {"http://host/x", "http", "host", "80", "/y", TRUE},
+#ifdef USE_SSL
+ {"http://host:123/x", "https", "host", "123", "/y", FALSE},
+ {"https://host/x", "http", "host", "443", "/y", FALSE},
+ {"https://host/x", "https", "host", "443", "/y", TRUE},
+#endif
+ };
+
+ for(i = 0; i < CURL_ARRAYSIZE(tests); i++) {
+ CURLUcode uc;
+ base = curl_url();
+ href = curl_url();
+ if(!base || !href) {
+ curl_mfprintf(stderr, "%d: failed to allocate memory\n", i);
+ fails++;
+ goto loop_end;
+ }
+ uc = curl_url_set(base, CURLUPART_URL, tests[i].base, 0);
+ if(uc) {
+ curl_mfprintf(stderr, "failed to parse %d base %s -> %d\n", i,
+ tests[i].base, uc);
+ fails++;
+ goto loop_end;
+ }
+ if(tests[i].scheme)
+ uc = curl_url_set(href, CURLUPART_SCHEME, tests[i].scheme, 0);
+ if(!uc && tests[i].host)
+ uc = curl_url_set(href, CURLUPART_HOST, tests[i].host, 0);
+ if(!uc && tests[i].port)
+ uc = curl_url_set(href, CURLUPART_PORT, tests[i].port, 0);
+ if(!uc && tests[i].path)
+ uc = curl_url_set(href, CURLUPART_PATH, tests[i].path, 0);
+ if(uc) {
+ curl_mfprintf(stderr, "failed to parse %d href %s://%s:%s%s -> %d\n",
+ i, tests[i].scheme, tests[i].host, tests[i].port,
+ tests[i].path, uc);
+ fails++;
+ goto loop_end;
+ }
+
+ match = Curl_url_same_origin(base, href);
+ if(match != tests[i].expect_match) {
+ curl_mfprintf(stderr, "ERROR: %d base %s and href %s://%s:%s%s %s\n",
+ i, tests[i].base, tests[i].scheme, tests[i].host,
+ tests[i].port, tests[i].path,
+ match ? "matched" : "did not match");
+ fails++;
+ }
+
+loop_end:
+ curl_url_cleanup(base);
+ curl_url_cleanup(href);
+ }
+ abort_if(fails, "same_origin tests failed");
+ }
+#endif /* !CURL_DISABLE_HTTP */
+
UNITTEST_END_SIMPLE
}