Skip to content

Commit b96ea74

Browse files
committed
JS may not set/get HttpOnly cookies
1 parent 9f89341 commit b96ea74

File tree

5 files changed

+41
-11
lines changed

5 files changed

+41
-11
lines changed

src/browser/html/document.zig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ pub const HTMLDocument = struct {
8181

8282
pub fn get_cookie(_: *parser.DocumentHTML, page: *Page) ![]const u8 {
8383
var buf: std.ArrayListUnmanaged(u8) = .{};
84-
try page.cookie_jar.forRequest(&page.url.uri, buf.writer(page.arena), .{ .navigation = true });
84+
try page.cookie_jar.forRequest(&page.url.uri, buf.writer(page.arena), .{ .navigation = true, .is_http = false });
8585
return buf.items;
8686
}
8787

@@ -90,6 +90,10 @@ pub const HTMLDocument = struct {
9090
// outlives the page's arena.
9191
const c = try Cookie.parse(page.cookie_jar.allocator, &page.url.uri, cookie_str);
9292
errdefer c.deinit();
93+
if (c.http_only) {
94+
c.deinit();
95+
return ""; // HttpOnly cookies cannot be set from JS
96+
}
9397
try page.cookie_jar.add(c, std.time.timestamp());
9498
return cookie_str;
9599
}
@@ -333,6 +337,8 @@ test "Browser.HTML.Document" {
333337
.{ "document.cookie = 'name=Oeschger; SameSite=None; Secure'", "name=Oeschger; SameSite=None; Secure" },
334338
.{ "document.cookie = 'favorite_food=tripe; SameSite=None; Secure'", "favorite_food=tripe; SameSite=None; Secure" },
335339
.{ "document.cookie", "name=Oeschger; favorite_food=tripe" },
340+
.{ "document.cookie = 'IgnoreMy=Ghost; HttpOnly'", null }, // "" should be returned, but the framework overrules it atm
341+
.{ "document.cookie", "name=Oeschger; favorite_food=tripe" },
336342
}, .{});
337343

