mirror of
https://github.com/teloxide/teloxide.git
synced 2024-12-23 06:51:01 +01:00
Merge pull request #200 from teloxide/hide_token_in_errors
Hide bot token in errors
This commit is contained in:
commit
7da630e4c0
5 changed files with 81 additions and 17 deletions
|
@ -11,6 +11,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
- `user.id` now uses `UserId` type, `ChatId` now represents only _chat id_, not channel username, all `chat_id` function parameters now accept `Recipient` [**BC**]
|
||||
|
||||
## 0.4.5 - 2022-04-03
|
||||
|
||||
### Fixed
|
||||
|
||||
- Hide bot token in errors ([#200][200])
|
||||
|
||||
[200]: https://github.com/teloxide/teloxide-core/pull/200
|
||||
|
||||
## 0.4.4 - 2022-04-21
|
||||
|
||||
### Added
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "teloxide-core"
|
||||
description = "Core part of the `teloxide` library - telegram bot API client"
|
||||
version = "0.4.4"
|
||||
version = "0.4.5"
|
||||
edition = "2018"
|
||||
|
||||
license = "MIT"
|
||||
|
|
|
@ -41,6 +41,7 @@ impl<'w> Download<'w> for Bot {
|
|||
&self.token,
|
||||
path,
|
||||
)
|
||||
.map(|res| res.map_err(crate::errors::hide_token))
|
||||
.boxed()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
use std::io;
|
||||
|
||||
use derive_more::From;
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
|
||||
/// An error caused by downloading a file.
|
||||
#[derive(Debug, Error, From)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum DownloadError {
|
||||
/// A network error while downloading a file from Telegram.
|
||||
#[error("A network error: {0}")]
|
||||
// NOTE: this variant must not be created by anything except the From impl
|
||||
Network(#[source] reqwest::Error),
|
||||
|
||||
/// An I/O error while writing a file to destination.
|
||||
#[error("An I/O error: {0}")]
|
||||
Io(#[source] std::io::Error),
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
||||
/// An error caused by sending a request to Telegram.
|
||||
|
@ -35,6 +35,7 @@ pub enum RequestError {
|
|||
|
||||
/// Network error while sending a request to Telegram.
|
||||
#[error("A network error: {0}")]
|
||||
// NOTE: this variant must not be created by anything except the From impl
|
||||
Network(#[source] reqwest::Error),
|
||||
|
||||
/// Error while parsing a response from Telegram.
|
||||
|
@ -727,3 +728,65 @@ pub enum ApiError {
|
|||
#[error("Unknown error: {0:?}")]
|
||||
Unknown(String),
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for DownloadError {
|
||||
fn from(error: reqwest::Error) -> Self {
|
||||
DownloadError::Network(hide_token(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<reqwest::Error> for RequestError {
|
||||
fn from(error: reqwest::Error) -> Self {
|
||||
RequestError::Network(hide_token(error))
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces token in the url in the error with `token:redacted` string.
|
||||
pub(crate) fn hide_token(mut error: reqwest::Error) -> reqwest::Error {
|
||||
let url = match error.url_mut() {
|
||||
Some(url) => url,
|
||||
None => return error,
|
||||
};
|
||||
|
||||
if let Some(mut segments) = url.path_segments() {
|
||||
// Usually the url looks like "bot<token>/..." or "file/bot<token>/...".
|
||||
let (beginning, segment) = match segments.next() {
|
||||
Some("file") => ("file/", segments.next()),
|
||||
segment => ("", segment),
|
||||
};
|
||||
|
||||
if let Some(token) = segment.and_then(|s| s.strip_prefix("bot")) {
|
||||
// make sure that what we are about to delete looks like a bot token
|
||||
if let Some((id, secret)) = token.split_once(':') {
|
||||
// The part before the : in the token is the id of the bot.
|
||||
let id_character = |c: char| c.is_ascii_digit();
|
||||
|
||||
// The part after the : in the token is the secret.
|
||||
//
|
||||
// In all bot tokens we could find the secret is 35 characters long and is
|
||||
// 0-9a-zA-Z_- only.
|
||||
//
|
||||
// It would be nice to research if TBA always has 35 character secrets or if it
|
||||
// is just a coincidence.
|
||||
const SECRET_LENGTH: usize = 35;
|
||||
let secret_character = |c: char| c.is_ascii_alphanumeric() || c == '-' || c == '_';
|
||||
|
||||
if secret.len() >= SECRET_LENGTH
|
||||
&& id.chars().all(id_character)
|
||||
&& secret.chars().all(secret_character)
|
||||
{
|
||||
// found token, hide only the token
|
||||
let without_token =
|
||||
&url.path()[(beginning.len() + "/bot".len() + token.len())..];
|
||||
let redacted = format!("{beginning}token:redacted{without_token}");
|
||||
|
||||
url.set_path(&redacted);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// couldn't find token in the url, hide the whole url
|
||||
error.without_url()
|
||||
}
|
||||
|
|
|
@ -37,18 +37,14 @@ where
|
|||
let request = client
|
||||
.post(crate::net::method_url(api_url, token, method_name))
|
||||
.multipart(params)
|
||||
.build()
|
||||
.map_err(RequestError::Network)?;
|
||||
.build()?;
|
||||
|
||||
// FIXME: uncomment this, when reqwest starts setting default timeout early
|
||||
// if let Some(timeout) = timeout_hint {
|
||||
// *request.timeout_mut().get_or_insert(Duration::ZERO) += timeout;
|
||||
// }
|
||||
|
||||
let response = client
|
||||
.execute(request)
|
||||
.await
|
||||
.map_err(RequestError::Network)?;
|
||||
let response = client.execute(request).await?;
|
||||
|
||||
process_response(response).await
|
||||
}
|
||||
|
@ -81,18 +77,14 @@ where
|
|||
.post(crate::net::method_url(api_url, token, method_name))
|
||||
.header(CONTENT_TYPE, HeaderValue::from_static("application/json"))
|
||||
.body(params)
|
||||
.build()
|
||||
.map_err(RequestError::Network)?;
|
||||
.build()?;
|
||||
|
||||
// FIXME: uncomment this, when reqwest starts setting default timeout early
|
||||
// if let Some(timeout) = timeout_hint {
|
||||
// *request.timeout_mut().get_or_insert(Duration::ZERO) += timeout;
|
||||
// }
|
||||
|
||||
let response = client
|
||||
.execute(request)
|
||||
.await
|
||||
.map_err(RequestError::Network)?;
|
||||
let response = client.execute(request).await?;
|
||||
|
||||
process_response(response).await
|
||||
}
|
||||
|
@ -105,7 +97,7 @@ where
|
|||
tokio::time::sleep(DELAY_ON_SERVER_ERROR).await;
|
||||
}
|
||||
|
||||
let text = response.text().await.map_err(RequestError::Network)?;
|
||||
let text = response.text().await?;
|
||||
|
||||
serde_json::from_str::<TelegramResponse<T>>(&text)
|
||||
.map_err(|source| RequestError::InvalidJson {
|
||||
|
|
Loading…
Reference in a new issue