Harden Android accessibility, localization and startup (#105)
Android ARM verification / rust (push) Failing after 46m41s
Android ARM verification / android (push) Failing after 8s
Dependency security audit / rustsec (push) Successful in 22s

Implement independent hardening and regression coverage. Keep #105 open for final feature and environment coverage after #106–#110; document the completion boundary and resume order.
This commit is contained in:
hugo committed 2026-10-11 15:56:33 +02:00
1 parent 43f660ace3
commit 6669b77f4e
55 files changed
+24445 -296

No files matched your search

+18 -3
View File
@@ -127,12 +127,12 @@ impl MobilePasswordPage {
) -> Result<Self, MobilePasswordError> {
let query = query.trim();
let folded = query.to_lowercase();
let snapshot = repository
.snapshot()
.map_err(MobilePasswordError::repository)?;
let rows = if folded.is_empty() {
Vec::new()
} else {
let snapshot = repository
.snapshot()
.map_err(MobilePasswordError::repository)?;
snapshot
.entries()
.filter(|entry| !hidden_path(entry.path().as_path()))
@@ -312,6 +312,21 @@ mod tests {
use super::{MobilePasswordErrorKind, MobilePasswordPage, MobilePasswordRowKind};
use crate::repository::Repository;
#[cfg(unix)]
#[test]
fn blank_search_does_not_walk_the_repository() -> Result<(), Box<dyn std::error::Error>> {
let temporary = tempdir()?;
let repository = Repository::open(temporary.path())?;
std::os::unix::fs::symlink("missing", temporary.path().join("unsafe.gpg"))?;
assert!(MobilePasswordPage::search_repository(&repository, "unsafe").is_err());
assert!(
MobilePasswordPage::search_repository(&repository, " ")?
.rows()
.is_empty()
);
Ok(())
}
#[test]
fn locked_pages_preserve_storage_identity_across_nested_refreshes()
-> Result<(), Box<dyn std::error::Error>> {
+22 -5
View File
@@ -286,11 +286,10 @@ impl QrMatrix {
.decode()
.map_err(|_| QrError::InvalidImage)?
.into_luma8();
Self::decode_luminance(
grayscale.width() as usize,
grayscale.height() as usize,
|x, y| grayscale.get_pixel(x as u32, y as u32).0[0],
)
let width = grayscale.width() as usize;
let height = grayscale.height() as usize;
let pixels = zeroize::Zeroizing::new(grayscale.into_raw());
Self::decode_luminance(width, height, |x, y| pixels[y * width + x])
}
pub(crate) fn decode_luminance(
@@ -298,6 +297,9 @@ impl QrMatrix {
height: usize,
pixel: impl Fn(usize, usize) -> u8,
) -> Result<SecretBytes, QrError> {
if width == 0 || height == 0 || width > 4096 || height > 4096 {
return Err(QrError::InvalidImage);
}
let mut prepared = rqrr::PreparedImage::prepare_from_greyscale(width, height, pixel);
let grid = prepared
.detect_grids()
@@ -374,3 +376,18 @@ impl Drop for QrMatrix {
self.modules.zeroize();
}
}
#[cfg(test)]
mod qr_dimensions_tests {
use super::{QrError, QrMatrix};
#[test]
fn invalid_dimensions_are_rejected_before_pixel_access_or_allocation() {
for (width, height) in [(0, 1), (1, 0), (4097, 1), (1, 4097), (usize::MAX, 2)] {
assert!(matches!(
QrMatrix::decode_luminance(width, height, |_, _| panic!("untrusted pixels read")),
Err(QrError::InvalidImage)
));
}
}
}