**Feat: Verbessert Token-Handling und String-Sanitization**
- Leere/Whitespace-Tokens werden konsistent als `None` behandelt - Sanitization für Anführungszeichen (`"` und `'`) und Umgebungsvariablen - Getter-Methode für GitHub-Token hinzugefügt
This commit is contained in:
+23
-11
@@ -1,6 +1,6 @@
|
||||
use crate::utils::sanitize_string;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use crate::utils::sanitize_env_vars;
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
@@ -110,20 +110,32 @@ pub fn get_config_file_path() -> PathBuf {
|
||||
/// Lädt die Anwendungskonfiguration. Verwendet Standardwerte, falls keine Datei vorhanden ist.
|
||||
pub fn load_config() -> AppConfig {
|
||||
let mut config = config_ctdra::load_config::<AppConfig>();
|
||||
let mut env_vars = std::env::vars().collect();
|
||||
sanitize_env_vars(&mut env_vars);
|
||||
if let Some(gitea_url) = env_vars.get("GITEA_URL") {
|
||||
config.gitea_url = Some(gitea_url.clone());
|
||||
|
||||
if let Ok(val) = std::env::var("GITEA_URL") {
|
||||
let val = sanitize_string(&val);
|
||||
if !val.is_empty() {
|
||||
config.gitea_url = Some(val);
|
||||
}
|
||||
}
|
||||
if let Some(gitea_token) = env_vars.get("GITEA_TOKEN") {
|
||||
config.gitea_token = Some(gitea_token.clone());
|
||||
if let Ok(val) = std::env::var("GITEA_TOKEN") {
|
||||
let val = sanitize_string(&val);
|
||||
if !val.is_empty() {
|
||||
config.gitea_token = Some(val);
|
||||
}
|
||||
}
|
||||
if let Some(registry_owner) = env_vars.get("REGISTRY_OWNER") {
|
||||
config.registry_owner = Some(registry_owner.clone());
|
||||
if let Ok(val) = std::env::var("REGISTRY_OWNER") {
|
||||
let val = sanitize_string(&val);
|
||||
if !val.is_empty() {
|
||||
config.registry_owner = Some(val);
|
||||
}
|
||||
}
|
||||
if let Some(github_token) = env_vars.get("GITHUB_TOKEN") {
|
||||
config.github_token = Some(github_token.clone());
|
||||
if let Ok(val) = std::env::var("GITHUB_TOKEN") {
|
||||
let val = sanitize_string(&val);
|
||||
if !val.is_empty() {
|
||||
config.github_token = Some(val);
|
||||
}
|
||||
}
|
||||
|
||||
config
|
||||
}
|
||||
|
||||
|
||||
+9
-7
@@ -81,12 +81,13 @@ struct GhApiAsset {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GitHubClient {
|
||||
client: reqwest::Client,
|
||||
pub token: Option<String>,
|
||||
token: Option<String>,
|
||||
}
|
||||
|
||||
impl GitHubClient {
|
||||
/// Erstellt einen neuen GitHub-API-Client mit optionalem Authentifizierungstoken.
|
||||
pub fn new(token: Option<String>) -> Result<Self> {
|
||||
let token = token.filter(|t| !t.trim().is_empty());
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
headers.insert(
|
||||
reqwest::header::USER_AGENT,
|
||||
@@ -105,6 +106,11 @@ impl GitHubClient {
|
||||
Ok(Self { client, token })
|
||||
}
|
||||
|
||||
/// Gibt das optionale Authentifizierungstoken zurück (falls gesetzt).
|
||||
pub fn token(&self) -> Option<&str> {
|
||||
self.token.as_deref()
|
||||
}
|
||||
|
||||
|
||||
/// Ruft Releases für ein angegebenes Repository ab (Format "owner/repo").
|
||||
///
|
||||
@@ -130,9 +136,7 @@ impl GitHubClient {
|
||||
|
||||
let mut req = self.client.get(&url);
|
||||
if let Some(token) = &self.token {
|
||||
if !token.is_empty() {
|
||||
req = req.bearer_auth(token);
|
||||
}
|
||||
req = req.bearer_auth(token);
|
||||
}
|
||||
|
||||
let resp = req.send().await.with_context(|| format!("Failed to send request to {}", url))?;
|
||||
@@ -220,9 +224,7 @@ impl GitHubClient {
|
||||
pub async fn download_asset_stream(&self, download_url: &str) -> Result<reqwest::Response> {
|
||||
let mut req = self.client.get(download_url);
|
||||
if let Some(token) = &self.token {
|
||||
if !token.is_empty() {
|
||||
req = req.bearer_auth(token);
|
||||
}
|
||||
req = req.bearer_auth(token);
|
||||
}
|
||||
|
||||
let resp = req
|
||||
|
||||
+4
-4
@@ -10,10 +10,10 @@
|
||||
/// The sanitized string with surrounding quotes removed.
|
||||
pub fn sanitize_string(input: &str) -> String {
|
||||
let trimmed = input.trim();
|
||||
if trimmed.starts_with('"') && trimmed.ends_with('"') {
|
||||
trimmed[1..trimmed.len() - 1].to_string()
|
||||
} else if trimmed.starts_with('') && trimmed.ends_with('') {
|
||||
trimmed[1..trimmed.len() - 1].to_string()
|
||||
if let Some(stripped) = trimmed.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
|
||||
stripped.to_string()
|
||||
} else if let Some(stripped) = trimmed.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
|
||||
stripped.to_string()
|
||||
} else {
|
||||
trimmed.to_string()
|
||||
}
|
||||
|
||||
+17
-1
@@ -1,4 +1,4 @@
|
||||
use mirror_package::config::{load_config, AppConfig, RepoConfig};
|
||||
use mirror_package::config::{AppConfig, RepoConfig, load_config};
|
||||
use mirror_package::utils::{sanitize_env_vars, sanitize_string};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -41,12 +41,28 @@ fn test_load_config_with_env_vars() {
|
||||
#[test]
|
||||
fn test_sanitize_string() {
|
||||
assert_eq!(sanitize_string("\"https://gitea.example.com\""), "https://gitea.example.com");
|
||||
assert_eq!(sanitize_string("'https://gitea.example.com'"), "https://gitea.example.com");
|
||||
assert_eq!(sanitize_string("\"token123\""), "token123");
|
||||
assert_eq!(sanitize_string("'token123'"), "token123");
|
||||
assert_eq!(sanitize_string("\"owner\""), "owner");
|
||||
assert_eq!(sanitize_string("'owner'"), "owner");
|
||||
assert_eq!(sanitize_string("\"github_token123\""), "github_token123");
|
||||
assert_eq!(sanitize_string("'github_token123'"), "github_token123");
|
||||
assert_eq!(sanitize_string("no_quotes"), "no_quotes");
|
||||
assert_eq!(sanitize_string("\"single_quote\""), "single_quote");
|
||||
assert_eq!(sanitize_string("'single_quote'"), "single_quote");
|
||||
assert_eq!(sanitize_string("\"escaped_quote\""), "escaped_quote");
|
||||
// Edge cases: single character inputs
|
||||
assert_eq!(sanitize_string("\""), "\"");
|
||||
assert_eq!(sanitize_string("'"), "'");
|
||||
assert_eq!(sanitize_string("a"), "a");
|
||||
// Edge cases: empty quotes and empty strings
|
||||
assert_eq!(sanitize_string("\"\""), "");
|
||||
assert_eq!(sanitize_string("''"), "");
|
||||
assert_eq!(sanitize_string(""), "");
|
||||
assert_eq!(sanitize_string(" "), "");
|
||||
assert_eq!(sanitize_string(" 'hello' "), "hello");
|
||||
assert_eq!(sanitize_string(" \"world\" "), "world");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -73,17 +73,18 @@ fn test_parse_repo_owner_name() {
|
||||
async fn test_github_client_empty_token() {
|
||||
use mirror_package::github::GitHubClient;
|
||||
|
||||
// Test that a client with an empty token can be created
|
||||
// Test that a client with an empty or whitespace token sanitizes it to None
|
||||
let client = GitHubClient::new(Some("".to_string())).expect("Failed to create client with empty token");
|
||||
assert_eq!(client.token(), None);
|
||||
|
||||
// The client should be created successfully but has_auth() should return false
|
||||
assert!(client.token.is_none() || client.token.as_ref().map_or(true, |t| t.is_empty()));
|
||||
let client_whitespace = GitHubClient::new(Some(" ".to_string())).expect("Failed to create client with whitespace token");
|
||||
assert_eq!(client_whitespace.token(), None);
|
||||
|
||||
// Test with a valid token
|
||||
let client_with_token = GitHubClient::new(Some("valid_token".to_string())).expect("Failed to create client with valid token");
|
||||
assert!(client_with_token.token.as_ref().map_or(false, |t| !t.is_empty()));
|
||||
assert_eq!(client_with_token.token(), Some("valid_token"));
|
||||
|
||||
// Test with no token
|
||||
let client_no_token = GitHubClient::new(None).expect("Failed to create client with no token");
|
||||
assert!(client_no_token.token.is_none());
|
||||
assert_eq!(client_no_token.token(), None);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user