From 270b89830ae6a18e8d0950ca64ae23f5a487e234 Mon Sep 17 00:00:00 2001 From: sjorsdonkers <72333389+sjorsdonkers@users.noreply.github.com> Date: Fri, 13 Jun 2025 13:35:38 +0200 Subject: [PATCH] Cleaning up crumbles --- src/browser/storage/cookie.zig | 29 ++++++++++++------- src/cdp/domains/network.zig | 24 ++++++++-------- src/cdp/domains/storage.zig | 52 +++------------------------------- 3 files changed, 35 insertions(+), 70 deletions(-) diff --git a/src/browser/storage/cookie.zig b/src/browser/storage/cookie.zig index ca96531d..90a23907 100644 --- a/src/browser/storage/cookie.zig +++ b/src/browser/storage/cookie.zig @@ -274,9 +274,8 @@ pub const Cookie = struct { const aa = arena.allocator(); const owned_name = try aa.dupe(u8, cookie_name); const owned_value = try aa.dupe(u8, cookie_value); - const owned_path = try parse_path(aa, uri.path, path); - const host = uri.host orelse return error.InvalidURI; - const owned_domain = try parse_domain(aa, host, domain); + const owned_path = try parsePath(aa, uri, path); + const owned_domain = try parseDomain(aa, uri, domain); var normalized_expires: ?i64 = null; if (max_age) |ma| { @@ -301,7 +300,7 @@ pub const Cookie = struct { }; } - pub fn parse_path(arena: Allocator, url_path: std.Uri.Component, explicit_path: ?[]const u8) ![]const u8 { + pub fn parsePath(arena: Allocator, uri: ?*const std.Uri, explicit_path: ?[]const u8) ![]const u8 { // path attribute value either begins with a '/' or we // ignore it and use the "default-path" algorithm if (explicit_path) |path| { @@ -311,6 +310,8 @@ pub const Cookie = struct { } // default-path + const url_path = (uri orelse return "/").path; + const either = url_path.percent_encoded; if (either.len == 0 or (either.len == 1 and either[0] == '/')) { return "/"; @@ -323,9 +324,14 @@ pub const Cookie = struct { return try arena.dupe(u8, owned_path[0 .. last + 1]); } - pub fn parse_domain(arena: Allocator, url_host: std.Uri.Component, explicit_domain: ?[]const u8) ![]const u8 { - const encoded_host = try percentEncode(arena, url_host, isHostChar); - _ = toLower(encoded_host); + pub fn parseDomain(arena: Allocator, uri: ?*const std.Uri, explicit_domain: ?[]const u8) ![]const u8 { + var encoded_host: ?[]const u8 = null; + if (uri) |uri_| { + const uri_host = uri_.host orelse return error.InvalidURI; + const host = try percentEncode(arena, uri_host, isHostChar); + _ = toLower(host); + encoded_host = host; + } if (explicit_domain) |domain| { if (domain.len > 0) { @@ -342,14 +348,17 @@ pub const Cookie = struct { // can't set a cookie for a TLD return error.InvalidDomain; } - if (std.mem.endsWith(u8, encoded_host, owned_domain[1..]) == false) { - return error.InvalidDomain; + if (encoded_host) |host| { + if (std.mem.endsWith(u8, host, owned_domain[1..]) == false) { + return error.InvalidDomain; + } } + return owned_domain; } } - return encoded_host; // default-domain + return encoded_host orelse return error.InvalidDomain; // default-domain } // TODO when getting cookeis Note: Chrome does not apply rules like removing a leading `.` from the domain. diff --git a/src/cdp/domains/network.zig b/src/cdp/domains/network.zig index 9c0fe27b..e0678186 100644 --- a/src/cdp/domains/network.zig +++ b/src/cdp/domains/network.zig @@ -116,14 +116,18 @@ fn deleteCookies(cmd: anytype) !void { const cookies = &bc.session.cookie_jar.cookies; const uri = if (params.url) |url| std.Uri.parse(url) catch return error.InvalidParams else null; + const uri_ptr = if (uri) |u| &u else null; var index = cookies.items.len; while (index > 0) { index -= 1; const cookie = &cookies.items[index]; - const domain = try CdpStorage.percentEncodedDomainOrHost(cmd.arena, uri, params.domain); - // TBD does chrome take the path from the url as default? (unlike setCookies) - if (cookieMatches(cookie, params.name, domain, params.path)) { + const domain = try Cookie.parseDomain(cmd.arena, uri_ptr, params.domain); + const path = try Cookie.parsePath(cmd.arena, uri_ptr, params.path); + + // We do not want to use Cookie.appliesTo here. As a Cookie with a shorter path would match. + // Similar to deduplicating with areCookiesEqual, except domain and path are optional. + if (cookieMatches(cookie, params.name, domain, path)) { cookies.swapRemove(index).deinit(); } } @@ -173,15 +177,11 @@ fn getCookies(cmd: anytype) !void { for (params.urls) |url| { const uri = std.Uri.parse(url) catch return error.InvalidParams; - const host_component = uri.host orelse return error.InvalidParams; - const host = CdpStorage.toLower(try CdpStorage.percentEncode(cmd.arena, host_component, CdpStorage.isHostChar)); - - var path: []const u8 = try CdpStorage.percentEncode(cmd.arena, uri.path, CdpStorage.isPathChar); - if (path.len == 0) path = "/"; - - const secure = std.mem.eql(u8, uri.scheme, "https"); - - urls.appendAssumeCapacity(.{ .host = host, .path = path, .secure = secure }); + urls.appendAssumeCapacity(.{ + .host = try Cookie.parseDomain(cmd.arena, &uri, null), + .path = try Cookie.parsePath(cmd.arena, &uri, null), + .secure = std.mem.eql(u8, uri.scheme, "https"), + }); } var jar = &bc.session.cookie_jar; diff --git a/src/cdp/domains/storage.zig b/src/cdp/domains/storage.zig index f4983c3f..033c67b7 100644 --- a/src/cdp/domains/storage.zig +++ b/src/cdp/domains/storage.zig @@ -23,7 +23,6 @@ const log = @import("../../log.zig"); const Cookie = @import("../../browser/storage/storage.zig").Cookie; const CookieJar = @import("../../browser/storage/storage.zig").CookieJar; pub const PreparedUri = @import("../../browser/storage/cookie.zig").PreparedUri; -pub const toLower = @import("../../browser/storage/cookie.zig").toLower; pub fn processMessage(cmd: anytype) !void { const action = std.meta.stringToEnum(enum { @@ -143,13 +142,15 @@ pub fn setCdpCookie(cookie_jar: *CookieJar, param: CdpCookie) !void { // NOTE: The param.url can affect the default domain, path, source port, and source scheme. const uri = if (param.url) |url| std.Uri.parse(url) catch return error.InvalidParams else null; - const domain = try percentEncodedDomainOrHost(a, uri, param.domain) orelse return error.InvalidParams; // TODO Domain needs to be prefixed with a dot if is explicitely set + const uri_ptr = if (uri) |*u| u else null; + const domain = try Cookie.parseDomain(a, uri_ptr, param.domain); + const path = try Cookie.parsePath(a, uri_ptr, param.path); const cookie = Cookie{ .arena = arena, .name = try a.dupe(u8, param.name), .value = try a.dupe(u8, param.value), - .path = if (param.path) |path| try a.dupe(u8, path) else "/", // Chrome does not actually take the path from the url and just defaults to "/". + .path = path, .domain = domain, .expires = param.expires, .secure = param.secure, @@ -163,51 +164,6 @@ pub fn setCdpCookie(cookie_jar: *CookieJar, param: CdpCookie) !void { try cookie_jar.add(cookie, std.time.timestamp()); } -// Note: Chrome does not apply rules like removing a leading `.` from the domain. -pub fn percentEncodedDomainOrHost(allocator: Allocator, default_url: ?std.Uri, domain: ?[]const u8) !?[]const u8 { - if (domain) |domain_| { - const output = try allocator.dupe(u8, domain_); - return toLower(output); - } else if (default_url) |url| { - const host = url.host orelse return error.InvalidParams; - const output = try percentEncode(allocator, host, isHostChar); // TODO remove subdomains - return toLower(output); - } else return null; -} - -pub fn percentEncode(arena: Allocator, component: std.Uri.Component, comptime isValidChar: fn (u8) bool) ![]u8 { - switch (component) { - .raw => |str| { - var list = std.ArrayList(u8).init(arena); - try list.ensureTotalCapacity(str.len); // Expect no precents needed - try std.Uri.Component.percentEncode(list.writer(), str, isValidChar); - return list.items; // @memory retains memory used before growing - }, - .percent_encoded => |str| { - return try arena.dupe(u8, str); - }, - } -} - -pub fn isHostChar(c: u8) bool { - return switch (c) { - 'A'...'Z', 'a'...'z', '0'...'9', '-', '.', '_', '~' => true, - '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' => true, - ':' => true, - '[', ']' => true, - else => false, - }; -} - -pub fn isPathChar(c: u8) bool { - return switch (c) { - 'A'...'Z', 'a'...'z', '0'...'9', '-', '.', '_', '~' => true, - '!', '$', '&', '\'', '(', ')', '*', '+', ',', ';', '=' => true, - '/', ':', '@' => true, - else => false, - }; -} - pub const CookieWriter = struct { cookies: []const Cookie, urls: ?[]const PreparedUri = null,