mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 20:31:13 +00:00
Make using insecure connections opt-in (#1309)
* add insecure url checks * rename variables * add feature flags to expose Url properly * fix test compile error * fix feature errors * remove comment * add url crate and use it for url parsing * fix compile errors * satisfy the holy clippy * fix typos and host loopback * macro attribute, provide validation function in utils * fix expected output of ui tests * remove the success case for --allow-insecure because we cannot establish ws:// connection at the moment.
This commit is contained in:
@@ -20,7 +20,18 @@ pub struct RpcClient {
|
||||
impl RpcClient {
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
/// Create a default RPC client pointed at some URL, currently based on [`jsonrpsee`].
|
||||
///
|
||||
/// Errors if an insecure URL is provided. In this case, use [`RpcClient::from_insecure_url`] instead.
|
||||
pub async fn from_url<U: AsRef<str>>(url: U) -> Result<Self, Error> {
|
||||
crate::utils::validate_url_is_secure(url.as_ref())?;
|
||||
RpcClient::from_insecure_url(url).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
/// Create a default RPC client pointed at some URL, currently based on [`jsonrpsee`].
|
||||
///
|
||||
/// Allows insecure URLs without SSL encryption, e.g. (http:// and ws:// URLs).
|
||||
pub async fn from_insecure_url<U: AsRef<str>>(url: U) -> Result<Self, Error> {
|
||||
let client = jsonrpsee_helpers::client(url.as_ref())
|
||||
.await
|
||||
.map_err(|e| crate::error::RpcError::ClientError(Box::new(e)))?;
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
use super::{rpc::LightClientRpc, LightClient, LightClientError};
|
||||
use crate::backend::rpc::RpcClient;
|
||||
use crate::client::RawLightClient;
|
||||
use crate::error::RpcError;
|
||||
use crate::utils::validate_url_is_secure;
|
||||
use crate::{config::Config, error::Error, OnlineClient};
|
||||
use std::num::NonZeroU32;
|
||||
use subxt_lightclient::{smoldot, AddedChain};
|
||||
@@ -101,8 +103,19 @@ impl<T: Config> LightClientBuilder<T> {
|
||||
/// https://docs.rs/wasm-bindgen-futures/latest/wasm_bindgen_futures/fn.future_to_promise.html.
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
pub async fn build_from_url<Url: AsRef<str>>(self, url: Url) -> Result<LightClient<T>, Error> {
|
||||
let chain_spec = fetch_url(url.as_ref()).await?;
|
||||
validate_url_is_secure(url.as_ref())?;
|
||||
self.build_from_insecure_url(url).await
|
||||
}
|
||||
|
||||
/// Build the light client with specified URL to connect to. Allows insecure URLs (no SSL, ws:// or http://).
|
||||
///
|
||||
/// For secure connections only, please use [`crate::LightClientBuilder::build_from_url`].
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
pub async fn build_from_insecure_url<Url: AsRef<str>>(
|
||||
self,
|
||||
url: Url,
|
||||
) -> Result<LightClient<T>, Error> {
|
||||
let chain_spec = fetch_url(url.as_ref()).await?;
|
||||
self.build_client(chain_spec).await
|
||||
}
|
||||
|
||||
@@ -235,7 +248,6 @@ async fn build_client_from_rpc<T: Config>(
|
||||
#[cfg(feature = "jsonrpsee")]
|
||||
async fn fetch_url(url: impl AsRef<str>) -> Result<serde_json::Value, Error> {
|
||||
use jsonrpsee::core::client::ClientT;
|
||||
|
||||
let client = jsonrpsee_helpers::client(url.as_ref()).await?;
|
||||
|
||||
client
|
||||
|
||||
@@ -66,7 +66,15 @@ impl<T: Config> OnlineClient<T> {
|
||||
|
||||
/// Construct a new [`OnlineClient`], providing a URL to connect to.
|
||||
pub async fn from_url(url: impl AsRef<str>) -> Result<OnlineClient<T>, Error> {
|
||||
let client = RpcClient::from_url(url).await?;
|
||||
crate::utils::validate_url_is_secure(url.as_ref())?;
|
||||
OnlineClient::from_insecure_url(url).await
|
||||
}
|
||||
|
||||
/// Construct a new [`OnlineClient`], providing a URL to connect to.
|
||||
///
|
||||
/// Allows insecure URLs without SSL encryption, e.g. (http:// and ws:// URLs).
|
||||
pub async fn from_insecure_url(url: impl AsRef<str>) -> Result<OnlineClient<T>, Error> {
|
||||
let client = RpcClient::from_insecure_url(url).await?;
|
||||
let backend = LegacyBackend::new(client);
|
||||
OnlineClient::from_backend(Arc::new(backend)).await
|
||||
}
|
||||
|
||||
@@ -115,6 +115,9 @@ pub enum RpcError {
|
||||
/// The RPC subscription dropped.
|
||||
#[error("RPC error: subscription dropped.")]
|
||||
SubscriptionDropped,
|
||||
/// The requested URL is insecure.
|
||||
#[error("RPC error: insecure URL: {0}")]
|
||||
InsecureUrl(String),
|
||||
}
|
||||
|
||||
impl RpcError {
|
||||
|
||||
+4
-4
@@ -220,7 +220,7 @@ pub mod ext {
|
||||
/// mod polkadot {}
|
||||
/// ```
|
||||
///
|
||||
/// ## `runtime_metadata_url = "..."`
|
||||
/// ## `runtime_metadata_insecure_url = "..."`
|
||||
///
|
||||
/// This attribute can be used instead of `runtime_metadata_path` and will tell the macro to download metadata from a node running
|
||||
/// at the provided URL, rather than a node running locally. This can be useful in CI, but is **not recommended** in production code,
|
||||
@@ -228,7 +228,7 @@ pub mod ext {
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[subxt::subxt(
|
||||
/// runtime_metadata_url = "wss://rpc.polkadot.io:443"
|
||||
/// runtime_metadata_insecure_url = "wss://rpc.polkadot.io:443"
|
||||
/// )]
|
||||
/// mod polkadot {}
|
||||
/// ```
|
||||
@@ -281,14 +281,14 @@ pub mod ext {
|
||||
///
|
||||
/// ## `unstable_metadata`
|
||||
///
|
||||
/// This attribute works only in combination with `runtime_metadata_url`. By default, the macro will fetch the latest stable
|
||||
/// This attribute works only in combination with `runtime_metadata_insecure_url`. By default, the macro will fetch the latest stable
|
||||
/// version of the metadata from the target node. This attribute makes the codegen attempt to fetch the unstable version of
|
||||
/// the metadata first. This is **not recommended** in production code, since the unstable metadata a node is providing is likely
|
||||
/// to be incompatible with Subxt.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// #[subxt::subxt(
|
||||
/// runtime_metadata_url = "wss://rpc.polkadot.io:443",
|
||||
/// runtime_metadata_insecure_url = "wss://rpc.polkadot.io:443",
|
||||
/// unstable_metadata
|
||||
/// )]
|
||||
/// mod polkadot {}
|
||||
|
||||
@@ -13,8 +13,11 @@ mod static_type;
|
||||
mod unchecked_extrinsic;
|
||||
mod wrapper_opaque;
|
||||
|
||||
use crate::error::RpcError;
|
||||
use crate::Error;
|
||||
use codec::{Compact, Decode, Encode};
|
||||
use derivative::Derivative;
|
||||
use url::Url;
|
||||
|
||||
pub use account_id::AccountId32;
|
||||
pub use era::Era;
|
||||
@@ -47,6 +50,31 @@ pub(crate) fn strip_compact_prefix(bytes: &[u8]) -> Result<(u64, &[u8]), codec::
|
||||
Ok((val.0, *cursor))
|
||||
}
|
||||
|
||||
/// A URL is considered secure if it uses a secure scheme ("https" or "wss") or is referring to localhost.
|
||||
///
|
||||
/// Returns an error if the the string could not be parsed into a URL.
|
||||
pub fn url_is_secure(url: &str) -> Result<bool, Error> {
|
||||
let url = Url::parse(url).map_err(|e| Error::Rpc(RpcError::ClientError(Box::new(e))))?;
|
||||
|
||||
let secure_scheme = url.scheme() == "https" || url.scheme() == "wss";
|
||||
let is_localhost = url.host().is_some_and(|e| match e {
|
||||
url::Host::Domain(e) => e == "localhost",
|
||||
url::Host::Ipv4(e) => e.is_loopback(),
|
||||
url::Host::Ipv6(e) => e.is_loopback(),
|
||||
});
|
||||
|
||||
Ok(secure_scheme || is_localhost)
|
||||
}
|
||||
|
||||
/// Validates, that the given Url is secure ("https" or "wss" scheme) or is referring to localhost.
|
||||
pub fn validate_url_is_secure(url: &str) -> Result<(), Error> {
|
||||
if !url_is_secure(url)? {
|
||||
Err(Error::Rpc(crate::error::RpcError::InsecureUrl(url.into())))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A version of [`std::marker::PhantomData`] that is also Send and Sync (which is fine
|
||||
/// because regardless of the generic param, it is always possible to Send + Sync this
|
||||
/// 0 size type).
|
||||
|
||||
Reference in New Issue
Block a user