diff options
author | Timothy Flynn <trflynn89@pm.me> | 2023-02-24 11:51:56 -0500 |
---|---|---|
committer | Tim Flynn <trflynn89@pm.me> | 2023-02-24 15:50:42 -0500 |
commit | 87c4080d009d8f3d714a2f9ad4774696d933902d (patch) | |
tree | 0d5f5e47562afdc329fa072ed9418a375e593240 | |
parent | 1858163d3ca39b538f847046d781422e5b6026a0 (diff) | |
download | serenity-87c4080d009d8f3d714a2f9ad4774696d933902d.zip |
Browser+LibWeb+WebContent: Store cookie expiry times in UTC
We are currently converting parsed expiry times to local time, whereas
the RFC dictates we parse them as UTC. When expiring cookies, we must
also use the current UTC time to compare against the cookies' expiry
times.
-rw-r--r-- | Userland/Applications/Browser/CookieJar.cpp | 40 | ||||
-rw-r--r-- | Userland/Applications/Browser/CookiesModel.cpp | 2 | ||||
-rw-r--r-- | Userland/Applications/Browser/StorageWidget.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Cookie/Cookie.cpp | 29 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Cookie/Cookie.h | 14 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp | 26 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Cookie/ParsedCookie.h | 8 | ||||
-rw-r--r-- | Userland/Services/WebContent/WebDriverConnection.cpp | 9 |
8 files changed, 83 insertions, 47 deletions
diff --git a/Userland/Applications/Browser/CookieJar.cpp b/Userland/Applications/Browser/CookieJar.cpp index 2e39fce90d..76d987f8f8 100644 --- a/Userland/Applications/Browser/CookieJar.cpp +++ b/Userland/Applications/Browser/CookieJar.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2022, Tim Flynn <trflynn89@serenityos.org> + * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org> * Copyright (c) 2022, the SerenityOS developers. * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org> * @@ -11,6 +11,7 @@ #include <AK/IPv4Address.h> #include <AK/StringBuilder.h> #include <AK/StringView.h> +#include <AK/Time.h> #include <AK/URL.h> #include <AK/Vector.h> #include <LibCore/Promise.h> @@ -140,9 +141,9 @@ void CookieJar::dump_cookies() builder.appendff("{}{}{}\n", key_color, cookie.path, no_color); builder.appendff("\t{}Value{} = {}\n", attribute_color, no_color, cookie.value); - builder.appendff("\t{}CreationTime{} = {}\n", attribute_color, no_color, cookie.creation_time.to_deprecated_string()); - builder.appendff("\t{}LastAccessTime{} = {}\n", attribute_color, no_color, cookie.last_access_time.to_deprecated_string()); - builder.appendff("\t{}ExpiryTime{} = {}\n", attribute_color, no_color, cookie.expiry_time.to_deprecated_string()); + builder.appendff("\t{}CreationTime{} = {}\n", attribute_color, no_color, cookie.creation_time_to_string()); + builder.appendff("\t{}LastAccessTime{} = {}\n", attribute_color, no_color, cookie.last_access_time_to_string()); + builder.appendff("\t{}ExpiryTime{} = {}\n", attribute_color, no_color, cookie.expiry_time_to_string()); builder.appendff("\t{}Secure{} = {:s}\n", attribute_color, no_color, cookie.secure); builder.appendff("\t{}HttpOnly{} = {:s}\n", attribute_color, no_color, cookie.http_only); builder.appendff("\t{}HostOnly{} = {:s}\n", attribute_color, no_color, cookie.host_only); @@ -275,7 +276,7 @@ void CookieJar::store_cookie(Web::Cookie::ParsedCookie const& parsed_cookie, con // 2. Create a new cookie with name cookie-name, value cookie-value. Set the creation-time and the last-access-time to the current date and time. Web::Cookie::Cookie cookie { parsed_cookie.name, parsed_cookie.value, parsed_cookie.same_site_attribute }; - cookie.creation_time = Core::DateTime::now(); + cookie.creation_time = Time::now_realtime(); cookie.last_access_time = cookie.creation_time; if (parsed_cookie.expiry_time_from_max_age_attribute.has_value()) { @@ -289,9 +290,9 @@ void CookieJar::store_cookie(Web::Cookie::ParsedCookie const& parsed_cookie, con cookie.persistent = true; cookie.expiry_time = parsed_cookie.expiry_time_from_expires_attribute.value(); } else { - // Set the cookie's persistent-flag to false. Set the cookie's expiry-time to the latest representable gddate. + // Set the cookie's persistent-flag to false. Set the cookie's expiry-time to the latest representable date. cookie.persistent = false; - cookie.expiry_time = Core::DateTime::create(9999, 12, 31, 23, 59, 59); + cookie.expiry_time = Time::max(); } // 4. If the cookie-attribute-list contains an attribute with an attribute-name of "Domain": @@ -396,13 +397,13 @@ Vector<Web::Cookie::Cookie> CookieJar::get_matching_cookies(const URL& url, Depr // - Cookies with longer paths are listed before cookies with shorter paths. // - Among cookies that have equal-length path fields, cookies with earlier creation-times are listed before cookies with later creation-times. auto cookie_path_length = cookie.path.length(); - auto cookie_creation_time = cookie.creation_time.timestamp(); + auto cookie_creation_time = cookie.creation_time; cookie_list.insert_before_matching(move(cookie), [cookie_path_length, cookie_creation_time](auto const& entry) { if (cookie_path_length > entry.path.length()) { return true; } else if (cookie_path_length == entry.path.length()) { - if (cookie_creation_time < entry.creation_time.timestamp()) + if (cookie_creation_time < entry.creation_time) return true; } return false; @@ -410,7 +411,7 @@ Vector<Web::Cookie::Cookie> CookieJar::get_matching_cookies(const URL& url, Depr }); // 3. Update the last-access-time of each cookie in the cookie-list to the current date and time. - auto now = Core::DateTime::now(); + auto now = Time::now_realtime(); for (auto& cookie : cookie_list) { cookie.last_access_time = now; @@ -450,8 +451,8 @@ static ErrorOr<Web::Cookie::Cookie> parse_cookie(ReadonlySpan<SQL::Value> row) if (value.type() != SQL::SQLType::Integer) return Error::from_string_view(name); - auto time = value.to_int<time_t>().value(); - field = Core::DateTime::from_timestamp(time); + auto time = value.to_int<i64>().value(); + field = Time::from_seconds(time); return {}; }; @@ -492,9 +493,9 @@ void CookieJar::insert_cookie_into_database(Web::Cookie::Cookie const& cookie) cookie.name, cookie.value, to_underlying(cookie.same_site), - cookie.creation_time.timestamp(), - cookie.last_access_time.timestamp(), - cookie.expiry_time.timestamp(), + cookie.creation_time.to_seconds(), + cookie.last_access_time.to_seconds(), + cookie.expiry_time.to_seconds(), cookie.domain, cookie.path, cookie.secure, @@ -509,9 +510,9 @@ void CookieJar::update_cookie_in_database(Web::Cookie::Cookie const& cookie) m_statements.update_cookie, {}, [this]() { purge_expired_cookies(); }, {}, cookie.value, to_underlying(cookie.same_site), - cookie.creation_time.timestamp(), - cookie.last_access_time.timestamp(), - cookie.expiry_time.timestamp(), + cookie.creation_time.to_seconds(), + cookie.last_access_time.to_seconds(), + cookie.expiry_time.to_seconds(), cookie.secure, cookie.http_only, cookie.host_only, @@ -581,7 +582,8 @@ void CookieJar::select_all_cookies_from_database(OnSelectAllCookiesResult on_res void CookieJar::purge_expired_cookies() { - auto now = Core::DateTime::now().timestamp(); + auto now = Time::now_realtime().to_seconds(); m_database.execute_statement(m_statements.expire_cookie, {}, {}, {}, now); } + } diff --git a/Userland/Applications/Browser/CookiesModel.cpp b/Userland/Applications/Browser/CookiesModel.cpp index 50df78a243..2d16901bd5 100644 --- a/Userland/Applications/Browser/CookiesModel.cpp +++ b/Userland/Applications/Browser/CookiesModel.cpp @@ -80,7 +80,7 @@ GUI::Variant CookiesModel::data(GUI::ModelIndex const& index, GUI::ModelRole rol case Column::Value: return cookie.value; case Column::ExpiryTime: - return cookie.expiry_time.to_deprecated_string(); + return cookie.expiry_time_to_string(); case Column::SameSite: return Web::Cookie::same_site_to_string(cookie.same_site); } diff --git a/Userland/Applications/Browser/StorageWidget.cpp b/Userland/Applications/Browser/StorageWidget.cpp index 4c7314fd87..9837b879cd 100644 --- a/Userland/Applications/Browser/StorageWidget.cpp +++ b/Userland/Applications/Browser/StorageWidget.cpp @@ -130,7 +130,7 @@ void StorageWidget::clear_session_storage_entries() void StorageWidget::delete_cookie(Web::Cookie::Cookie cookie) { // Delete cookie by making its expiry time in the past. - cookie.expiry_time = Core::DateTime::from_timestamp(0); + cookie.expiry_time = Time::from_seconds(0); if (on_update_cookie) on_update_cookie(move(cookie)); } diff --git a/Userland/Libraries/LibWeb/Cookie/Cookie.cpp b/Userland/Libraries/LibWeb/Cookie/Cookie.cpp index 229ca38090..bbef4fb5c6 100644 --- a/Userland/Libraries/LibWeb/Cookie/Cookie.cpp +++ b/Userland/Libraries/LibWeb/Cookie/Cookie.cpp @@ -1,15 +1,38 @@ /* * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org> + * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ #include "Cookie.h" +#include <LibCore/DateTime.h> #include <LibIPC/Decoder.h> #include <LibIPC/Encoder.h> namespace Web::Cookie { +static DeprecatedString time_to_string(Time const& time) +{ + auto local_time = Core::DateTime::from_timestamp(time.to_seconds()); + return local_time.to_deprecated_string("%Y-%m-%d %H:%M:%S %Z"sv); +} + +DeprecatedString Cookie::creation_time_to_string() const +{ + return time_to_string(creation_time); +} + +DeprecatedString Cookie::last_access_time_to_string() const +{ + return time_to_string(last_access_time); +} + +DeprecatedString Cookie::expiry_time_to_string() const +{ + return time_to_string(expiry_time); +} + StringView same_site_to_string(SameSite same_site) { switch (same_site) { @@ -64,11 +87,11 @@ ErrorOr<Web::Cookie::Cookie> IPC::decode(Decoder& decoder) auto value = TRY(decoder.decode<DeprecatedString>()); auto domain = TRY(decoder.decode<DeprecatedString>()); auto path = TRY(decoder.decode<DeprecatedString>()); - auto creation_time = TRY(decoder.decode<Core::DateTime>()); - auto expiry_time = TRY(decoder.decode<Core::DateTime>()); + auto creation_time = TRY(decoder.decode<Time>()); + auto expiry_time = TRY(decoder.decode<Time>()); auto host_only = TRY(decoder.decode<bool>()); auto http_only = TRY(decoder.decode<bool>()); - auto last_access_time = TRY(decoder.decode<Core::DateTime>()); + auto last_access_time = TRY(decoder.decode<Time>()); auto persistent = TRY(decoder.decode<bool>()); auto secure = TRY(decoder.decode<bool>()); auto same_site = TRY(decoder.decode<Web::Cookie::SameSite>()); diff --git a/Userland/Libraries/LibWeb/Cookie/Cookie.h b/Userland/Libraries/LibWeb/Cookie/Cookie.h index c3b4c9768c..e54d168621 100644 --- a/Userland/Libraries/LibWeb/Cookie/Cookie.h +++ b/Userland/Libraries/LibWeb/Cookie/Cookie.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org> + * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -7,7 +7,7 @@ #pragma once #include <AK/DeprecatedString.h> -#include <LibCore/DateTime.h> +#include <AK/Time.h> #include <LibIPC/Forward.h> namespace Web::Cookie { @@ -25,12 +25,16 @@ enum class Source { }; struct Cookie { + DeprecatedString creation_time_to_string() const; + DeprecatedString last_access_time_to_string() const; + DeprecatedString expiry_time_to_string() const; + DeprecatedString name; DeprecatedString value; SameSite same_site; - Core::DateTime creation_time {}; - Core::DateTime last_access_time {}; - Core::DateTime expiry_time {}; + Time creation_time {}; + Time last_access_time {}; + Time expiry_time {}; DeprecatedString domain {}; DeprecatedString path {}; bool secure { false }; diff --git a/Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp b/Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp index e04870f1bb..0d6102b114 100644 --- a/Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp +++ b/Userland/Libraries/LibWeb/Cookie/ParsedCookie.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org> + * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,6 +8,7 @@ #include <AK/DateConstants.h> #include <AK/Function.h> #include <AK/StdLibExtras.h> +#include <AK/Time.h> #include <AK/Vector.h> #include <LibIPC/Decoder.h> #include <LibIPC/Encoder.h> @@ -26,7 +27,7 @@ static void on_path_attribute(ParsedCookie& parsed_cookie, StringView attribute_ static void on_secure_attribute(ParsedCookie& parsed_cookie); static void on_http_only_attribute(ParsedCookie& parsed_cookie); static void on_same_site_attribute(ParsedCookie& parsed_cookie, StringView attribute_value); -static Optional<Core::DateTime> parse_date_time(StringView date_string); +static Optional<Time> parse_date_time(StringView date_string); Optional<ParsedCookie> parse_cookie(DeprecatedString const& cookie_string) { @@ -153,7 +154,7 @@ void on_expires_attribute(ParsedCookie& parsed_cookie, StringView attribute_valu { // https://tools.ietf.org/html/rfc6265#section-5.2.1 if (auto expiry_time = parse_date_time(attribute_value); expiry_time.has_value()) - parsed_cookie.expiry_time_from_expires_attribute = move(*expiry_time); + parsed_cookie.expiry_time_from_expires_attribute = expiry_time.release_value(); } void on_max_age_attribute(ParsedCookie& parsed_cookie, StringView attribute_value) @@ -168,11 +169,10 @@ void on_max_age_attribute(ParsedCookie& parsed_cookie, StringView attribute_valu if (auto delta_seconds = attribute_value.to_int(); delta_seconds.has_value()) { if (*delta_seconds <= 0) { // If delta-seconds is less than or equal to zero (0), let expiry-time be the earliest representable date and time. - parsed_cookie.expiry_time_from_max_age_attribute = Core::DateTime::from_timestamp(0); + parsed_cookie.expiry_time_from_max_age_attribute = Time::min(); } else { // Otherwise, let the expiry-time be the current date and time plus delta-seconds seconds. - time_t now = Core::DateTime::now().timestamp(); - parsed_cookie.expiry_time_from_max_age_attribute = Core::DateTime::from_timestamp(now + *delta_seconds); + parsed_cookie.expiry_time_from_max_age_attribute = Time::now_realtime() + Time::from_seconds(*delta_seconds); } } } @@ -236,7 +236,7 @@ void on_same_site_attribute(ParsedCookie& parsed_cookie, StringView attribute_va parsed_cookie.same_site_attribute = same_site_from_string(attribute_value); } -Optional<Core::DateTime> parse_date_time(StringView date_string) +Optional<Time> parse_date_time(StringView date_string) { // https://tools.ietf.org/html/rfc6265#section-5.1.1 unsigned hour = 0; @@ -341,8 +341,14 @@ Optional<Core::DateTime> parse_date_time(StringView date_string) if (second > 59) return {}; + // 6. Let the parsed-cookie-date be the date whose day-of-month, month, year, hour, minute, and second (in UTC) are the + // day-of-month-value, the month-value, the year-value, the hour-value, the minute-value, and the second-value, respectively. + // If no such date exists, abort these steps and fail to parse the cookie-date. // FIXME: Fail on dates that do not exist. - return Core::DateTime::create(year, month, day_of_month, hour, minute, second); + auto parsed_cookie_date = Time::from_timestamp(year, month, day_of_month, hour, minute, second, 0); + + // 7. Return the parsed-cookie-date as the result of this algorithm. + return parsed_cookie_date; } } @@ -368,8 +374,8 @@ ErrorOr<Web::Cookie::ParsedCookie> IPC::decode(Decoder& decoder) { auto name = TRY(decoder.decode<DeprecatedString>()); auto value = TRY(decoder.decode<DeprecatedString>()); - auto expiry_time_from_expires_attribute = TRY(decoder.decode<Optional<Core::DateTime>>()); - auto expiry_time_from_max_age_attribute = TRY(decoder.decode<Optional<Core::DateTime>>()); + auto expiry_time_from_expires_attribute = TRY(decoder.decode<Optional<Time>>()); + auto expiry_time_from_max_age_attribute = TRY(decoder.decode<Optional<Time>>()); auto domain = TRY(decoder.decode<Optional<DeprecatedString>>()); auto path = TRY(decoder.decode<Optional<DeprecatedString>>()); auto secure_attribute_present = TRY(decoder.decode<bool>()); diff --git a/Userland/Libraries/LibWeb/Cookie/ParsedCookie.h b/Userland/Libraries/LibWeb/Cookie/ParsedCookie.h index 07cc36b292..5779f8d5ee 100644 --- a/Userland/Libraries/LibWeb/Cookie/ParsedCookie.h +++ b/Userland/Libraries/LibWeb/Cookie/ParsedCookie.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Tim Flynn <trflynn89@serenityos.org> + * Copyright (c) 2021-2023, Tim Flynn <trflynn89@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,7 +8,7 @@ #include <AK/DeprecatedString.h> #include <AK/Optional.h> -#include <LibCore/DateTime.h> +#include <AK/Time.h> #include <LibIPC/Forward.h> #include <LibWeb/Cookie/Cookie.h> @@ -18,8 +18,8 @@ struct ParsedCookie { DeprecatedString name; DeprecatedString value; SameSite same_site_attribute { SameSite::Default }; - Optional<Core::DateTime> expiry_time_from_expires_attribute {}; - Optional<Core::DateTime> expiry_time_from_max_age_attribute {}; + Optional<Time> expiry_time_from_expires_attribute {}; + Optional<Time> expiry_time_from_max_age_attribute {}; Optional<DeprecatedString> domain {}; Optional<DeprecatedString> path {}; bool secure_attribute_present { false }; diff --git a/Userland/Services/WebContent/WebDriverConnection.cpp b/Userland/Services/WebContent/WebDriverConnection.cpp index 0361199cad..bc9fcc9514 100644 --- a/Userland/Services/WebContent/WebDriverConnection.cpp +++ b/Userland/Services/WebContent/WebDriverConnection.cpp @@ -3,13 +3,14 @@ * Copyright (c) 2022-2023, Sam Atkins <atkinssj@serenityos.org> * Copyright (c) 2022, Tobias Christiansen <tobyase@serenityos.org> * Copyright (c) 2022, Linus Groh <linusg@serenityos.org> - * Copyright (c) 2022, Tim Flynn <trflynn89@serenityos.org> + * Copyright (c) 2022-2023, Tim Flynn <trflynn89@serenityos.org> * * SPDX-License-Identifier: BSD-2-Clause */ #include <AK/JsonObject.h> #include <AK/JsonValue.h> +#include <AK/Time.h> #include <AK/Vector.h> #include <LibJS/Runtime/JSONObject.h> #include <LibJS/Runtime/Value.h> @@ -54,7 +55,7 @@ static JsonValue serialize_cookie(Web::Cookie::Cookie const& cookie) serialized_cookie.set("domain"sv, cookie.domain); serialized_cookie.set("secure"sv, cookie.secure); serialized_cookie.set("httpOnly"sv, cookie.http_only); - serialized_cookie.set("expiry"sv, cookie.expiry_time.timestamp()); + serialized_cookie.set("expiry"sv, cookie.expiry_time.to_seconds()); serialized_cookie.set("sameSite"sv, Web::Cookie::same_site_to_string(cookie.same_site)); return serialized_cookie; @@ -1593,7 +1594,7 @@ Messages::WebDriverClient::AddCookieResponse WebDriverConnection::add_cookie(Jso if (data.has("expiry"sv)) { // NOTE: less than 0 or greater than safe integer are handled by the JSON parser auto expiry = TRY(get_property<u32>(data, "expiry"sv)); - cookie.expiry_time_from_expires_attribute = Core::DateTime::from_timestamp(expiry); + cookie.expiry_time_from_expires_attribute = Time::from_seconds(expiry); } // Cookie same site @@ -1985,7 +1986,7 @@ void WebDriverConnection::delete_cookies(Optional<StringView> const& name) // -> name is equal to cookie name if (!name.has_value() || name.value() == cookie.name) { // Set the cookie expiry time to a Unix timestamp in the past. - cookie.expiry_time = Core::DateTime::from_timestamp(0); + cookie.expiry_time = Time::from_seconds(0); m_page_client.page_did_update_cookie(move(cookie)); } // -> Otherwise |