Skip to content

Commit a00d1d0

Browse files
committed
Cookie with SameSite=None is only valid when Secure
1 parent 6f50286 commit a00d1d0

File tree

1 file changed

+130
-27
lines changed

1 file changed

+130
-27
lines changed

src/storage/cookie.zig

Lines changed: 130 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,6 @@ pub const Cookie = struct {
244244
}
245245
}
246246

247-
var arena = ArenaAllocator.init(allocator);
248-
errdefer arena.deinit();
249-
250247
const cookie_name, const cookie_value, const rest = parseNameValue(str) catch {
251248
return error.InvalidNameValue;
252249
};
@@ -322,6 +319,12 @@ pub const Cookie = struct {
322319
}
323320
}
324321

322+
if (same_site == .none and secure == null) {
323+
return error.InsecureSameSite;
324+
}
325+
326+
var arena = ArenaAllocator.init(allocator);
327+
errdefer arena.deinit();
325328
const aa = arena.allocator();
326329
const owned_name = try aa.dupe(u8, cookie_name);
327330
const owned_value = try aa.dupe(u8, cookie_value);
@@ -505,7 +508,7 @@ test "Jar: forRequest" {
505508
try jar.add(try Cookie.parse(testing.allocator, test_uri, "path1=3;Path=/about"), now);
506509
try jar.add(try Cookie.parse(testing.allocator, test_uri, "path2=4;Path=/docs/"), now);
507510
try jar.add(try Cookie.parse(testing.allocator, test_uri, "secure=5;Secure"), now);
508-
try jar.add(try Cookie.parse(testing.allocator, test_uri, "sitenone=6;SameSite=None;Path=/x/"), now);
511+
try jar.add(try Cookie.parse(testing.allocator, test_uri, "sitenone=6;SameSite=None;Path=/x/;Secure"), now);
509512
try jar.add(try Cookie.parse(testing.allocator, test_uri, "sitelax=7;SameSite=Lax;Path=/x/"), now);
510513
try jar.add(try Cookie.parse(testing.allocator, test_uri, "sitestrict=8;SameSite=Strict;Path=/x/"), now);
511514
try jar.add(try Cookie.parse(testing.allocator, test_uri_2, "domain1=9;domain=test.lightpanda.io"), now);
@@ -518,55 +521,133 @@ test "Jar: forRequest" {
518521

519522
{
520523
// matching path without trailing /
521-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://lightpanda.io/about"), true);
524+
var matches = try jar.forRequest(
525+
testing.allocator,
526+
now,
527+
test_uri,
528+
try std.Uri.parse("http://lightpanda.io/about"),
529+
true,
530+
);
522531
try expectCookies(&.{ "global1", "global2", "path1" }, &matches);
523532
}
524533

525534
{
526535
// incomplete prefix path
527-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://lightpanda.io/abou"), true);
536+
var matches = try jar.forRequest(
537+
testing.allocator,
538+
now,
539+
test_uri,
540+
try std.Uri.parse("http://lightpanda.io/abou"),
541+
true,
542+
);
528543
try expectCookies(&.{ "global1", "global2" }, &matches);
529544
}
530545

531546
{
532547
// path doesn't match
533-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://lightpanda.io/aboutus"), true);
548+
var matches = try jar.forRequest(
549+
testing.allocator,
550+
now,
551+
test_uri,
552+
try std.Uri.parse("http://lightpanda.io/aboutus"),
553+
true,
554+
);
534555
try expectCookies(&.{ "global1", "global2" }, &matches);
535556
}
536557

537558
{
538559
// path doesn't match cookie directory
539-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://lightpanda.io/docs"), true);
560+
var matches = try jar.forRequest(
561+
testing.allocator,
562+
now,
563+
test_uri,
564+
try std.Uri.parse("http://lightpanda.io/docs"),
565+
true,
566+
);
540567
try expectCookies(&.{ "global1", "global2" }, &matches);
541568
}
542569

543570
{
544571
// exact directory match
545-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://lightpanda.io/docs/"), true);
572+
var matches = try jar.forRequest(
573+
testing.allocator,
574+
now,
575+
test_uri,
576+
try std.Uri.parse("http://lightpanda.io/docs/"),
577+
true,
578+
);
546579
try expectCookies(&.{ "global1", "global2", "path2" }, &matches);
547580
}
548581

549582
{
550583
// sub directory match
551-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://lightpanda.io/docs/more"), true);
584+
var matches = try jar.forRequest(
585+
testing.allocator,
586+
now,
587+
test_uri,
588+
try std.Uri.parse("http://lightpanda.io/docs/more"),
589+
true,
590+
);
552591
try expectCookies(&.{ "global1", "global2", "path2" }, &matches);
553592
}
554593

555594
{
556595
// secure
557-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("https://lightpanda.io/"), true);
596+
var matches = try jar.forRequest(
597+
testing.allocator,
598+
now,
599+
test_uri,
600+
try std.Uri.parse("https://lightpanda.io/"),
601+
true,
602+
);
558603
try expectCookies(&.{ "global1", "global2", "secure" }, &matches);
559604
}
560605

561606
{
562-
// navigational cross domain
563-
var matches = try jar.forRequest(testing.allocator, now, try std.Uri.parse("http://example.com/"), try std.Uri.parse("http://lightpanda.io/x/"), true);
564-
try expectCookies(&.{ "global1", "global2", "sitenone", "sitelax" }, &matches);
607+
// navigational cross domain, secure
608+
var matches = try jar.forRequest(
609+
testing.allocator,
610+
now,
611+
try std.Uri.parse("https://example.com/"),
612+
try std.Uri.parse("https://lightpanda.io/x/"),
613+
true,
614+
);
615+
try expectCookies(&.{ "global1", "global2", "sitenone", "sitelax", "secure" }, &matches);
616+
}
617+
618+
{
619+
// navigational cross domain, insecure
620+
var matches = try jar.forRequest(
621+
testing.allocator,
622+
now,
623+
try std.Uri.parse("http://example.com/"),
624+
try std.Uri.parse("http://lightpanda.io/x/"),
625+
true,
626+
);
627+
try expectCookies(&.{ "global1", "global2", "sitelax" }, &matches);
565628
}
566629

567630
{
568-
// non-navigational cross domain
569-
var matches = try jar.forRequest(testing.allocator, now, try std.Uri.parse("http://example.com/"), try std.Uri.parse("http://lightpanda.io/x/"), false);
631+
// non-navigational cross domain, insecure
632+
var matches = try jar.forRequest(
633+
testing.allocator,
634+
now,
635+
try std.Uri.parse("http://example.com/"),
636+
try std.Uri.parse("http://lightpanda.io/x/"),
637+
false,
638+
);
639+
try expectCookies(&.{}, &matches);
640+
}
641+
642+
{
643+
// non-navigational cross domain, secure
644+
var matches = try jar.forRequest(
645+
testing.allocator,
646+
now,
647+
try std.Uri.parse("https://example.com/"),
648+
try std.Uri.parse("https://lightpanda.io/x/"),
649+
false,
650+
);
570651
try expectCookies(&.{"sitenone"}, &matches);
571652
}
572653

@@ -579,24 +660,42 @@ test "Jar: forRequest" {
579660
try std.Uri.parse("http://lightpanda.io/x/"),
580661
false,
581662
);
582-
try expectCookies(&.{ "global1", "global2", "sitenone", "sitelax", "sitestrict" }, &matches);
663+
try expectCookies(&.{ "global1", "global2", "sitelax", "sitestrict" }, &matches);
583664
}
584665