338344
try runner.testCases(&.{

src/browser/page.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ pub const Page = struct {
217217
{
218218
// block exists to limit the lifetime of the request, which holds
219219
// onto a connection
220-
var request = try self.newHTTPRequest(opts.method, &self.url, .{ .navigation = true });
220+
var request = try self.newHTTPRequest(opts.method, &self.url, .{ .navigation = true, .is_http = true });
221221
defer request.deinit();
222222

223223
request.body = opts.body;
@@ -498,6 +498,7 @@ pub const Page = struct {
498498
var request = try self.newHTTPRequest(.GET, &url, .{
499499
.origin_uri = &origin_url.uri,
500500
.navigation = false,
501+
.is_http = true,
501502
});
502503
defer request.deinit();
503504

src/browser/storage/cookie.zig

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ pub const LookupOpts = struct {
1212
request_time: ?i64 = null,
1313
origin_uri: ?*const Uri = null,
1414
navigation: bool = true,
15+
is_http: bool,
1516
};
1617

1718
pub const Jar = struct {
@@ -91,7 +92,7 @@ pub const Jar = struct {
9192

9293
var first = true;
9394
for (self.cookies.items) |*cookie| {
94-
if (!cookie.appliesTo(&target, same_site, opts.navigation)) continue;
95+
if (!cookie.appliesTo(&target, same_site, opts.navigation, opts.is_http)) continue;
9596

9697
// we have a match!
9798
if (first) {
@@ -411,7 +412,12 @@ pub const Cookie = struct {
411412
return .{ name, value, rest };
412413
}
413414

414-
pub fn appliesTo(self: *const Cookie, url: *const PreparedUri, same_site: bool, navigation: bool) bool {
415+
pub fn appliesTo(self: *const Cookie, url: *const PreparedUri, same_site: bool, navigation: bool, is_http: bool) bool {
416+
if (self.http_only and is_http == false) {
417+
// http only cookies can be accessed from Javascript
418+
return false;
419+
}
420+
415421
if (url.secure == false and self.secure) {
416422
// secure cookie can only be sent over HTTPs
417423
return false;
@@ -581,7 +587,7 @@ test "Jar: forRequest" {
581587

582588
{
583589
// test with no cookies
584-
try expectCookies("", &jar, test_uri, .{});
590+
try expectCookies("", &jar, test_uri, .{ .is_http = true });
585591
}
586592

587593
try jar.add(try Cookie.parse(testing.allocator, &test_uri, "global1=1"), now);
@@ -595,97 +601,114 @@ test "Jar: forRequest" {
595601
try jar.add(try Cookie.parse(testing.allocator, &test_uri_2, "domain1=9;domain=test.lightpanda.io"), now);
596602

597603
// nothing fancy here
598-
try expectCookies("global1=1; global2=2", &jar, test_uri, .{});
599-
try expectCookies("global1=1; global2=2", &jar, test_uri, .{ .origin_uri = &test_uri, .navigation = false });
604+
try expectCookies("global1=1; global2=2", &jar, test_uri, .{ .is_http = true });
605+
try expectCookies("global1=1; global2=2", &jar, test_uri, .{ .origin_uri = &test_uri, .navigation = false, .is_http = true });
600606

601607
// We have a cookie where Domain=lightpanda.io
602608
// This should _not_ match xyxlightpanda.io
603609
try expectCookies("", &jar, try std.Uri.parse("http://anothersitelightpanda.io/"), .{
604610
.origin_uri = &test_uri,
611+
.is_http = true,
605612
});
606613

607614
// matching path without trailing /
608615
try expectCookies("global1=1; global2=2; path1=3", &jar, try std.Uri.parse("http://lightpanda.io/about"), .{
609616
.origin_uri = &test_uri,
617+
.is_http = true,
610618
});
611619

612620
// incomplete prefix path
613621
try expectCookies("global1=1; global2=2", &jar, try std.Uri.parse("http://lightpanda.io/abou"), .{
614622
.origin_uri = &test_uri,
623+
.is_http = true,
615624
});
616625

617626
// path doesn't match
618627
try expectCookies("global1=1; global2=2", &jar, try std.Uri.parse("http://lightpanda.io/aboutus"), .{
619628
.origin_uri = &test_uri,
629+
.is_http = true,
620630
});
621631

622632
// path doesn't match cookie directory
623633
try expectCookies("global1=1; global2=2", &jar, try std.Uri.parse("http://lightpanda.io/docs"), .{
624634
.origin_uri = &test_uri,
635+
.is_http = true,
625636
});
626637

627638
// exact directory match
628639
try expectCookies("global1=1; global2=2; path2=4", &jar, try std.Uri.parse("http://lightpanda.io/docs/"), .{
629640
.origin_uri = &test_uri,
641+
.is_http = true,
630642
});
631643

632644
// sub directory match
633645
try expectCookies("global1=1; global2=2; path2=4", &jar, try std.Uri.parse("http://lightpanda.io/docs/more"), .{
634646
.origin_uri = &test_uri,
647+
.is_http = true,
635648
});
636649

637650
// secure
638651
try expectCookies("global1=1; global2=2; secure=5", &jar, try std.Uri.parse("https://lightpanda.io/"), .{
639652
.origin_uri = &test_uri,
653+
.is_http = true,
640654
});
641655

642656
// navigational cross domain, secure
643657
try expectCookies("global1=1; global2=2; secure=5; sitenone=6; sitelax=7", &jar, try std.Uri.parse("https://lightpanda.io/x/"), .{
644658
.origin_uri = &(try std.Uri.parse("https://example.com/")),
659+
.is_http = true,
645660
});
646661

647662
// navigational cross domain, insecure
648663
try expectCookies("global1=1; global2=2; sitelax=7", &jar, try std.Uri.parse("http://lightpanda.io/x/"), .{
649664
.origin_uri = &(try std.Uri.parse("https://example.com/")),
665+
.is_http = true,
650666
});
651667

652668
// non-navigational cross domain, insecure
653669
try expectCookies("", &jar, try std.Uri.parse("http://lightpanda.io/x/"), .{
654670
.origin_uri = &(try std.Uri.parse("https://example.com/")),
655671
.navigation = false,
672+
.is_http = true,
656673
});
657674

658675
// non-navigational cross domain, secure
659676
try expectCookies("sitenone=6", &jar, try std.Uri.parse("https://lightpanda.io/x/"), .{
660677
.origin_uri = &(try std.Uri.parse("https://example.com/")),
661678
.navigation = false,
679+
.is_http = true,
662680
});
663681

664682
// non-navigational same origin
665683
try expectCookies("global1=1; global2=2; sitelax=7; sitestrict=8", &jar, try std.Uri.parse("http://lightpanda.io/x/"), .{
666684
.origin_uri = &(try std.Uri.parse("https://lightpanda.io/")),
667685
.navigation = false,
686+
.is_http = true,
668687
});
669688

670689
// exact domain match + suffix
671690
try expectCookies("global2=2; domain1=9", &jar, try std.Uri.parse("http://test.lightpanda.io/"), .{
672691
.origin_uri = &test_uri,
692+
.is_http = true,
673693
});
674694

675695
// domain suffix match + suffix
676696
try expectCookies("global2=2; domain1=9", &jar, try std.Uri.parse("http://1.test.lightpanda.io/"), .{
677697
.origin_uri = &test_uri,
698+
.is_http = true,
678699
});
679700

680701
// non-matching domain
681702
try expectCookies("global2=2", &jar, try std.Uri.parse("http://other.lightpanda.io/"), .{
682703
.origin_uri = &test_uri,
704+
.is_http = true,
683705
});
684706

685707
const l = jar.cookies.items.len;
686708
try expectCookies("global1=1", &jar, test_uri, .{
687709
.request_time = now + 100,
688710
.origin_uri = &test_uri,
711+
.is_http = true,
689712
});
690713
try testing.expectEqual(l - 1, jar.cookies.items.len);
691714

src/browser/xhr/xhr.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,6 +475,7 @@ pub const XMLHttpRequest = struct {
475475
try self.cookie_jar.forRequest(&self.url.?.uri, arr.writer(self.arena), .{
476476
.navigation = false,
477477
.origin_uri = &self.origin_url.uri,
478+
.is_http = true,
478479
});
479480

480481
if (arr.items.len > 0) {

src/cdp/domains/storage.zig

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,6 @@ pub fn setCdpCookie(cookie_jar: *CookieJar, param: CdpCookie) !void {
133133
if (param.priority != .Medium or param.sameParty != null or param.sourceScheme != null or param.partitionKey != null) {
134134
return error.NotYetImplementedParams;
135135
}
136-
if (param.name.len == 0) return error.InvalidParams;
137-
if (param.value.len == 0) return error.InvalidParams;
138136

139137
var arena = std.heap.ArenaAllocator.init(cookie_jar.allocator);
140138
errdefer arena.deinit();
@@ -183,7 +181,7 @@ pub const CookieWriter = struct {
183181
if (self.urls) |urls| {
184182
for (self.cookies) |*cookie| {
185183
for (urls) |*url| {
186-
if (cookie.appliesTo(url, true, true)) { // TBD same_site, should we compare to the pages url?
184+
if (cookie.appliesTo(url, true, true, true)) { // TBD same_site, should we compare to the pages url?
187185
try writeCookie(cookie, w);
188186
break;
189187
}
@@ -223,7 +221,8 @@ pub fn writeCookie(cookie: *const Cookie, w: anytype) !void {
223221
try w.objectField("secure");
224222
try w.write(cookie.secure);
225223

226-
// TODO session
224+
try w.objectField("session");
225+
try w.write(cookie.expires == null);
227226

228227
try w.objectField("sameSite");
229228
switch (cookie.same_site) {

0 commit comments

Comments
 (0)