Expand description
Reading Procreate .brush and .brushset archives: the guarded zip and
plist readers, the Shape.png decoder and the two name/member lookups the
preview API needs.
Every entry here parses untrusted bytes, so each size, count and dimension is checked against a ceiling before anything is allocated.
Enums§
- Shape
PngError - Why a
Shape.pngdid not decode.
Constants§
- MAX_
ENTRY_ BYTES - Defensive ceilings for untrusted archives: anything above them is treated as malformed rather than allocated from.
- MAX_
PLIST_ BYTES - MAX_
PLIST_ DEPTH - MAX_
PNG_ DIMENSION
Functions§
- archive_
objects_ and_ main - The NSKeyedArchiver settings dictionary lives at
$objects[1], and every non-scalar field of it is a UID into the same$objectsarray — so a caller needs the pair, not just the dictionary. - brush_
name - The display name stored in a
Brush.archive($objects[1].name),Nonewhen the archive has no readable name. - decode_
tip_ png - Decode a Procreate
Shape.pnginto a grayscale tip. White is stamp coverage, so the luminance is taken as-is. - members_
in_ zip_ order - Members of a set that has no
brushset.plist: every top-level directorydwith an entry named exactlyd/Brush.archive, in first-appearance zip order.d/Reset/Brush.archivedoes not maked/Reseta member. - parse_
brushset_ plist - The set name and the member uuids a
brushset.plistdeclares, in its order. - parse_
plist_ guarded - The streaming depth pre-check builds no tree, so it bails early: without it
a deeply nested plist produces a
Valuewhose recursiveDropoverflows the call stack. The bytes are parsed twice, stream then tree. - read_
zip_ entry - The declared uncompressed size is an attacker-controlled zip-header field, so it is only a clamped pre-allocation hint and the read is capped independently — a small declared size can hide a huge inflate.
- resolve_
string - Follow a UID-valued field of the settings dictionary to the string it names,
treating NSKeyedArchiver’s literal
"$null"marker as absent.