# ANSI Fuzzing
## Overview
Panasyn includes a robust ANSI fuzzing framework for testing terminal robustness against malformed, invalid, or malicious input sequences. The terminal must **never panic** regardless of input.
## Fuzzing approach
The fuzzer generates random ANSI byte sequences and replays them through the parser/grid engine. Two modes are available:
### Random fuzzing
Generates fully random byte sequences (0-255) of varying lengths (1-4096 bytes):
```bash
cargo run --release -- fuzz 10000
```
### Categorized fuzzing
Targets specific vulnerability categories:
```bash
cargo run --release -- fuzz --categories
```
Output:
```
=== Fuzz Category Report ===
✓ malformed_utf8: 100 passes, 0 crashes
✓ broken_csi: 100 passes, 0 crashes
✓ oversized_params: 100 passes, 0 crashes
✓ incomplete_osc: 100 passes, 0 crashes
✓ all_byte_values: 100 passes, 0 crashes
✓ unicode_edge: 100 passes, 0 crashes
```
## Input categories
| Malformed UTF-8 | Incomplete sequences, overlong encodings | `\xE2\x82` (incomplete 3-byte) |
| Broken CSI | CSI sequences without final byte | `\x1B[0` (truncated) |
| Oversized parameters | Extreme SGR values, 9-digit numbers | `\x1B[999999999m` |
| Incomplete OSC | OSC sequences without BEL/ST | `\x1B]0;title` (never terminated) |
| Strobed ESC | Rapid ESC sequences | `\x1B\x1B\x1B\x1B...` |
| Unicode edge | Non-characters, BOM, zero-width | U+FFFE, U+FFFF, U+200B |
| Invalid wide chars | Combining marks in isolation | U+0300, U+094D |
| DEC private sequences | Random `\x1B[?...` | `\x1B[?9999h` |
| Interleaved controls | Mixed control + printable | `\x01\x02Hello\x03` |
## Fuzzing requirements
- Terminal must **never panic** on any input
- Renderer must **never crash**
- Invalid sequences are **handled safely** (ignored or gracefully degraded)
- Errors are **only logged in debug mode** (no stderr spam in release)
- Memory must not grow unboundedly
- No undefined behavior or unsafe code panics
## How it works
```
fuzz::fuzz_iterations(n, cols, rows)
│
├── generate_random_fuzz() (75% of iterations)
└── generate_structured_fuzz() (25% of iterations)
│
▼
fuzz_bytes(data, cols, rows)
│
├── std::panic::catch_unwind
│ │
│ ├── Grid::new()
│ ├── Scrollback::new_scrollback()
│ ├── Parser::new()
│ ├── parser.advance(data, terminal)
│ └── return "ok" or panic message
│
├── Ok → pass
└── Err → crash (record data + message)
```
## Adding new fuzz patterns
Edit `generate_structured_fuzz()` in `src/replay/fuzz.rs`:
```rust
fn generate_structured_fuzz(rng: &mut impl FnMut() -> u8) -> Vec<u8> {
let kind = rng(); // which pattern to generate
match kind % 12 {
0 => { /* malformed UTF-8 */ }
1 => { /* broken CSI */ }
// ... add new patterns here
_ => {}
}
}
```
## CI integration
On every push:
```bash
cargo run --release -- fuzz 1000 --categories
```
Any crash fails CI immediately.
## Performance
- ~50,000 iterations/second on M4 MacBook
- Memory: ~1 MB per iteration (cleaned between runs)
- Each iteration creates fresh grid + scrollback + parser
## Future improvements
- [ ] `cargo-fuzz` / `libfuzzer` integration for coverage-guided fuzzing
- [ ] AFL++ corpus generation
- [ ] Structured fuzzing for OSC sequences (title, clipboard, hyperlinks)
- [ ] Fuzzing with large scrollback states
- [ ] Differential fuzzing against Alacritty/Kitty output