585666
{
586667
// exact domain match
587-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://test.lightpanda.io/"), true);
668+
var matches = try jar.forRequest(
669+
testing.allocator,
670+
now,
671+
test_uri,
672+
try std.Uri.parse("http://test.lightpanda.io/"),
673+
true,
674+
);
588675
try expectCookies(&.{"domain1"}, &matches);
589676
}
590677

591678
{
592679
// domain suffix match
593-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://1.test.lightpanda.io/"), true);
680+
var matches = try jar.forRequest(
681+
testing.allocator,
682+
now,
683+
test_uri,
684+
try std.Uri.parse("http://1.test.lightpanda.io/"),
685+
true,
686+
);
594687
try expectCookies(&.{"domain1"}, &matches);
595688
}
596689

597690
{
598691
// non-matching domain
599-
var matches = try jar.forRequest(testing.allocator, now, test_uri, try std.Uri.parse("http://other.lightpanda.io/"), true);
692+
var matches = try jar.forRequest(
693+
testing.allocator,
694+
now,
695+
test_uri,
696+
try std.Uri.parse("http://other.lightpanda.io/"),
697+
true,
698+
);
600699
try expectCookies(&.{}, &matches);
601700
}
602701

@@ -677,7 +776,7 @@ test "Cookie: parse secure" {
677776
try expectAttribute(.{ .secure = true }, null, "b; seCUre=Off ");
678777
}
679778

