diff --git a/SECURITY_DEFER.md b/SECURITY_DEFER.md index 3c47259..46af98f 100644 --- a/SECURITY_DEFER.md +++ b/SECURITY_DEFER.md @@ -5,8 +5,8 @@ Description: The current Argon2id memory cost is set to 64MB (m_cost=65536). Sec Why not fixed: no-touch zone Suggested fix: Update Argon2id Params in derive_key and derive_key_v3 to use m_cost=262144. First detected: 2026-05-09 -Last attempted: 2026-05-23 -Attempt count: 2 +Last attempted: 2026-06-06 +Attempt count: 3 --- [UNEXPECTED_PANICS] - Unhandled unwrap() calls in crypto-adjacent paths Severity: Medium @@ -15,8 +15,8 @@ Description: Multiple uses of .unwrap() or .unwrap_or_else() on locks/IO results Why not fixed: no-touch zone Suggested fix: Replace .unwrap() with proper error handling using CryptoError or .expect() with descriptive panic messages. First detected: 2026-05-09 -Last attempted: 2026-05-23 -Attempt count: 2 +Last attempted: 2026-06-06 +Attempt count: 3 --- [RUSTSEC-2024-0436] - Unmaintained dependency: paste Severity: Medium @@ -25,8 +25,8 @@ Description: The `paste` crate is no longer maintained. While it's a macro-utili Why not fixed: no-touch zone (Cargo.lock modification restricted) Suggested fix: Replace `paste` with `pastey` or `with_builtin_macros`. First detected: 2026-05-09 -Last attempted: 2026-05-23 -Attempt count: 2 +Last attempted: 2026-06-06 +Attempt count: 3 --- [TMP_PATH_FALLBACK] - Unsafe fallback in tmp_path for paths without filenames Severity: Medium @@ -35,6 +35,6 @@ Description: In `tmp_path`, if the destination path lacks a filename (e.g., root Why not fixed: no-touch zone Suggested fix: Provide a safe fallback filename like "output" or "file" when `file_name()` is None. First detected: 2026-05-23 -Last attempted: 2026-05-23 -Attempt count: 1 +Last attempted: 2026-06-06 +Attempt count: 2 --- diff --git a/android/app/src/main/kotlin/com/neuronencrypt/app/ui/BatchScreen.kt b/android/app/src/main/kotlin/com/neuronencrypt/app/ui/BatchScreen.kt index 9abc836..cad8adf 100644 --- a/android/app/src/main/kotlin/com/neuronencrypt/app/ui/BatchScreen.kt +++ b/android/app/src/main/kotlin/com/neuronencrypt/app/ui/BatchScreen.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* +import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/android/app/src/main/kotlin/com/neuronencrypt/app/ui/HomeScreen.kt b/android/app/src/main/kotlin/com/neuronencrypt/app/ui/HomeScreen.kt index a670a6a..fa8f939 100644 --- a/android/app/src/main/kotlin/com/neuronencrypt/app/ui/HomeScreen.kt +++ b/android/app/src/main/kotlin/com/neuronencrypt/app/ui/HomeScreen.kt @@ -16,7 +16,8 @@ import com.neuronencrypt.app.ui.theme.* @Composable fun HomeScreen( onEncryptDecrypt: () -> Unit, - onBatch: () -> Unit + onBatch: () -> Unit, + onAbout: () -> Unit ) { var showAbout by remember { mutableStateOf(false) } @@ -104,7 +105,10 @@ fun HomeScreen( Spacer(modifier = Modifier.height(32.dp)) - TextButton(onClick = { showAbout = true }) { + TextButton(onClick = { + onAbout() + showAbout = true + }) { Text( text = "About", style = MaterialTheme.typography.bodySmall, diff --git a/android/app/src/main/kotlin/com/neuronencrypt/app/ui/SingleFileScreen.kt b/android/app/src/main/kotlin/com/neuronencrypt/app/ui/SingleFileScreen.kt index f640eaa..862cd15 100644 --- a/android/app/src/main/kotlin/com/neuronencrypt/app/ui/SingleFileScreen.kt +++ b/android/app/src/main/kotlin/com/neuronencrypt/app/ui/SingleFileScreen.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* +import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier diff --git a/android/neuron-encrypt-jni/src/lib.rs b/android/neuron-encrypt-jni/src/lib.rs index 95a4695..00dca0b 100644 --- a/android/neuron-encrypt-jni/src/lib.rs +++ b/android/neuron-encrypt-jni/src/lib.rs @@ -2,6 +2,7 @@ use std::fs::File; use std::io::{self, Read, Seek}; use std::os::fd::{FromRawFd, RawFd}; use std::panic::{self, AssertUnwindSafe}; +use std::sync::Arc; use jni::JNIEnv; use jni::objects::{GlobalRef, JByteArray, JClass, JObject}; @@ -20,7 +21,7 @@ const CANCELLED_IO_MESSAGE: &str = "operation cancelled"; struct CancelableReader { inner: File, cancel: GlobalRef, - vm: JavaVM, + vm: Arc, } impl CancelableReader { @@ -62,7 +63,7 @@ impl Seek for CancelableReader { struct JniReporter { listener: GlobalRef, - vm: JavaVM, + vm: Arc, } impl ProgressReporter for JniReporter { @@ -101,9 +102,14 @@ fn map_error(env: &mut JNIEnv, err: CryptoError) -> jint { | CryptoError::InvalidDestination(_) | CryptoError::SourceAndDestinationSame(_) | CryptoError::NotAFile(_) + | CryptoError::InvalidSaltLength { .. } + | CryptoError::InvalidNonceLength { .. } | CryptoError::FileTooLarge { .. } => (2, "Invalid input or output file.".to_owned()), CryptoError::Io(_) => (1, "Storage I/O failed.".to_owned()), + CryptoError::LegacyFileTooLarge => { + (1, "Legacy file too large for this operation.".to_owned()) + } CryptoError::Argon2Failed(_) | CryptoError::HkdfFailed(_) | CryptoError::EncryptionFailed(_) => (1, "Cryptographic operation failed.".to_owned()), @@ -174,15 +180,16 @@ fn native_encrypt_impl( let input_file = unsafe { File::from_raw_fd(input_fd as RawFd) }; let mut output_file = unsafe { File::from_raw_fd(output_fd as RawFd) }; + let vm = Arc::new(vm); let mut reader = CancelableReader { inner: input_file, cancel: cancel_ref, - vm: vm.clone(), + vm: Arc::clone(&vm), }; let reporter = JniReporter { listener: listener_ref, - vm, + vm: Arc::clone(&vm), }; let throttled = ThrottledReporter::new(&reporter); @@ -249,15 +256,16 @@ fn native_decrypt_impl( let input_file = unsafe { File::from_raw_fd(input_fd as RawFd) }; let mut output_file = unsafe { File::from_raw_fd(output_fd as RawFd) }; + let vm = Arc::new(vm); let mut reader = CancelableReader { inner: input_file, cancel: cancel_ref, - vm: vm.clone(), + vm: Arc::clone(&vm), }; let reporter = JniReporter { listener: listener_ref, - vm, + vm: Arc::clone(&vm), }; let throttled = ThrottledReporter::new(&reporter); diff --git a/audit_output.json b/audit_output.json index 262bcc9..08e11e9 100644 --- a/audit_output.json +++ b/audit_output.json @@ -1 +1 @@ -{"database":{"advisory-count":1098,"last-commit":"130152155e1184c441b0e6924d21d7cbfb23f297","last-updated":"2026-05-22T12:09:18+02:00"},"lockfile":{"dependency-count":537},"settings":{"target_arch":[],"target_os":[],"severity":null,"ignore":[],"informational_warnings":["unmaintained","unsound","notice"]},"vulnerabilities":{"found":false,"count":0,"list":[]},"warnings":{"unmaintained":[{"kind":"unmaintained","package":{"name":"paste","version":"1.0.15","source":"registry+https://github.com/rust-lang/crates.io-index","checksum":"57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a","replace":null},"advisory":{"id":"RUSTSEC-2024-0436","package":"paste","title":"paste - no longer maintained","description":"The creator of the crate `paste` has stated in the [`README.md`](https://github.com/dtolnay/paste/blob/master/README.md) \nthat this project is not longer maintained as well as archived the repository\n\n## Possible Alternative(s)\n\n- [`pastey`]: a fork of paste and is aimed to be a drop-in replacement with additional features for paste crate\n- [`with_builtin_macros`]: crate providing a [superset of `paste`'s functionality including general `macro_rules!` eager expansions](https://docs.rs/with_builtin_macros/0.1.0/with_builtin_macros/macro.with_eager_expansions.html) and `concat!`/`concat_idents!` macros\n\n[`pastey`]: https://crates.io/crates/pastey\n[`with_builtin_macros`]: https://crates.io/crates/with_builtin_macros","date":"2024-10-07","aliases":[],"related":[],"collection":"crates","categories":[],"keywords":[],"cvss":null,"informational":"unmaintained","references":[],"source":null,"url":"https://github.com/dtolnay/paste","withdrawn":null,"license":"CC0-1.0","expect-deleted":false},"affected":null,"versions":{"patched":[],"unaffected":[]}}]}} +{"database":{"advisory-count":1121,"last-commit":"8fe151aab7c88bcc98f809982cc84ee7b0ba39a6","last-updated":"2026-06-06T16:44:53+02:00"},"lockfile":{"dependency-count":531},"settings":{"target_arch":[],"target_os":[],"severity":null,"ignore":[],"informational_warnings":["unmaintained","unsound","notice"]},"vulnerabilities":{"found":false,"count":0,"list":[]},"warnings":{"unmaintained":[{"kind":"unmaintained","package":{"name":"paste","version":"1.0.15","source":"registry+https://github.com/rust-lang/crates.io-index","checksum":"57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a","replace":null},"advisory":{"id":"RUSTSEC-2024-0436","package":"paste","title":"paste - no longer maintained","description":"The creator of the crate `paste` has stated in the [`README.md`](https://github.com/dtolnay/paste/blob/master/README.md) \nthat this project is not longer maintained as well as archived the repository\n\n## Possible Alternative(s)\n\n- [`pastey`]: a fork of paste and is aimed to be a drop-in replacement with additional features for paste crate\n- [`with_builtin_macros`]: crate providing a [superset of `paste`'s functionality including general `macro_rules!` eager expansions](https://docs.rs/with_builtin_macros/0.1.0/with_builtin_macros/macro.with_eager_expansions.html) and `concat!`/`concat_idents!` macros\n\n[`pastey`]: https://crates.io/crates/pastey\n[`with_builtin_macros`]: https://crates.io/crates/with_builtin_macros","date":"2024-10-07","aliases":[],"related":[],"collection":"crates","categories":[],"keywords":[],"cvss":null,"informational":"unmaintained","references":[],"source":null,"url":"https://github.com/dtolnay/paste","withdrawn":null,"license":"CC0-1.0","expect-deleted":false},"affected":null,"versions":{"patched":[],"unaffected":[]}}]}} diff --git a/neuron-encrypt/src/bin/cli.rs b/neuron-encrypt/src/bin/cli.rs index 760eb6a..46a170c 100644 --- a/neuron-encrypt/src/bin/cli.rs +++ b/neuron-encrypt/src/bin/cli.rs @@ -107,11 +107,17 @@ enum Command { fn read_password(password_file: &Option) -> Zeroizing { if let Some(path) = password_file { - let pw = fs::read_to_string(path).unwrap_or_else(|e| { + let bytes = fs::read(path).unwrap_or_else(|e| { eprintln!("Error: Cannot read password file: {e}"); std::process::exit(ExitCode::BadInput as i32); }); - return Zeroizing::new(pw.trim_end_matches(&['\r', '\n'][..]).to_owned()); + let mut pw = Zeroizing::new(String::from_utf8(bytes).unwrap_or_else(|e| { + eprintln!("Error: Password file contains invalid UTF-8: {e}"); + std::process::exit(ExitCode::BadInput as i32); + })); + let trimmed_len = pw.trim_end_matches(&['\r', '\n'][..]).len(); + pw.truncate(trimmed_len); + return pw; } if let Ok(pw) = std::env::var("NEURON_PASSWORD") { @@ -397,9 +403,9 @@ fn emit_json(result: &JsonResult) { fn compute_sha256(path: &Path) -> Result { let mut file = fs::File::open(path).map_err(|e| e.to_string())?; let mut hasher = Sha256::new(); - let mut buf = [0u8; 65536]; + let mut buf = Zeroizing::new([0u8; 65536]); loop { - let n = file.read(&mut buf).map_err(|e| e.to_string())?; + let n = file.read(&mut *buf).map_err(|e| e.to_string())?; if n == 0 { break; } diff --git a/neuron-encrypt/src/gui.rs b/neuron-encrypt/src/gui.rs index 85a3457..ef5e781 100644 --- a/neuron-encrypt/src/gui.rs +++ b/neuron-encrypt/src/gui.rs @@ -21,9 +21,9 @@ use zeroize::Zeroizing; fn compute_sha256(path: &Path) -> Option { let mut file = fs::File::open(path).ok()?; let mut hasher = Sha256::new(); - let mut buf = [0u8; 65536]; + let mut buf = Zeroizing::new([0u8; 65536]); loop { - let n = file.read(&mut buf).ok()?; + let n = file.read(&mut *buf).ok()?; if n == 0 { break; }