• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2010 Igalia S.L.
4  */
5 
6 #include "test-utils.h"
7 
8 SoupServer *server;
9 SoupURI *first_party_uri, *third_party_uri;
10 const char *first_party = "http://127.0.0.1/";
11 const char *third_party = "http://localhost/";
12 
13 static void
server_callback(SoupServer * server,SoupMessage * msg,const char * path,GHashTable * query,SoupClientContext * context,gpointer data)14 server_callback (SoupServer *server, SoupMessage *msg,
15 		 const char *path, GHashTable *query,
16 		 SoupClientContext *context, gpointer data)
17 {
18 	if (g_str_equal (path, "/index.html")) {
19 		soup_message_headers_replace (msg->response_headers,
20 					      "Set-Cookie",
21 					      "foo=bar");
22 	} else if (g_str_equal (path, "/foo.jpg")) {
23 		soup_message_headers_replace (msg->response_headers,
24 					      "Set-Cookie",
25 					      "baz=qux");
26 	} else if (soup_message_headers_get_one (msg->request_headers,
27 						 "Echo-Set-Cookie")) {
28 		soup_message_headers_replace (msg->response_headers,
29 					      "Set-Cookie",
30 					      soup_message_headers_get_one (msg->request_headers,
31 									    "Echo-Set-Cookie"));
32 	}
33 
34 	soup_message_set_status (msg, SOUP_STATUS_OK);
35 }
36 
37 typedef struct {
38 	SoupCookieJarAcceptPolicy policy;
39 	gboolean try_third_party_again;
40 	int n_cookies;
41 } CookiesForPolicy;
42 
43 static const CookiesForPolicy validResults[] = {
44 	{ SOUP_COOKIE_JAR_ACCEPT_ALWAYS, FALSE, 2 },
45 	{ SOUP_COOKIE_JAR_ACCEPT_NEVER, FALSE, 0 },
46 	{ SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY, FALSE, 1 },
47 	{ SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY, FALSE, 1 },
48 	{ SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY, TRUE, 2 }
49 };
50 
51 static void
do_cookies_accept_policy_test(void)52 do_cookies_accept_policy_test (void)
53 {
54 	SoupSession *session;
55 	SoupMessage *msg;
56 	SoupURI *uri;
57 	SoupCookieJar *jar;
58 	GSList *l, *p;
59 	int i;
60 
61 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
62 	soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
63 	jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR));
64 
65 	for (i = 0; i < G_N_ELEMENTS (validResults); i++) {
66 		soup_cookie_jar_set_accept_policy (jar, validResults[i].policy);
67 
68 		/* We can't use two servers due to limitations in
69 		 * test_server, so let's swap first and third party here
70 		 * to simulate a cookie coming from a third party.
71 		 */
72 		uri = soup_uri_new_with_base (first_party_uri, "/foo.jpg");
73 		msg = soup_message_new_from_uri ("GET", uri);
74 		soup_message_set_first_party (msg, third_party_uri);
75 		soup_session_send_message (session, msg);
76 		soup_uri_free (uri);
77 		g_object_unref (msg);
78 
79 		uri = soup_uri_new_with_base (first_party_uri, "/index.html");
80 		msg = soup_message_new_from_uri ("GET", uri);
81 		soup_message_set_first_party (msg, first_party_uri);
82 		soup_session_send_message (session, msg);
83 		soup_uri_free (uri);
84 		g_object_unref (msg);
85 
86 		if (validResults[i].try_third_party_again) {
87 			uri = soup_uri_new_with_base (first_party_uri, "/foo.jpg");
88 			msg = soup_message_new_from_uri ("GET", uri);
89 			soup_message_set_first_party (msg, third_party_uri);
90 			soup_session_send_message (session, msg);
91 			soup_uri_free (uri);
92 			g_object_unref (msg);
93 		}
94 
95 		l = soup_cookie_jar_all_cookies (jar);
96 		g_assert_cmpint (g_slist_length (l), ==, validResults[i].n_cookies);
97 
98 		for (p = l; p; p = p->next) {
99 			soup_cookie_jar_delete_cookie (jar, p->data);
100 			soup_cookie_free (p->data);
101 		}
102 
103 		g_slist_free (l);
104 	}
105 
106 	soup_test_session_abort_unref (session);
107 }
108 
109 static void
do_cookies_subdomain_policy_test(void)110 do_cookies_subdomain_policy_test (void)
111 {
112 	SoupCookieJar *jar;
113 	GSList *cookies;
114 	SoupURI *uri1;
115 	SoupURI *uri2;
116 	SoupURI *uri3;
117 
118 	g_test_bug ("792130");
119 
120 	/* Only the base domain should be considered when deciding
121 	 * whether a cookie is a third-party cookie.
122 	 */
123 	uri1 = soup_uri_new ("https://www.gnome.org");
124 	uri2 = soup_uri_new ("https://foundation.gnome.org");
125 	uri3 = soup_uri_new ("https://www.gnome.org.");
126 
127 	/* We can't check subdomains with a test server running on
128 	 * localhost, so we'll just check the cookie jar API itself.
129 	 */
130 
131 	/* Cookie should be accepted. One cookie in the jar. */
132 	jar = soup_cookie_jar_new ();
133 	soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY);
134 	soup_cookie_jar_set_cookie_with_first_party (jar, uri1, uri2, "1=foo");
135 	cookies = soup_cookie_jar_all_cookies (jar);
136 	g_assert_cmpint (g_slist_length (cookies), ==, 1);
137 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
138 
139 	/* Cookie should be accepted. Two cookies in the jar. */
140 	soup_cookie_jar_set_cookie_with_first_party (jar, uri2, uri1, "2=foo");
141 	cookies = soup_cookie_jar_all_cookies (jar);
142 	g_assert_cmpint (g_slist_length (cookies), ==, 2);
143 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
144 
145 	/* Third-party cookie should be rejected, so there are still
146 	 * only two cookies in the jar.
147 	 */
148 	soup_cookie_jar_set_cookie_with_first_party (jar, third_party_uri, uri1, "3=foo");
149 	cookies = soup_cookie_jar_all_cookies (jar);
150 	g_assert_cmpint (g_slist_length (cookies), ==, 2);
151 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
152 
153 	/* Now, we allow the "third-party" to set one cookie as the
154 	 * first party. Three cookies in the jar.
155 	 */
156 	soup_cookie_jar_set_cookie_with_first_party (jar, third_party_uri, third_party_uri, "4=foo");
157 	cookies = soup_cookie_jar_all_cookies (jar);
158 	g_assert_cmpint (g_slist_length (cookies), ==, 3);
159 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
160 
161 	/* Third-party cookie should now be allowed by grandfathering, though
162 	 * it was blocked before on the same URL. Four cookies in the jar.
163 	 */
164 	soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_GRANDFATHERED_THIRD_PARTY);
165 	soup_cookie_jar_set_cookie_with_first_party (jar, third_party_uri, uri1, "5=foo");
166 	cookies = soup_cookie_jar_all_cookies (jar);
167 	g_assert_cmpint (g_slist_length (cookies), ==, 4);
168 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
169 
170 	/* Now some Domain attribute tests.*/
171 	soup_cookie_jar_set_accept_policy (jar, SOUP_COOKIE_JAR_ACCEPT_ALWAYS);
172 
173 	/* The cookie must be rejected if the Domain is not an appropriate
174 	 * match for the URI. Still four cookies in the jar.
175 	 */
176 	soup_cookie_jar_set_cookie (jar, uri1, "6=foo; Domain=gitlab.gnome.org");
177 	cookies = soup_cookie_jar_all_cookies (jar);
178 	g_assert_cmpint (g_slist_length (cookies), ==, 4);
179 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
180 
181 	/* Now the Domain is an appropriate match. Five cookies in the jar. */
182 	soup_cookie_jar_set_cookie (jar, uri1, "7=foo; Domain=gnome.org");
183 	cookies = soup_cookie_jar_all_cookies (jar);
184 	g_assert_cmpint (g_slist_length (cookies), ==, 5);
185 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
186 
187 	/* A leading dot in the domain property should not affect things.
188 	 * This cookie should be accepted. Six cookies in the jar.
189 	 */
190 	soup_cookie_jar_set_cookie (jar, uri1, "8=foo; Domain=.www.gnome.org");
191 	cookies = soup_cookie_jar_all_cookies (jar);
192 	g_assert_cmpint (g_slist_length (cookies), ==, 6);
193 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
194 
195 	/* The cookie must be rejected if the Domain ends in a trailing dot
196 	 * but the uri doesn't.
197 	 */
198 	soup_cookie_jar_set_cookie (jar, uri1, "9=foo; Domain=www.gnome.org.");
199 	cookies = soup_cookie_jar_all_cookies (jar);
200 	g_assert_cmpint (g_slist_length (cookies), ==, 6);
201 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
202 
203 	/* The cookie should be accepted if both Domain and URI end with a trailing
204 	 * dot and they are a match. Seven cookies in the jar.
205 	 */
206 	soup_cookie_jar_set_cookie (jar, uri3, "10=foo; Domain=gnome.org.");
207 	cookies = soup_cookie_jar_all_cookies (jar);
208 	g_assert_cmpint (g_slist_length (cookies), ==, 7);
209 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
210 
211 	/* The cookie should be rejected if URI has trailing dot but Domain doesn't.
212 	 * Seven cookies in the jar.
213 	 */
214 	soup_cookie_jar_set_cookie (jar, uri3, "11=foo; Domain=gnome.org");
215 	cookies = soup_cookie_jar_all_cookies (jar);
216 	g_assert_cmpint (g_slist_length (cookies), ==, 7);
217 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
218 
219 	/* It should not be possible to set a cookie for a TLD. Still seven
220 	 * cookies in the jar.
221 	 */
222 	soup_cookie_jar_set_cookie (jar, uri1, "12=foo; Domain=.org");
223 	cookies = soup_cookie_jar_all_cookies (jar);
224 	g_assert_cmpint (g_slist_length (cookies), ==, 7);
225 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
226 
227 	/* It should still not be possible to set a cookie for a TLD, even if
228 	 * we are tricksy and have a trailing dot. Still only seven cookies.
229 	 */
230 	soup_cookie_jar_set_cookie (jar, uri3, "13=foo; Domain=.org.");
231 	cookies = soup_cookie_jar_all_cookies (jar);
232 	g_assert_cmpint (g_slist_length (cookies), ==, 7);
233 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
234 
235 	soup_uri_free (uri1);
236 	soup_uri_free (uri2);
237 	soup_uri_free (uri3);
238 	g_object_unref (jar);
239 }
240 
241 static void
do_cookies_strict_secure_test(void)242 do_cookies_strict_secure_test (void)
243 {
244 	SoupCookieJar *jar;
245 	GSList *cookies;
246 	SoupURI *insecure_uri;
247 	SoupURI *secure_uri;
248 
249 	insecure_uri = soup_uri_new ("http://gnome.org");
250 	secure_uri = soup_uri_new ("https://gnome.org");
251 	jar = soup_cookie_jar_new ();
252 
253 	/* Set a cookie from secure origin */
254 	soup_cookie_jar_set_cookie (jar, secure_uri, "1=foo; secure");
255 	cookies = soup_cookie_jar_all_cookies (jar);
256 	g_assert_cmpint (g_slist_length (cookies), ==, 1);
257 	g_assert_cmpstr (soup_cookie_get_value(cookies->data), ==, "foo");
258 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
259 
260 	/* Do not allow an insecure origin to overwrite a secure cookie */
261 	soup_cookie_jar_set_cookie (jar, insecure_uri, "1=bar");
262 	cookies = soup_cookie_jar_all_cookies (jar);
263 	g_assert_cmpint (g_slist_length (cookies), ==, 1);
264 	g_assert_cmpstr (soup_cookie_get_value(cookies->data), ==, "foo");
265 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
266 
267 	/* Secure can only be set by from secure origin */
268 	soup_cookie_jar_set_cookie (jar, insecure_uri, "2=foo; secure");
269 	cookies = soup_cookie_jar_all_cookies (jar);
270 	g_assert_cmpint (g_slist_length (cookies), ==, 1);
271 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
272 
273 	/* But we can make one for another path */
274 	soup_cookie_jar_set_cookie (jar, insecure_uri, "1=foo; path=/foo");
275 	cookies = soup_cookie_jar_all_cookies (jar);
276 	g_assert_cmpint (g_slist_length (cookies), ==, 2);
277 	g_slist_free_full (cookies, (GDestroyNotify)soup_cookie_free);
278 
279 	soup_uri_free (insecure_uri);
280 	soup_uri_free (secure_uri);
281 	g_object_unref (jar);
282 }
283 
284 /* FIXME: moar tests! */
285 static void
do_cookies_parsing_test(void)286 do_cookies_parsing_test (void)
287 {
288 	SoupSession *session;
289 	SoupMessage *msg;
290 	SoupCookieJar *jar;
291 	GSList *cookies, *iter;
292 	SoupCookie *cookie;
293 	gboolean got1, got2, got3;
294 
295 	g_test_bug ("678753");
296 
297 	session = soup_test_session_new (SOUP_TYPE_SESSION_ASYNC, NULL);
298 	soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
299 	jar = SOUP_COOKIE_JAR (soup_session_get_feature (session, SOUP_TYPE_COOKIE_JAR));
300 
301 	/* "httponly" is case-insensitive, and its value (if any) is ignored */
302 	msg = soup_message_new_from_uri ("GET", first_party_uri);
303 	soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
304 				     "one=1; httponly; max-age=100");
305 	soup_session_send_message (session, msg);
306 	g_object_unref (msg);
307 
308 	msg = soup_message_new_from_uri ("GET", first_party_uri);
309 	soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
310 				     "two=2; HttpOnly; max-age=100; SameSite=Invalid");
311 	soup_session_send_message (session, msg);
312 	g_object_unref (msg);
313 
314 	msg = soup_message_new_from_uri ("GET", first_party_uri);
315 	soup_message_headers_append (msg->request_headers, "Echo-Set-Cookie",
316 				     "three=3; httpONLY=Wednesday; max-age=100; SameSite=Lax");
317 	soup_session_send_message (session, msg);
318 	g_object_unref (msg);
319 
320 	cookies = soup_cookie_jar_get_cookie_list (jar, first_party_uri, TRUE);
321 	got1 = got2 = got3 = FALSE;
322 
323 	for (iter = cookies; iter; iter = iter->next) {
324 		cookie = iter->data;
325 
326 		if (!strcmp (soup_cookie_get_name (cookie), "one")) {
327 			got1 = TRUE;
328 			g_assert_true (soup_cookie_get_http_only (cookie));
329 			g_assert_true (soup_cookie_get_expires (cookie) != NULL);
330 		} else if (!strcmp (soup_cookie_get_name (cookie), "two")) {
331 			got2 = TRUE;
332 			g_assert_true (soup_cookie_get_http_only (cookie));
333 			g_assert_true (soup_cookie_get_expires (cookie) != NULL);
334 			g_assert_cmpint (soup_cookie_get_same_site_policy (cookie), ==, SOUP_SAME_SITE_POLICY_NONE);
335 		} else if (!strcmp (soup_cookie_get_name (cookie), "three")) {
336 			got3 = TRUE;
337 			g_assert_true (soup_cookie_get_http_only (cookie));
338 			g_assert_true (soup_cookie_get_expires (cookie) != NULL);
339 			g_assert_cmpint (soup_cookie_get_same_site_policy (cookie), ==, SOUP_SAME_SITE_POLICY_LAX);
340 		} else {
341 			soup_test_assert (FALSE, "got unexpected cookie '%s'",
342 					  soup_cookie_get_name (cookie));
343 		}
344 
345 		soup_cookie_free (cookie);
346 	}
347 	g_slist_free (cookies);
348 
349 	g_assert_true (got1);
350 	g_assert_true (got2);
351 	g_assert_true (got3);
352 
353 	soup_test_session_abort_unref (session);
354 }
355 
356 static void
do_cookies_parsing_nopath_nullorigin(void)357 do_cookies_parsing_nopath_nullorigin (void)
358 {
359 	SoupCookie *cookie = soup_cookie_parse ("NAME=Value", NULL);
360 	g_assert_nonnull (cookie);
361 	g_assert_cmpstr ("/", ==, soup_cookie_get_path (cookie));
362 	soup_cookie_free (cookie);
363 }
364 
365 static void
do_get_cookies_empty_host_test(void)366 do_get_cookies_empty_host_test (void)
367 {
368 	SoupCookieJar *jar;
369 	SoupURI *uri;
370 	char *cookies;
371 
372 	jar = soup_cookie_jar_new ();
373 	uri = soup_uri_new ("file:///whatever.html");
374 
375 	cookies = soup_cookie_jar_get_cookies (jar, uri, FALSE);
376 
377 	g_assert_null (cookies);
378 
379 	g_object_unref (jar);
380 	soup_uri_free (uri);
381 }
382 
383 static void
send_callback(GObject * source_object,GAsyncResult * res,GMainLoop * loop)384 send_callback (GObject *source_object,
385 	       GAsyncResult *res,
386 	       GMainLoop *loop)
387 {
388 	g_main_loop_quit (loop);
389 }
390 
391 static void
do_remove_feature_test(void)392 do_remove_feature_test (void)
393 {
394 	SoupSession *session;
395 	SoupMessage *msg;
396 	SoupURI *uri;
397 	GMainLoop *loop;
398 
399 	session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
400 	soup_session_add_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
401 	uri = soup_uri_new_with_base (first_party_uri, "/index.html");
402 	msg = soup_message_new_from_uri ("GET", uri);
403 	soup_message_set_first_party (msg, first_party_uri);
404 
405 	loop = g_main_loop_new (NULL, TRUE);
406 	soup_session_send_async (session, msg, NULL, (GAsyncReadyCallback)send_callback, loop);
407 	soup_session_remove_feature_by_type (session, SOUP_TYPE_COOKIE_JAR);
408 
409 	g_main_loop_run(loop);
410 
411 	g_main_loop_unref (loop);
412 	g_object_unref (msg);
413 	soup_uri_free (uri);
414 }
415 
416 int
main(int argc,char ** argv)417 main (int argc, char **argv)
418 {
419 	SoupURI *server_uri;
420 	int ret;
421 
422 	test_init (argc, argv, NULL);
423 
424 	server = soup_test_server_new (SOUP_TEST_SERVER_IN_THREAD);
425 	soup_server_add_handler (server, NULL, server_callback, NULL, NULL);
426 	server_uri = soup_test_server_get_uri (server, "http", NULL);
427 
428 	first_party_uri = soup_uri_new (first_party);
429 	third_party_uri = soup_uri_new (third_party);
430 	soup_uri_set_port (first_party_uri, server_uri->port);
431 	soup_uri_set_port (third_party_uri, server_uri->port);
432 
433 	g_test_add_func ("/cookies/accept-policy", do_cookies_accept_policy_test);
434 	g_test_add_func ("/cookies/accept-policy-subdomains", do_cookies_subdomain_policy_test);
435 	g_test_add_func ("/cookies/parsing", do_cookies_parsing_test);
436 	g_test_add_func ("/cookies/parsing/no-path-null-origin", do_cookies_parsing_nopath_nullorigin);
437 	g_test_add_func ("/cookies/get-cookies/empty-host", do_get_cookies_empty_host_test);
438 	g_test_add_func ("/cookies/remove-feature", do_remove_feature_test);
439 	g_test_add_func ("/cookies/secure-cookies", do_cookies_strict_secure_test);
440 
441 	ret = g_test_run ();
442 
443 	soup_uri_free (first_party_uri);
444 	soup_uri_free (third_party_uri);
445 	soup_uri_free (server_uri);
446 	soup_test_server_quit_unref (server);
447 
448 	test_cleanup ();
449 	return ret;
450 }
451