diff --git a/docs/api/cookies.md b/docs/api/cookies.md index 5bca7b570fc1..8a3576d3fc60 100644 --- a/docs/api/cookies.md +++ b/docs/api/cookies.md @@ -74,7 +74,7 @@ The following methods are available on instances of `Cookies`: `url`. Empty implies retrieving cookies of all URLs. * `name` string (optional) - Filters cookies by name. * `domain` string (optional) - Retrieves cookies whose domains match or are - subdomains of `domain`. + subdomains of `domains`. * `path` string (optional) - Retrieves cookies whose path matches `path`. * `secure` boolean (optional) - Filters cookies by their Secure property. * `session` boolean (optional) - Filters out session or persistent cookies. diff --git a/shell/browser/api/electron_api_cookies.cc b/shell/browser/api/electron_api_cookies.cc index 7873b817c585..8870355084bd 100644 --- a/shell/browser/api/electron_api_cookies.cc +++ b/shell/browser/api/electron_api_cookies.cc @@ -30,7 +30,6 @@ #include "shell/common/gin_helper/dictionary.h" #include "shell/common/gin_helper/object_template_builder.h" #include "shell/common/gin_helper/promise.h" -#include "url/url_util.h" namespace gin { @@ -101,12 +100,25 @@ namespace electron::api { namespace { -bool DomainIs(std::string_view host, const std::string_view domain) { - // Strip any leading '.' character from the input cookie domain. - if (host.starts_with('.')) - host.remove_prefix(1); +// Returns whether |domain| matches |filter|. +bool MatchesDomain(std::string filter, const std::string& domain) { + // Add a leading '.' character to the filter domain if it doesn't exist. + if (net::cookie_util::DomainIsHostOnly(filter)) + filter.insert(0, "."); - return url::DomainIs(host, domain); + std::string sub_domain(domain); + // Strip any leading '.' character from the input cookie domain. + if (!net::cookie_util::DomainIsHostOnly(sub_domain)) + sub_domain = sub_domain.substr(1); + + // Now check whether the domain argument is a subdomain of the filter domain. + for (sub_domain.insert(0, "."); sub_domain.length() >= filter.length();) { + if (sub_domain == filter) + return true; + const size_t next_dot = sub_domain.find('.', 1); // Skip over leading dot. + sub_domain.erase(0, next_dot); + } + return false; } // Returns whether |cookie| matches |filter|. @@ -117,7 +129,8 @@ bool MatchesCookie(const base::Value::Dict& filter, return false; if ((str = filter.FindString("path")) && *str != cookie.Path()) return false; - if ((str = filter.FindString("domain")) && !DomainIs(cookie.Domain(), *str)) + if ((str = filter.FindString("domain")) && + !MatchesDomain(*str, cookie.Domain())) return false; std::optional secure_filter = filter.FindBool("secure"); if (secure_filter && *secure_filter != cookie.SecureAttribute()) diff --git a/spec/api-session-spec.ts b/spec/api-session-spec.ts index aa7cf1183611..d4754ceec666 100644 --- a/spec/api-session-spec.ts +++ b/spec/api-session-spec.ts @@ -5,7 +5,6 @@ import { expect } from 'chai'; import * as send from 'send'; import * as ChildProcess from 'node:child_process'; -import * as crypto from 'node:crypto'; import { once } from 'node:events'; import * as fs from 'node:fs'; import * as http from 'node:http'; @@ -130,54 +129,6 @@ describe('session module', () => { expect(cs.some(c => c.name === name && c.value === value)).to.equal(true); }); - it('does not match on empty domain filter strings', async () => { - const { cookies } = session.defaultSession; - const name = crypto.randomBytes(20).toString('hex'); - const value = '1'; - const url = 'https://microsoft.com/'; - - await cookies.set({ url, name, value }); - const cs = await cookies.get({ domain: '' }); - expect(cs.some(c => c.name === name && c.value === value)).to.equal(false); - cookies.remove(url, name); - }); - - it('gets domain-equal cookies', async () => { - const { cookies } = session.defaultSession; - const name = crypto.randomBytes(20).toString('hex'); - const value = '1'; - const url = 'https://microsoft.com/'; - - await cookies.set({ url, name, value }); - const cs = await cookies.get({ domain: 'microsoft.com' }); - expect(cs.some(c => c.name === name && c.value === value)).to.equal(true); - cookies.remove(url, name); - }); - - it('gets domain-inclusive cookies', async () => { - const { cookies } = session.defaultSession; - const name = crypto.randomBytes(20).toString('hex'); - const value = '1'; - const url = 'https://subdomain.microsoft.com/'; - - await cookies.set({ url, name, value }); - const cs = await cookies.get({ domain: 'microsoft.com' }); - expect(cs.some(c => c.name === name && c.value === value)).to.equal(true); - cookies.remove(url, name); - }); - - it('omits domain-exclusive cookies', async () => { - const { cookies } = session.defaultSession; - const name = crypto.randomBytes(20).toString('hex'); - const value = '1'; - const url = 'https://microsoft.com'; - - await cookies.set({ url, name, value }); - const cs = await cookies.get({ domain: 'subdomain.microsoft.com' }); - expect(cs.some(c => c.name === name && c.value === value)).to.equal(false); - cookies.remove(url, name); - }); - it('rejects when setting a cookie with missing required fields', async () => { const { cookies } = session.defaultSession; const name = '1';