mirror of
https://tangled.org/tranquil.farm/tranquil-pds
synced 2026-09-29 05:25:34 +00:00
fix: Address review comments
This commit is contained in:
@@ -7,7 +7,7 @@ const COMPRESSED_PREFIX: &str = "$br$";
|
||||
const QUALITY: u32 = 9;
|
||||
const WINDOW_BITS: u32 = 16;
|
||||
const BUFFER_SIZE: usize = 4096;
|
||||
const MAX_DECOMPRESSED_LEN: u64 = 64 * 1024;
|
||||
const MAX_SCOPE_LEN: u64 = 64 * 1024;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ScopeDecodeError {
|
||||
@@ -28,6 +28,21 @@ impl fmt::Display for ScopeDecodeError {
|
||||
|
||||
impl std::error::Error for ScopeDecodeError {}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ScopeEncodeError {
|
||||
TooLarge,
|
||||
}
|
||||
|
||||
impl fmt::Display for ScopeEncodeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::TooLarge => write!(f, "Scope exceeds maximum length"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ScopeEncodeError {}
|
||||
|
||||
fn brotli_compress(input: &str) -> Vec<u8> {
|
||||
let mut writer = CompressorWriter::new(Vec::new(), BUFFER_SIZE, QUALITY, WINDOW_BITS);
|
||||
|
||||
@@ -42,24 +57,28 @@ fn brotli_decompress(input: &[u8]) -> Result<String, ScopeDecodeError> {
|
||||
let mut output = String::new();
|
||||
|
||||
Decompressor::new(input, BUFFER_SIZE)
|
||||
.take(MAX_DECOMPRESSED_LEN + 1)
|
||||
.take(MAX_SCOPE_LEN + 1)
|
||||
.read_to_string(&mut output)
|
||||
.map_err(|_| ScopeDecodeError::DecompressFailed)?;
|
||||
|
||||
if output.len() as u64 > MAX_DECOMPRESSED_LEN {
|
||||
if output.len() as u64 > MAX_SCOPE_LEN {
|
||||
return Err(ScopeDecodeError::TooLarge);
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn encode_scope(scope: &str) -> String {
|
||||
pub fn encode_scope(scope: &str) -> Result<String, ScopeEncodeError> {
|
||||
if scope.len() as u64 > MAX_SCOPE_LEN {
|
||||
return Err(ScopeEncodeError::TooLarge);
|
||||
}
|
||||
|
||||
let encoded = URL_SAFE_NO_PAD.encode(brotli_compress(scope));
|
||||
|
||||
if COMPRESSED_PREFIX.len() + encoded.len() < scope.len() {
|
||||
format!("{COMPRESSED_PREFIX}{encoded}")
|
||||
Ok(format!("{COMPRESSED_PREFIX}{encoded}"))
|
||||
} else {
|
||||
scope.to_owned()
|
||||
Ok(scope.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +115,7 @@ mod tests {
|
||||
#[test]
|
||||
fn long_scope_roundtrips_through_compression() {
|
||||
let scope = long_scope();
|
||||
let encoded = encode_scope(&scope);
|
||||
let encoded = encode_scope(&scope).unwrap();
|
||||
|
||||
assert!(encoded.starts_with(COMPRESSED_PREFIX));
|
||||
assert!(encoded.len() < scope.len());
|
||||
@@ -105,7 +124,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn short_scope_stays_plaintext() {
|
||||
let encoded = encode_scope("com.atproto.access");
|
||||
let encoded = encode_scope("com.atproto.access").unwrap();
|
||||
|
||||
assert_eq!(encoded, "com.atproto.access");
|
||||
assert_eq!(decode_scope(&encoded).unwrap(), "com.atproto.access");
|
||||
@@ -134,12 +153,19 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn compression_bomb_is_rejected() {
|
||||
let bomb = encode_scope(&"a".repeat(MAX_DECOMPRESSED_LEN as usize * 2));
|
||||
let encoded = bomb.strip_prefix(COMPRESSED_PREFIX).unwrap_or(&bomb);
|
||||
let bomb = URL_SAFE_NO_PAD.encode(brotli_compress(&"a".repeat(MAX_SCOPE_LEN as usize * 2)));
|
||||
|
||||
assert_eq!(
|
||||
decode_scope(&format!("{COMPRESSED_PREFIX}{encoded}")),
|
||||
decode_scope(&format!("{COMPRESSED_PREFIX}{bomb}")),
|
||||
Err(ScopeDecodeError::TooLarge)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn encode_rejects_oversized_scope() {
|
||||
let oversized = "a".repeat(MAX_SCOPE_LEN as usize + 1);
|
||||
|
||||
assert_eq!(encode_scope(&oversized), Err(ScopeEncodeError::TooLarge));
|
||||
assert!(encode_scope(&"a".repeat(MAX_SCOPE_LEN as usize)).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ pub use token::{
|
||||
create_service_token_hs256,
|
||||
};
|
||||
|
||||
pub use compress::{ScopeDecodeError, decode_scope, encode_scope};
|
||||
pub use compress::{ScopeDecodeError, ScopeEncodeError, decode_scope, encode_scope};
|
||||
|
||||
pub use totp::{
|
||||
TotpError, decrypt_totp_secret, encrypt_totp_secret, generate_backup_codes,
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::compress::encode_scope;
|
||||
use super::types::{
|
||||
ActClaim, Claims, Header, SigningAlgorithm, TokenScope, TokenType, TokenWithMetadata,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use anyhow::{Context, Result};
|
||||
use base64::Engine as _;
|
||||
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
@@ -207,7 +207,7 @@ fn create_signed_token_pinned(
|
||||
aud: format!("did:web:{}", aud_hostname),
|
||||
exp: expiration,
|
||||
iat: Utc::now().timestamp(),
|
||||
scope: Some(encode_scope(scope)),
|
||||
scope: Some(encode_scope(scope).context("Scope too large to encode")?),
|
||||
lxm: None,
|
||||
jti: jti.clone(),
|
||||
act,
|
||||
@@ -330,7 +330,7 @@ fn create_hs256_token_with_metadata(
|
||||
),
|
||||
exp: expiration,
|
||||
iat: Utc::now().timestamp(),
|
||||
scope: Some(encode_scope(scope)),
|
||||
scope: Some(encode_scope(scope).context("Scope too large to encode")?),
|
||||
lxm: None,
|
||||
jti: jti.clone(),
|
||||
act: None,
|
||||
|
||||
Reference in New Issue
Block a user