v0.0.2
RustBox CI / rustbox (push) Waiting to run

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-29 19:51:35 +01:00
1 parent 6c15491284
commit 31634543f0
6 files changed
+40 -6

No files matched your search

+1
View File
@@ -11,6 +11,7 @@
- Updated UI/build contract tests for the modular JavaScript build and expanded interface-style validation.
- Fixed the 0.0.2 release build regression by centralizing `AppConfig::default()` and updating stale `AccountSession` test fixtures for the new session metadata fields.
- Updated UI contract tests and visual fixtures after removal of the legacy `ui/` directory.
- Fixed release-test regressions in OpenPGP fingerprint validation, protected system-folder recognition, nested `base_path` UI routing, and missing SMTP endpoint handling.
### 0.0.1
+12 -1
View File
@@ -373,8 +373,19 @@ pub fn store_secret_key(
let dir = private_key_dir(data_dir, user_hash);
fs::create_dir_all(&dir).map_err(|e| format!("Failed to create PGP key directory: {e}"))?;
set_dir_permissions(&dir)?;
// Reject path/control punctuation before normalization. `normalize_fingerprint`
// intentionally strips formatting characters, so validating only its output
// would turn values such as "../../escape" into a short hexadecimal string.
if fingerprint.is_empty()
|| !fingerprint
.chars()
.all(|c| c.is_ascii_hexdigit() || c.is_ascii_whitespace() || c == ':')
{
return Err("Invalid OpenPGP fingerprint".to_string());
}
let safe = normalize_fingerprint(fingerprint);
if safe.is_empty() || !safe.chars().all(|c| c.is_ascii_hexdigit()) {
if safe.is_empty() {
return Err("Invalid OpenPGP fingerprint".to_string());
}
let final_path = dir.join(format!("{safe}.asc"));
+1 -1
View File
@@ -41,7 +41,7 @@ struct MessageResponse {
// ---------------------------------------------------------------------------
/// Folder names that must not be renamed or deleted.
const SYSTEM_FOLDERS: &[&str] = &["INBOX"];
const SYSTEM_FOLDERS: &[&str] = &["INBOX", "Sent", "Drafts", "Trash", "Junk", "Spam"];
/// Returns `true` if the folder is a system folder (case-insensitive).
fn is_system_folder(name: &str) -> bool {
+3
View File
@@ -494,6 +494,9 @@ pub fn create_router(
// RustBox serves API and UI from the same Rust process.
let inner = Router::new()
.nest("/api", api_router)
// An explicit index route is required for the root of a nested base path
// (for example `/mail/`). The fallback still handles SPA deep links.
.route("/", get(crate::ui::serve_ui))
.fallback(crate::ui::serve_ui);
// Use the same base path for routing and authentication cookies.
+7
View File
@@ -117,6 +117,13 @@ pub async fn send_message_handler(
// Build SMTP credentials from the authenticated account session.
// Server endpoints are resolved from RustBox configuration at login and stored in the session.
// Normal login falls back from an unset smtp_host to the IMAP host, but reject
// an empty endpoint defensively for stale/legacy sessions.
if session.smtp_host.trim().is_empty() {
return Err(AppError::ServiceUnavailable(
"SMTP server is not configured for this account".to_string(),
));
}
let smtp_creds = SmtpCredentials {
host: session.smtp_host.clone(),
port: session.smtp_port,
+16 -4
View File
@@ -2002,18 +2002,30 @@ async fn send_returns_400_with_empty_body_and_subject() {
}
#[tokio::test]
async fn send_returns_503_when_smtp_not_configured() {
async fn send_returns_503_when_session_has_no_smtp_endpoint() {
let static_dir = setup_static_dir();
let data_dir = TempDir::new().unwrap();
// Use config WITHOUT smtp_host
let config = test_config_with_imap(
static_dir.path().to_str().unwrap(),
data_dir.path().to_str().unwrap(),
);
let store = test_store();
let (browser_id, account_id, token) = setup_test_account(&store, "alice@example.com");
let browser_id = store.create_browser();
let email = "alice@example.com";
let (token, account_id) = store.add_account_to_browser(
&browser_id,
email.to_string(),
"password".to_string(),
crate::auth::user_data::hash_email(email),
"imap.example.com".to_string(),
993,
true,
String::new(),
587,
true,
);
let user_hash = crate::auth::user_data::hash_email("alice@example.com");
let user_hash = crate::auth::user_data::hash_email(email);
provision_user_db(data_dir.path().to_str().unwrap(), &user_hash);
let app = create_router(