From a06b97dae032912872113e267ec59b1ed05e6e22 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Wed, 7 Dec 2022 19:14:45 +0000 Subject: LibWeb/Fetch: Share validation logic in the Headers class This is an editorial change in the Fetch spec. See: https://github.com/whatwg/fetch/commit/f435978 --- Userland/Libraries/LibWeb/Fetch/Headers.cpp | 99 +++++++++++++---------------- Userland/Libraries/LibWeb/Fetch/Headers.h | 1 + 2 files changed, 45 insertions(+), 55 deletions(-) (limited to 'Userland') diff --git a/Userland/Libraries/LibWeb/Fetch/Headers.cpp b/Userland/Libraries/LibWeb/Fetch/Headers.cpp index 2c3890f920..2cdb3d56ea 100644 --- a/Userland/Libraries/LibWeb/Fetch/Headers.cpp +++ b/Userland/Libraries/LibWeb/Fetch/Headers.cpp @@ -62,36 +62,24 @@ WebIDL::ExceptionOr Headers::delete_(DeprecatedString const& name_string) auto& realm = this->realm(); auto name = name_string.bytes(); - // 1. If name is not a header name, then throw a TypeError. - if (!Infrastructure::is_header_name(name)) - return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid header name"sv }; - - // 2. If this’s guard is "immutable", then throw a TypeError. - if (m_guard == Guard::Immutable) - return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Headers object is immutable"sv }; - - // 3. Otherwise, if this's guard is "request" and (name, ``) is a forbidden request-header, return. - // NOTE: Passing a dummy header value to forbidden request-header ought not to have any negative repercussions. + // 1. If validating (name, ``) for headers returns false, then return. + // NOTE: Passing a dummy header value ought not to have any negative repercussions. auto header = TRY_OR_RETURN_OOM(realm, Infrastructure::Header::from_string_pair(name, ""sv)); - if (m_guard == Guard::Request && TRY_OR_RETURN_OOM(realm, Infrastructure::is_forbidden_request_header(header))) + if (!TRY(validate(header))) return {}; - // 4. Otherwise, if this’s guard is "request-no-cors", name is not a no-CORS-safelisted request-header name, and name is not a privileged no-CORS request-header name, return. + // 2. If this’s guard is "request-no-cors", name is not a no-CORS-safelisted request-header name, and name is not a privileged no-CORS request-header name, then return. if (m_guard == Guard::RequestNoCORS && !Infrastructure::is_no_cors_safelisted_request_header_name(name) && !Infrastructure::is_privileged_no_cors_request_header_name(name)) return {}; - // 5. Otherwise, if this’s guard is "response" and name is a forbidden response-header name, return. - if (m_guard == Guard::Response && Infrastructure::is_forbidden_response_header_name(name)) - return {}; - - // 6. If this’s header list does not contain name, then return. + // 3. If this’s header list does not contain name, then return. if (!m_header_list->contains(name)) return {}; - // 7. Delete name from this’s header list. + // 4. Delete name from this’s header list. m_header_list->delete_(name); - // 8. If this’s guard is "request-no-cors", then remove privileged no-CORS request-headers from this. + // 5. If this’s guard is "request-no-cors", then remove privileged no-CORS request-headers from this. if (m_guard == Guard::RequestNoCORS) remove_privileged_no_cors_request_headers(); @@ -144,32 +132,18 @@ WebIDL::ExceptionOr Headers::set(DeprecatedString const& name_string, Depr .value = move(normalized_value), }; - // 2. If name is not a header name or value is not a header value, then throw a TypeError. - if (!Infrastructure::is_header_name(name)) - return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid header name"sv }; - if (!Infrastructure::is_header_value(value)) - return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid header value"sv }; - - // 3. If this’s guard is "immutable", then throw a TypeError. - if (m_guard == Guard::Immutable) - return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Headers object is immutable"sv }; - - // 4. Otherwise, if this’s guard is "request" and (name, value) is a forbidden request-header, return. - if (m_guard == Guard::Request && TRY_OR_RETURN_OOM(realm, Infrastructure::is_forbidden_request_header(header))) + // 2. If validating (name, value) for headers returns false, then return. + if (!TRY(validate(header))) return {}; - // 5. Otherwise, if this’s guard is "request-no-cors" and (name, value) is not a no-CORS-safelisted request-header, return. + // 3. If this’s guard is "request-no-cors" and (name, value) is not a no-CORS-safelisted request-header, then return. if (m_guard == Guard::RequestNoCORS && !Infrastructure::is_no_cors_safelisted_request_header(header)) return {}; - // 6. Otherwise, if this’s guard is "response" and name is a forbidden response-header name, return. - if (m_guard == Guard::Response && Infrastructure::is_forbidden_response_header_name(name)) - return {}; - - // 7. Set (name, value) in this’s header list. + // 4. Set (name, value) in this’s header list. TRY_OR_RETURN_OOM(realm, m_header_list->set(move(header))); - // 8. If this’s guard is "request-no-cors", then remove privileged no-CORS request-headers from this. + // 5. If this’s guard is "request-no-cors", then remove privileged no-CORS request-headers from this. if (m_guard == Guard::RequestNoCORS) remove_privileged_no_cors_request_headers(); @@ -213,31 +187,50 @@ JS::ThrowCompletionOr Headers::for_each(ForEachCallback callback) return {}; } -// https://fetch.spec.whatwg.org/#concept-headers-append -WebIDL::ExceptionOr Headers::append(Infrastructure::Header header) +// https://fetch.spec.whatwg.org/#headers-validate +WebIDL::ExceptionOr Headers::validate(Infrastructure::Header const& header) const { - // To append a header (name, value) to a Headers object headers, run these steps: + // To validate a header (name, value) for a Headers object headers: auto& realm = this->realm(); - auto& [name, value] = header; - - // 1. Normalize value. - value = TRY_OR_RETURN_OOM(realm, Infrastructure::normalize_header_value(value)); + auto const& [name, value] = header; - // 2. If name is not a header name or value is not a header value, then throw a TypeError. + // 1. If name is not a header name or value is not a header value, then throw a TypeError. if (!Infrastructure::is_header_name(name)) return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid header name"sv }; if (!Infrastructure::is_header_value(value)) return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Invalid header value"sv }; - // 3. If headers’s guard is "immutable", then throw a TypeError. + // 2. If headers’s guard is "immutable", then throw a TypeError. if (m_guard == Guard::Immutable) return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Headers object is immutable"sv }; - // 4. Otherwise, if headers’s guard is "request" and (name, value) is a forbidden request-header, return. + // 3. If headers’s guard is "request" and (name, value) is a forbidden request-header, then return false. if (m_guard == Guard::Request && TRY_OR_RETURN_OOM(realm, Infrastructure::is_forbidden_request_header(header))) + return false; + + // 4. If headers’s guard is "response" and name is a forbidden response-header name, then return false. + if (m_guard == Guard::Response && Infrastructure::is_forbidden_response_header_name(name)) + return false; + + // 5. Return true. + return true; +} + +// https://fetch.spec.whatwg.org/#concept-headers-append +WebIDL::ExceptionOr Headers::append(Infrastructure::Header header) +{ + // To append a header (name, value) to a Headers object headers, run these steps: + auto& realm = this->realm(); + auto& [name, value] = header; + + // 1. Normalize value. + value = TRY_OR_RETURN_OOM(realm, Infrastructure::normalize_header_value(value)); + + // 2. If validating (name, value) for headers returns false, then return. + if (!TRY(validate(header))) return {}; - // 5. Otherwise, if headers’s guard is "request-no-cors": + // 3. If headers’s guard is "request-no-cors": if (m_guard == Guard::RequestNoCORS) { // 1. Let temporaryValue be the result of getting name from headers’s header list. auto temporary_value = TRY_OR_RETURN_OOM(realm, m_header_list->get(name)); @@ -263,14 +256,10 @@ WebIDL::ExceptionOr Headers::append(Infrastructure::Header header) return {}; } - // 6. Otherwise, if headers’s guard is "response" and name is a forbidden response-header name, return. - if (m_guard == Guard::Response && Infrastructure::is_forbidden_response_header_name(name)) - return {}; - - // 7. Append (name, value) to headers’s header list. + // 4. Append (name, value) to headers’s header list. TRY_OR_RETURN_OOM(realm, m_header_list->append(move(header))); - // 8. If headers’s guard is "request-no-cors", then remove privileged no-CORS request-headers from headers. + // 5. If headers’s guard is "request-no-cors", then remove privileged no-CORS request-headers from headers. if (m_guard == Guard::RequestNoCORS) remove_privileged_no_cors_request_headers(); diff --git a/Userland/Libraries/LibWeb/Fetch/Headers.h b/Userland/Libraries/LibWeb/Fetch/Headers.h index 909a9c2b26..5a905240ed 100644 --- a/Userland/Libraries/LibWeb/Fetch/Headers.h +++ b/Userland/Libraries/LibWeb/Fetch/Headers.h @@ -62,6 +62,7 @@ private: virtual void visit_edges(JS::Cell::Visitor&) override; + WebIDL::ExceptionOr validate(Infrastructure::Header const&) const; void remove_privileged_no_cors_request_headers(); // https://fetch.spec.whatwg.org/#concept-headers-header-list -- cgit v1.2.3