Skip to content

Commit dd6b8a3

Browse files
factorydroidechobt
authored andcommitted
fix(auth): trim whitespace from auth tokens
Fixes bounty issue #1630 Auth tokens are now trimmed of leading/trailing whitespace in: - CORTEX_AUTH_TOKEN environment variable reading in cortex-engine - API key storage via SecureAuthData::with_api_key() in cortex-login - OAuth token storage via SecureAuthData::with_oauth() in cortex-login - login_with_api_key() function in cortex-login This prevents authentication failures when tokens accidentally include extra whitespace (common when copying/pasting from web interfaces or when environment variables are set with trailing newlines).
1 parent 904f6c0 commit dd6b8a3

2 files changed

Lines changed: 66 additions & 6 deletions

File tree

cortex-engine/src/auth_token.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,13 @@ pub fn get_auth_token(instance_token: Option<&str>) -> Result<String> {
5050

5151
// Priority 2: CORTEX_AUTH_TOKEN environment variable
5252
if let Ok(token) = std::env::var("CORTEX_AUTH_TOKEN") {
53+
let token = token.trim();
5354
if !token.is_empty() {
5455
tracing::debug!(
5556
source = "env_var",
5657
"Using auth token from CORTEX_AUTH_TOKEN"
5758
);
58-
return Ok(token);
59+
return Ok(token.to_string());
5960
}
6061
}
6162

@@ -90,7 +91,7 @@ pub fn is_authenticated(instance_token: Option<&str>) -> bool {
9091
}
9192

9293
// Check env var
93-
if std::env::var("CORTEX_AUTH_TOKEN").map_or(false, |t| !t.is_empty()) {
94+
if std::env::var("CORTEX_AUTH_TOKEN").map_or(false, |t| !t.trim().is_empty()) {
9495
return true;
9596
}
9697

@@ -135,4 +136,33 @@ mod tests {
135136
let header = auth_header(Some("my-token"));
136137
assert_eq!(header, Some("Bearer my-token".to_string()));
137138
}
139+
140+
#[test]
141+
fn test_env_var_whitespace_trimmed() {
142+
// Set env var with whitespace
143+
std::env::set_var("CORTEX_AUTH_TOKEN", " test-token ");
144+
145+
// Call get_auth_token with no instance token
146+
let result = get_auth_token(None);
147+
assert!(result.is_ok());
148+
assert_eq!(result.unwrap(), "test-token");
149+
150+
// Clean up
151+
std::env::remove_var("CORTEX_AUTH_TOKEN");
152+
}
153+
154+
#[test]
155+
fn test_env_var_only_whitespace_is_empty() {
156+
// Set env var with only whitespace
157+
std::env::set_var("CORTEX_AUTH_TOKEN", " ");
158+
159+
// Call is_authenticated - should return false since trimmed is empty
160+
let is_auth = is_authenticated(None);
161+
162+
// Clean up before assertion
163+
std::env::remove_var("CORTEX_AUTH_TOKEN");
164+
165+
// The env var with only whitespace should be treated as empty
166+
assert!(!is_auth);
167+
}
138168
}

cortex-login/src/lib.rs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,10 +112,11 @@ pub struct SecureAuthData {
112112

113113
impl SecureAuthData {
114114
/// Create new secure auth data with API key.
115+
/// Whitespace is automatically trimmed from the API key.
115116
pub fn with_api_key(api_key: String) -> Self {
116117
Self {
117118
mode: AuthMode::ApiKey,
118-
api_key: Some(SecretString::from(api_key)),
119+
api_key: Some(SecretString::from(api_key.trim().to_string())),
119120
access_token: None,
120121
refresh_token: None,
121122
expires_at: None,
@@ -124,6 +125,7 @@ impl SecureAuthData {
124125
}
125126

126127
/// Create new secure auth data with OAuth tokens.
128+
/// Whitespace is automatically trimmed from tokens.
127129
pub fn with_oauth(
128130
access_token: String,
129131
refresh_token: Option<String>,
@@ -132,8 +134,8 @@ impl SecureAuthData {
132134
Self {
133135
mode: AuthMode::OAuth,
134136
api_key: None,
135-
access_token: Some(SecretString::from(access_token)),
136-
refresh_token: refresh_token.map(SecretString::from),
137+
access_token: Some(SecretString::from(access_token.trim().to_string())),
138+
refresh_token: refresh_token.map(|t| SecretString::from(t.trim().to_string())),
137139
expires_at,
138140
account_id: None,
139141
}
@@ -269,12 +271,13 @@ pub fn delete_auth(cortex_home: &Path, mode: CredentialsStoreMode) -> Result<boo
269271
}
270272

271273
/// Login with API key (stores securely).
274+
/// Whitespace is automatically trimmed from the API key.
272275
pub fn login_with_api_key(
273276
cortex_home: &Path,
274277
api_key: &str,
275278
mode: CredentialsStoreMode,
276279
) -> Result<()> {
277-
let data = SecureAuthData::with_api_key(api_key.to_string());
280+
let data = SecureAuthData::with_api_key(api_key.trim().to_string());
278281
save_auth(cortex_home, &data, mode)?;
279282
Ok(())
280283
}
@@ -768,6 +771,21 @@ mod tests {
768771
assert_eq!(data.get_token(), Some("test-key"));
769772
}
770773

774+
#[test]
775+
fn test_secure_auth_data_api_key_trims_whitespace() {
776+
// Test leading and trailing whitespace
777+
let data = SecureAuthData::with_api_key(" test-key ".to_string());
778+
assert_eq!(data.get_token(), Some("test-key"));
779+
780+
// Test with newlines (common when pasting from clipboard)
781+
let data = SecureAuthData::with_api_key("test-key\n".to_string());
782+
assert_eq!(data.get_token(), Some("test-key"));
783+
784+
// Test with tabs
785+
let data = SecureAuthData::with_api_key("\ttest-key\t".to_string());
786+
assert_eq!(data.get_token(), Some("test-key"));
787+
}
788+
771789
#[test]
772790
fn test_secure_auth_data_oauth() {
773791
let data =
@@ -777,6 +795,18 @@ mod tests {
777795
assert_eq!(data.get_refresh_token(), Some("refresh"));
778796
}
779797

798+
#[test]
799+
fn test_secure_auth_data_oauth_trims_whitespace() {
800+
// Test leading and trailing whitespace on OAuth tokens
801+
let data = SecureAuthData::with_oauth(
802+
" access ".to_string(),
803+
Some(" refresh ".to_string()),
804+
None,
805+
);
806+
assert_eq!(data.get_token(), Some("access"));
807+
assert_eq!(data.get_refresh_token(), Some("refresh"));
808+
}
809+
780810
#[test]
781811
fn test_auth_data_metadata() {
782812
let data = SecureAuthData::with_api_key("test".to_string());

0 commit comments

Comments
 (0)