How Excalidraw Import Handles Scene Parsing and Adversarial Labels in diagram-design
The Excalidraw import pipeline in diagram-design validates incoming scenes through strict size and type checks, normalizes geometry via the parse_scene function, and neutralizes adversarial labels by escaping Markdown syntax through clean_label and _escape_inline, ensuring malicious payloads remain inert while preserving semantic content.
The diagram-design repository by cathrynlavery provides a security-hardened toolchain for converting Excalidraw scenes into normalized intermediate representations. At the core of this system, the Excalidraw import process handles scene parsing and adversarial labels through a two-phase pipeline that enforces strict trust boundaries while maintaining semantic fidelity.
Scene Parsing Architecture
The core extractor resides in skills/diagram-design/scripts/excalidraw_extract.py, where the parse_scene function orchestrates the conversion of raw .excalidraw JSON into a structured intermediate representation (IR).
Input Validation and Trust Boundaries
Before processing begins, the extractor enforces strict resource limits and file type constraints. The MAX_INPUT_BYTES constant (16 MiB) and MAX_ELEMENTS limit (10,000) prevent memory exhaustion attacks lines 35-39, 92-94. File suffix validation distinguishes between editable scenes (EXCALIDRAW_SUFFIXES including .excalidraw and .json) and export formats like PNG or SVG, immediately rejecting the latter lines 60-68.
Structural validation ensures the top-level JSON object contains "type": "excalidraw" and an "elements" array lines 86-92. Any UTF-8 decoding errors or schema violations trigger immediate termination with descriptive error codes.
Geometry Normalization and Element Classification
Once validated, the parser walks the scene graph using classification tables defined at lines 43-55. Elements map to Node objects (shapes) via NODE_SHAPES and CONTAINER_TYPES, or to Edge objects (arrows/lines) via EDGE_TYPES.
The _num helper function lines 140-158 guarantees that numeric fields like x, y, width, and height are finite floats, gracefully handling out-of-range values or type mismatches. This prevents NaN or infinity values from propagating into downstream geometry calculations.
Discarded content—including image payloads, embeds, and freedraw strokes—is tallied in a discarded dictionary lines 101-124, maintaining transparency about dropped data without breaking the trust boundary.
Label Sanitization Pipeline
Text labels undergo two-stage processing before entering the IR. First, clean_label normalizes whitespace. Second, _escape_inline near line 518 escapes Markdown metacharacters, ensuring that #, *, [, and other syntax markers render as literal text rather than formatting commands.
Adversarial Label Protection
The verification framework in scripts/verify-excalidraw-import.py provides concrete safeguards against prompt injection and data exfiltration attempts.
The Adversarial Test Fixture
The test suite loads scripts/fixtures/sample-adversarial.excalidraw, which contains deliberate attacks including:
- Prompt injection labels containing "IGNORE ALL PREVIOUS INSTRUCTIONS"
- Embedded URLs pointing to
https://example.invalid - CR/LF injection via raw carriage-return characters
- Binary payloads disguised as
dataURLand base-64 content
The check_adversarial function processes this fixture and asserts that the extracted IR contains exactly the escaped label text without interpretation lines 304-310.
Trust Boundary Verification
The verifier confirms that adversarial labels remain inert by asserting three invariants:
- Discard accuracy: Counters correctly report filtered links, embeds, and unknown elements lines 311-318
- Secret containment: No URLs,
dataURLs, or base-64 fragments appear in the final JSON output or Markdown digest lines 321-328, 334-342 - Literal rendering: The Markdown digest contains only escaped versions of label components (e.g.,
\#\# FORGED,\*\*IGNORE ALL PREVIOUS INSTRUCTIONS\*\*) lines 336-348
Practical Usage Examples
Basic extraction to Markdown:
python3 skills/diagram-design/scripts/excalidraw_extract.py examples/sample-whiteboard.excalidraw
JSON output for downstream tooling:
python3 skills/diagram-design/scripts/excalidraw_extract.py examples/sample-whiteboard.excalidraw --json > scene.json
Resource limit enforcement:
python3 skills/diagram-design/scripts/excalidraw_extract.py huge.excalidraw
# Exits with code 2 if MAX_INPUT_BYTES or MAX_ELEMENTS exceeded
Running adversarial verification:
python3 scripts/verify-excalidraw-import.py
Summary
- Strict validation occurs first: The extractor rejects non-Excalidraw file types, enforces 16 MiB size limits, and caps elements at 10,000 before parsing begins.
- Geometry normalization prevents corruption: The
_numhelper ensures all coordinate fields are finite floats, eliminating NaN and infinity values from the IR. - Adversarial labels are neutralized: The
clean_labeland_escape_inlinefunctions preserve label content while escaping Markdown syntax, rendering prompt injections inert. - Trust boundaries are explicit: The
discardeddictionary tracks dropped image payloads and embeds, while the verifier ensures no URLs or binary data crosses into the final representation. - Deterministic output is guaranteed: The
check_adversarialtest fixture validates that malicious content remains literal text, never interpreted as commands or markup.
Frequently Asked Questions
How does the Excalidraw import prevent prompt injection attacks?
The import pipeline treats all text labels as literal strings rather than executable content. The _escape_inline function prefixes Markdown metacharacters with backslashes, converting **IGNORE ALL PREVIOUS INSTRUCTIONS** into \*\*IGNORE ALL PREVIOUS INSTRUCTIONS\*\*. The adversarial test fixture verifies this behavior by asserting that escaped versions appear in the final Markdown digest, ensuring instructions remain inert.
What happens when an Excalidraw scene exceeds the size limits?
When a file exceeds MAX_INPUT_BYTES (16 MiB) or contains more than MAX_ELEMENTS (10,000), the extractor exits immediately with return code 2. This occurs in excalidraw_extract.py at lines 92-94, preventing memory exhaustion and denial-of-service scenarios before JSON parsing begins.
Can binary payloads or embedded images leak through the import process?
No. The extractor identifies image payloads, dataURL fields, and embeds during the scene walk, tallying them in the discarded dictionary rather than including them in the IR. The verifier lines 321-328 explicitly searches the final output for base-64 fragments and URLs, failing the test if any secret strings cross the trust boundary.
Which element types are supported by the diagram-design importer?
The importer recognizes shapes defined in NODE_SHAPES and CONTAINER_TYPES (converted to Node objects) and connections defined in EDGE_TYPES (converted to Edge objects). Any element types not listed in these classification tables at lines 43-55 are counted as unknown and tracked in the discard tally, ensuring the parser remains extensible without failing on new Excalidraw features.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →