mirror of
https://github.com/storytold/photocraft.git
synced 2026-10-24 09:57:08 -06:00
security(io): index comp IDs in PSD layer-comp import
decode_comps matched each nonzero layer-setting comp ID against the comp vector with iter_mut().find(), an O(n*m) scan over two file-controlled collections. A crafted PSD could cause quadratic CPU work during import. Build a comp-ID-to-first-index map once and use it for setting lookups. or_insert preserves first-match behavior for duplicate comp IDs; comp 0 (Last Document State) is still handled separately. Finding: 47567ac0-a75f-4557-b127-ffc855e5bd8d (CWE-407)
This commit is contained in:
@@ -12,6 +12,8 @@
|
||||
//! Both are re-emitted verbatim while they still decode to the document's state, and are
|
||||
//! regenerated otherwise (comp appearance is not written to PSD; `.pcraft` keeps it).
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use photocraft_doc::comps::layer_position;
|
||||
use photocraft_doc::{Artboard, ArtboardBackground, CompLayerState, Document, Guides, Layer, LayerComp};
|
||||
use photocraft_geom::Rect;
|
||||
@@ -203,6 +205,14 @@ pub(crate) fn decode_comps(resource: Option<&[u8]>, doc: &Document) -> (Vec<Laye
|
||||
}
|
||||
}
|
||||
let last_applied = int(d, "lastAppliedComp").map(|v| v as u32).filter(|id| comps.iter().any(|c| c.id == *id));
|
||||
// Index each comp ID to its first occurrence once. Both the comp list and the
|
||||
// per-layer setting ID lists are file-controlled, so matching settings against comps
|
||||
// with iter_mut().find() per ID is O(n*m) and lets a crafted document stall import.
|
||||
// or_insert preserves first-match behavior for duplicate comp IDs.
|
||||
let mut id_to_index: HashMap<u32, usize> = HashMap::with_capacity(comps.len());
|
||||
for (i, c) in comps.iter().enumerate() {
|
||||
id_to_index.entry(c.id).or_insert(i);
|
||||
}
|
||||
let mut last = LayerComp {
|
||||
id: 0,
|
||||
name: "Last Document State".into(),
|
||||
@@ -239,7 +249,7 @@ pub(crate) fn decode_comps(resource: Option<&[u8]>, doc: &Document) -> (Vec<Laye
|
||||
_ => None,
|
||||
};
|
||||
for id in &s.comps {
|
||||
let target = if *id == 0 { Some(&mut last) } else { comps.iter_mut().find(|c| c.id == *id) };
|
||||
let target = if *id == 0 { Some(&mut last) } else { id_to_index.get(id).map(|&i| &mut comps[i]) };
|
||||
let Some(c) = target else { continue };
|
||||
let visible = if *id != 0 && c.apply_visibility { Some(resolved) } else { s.visible };
|
||||
if visible.is_none() && s.offset.is_none() {
|
||||
|
||||
Reference in New Issue
Block a user