mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-22 20:45:14 +00:00
LibWeb+WebDriver: Validate WebDriver proxy capabilities
We don't yet support a proxy configuration, but we can still validate the capability received from the WebDriver client. We should also fail to create a WebDriver session if a proxy configuration is present.
This commit is contained in:
parent
88eda159af
commit
d873dc0744
Notes:
github-actions[bot]
2025-02-10 16:34:53 +00:00
Author: https://github.com/trflynn89 Commit: https://github.com/LadybirdBrowser/ladybird/commit/d873dc07449 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3497
5 changed files with 178 additions and 34 deletions
|
@ -828,6 +828,7 @@ set(SOURCES
|
|||
WebDriver/InputSource.cpp
|
||||
WebDriver/InputState.cpp
|
||||
WebDriver/JSON.cpp
|
||||
WebDriver/Proxy.cpp
|
||||
WebDriver/Response.cpp
|
||||
WebDriver/Screenshot.cpp
|
||||
WebDriver/TimeoutsConfiguration.cpp
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <AK/Optional.h>
|
||||
#include <LibWeb/Loader/UserAgent.h>
|
||||
#include <LibWeb/WebDriver/Capabilities.h>
|
||||
#include <LibWeb/WebDriver/Proxy.h>
|
||||
#include <LibWeb/WebDriver/TimeoutsConfiguration.h>
|
||||
#include <LibWeb/WebDriver/UserPrompt.h>
|
||||
|
||||
|
@ -31,33 +32,6 @@ static Response deserialize_as_a_page_load_strategy(JsonValue value)
|
|||
return value;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-deserialize-as-a-proxy
|
||||
static ErrorOr<JsonObject, Error> deserialize_as_a_proxy(JsonValue parameter)
|
||||
{
|
||||
// 1. If parameter is not a JSON Object return an error with error code invalid argument.
|
||||
if (!parameter.is_object())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Capability proxy must be an object"sv);
|
||||
|
||||
// 2. Let proxy be a new, empty proxy configuration object.
|
||||
JsonObject proxy;
|
||||
|
||||
// 3. For each enumerable own property in parameter run the following substeps:
|
||||
TRY(parameter.as_object().try_for_each_member([&](auto const& key, JsonValue const& value) -> ErrorOr<void, Error> {
|
||||
// 1. Let key be the name of the property.
|
||||
// 2. Let value be the result of getting a property named name from capability.
|
||||
|
||||
// FIXME: 3. If there is no matching key for key in the proxy configuration table return an error with error code invalid argument.
|
||||
// FIXME: 4. If value is not one of the valid values for that key, return an error with error code invalid argument.
|
||||
|
||||
// 5. Set a property key to value on proxy.
|
||||
proxy.set(key, value);
|
||||
|
||||
return {};
|
||||
}));
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
static InterfaceMode default_interface_mode { InterfaceMode::Graphical };
|
||||
|
||||
void set_default_interface_mode(InterfaceMode interface_mode)
|
||||
|
@ -332,7 +306,10 @@ static JsonValue match_capabilities(JsonObject const& capabilities, SessionFlags
|
|||
}
|
||||
// -> "proxy"
|
||||
else if (name == "proxy"sv) {
|
||||
// FIXME: If the has proxy configuration flag is set, or if the proxy configuration defined in value is not one that passes the endpoint node's implementation-specific validity checks, return success with data null.
|
||||
// If the has proxy configuration flag is set, or if the proxy configuration defined in value is not one that
|
||||
// passes the endpoint node's implementation-specific validity checks, return success with data null.
|
||||
if (has_proxy_configuration())
|
||||
return AK::Error::from_string_literal("proxy");
|
||||
}
|
||||
// -> "unhandledPromptBehavior"
|
||||
else if (name == "unhandledPromptBehavior"sv) {
|
||||
|
|
135
Libraries/LibWeb/WebDriver/Proxy.cpp
Normal file
135
Libraries/LibWeb/WebDriver/Proxy.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/JsonValue.h>
|
||||
#include <LibURL/URL.h>
|
||||
#include <LibWeb/WebDriver/Proxy.h>
|
||||
|
||||
namespace Web::WebDriver {
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-has-proxy-configuration
|
||||
// An endpoint node has an associated has proxy configuration flag that indicates whether the proxy is already configured.
|
||||
// The default value of the flag is true if the endpoint doesn't support proxy configuration, or false otherwise.
|
||||
static constexpr bool s_default_has_proxy_configuration = true;
|
||||
static bool s_has_proxy_configuration = s_default_has_proxy_configuration;
|
||||
|
||||
bool has_proxy_configuration()
|
||||
{
|
||||
return s_has_proxy_configuration;
|
||||
}
|
||||
|
||||
void set_has_proxy_configuration(bool has_proxy_configuration)
|
||||
{
|
||||
s_has_proxy_configuration = has_proxy_configuration;
|
||||
}
|
||||
|
||||
void reset_has_proxy_configuration()
|
||||
{
|
||||
s_has_proxy_configuration = s_default_has_proxy_configuration;
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-proxy-configuration
|
||||
static ErrorOr<void, Error> validate_proxy_item(StringView key, JsonValue const& value)
|
||||
{
|
||||
if (key == "proxyType"sv) {
|
||||
if (!value.is_string())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'proxyType' must be a string"sv);
|
||||
if (!value.as_string().is_one_of("pac"sv, "direct"sv, "autodetect"sv, "system"sv, "manual"sv))
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Invalid 'proxyType' value"sv);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (key == "proxyAutoconfigUrl"sv) {
|
||||
if (!value.is_string())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'proxyAutoconfigUrl' must be a string"sv);
|
||||
if (URL::URL url { value.as_string() }; !url.is_valid())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'proxyAutoconfigUrl' must be a valid URL"sv);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (key == "ftpProxy"sv) {
|
||||
if (!value.is_string())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'ftpProxy' must be a string"sv);
|
||||
if (URL::URL url { value.as_string() }; !url.is_valid() || url.scheme() != "ftp"sv)
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'ftpProxy' must be a valid FTP URL"sv);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (key == "httpProxy"sv) {
|
||||
if (!value.is_string())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'httpProxy' must be a string"sv);
|
||||
if (URL::URL url { value.as_string() }; !url.is_valid() || url.scheme() != "http"sv)
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'httpProxy' must be a valid HTTP URL"sv);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (key == "noProxy"sv) {
|
||||
if (!value.is_array())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'noProxy' must be a list"sv);
|
||||
|
||||
TRY(value.as_array().try_for_each([&](JsonValue const& item) -> ErrorOr<void, Error> {
|
||||
if (!item.is_string())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'noProxy' must be a list of strings"sv);
|
||||
return {};
|
||||
}));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
if (key == "sslProxy"sv) {
|
||||
if (!value.is_string())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'sslProxy' must be a string"sv);
|
||||
if (URL::URL url { value.as_string() }; !url.is_valid() || url.scheme() != "https"sv)
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'sslProxy' must be a valid HTTPS URL"sv);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (key == "socksProxy"sv) {
|
||||
if (!value.is_string())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'proxyAutoconfigUrl' must be a string"sv);
|
||||
if (URL::URL url { value.as_string() }; !url.is_valid())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'proxyAutoconfigUrl' must be a valid URL"sv);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (key == "socksVersion"sv) {
|
||||
if (!value.is_integer<u8>())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Proxy configuration item 'socksVersion' must be an integer in the range [0, 255]"sv);
|
||||
return {};
|
||||
}
|
||||
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Invalid proxy configuration item"sv);
|
||||
}
|
||||
|
||||
// https://w3c.github.io/webdriver/#dfn-deserialize-as-a-proxy
|
||||
ErrorOr<JsonObject, Error> deserialize_as_a_proxy(JsonValue const& parameter)
|
||||
{
|
||||
// 1. If parameter is not a JSON Object return an error with error code invalid argument.
|
||||
if (!parameter.is_object())
|
||||
return Error::from_code(ErrorCode::InvalidArgument, "Capability proxy must be an object"sv);
|
||||
|
||||
// 2. Let proxy be a new, empty proxy configuration object.
|
||||
JsonObject proxy;
|
||||
|
||||
// 3. For each enumerable own property in parameter run the following substeps:
|
||||
TRY(parameter.as_object().try_for_each_member([&](ByteString const& key, JsonValue const& value) -> ErrorOr<void, Error> {
|
||||
// 1. Let key be the name of the property.
|
||||
// 2. Let value be the result of getting a property named name from capability.
|
||||
|
||||
// 3. If there is no matching key for key in the proxy configuration table return an error with error code invalid argument.
|
||||
// 4. If value is not one of the valid values for that key, return an error with error code invalid argument.
|
||||
TRY(validate_proxy_item(key, value));
|
||||
|
||||
// 5. Set a property key to value on proxy.
|
||||
proxy.set(key, value);
|
||||
|
||||
return {};
|
||||
}));
|
||||
|
||||
return proxy;
|
||||
}
|
||||
|
||||
}
|
20
Libraries/LibWeb/WebDriver/Proxy.h
Normal file
20
Libraries/LibWeb/WebDriver/Proxy.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Tim Flynn <trflynn89@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/JsonObject.h>
|
||||
#include <LibWeb/WebDriver/Error.h>
|
||||
|
||||
namespace Web::WebDriver {
|
||||
|
||||
bool has_proxy_configuration();
|
||||
void set_has_proxy_configuration(bool);
|
||||
void reset_has_proxy_configuration();
|
||||
|
||||
ErrorOr<JsonObject, Error> deserialize_as_a_proxy(JsonValue const&);
|
||||
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
#include <LibCore/StandardPaths.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibWeb/Crypto/Crypto.h>
|
||||
#include <LibWeb/WebDriver/Proxy.h>
|
||||
#include <LibWeb/WebDriver/TimeoutsConfiguration.h>
|
||||
#include <LibWeb/WebDriver/UserPrompt.h>
|
||||
#include <unistd.h>
|
||||
|
@ -35,11 +36,19 @@ ErrorOr<NonnullRefPtr<Session>> Session::create(NonnullRefPtr<Client> client, Js
|
|||
auto session = adopt_ref(*new Session(client, capabilities, move(session_id), flags));
|
||||
TRY(session->start(client->launch_browser_callbacks()));
|
||||
|
||||
// FIXME: 3. Let proxy be the result of getting property "proxy" from capabilities and run the substeps of the first matching statement:
|
||||
// -> proxy is a proxy configuration object
|
||||
// Take implementation-defined steps to set the user agent proxy using the extracted proxy configuration. If the defined proxy cannot be configured return error with error code session not created. Otherwise set the has proxy configuration flag to true.
|
||||
// -> Otherwise
|
||||
// Set a property of capabilities with name "proxy" and a value that is a new JSON Object.
|
||||
// 3. Let proxy be the result of getting property "proxy" from capabilities and run the substeps of the first matching statement:
|
||||
// -> proxy is a proxy configuration object
|
||||
if (auto proxy = capabilities.get_object("proxy"sv); proxy.has_value()) {
|
||||
// Take implementation-defined steps to set the user agent proxy using the extracted proxy configuration. If the
|
||||
// defined proxy cannot be configured return error with error code session not created. Otherwise set the has
|
||||
// proxy configuration flag to true.
|
||||
return Error::from_string_literal("Proxy configuration is not yet supported");
|
||||
}
|
||||
// -> Otherwise
|
||||
else {
|
||||
// Set a property of capabilities with name "proxy" and a value that is a new JSON Object.
|
||||
capabilities.set("proxy", JsonObject {});
|
||||
}
|
||||
|
||||
// FIXME: 4. If capabilites has a property named "acceptInsecureCerts", set the endpoint node's accept insecure TLS flag
|
||||
// to the result of getting a property named "acceptInsecureCerts" from capabilities.
|
||||
|
@ -161,7 +170,9 @@ void Session::close()
|
|||
Web::WebDriver::set_user_prompt_handler({});
|
||||
|
||||
// FIXME: 3. Unset the accept insecure TLS flag.
|
||||
// FIXME: 4. Reset the has proxy configuration flag to its default value.
|
||||
|
||||
// 4. Reset the has proxy configuration flag to its default value.
|
||||
Web::WebDriver::reset_has_proxy_configuration();
|
||||
|
||||
// 5. Optionally, close all top-level browsing contexts, without prompting to unload.
|
||||
for (auto& it : m_windows)
|
||||
|
|
Loading…
Add table
Reference in a new issue