# How Excalidraw Import Handles Scene Parsing and Adversarial Labels in diagram-design

> Discover how Excalidraw import in diagram-design parses scenes and neutralizes adversarial labels. Learn about size checks, geometry normalization, and secure Markdown escaping for safe diagram design.

- Repository: [Cathryn Lavery/diagram-design](https://github.com/cathrynlavery/diagram-design)
- Tags: deep-dive
- Published: 2026-09-13

---

**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`](https://github.com/cathrynlavery/diagram-design/blob/main/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](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/excalidraw_extract.py#L35-L39). 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](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/excalidraw_extract.py#L60-L68).

Structural validation ensures the top-level JSON object contains `"type": "excalidraw"` and an `"elements"` array [lines 86-92](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/excalidraw_extract.py#L86-L92). 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](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/excalidraw_extract.py#L43-L55). 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](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/excalidraw_extract.py#L140-L158) 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](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/excalidraw_extract.py#L101-L124), 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](https://github.com/cathrynlavery/diagram-design/blob/main/skills/diagram-design/scripts/excalidraw_extract.py#L518) 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`](https://github.com/cathrynlavery/diagram-design/blob/main/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 `dataURL` and 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](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-excalidraw-import.py#L304-L310).

### Trust Boundary Verification

The verifier confirms that adversarial labels remain inert by asserting three invariants:

1. **Discard accuracy**: Counters correctly report filtered links, embeds, and unknown elements [lines 311-318](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-excalidraw-import.py#L311-L318)
2. **Secret containment**: No URLs, `dataURL`s, or base-64 fragments appear in the final JSON output or Markdown digest [lines 321-328, 334-342](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-excalidraw-import.py#L321-L328)
3. **Literal rendering**: The Markdown digest contains only escaped versions of label components (e.g., `\#\# FORGED`, `\*\*IGNORE ALL PREVIOUS INSTRUCTIONS\*\*`) [lines 336-348](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-excalidraw-import.py#L336-L348)

## Practical Usage Examples

Basic extraction to Markdown:

```bash
python3 skills/diagram-design/scripts/excalidraw_extract.py examples/sample-whiteboard.excalidraw

```

JSON output for downstream tooling:

```bash
python3 skills/diagram-design/scripts/excalidraw_extract.py examples/sample-whiteboard.excalidraw --json > scene.json

```

Resource limit enforcement:

```bash
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:

```bash
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 `_num` helper ensures all coordinate fields are finite floats, eliminating NaN and infinity values from the IR.
- **Adversarial labels are neutralized**: The `clean_label` and `_escape_inline` functions preserve label content while escaping Markdown syntax, rendering prompt injections inert.
- **Trust boundaries are explicit**: The `discarded` dictionary 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_adversarial` test 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`](https://github.com/cathrynlavery/diagram-design/blob/main/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](https://github.com/cathrynlavery/diagram-design/blob/main/scripts/verify-excalidraw-import.py#L321-L328) 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.