680-
test "Cookie: parse httponly" {
779+
test "Cookie: parse HttpOnly" {
681780
try expectAttribute(.{ .http_only = false }, null, "b");
682781
try expectAttribute(.{ .http_only = false }, null, "b;HttpOnly0");
683782
try expectAttribute(.{ .http_only = false }, null, "b;H ttpOnly");
@@ -689,24 +788,28 @@ test "Cookie: parse httponly" {
689788
try expectAttribute(.{ .http_only = true }, null, "b; HttpOnly=Off ");
690789
}
691790

692-
test "Cookie: parse strict" {
791+
test "Cookie: parse SameSite" {
693792
try expectAttribute(.{ .same_site = .lax }, null, "b;samesite");
694793
try expectAttribute(.{ .same_site = .lax }, null, "b;samesite=lax");
695794
try expectAttribute(.{ .same_site = .lax }, null, "b; SameSite=Lax ");
696795
try expectAttribute(.{ .same_site = .lax }, null, "b; SameSite=Other ");
697796
try expectAttribute(.{ .same_site = .lax }, null, "b; SameSite=Nope ");
698797

699-
try expectAttribute(.{ .same_site = .none }, null, "b; samesite=none ");
700-
try expectAttribute(.{ .same_site = .none }, null, "b; SameSite=None ");
701-
try expectAttribute(.{ .same_site = .none }, null, "b; SameSite=None;");
702-
try expectAttribute(.{ .same_site = .none }, null, "b; SameSite=None");
798+
// SameSite=none is only valid when Secure is set. The whole cookie is
799+
// rejected otherwise
800+
try expectError(error.InsecureSameSite, null, "b;samesite=none");
801+
try expectError(error.InsecureSameSite, null, "b;SameSite=None");
802+
try expectAttribute(.{ .same_site = .none }, null, "b; samesite=none; secure ");
803+
try expectAttribute(.{ .same_site = .none }, null, "b; SameSite=None ; SECURE");
804+
try expectAttribute(.{ .same_site = .none }, null, "b;Secure; SameSite=None");
805+
try expectAttribute(.{ .same_site = .none }, null, "b; SameSite=None; Secure");
703806

704807
try expectAttribute(.{ .same_site = .strict }, null, "b; samesite=Strict ");
705808
try expectAttribute(.{ .same_site = .strict }, null, "b; SameSite= STRICT ");
706809
try expectAttribute(.{ .same_site = .strict }, null, "b; SameSITE=strict;");
707810
try expectAttribute(.{ .same_site = .strict }, null, "b; SameSite=Strict");
708811

709-
try expectAttribute(.{ .same_site = .none }, null, "b; SameSite=Strict; SameSite=lax; SameSite=NONE");
812+
try expectAttribute(.{ .same_site = .strict }, null, "b; SameSite=None; SameSite=lax; SameSite=Strict");
710813
}
711814

712815
test "Cookie: parse max-age" {

0 commit comments

Comments
 (0)