Skip to content

Commit 5b6a1d4

Browse files
Replace outdated secrecy library with own impl (#2997)
`secrecy` has had major changes in it's transition from `0.8` -> `0.10` that would make it even less useful than it is now. This just brings the basic implementation in-house and exposes it publicly, which is somehow less lines of code that trying to use the `0.8` API, let alone `0.10`. --------- Co-authored-by: jamesbt365 <[email protected]>
1 parent 391b234 commit 5b6a1d4

File tree

10 files changed

+58
-73
lines changed

10 files changed

+58
-73
lines changed

Cargo.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,12 @@ bitflags = "2.4.2"
2222
serde_json = "1.0.108"
2323
async-trait = "0.1.74"
2424
tracing = { version = "0.1.40", features = ["log"] }
25-
serde = { version = "1.0.192", features = ["derive"] }
25+
serde = { version = "1.0.192", features = ["derive", "rc"] }
2626
url = { version = "2.4.1", features = ["serde"] }
2727
tokio = { version = "1.34.0", features = ["macros", "rt", "sync", "time", "io-util"] }
2828
futures = { version = "0.3.29", default-features = false, features = ["std"] }
2929
dep_time = { version = "0.3.36", package = "time", features = ["formatting", "parsing", "serde-well-known"] }
3030
base64 = { version = "0.22.0" }
31-
secrecy = { version = "0.8.0", features = ["serde"] }
3231
zeroize = { version = "1.7" } # Not used in serenity, but bumps the minimal version from secrecy
3332
arrayvec = { version = "0.7.4", features = ["serde"] }
3433
serde_cow = { version = "0.1.0" }

src/gateway/sharding/mod.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ use std::sync::Arc;
4545
use std::time::{Duration as StdDuration, Instant};
4646

4747
use aformat::{aformat, CapStr};
48-
use secrecy::{ExposeSecret as _, Secret};
4948
use tokio_tungstenite::tungstenite::error::Error as TungsteniteError;
5049
use tokio_tungstenite::tungstenite::protocol::frame::CloseFrame;
5150
use tracing::{debug, error, info, trace, warn};
@@ -57,7 +56,6 @@ pub use self::shard_queuer::{ShardQueue, ShardQueuer, ShardQueuerMessage};
5756
pub use self::shard_runner::{ShardRunner, ShardRunnerMessage, ShardRunnerOptions};
5857
use super::{ActivityData, ChunkGuildFilter, GatewayError, PresenceData, WsClient};
5958
use crate::constants::{self, close_codes};
60-
use crate::http::Token;
6159
use crate::internal::prelude::*;
6260
use crate::model::event::{Event, GatewayEvent};
6361
use crate::model::gateway::{GatewayIntents, ShardInfo};
@@ -111,7 +109,7 @@ pub struct Shard {
111109
// This acts as a timeout to determine if the shard has - for some reason - not started within
112110
// a decent amount of time.
113111
pub started: Instant,
114-
token: Secret<Token>,
112+
token: SecretString,
115113
ws_url: Arc<str>,
116114
resume_ws_url: Option<FixedString>,
117115
pub intents: GatewayIntents,
@@ -133,13 +131,14 @@ impl Shard {
133131
/// use serenity::gateway::Shard;
134132
/// use serenity::model::gateway::{GatewayIntents, ShardInfo};
135133
/// use serenity::model::id::ShardId;
134+
/// use serenity::secret_string::SecretString;
136135
/// use tokio::sync::Mutex;
137136
/// #
138137
/// # use serenity::http::Http;
139138
/// #
140139
/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
141140
/// # let http: Arc<Http> = unimplemented!();
142-
/// let token = Arc::from(std::env::var("DISCORD_BOT_TOKEN")?);
141+
/// let token = SecretString::new(Arc::from(std::env::var("DISCORD_BOT_TOKEN")?));
143142
/// let shard_info = ShardInfo {
144143
/// id: ShardId(0),
145144
/// total: NonZeroU16::MIN,
@@ -161,7 +160,7 @@ impl Shard {
161160
/// TLS error.
162161
pub async fn new(
163162
ws_url: Arc<str>,
164-
token: Arc<str>,
163+
token: SecretString,
165164
shard_info: ShardInfo,
166165
intents: GatewayIntents,
167166
presence: Option<PresenceData>,
@@ -188,7 +187,7 @@ impl Shard {
188187
seq,
189188
stage,
190189
started: Instant::now(),
191-
token: Token::new(token),
190+
token,
192191
session_id,
193192
shard_info,
194193
ws_url,

src/gateway/sharding/shard_queuer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ impl ShardQueuer {
213213
};
214214
let mut shard = Shard::new(
215215
Arc::clone(&self.ws_url),
216-
Arc::clone(self.http.token()),
216+
self.http.token(),
217217
shard_info,
218218
self.intents,
219219
self.presence.clone(),

src/http/client.rs

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use reqwest::header::{HeaderMap as Headers, HeaderValue};
1313
#[cfg(feature = "utils")]
1414
use reqwest::Url;
1515
use reqwest::{Client, ClientBuilder, Response as ReqwestResponse, StatusCode};
16-
use secrecy::{ExposeSecret as _, Secret};
1716
use serde::de::DeserializeOwned;
1817
use serde::ser::SerializeSeq as _;
1918
use serde_json::{from_value, to_string, to_vec};
@@ -37,34 +36,6 @@ use crate::constants;
3736
use crate::internal::prelude::*;
3837
use crate::model::prelude::*;
3938

40-
#[derive(Clone)]
41-
pub(crate) struct Token(Arc<str>);
42-
43-
impl Token {
44-
pub fn new(inner: Arc<str>) -> Secret<Self> {
45-
Secret::new(Self(inner))
46-
}
47-
}
48-
49-
impl std::ops::Deref for Token {
50-
type Target = str;
51-
52-
fn deref(&self) -> &Self::Target {
53-
&self.0
54-
}
55-
}
56-
57-
impl secrecy::Zeroize for Token {
58-
fn zeroize(&mut self) {
59-
if let Some(string) = Arc::get_mut(&mut self.0) {
60-
string.zeroize();
61-
}
62-
}
63-
}
64-
65-
impl secrecy::CloneableSecret for Token {}
66-
impl secrecy::DebugSecret for Token {}
67-
6839
// NOTE: This cannot be passed in from outside, due to `Cell` being !Send.
6940
struct SerializeIter<I>(Cell<Option<I>>);
7041

@@ -229,7 +200,7 @@ impl HttpBuilder {
229200
client,
230201
ratelimiter,
231202
proxy: self.proxy,
232-
token: Token::new(self.token),
203+
token: SecretString::new(self.token),
233204
application_id,
234205
default_allowed_mentions: self.default_allowed_mentions,
235206
}
@@ -268,7 +239,7 @@ pub struct Http {
268239
pub(crate) client: Client,
269240
pub ratelimiter: Option<Ratelimiter>,
270241
pub proxy: Option<FixedString<u16>>,
271-
token: Secret<Token>,
242+
token: SecretString,
272243
application_id: AtomicU64,
273244
pub default_allowed_mentions: Option<CreateAllowedMentions<'static>>,
274245
}
@@ -297,8 +268,8 @@ impl Http {
297268
}
298269

299270
#[cfg(feature = "gateway")]
300-
pub(crate) fn token(&self) -> &Arc<str> {
301-
&self.token.expose_secret().0
271+
pub(crate) fn token(&self) -> SecretString {
272+
self.token.clone()
302273
}
303274

304275
/// Adds a [`User`] to a [`Guild`] with a valid OAuth2 access token.

src/http/ratelimiting.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,12 @@ use std::time::SystemTime;
4444
use dashmap::DashMap;
4545
use reqwest::header::HeaderMap;
4646
use reqwest::{Client, Response, StatusCode};
47-
use secrecy::{ExposeSecret as _, Secret};
4847
use tokio::sync::Mutex;
4948
use tokio::time::{sleep, Duration};
5049
use tracing::debug;
5150

5251
pub use super::routing::RatelimitingBucket;
53-
use super::{HttpError, LightMethod, Request, Token};
52+
use super::{HttpError, LightMethod, Request};
5453
use crate::internal::prelude::*;
5554

5655
/// Passed to the [`Ratelimiter::set_ratelimit_callback`] callback. If using Client, that callback
@@ -86,7 +85,7 @@ pub struct Ratelimiter {
8685
client: Client,
8786
global: Mutex<()>,
8887
routes: DashMap<RatelimitingBucket, Ratelimit>,
89-
token: Secret<Token>,
88+
token: SecretString,
9089
absolute_ratelimits: bool,
9190
ratelimit_callback: parking_lot::RwLock<Box<dyn Fn(RatelimitInfo) + Send + Sync>>,
9291
}
@@ -112,7 +111,7 @@ impl Ratelimiter {
112111
pub fn new(client: Client, token: Arc<str>) -> Self {
113112
Self {
114113
client,
115-
token: Token::new(token),
114+
token: SecretString::new(token),
116115
global: Mutex::default(),
117116
routes: DashMap::new(),
118117
absolute_ratelimits: false,

src/internal/prelude.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@ pub use super::utils::join_to_string;
1313
#[cfg(feature = "http")]
1414
pub use crate::error::Error;
1515
pub use crate::error::Result;
16+
pub use crate::secret_string::SecretString;
1617

1718
pub type JsonMap = serde_json::Map<String, Value>;

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub mod gateway;
101101
pub mod http;
102102
#[cfg(feature = "interactions_endpoint")]
103103
pub mod interactions_endpoint;
104+
pub mod secret_string;
104105
#[cfg(feature = "utils")]
105106
pub mod utils;
106107

src/model/utils.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -203,25 +203,6 @@ pub mod single_recipient {
203203
}
204204
}
205205

206-
pub mod secret {
207-
use secrecy::{ExposeSecret, Secret, Zeroize};
208-
use serde::{Deserialize, Deserializer, Serialize, Serializer};
209-
210-
pub fn deserialize<'de, S: Deserialize<'de> + Zeroize, D: Deserializer<'de>>(
211-
deserializer: D,
212-
) -> Result<Option<Secret<S>>, D::Error> {
213-
Option::<S>::deserialize(deserializer).map(|s| s.map(Secret::new))
214-
}
215-
216-
#[allow(clippy::ref_option)]
217-
pub fn serialize<S: Serialize + Zeroize, Sr: Serializer>(
218-
secret: &Option<Secret<S>>,
219-
serializer: Sr,
220-
) -> Result<Sr::Ok, Sr::Error> {
221-
secret.as_ref().map(ExposeSecret::expose_secret).serialize(serializer)
222-
}
223-
}
224-
225206
pub fn discord_colours_opt<'de, D>(deserializer: D) -> Result<Option<Vec<Colour>>, D::Error>
226207
where
227208
D: Deserializer<'de>,

src/model/webhook.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
//! Webhook model and implementations.
22
3-
#[cfg(feature = "model")]
4-
use secrecy::ExposeSecret;
5-
use secrecy::SecretString;
6-
7-
use super::utils::secret;
83
#[cfg(feature = "model")]
94
use crate::builder::{EditWebhook, EditWebhookMessage, ExecuteWebhook};
105
#[cfg(feature = "cache")]
@@ -76,7 +71,6 @@ pub struct Webhook {
7671
/// This can be temporarily overridden via [`ExecuteWebhook::avatar_url`].
7772
pub avatar: Option<ImageHash>,
7873
/// The webhook's secure token.
79-
#[serde(with = "secret", default)]
8074
pub token: Option<SecretString>,
8175
/// The bot/OAuth2 application that created this webhook.
8276
pub application_id: Option<ApplicationId>,
@@ -87,7 +81,6 @@ pub struct Webhook {
8781
/// [`WebhookType::ChannelFollower`]).
8882
pub source_channel: Option<WebhookChannel>,
8983
/// The url used for executing the webhook (returned by the webhooks OAuth2 flow).
90-
#[serde(with = "secret", default)]
9184
pub url: Option<SecretString>,
9285
}
9386

@@ -314,7 +307,7 @@ impl Webhook {
314307
///
315308
/// Or may return an [`Error::Json`] if there is an error in deserialising Discord's response.
316309
pub async fn edit(&mut self, http: &Http, builder: EditWebhook<'_>) -> Result<()> {
317-
let token = self.token.as_ref().map(ExposeSecret::expose_secret).map(String::as_str);
310+
let token = self.token.as_ref().map(SecretString::expose_secret);
318311
*self = builder.execute(http, self.id, token).await?;
319312
Ok(())
320313
}

src/secret_string.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use std::sync::Arc;
2+
3+
/// A cheaply clonable, zeroed on drop, String.
4+
///
5+
/// This is a simple newtype of `Arc<str>` that uses [`zeroize::Zeroize`] on last drop to avoid
6+
/// keeping it around in memory.
7+
#[derive(Clone, serde::Deserialize, serde::Serialize)]
8+
pub struct SecretString(Arc<str>);
9+
10+
impl SecretString {
11+
#[must_use]
12+
pub fn new(inner: Arc<str>) -> Self {
13+
Self(inner)
14+
}
15+
16+
#[must_use]
17+
pub fn expose_secret(&self) -> &str {
18+
&self.0
19+
}
20+
}
21+
22+
impl std::fmt::Debug for SecretString {
23+
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24+
fmt.debug_tuple(std::any::type_name::<Self>()).field(&"<secret>").finish()
25+
}
26+
}
27+
28+
impl zeroize::Zeroize for SecretString {
29+
fn zeroize(&mut self) {
30+
if let Some(string) = Arc::get_mut(&mut self.0) {
31+
string.zeroize();
32+
}
33+
}
34+
}
35+
36+
#[cfg(feature = "typesize")]
37+
impl typesize::TypeSize for SecretString {
38+
fn extra_size(&self) -> usize {
39+
self.0.len() + (size_of::<usize>() * 2)
40+
}
41+
}

0 commit comments

Comments
 (0)