Harden Android accessibility, localization and startup (#105)
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:
1 parent
43f660ace3
commit
6669b77f4e
55 files changed
+24445
-296
No files matched your search
@@ -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>> {
|
||||
|
||||
@@ -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)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user