fix: PNG truncado não vira arquivo gigante; filtros de tamanho e megapixels

Descoberto ao abrir uma sessão real: texto ASCII após um PNG truncado era lido
como tamanho de bloco (~1,9 GB). Também adiciona filtros para separar fotos de
ícones e falha cedo ao retomar sem a origem.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas RayanandClaude Sonnet 5.5 committed 2026-10-18 16:44:03 -03:00
1 parent 354caaf8f0
commit b9bef85bc7
6 files changed
+90 -7

No files matched your search

+9
View File
@@ -180,7 +180,14 @@ pub fn facets(found: &[Found]) -> Value {
let mut conf = [0usize; 3];
let mut seen_sig = std::collections::HashSet::new();
let mut dups = 0usize;
let (mut small, mut mp1) = (0usize, 0usize);
for f in found {
if f.size < 30 * 1024 {
small += 1;
}
if f.px.is_some_and(|(w, h)| w as u64 * h as u64 >= 1_000_000) {
mp1 += 1;
}
if let Some(g) = f.sig {
if !seen_sig.insert((g, f.size)) {
dups += 1;
@@ -242,6 +249,8 @@ pub fn facets(found: &[Found]) -> Value {
"root": root,
"gps": gps,
"duplicates": dups,
"small": small,
"mp1": mp1,
})
}
+3
View File
@@ -217,6 +217,9 @@ impl Engine {
pub fn resume(&self, id: &str) -> Result<()> {
let j = self.job(id)?;
if j.source().is_none() {
return Err(no_source());
}
let cp = {
let mut s = j.st.lock().unwrap();
if s.status != Status::Paused {
+13 -3
View File
@@ -219,8 +219,10 @@ fn jpeg(src: &dyn Source, off: u64, max: u64) -> Option<Hit> {
// ---------------------------------------------------------------- PNG
fn png(src: &dyn Source, off: u64, max: u64) -> Option<Hit> {
let total = src.size().saturating_sub(off);
let mut p = 8u64;
let mut first = true;
let mut saw_idat = false;
for _ in 0..200_000 {
let c = read_vec(src, off + p, 8).ok()?;
if c.len() < 8 {
@@ -228,13 +230,20 @@ fn png(src: &dyn Source, off: u64, max: u64) -> Option<Hit> {
}
let len = u32::from_be_bytes([c[0], c[1], c[2], c[3]]) as u64;
let ty = &c[4..8];
if !ty.iter().all(|b| b.is_ascii_alphabetic()) || len > 0x7FFF_FFFF {
// Bloco válido: 4 letras, 3ª maiúscula (bit reservado), tamanho plausível e dentro do
// disco. Texto ASCII solto parece "tamanho" de ~1,9 GB e não pode virar um PNG gigante.
let valid = ty.iter().all(|b| b.is_ascii_alphabetic())
&& ty[2].is_ascii_uppercase()
&& len <= 128 * 1024 * 1024
&& p + 12 + len <= total;
if !valid {
break;
}
if first && ty != b"IHDR" {
return None;
}
first = false;
saw_idat |= ty == b"IDAT";
p += 12 + len;
if p > max {
break;
@@ -247,11 +256,12 @@ fn png(src: &dyn Source, off: u64, max: u64) -> Option<Hit> {
});
}
}
if first {
// Sem IEND: só vale como imagem truncada se já tem dados e é de tamanho razoável.
if first || !saw_idat || p > 64 * 1024 * 1024 {
return None;
}
Some(Hit {
len: p.min(max).min(src.size().saturating_sub(off)),
len: p,
ext: "png",
confidence: Confidence::Low,
})