What's the vulnerability?

Vim modeline handling for the tabpanel option allows sandbox escape via autocmd_add, enabling OS command execution when opening a crafted file.

Root Cause Analysis

# Root Cause Analysis: GHSA-2gmj-rpqf-pxvh

## Summary

A vulnerability in Vim's handling of the `tabpanel` option allows sandbox escape through modeline processing. The `tabpanel` option lacks the `P_MLE` (modelineexpr) security flag, allowing it to be set from a modeline without requiring the `modelineexpr` option to be enabled. When combined with the fact that `autocmd_add()` function lacks a `check_secure()` call, this enables arbitrary command execution when a victim opens a crafted file.

## Impact

**Package:** Vim (https://github.com/vim/vim)  
**Affected Versions:** < 9.2.0272  
**Patched Version:** 9.2.0272  
**Risk Level:** High  
**Consequences:** 
- Arbitrary OS command execution with user privileges
- Sandbox escape from Vim's restricted mode
- Code execution triggered by opening a text file

## Root Cause

The vulnerability exists due to two security oversights:

### 1. Missing P_MLE flag on tabpanel option

In `src/optiondefs.h`, the `tabpanel` option is defined without the `P_MLE` flag:

```c
{"tabpanel",  "tpl",    P_STRING|P_VI_DEF|P_RALL,
    (char_u *)&p_tpl, PV_NONE, NULL, NULL,
    {(char_u *)"", (char_u *)0L} SCTX_INIT},
```

Unlike similar options like `statusline` and `tabline` which have `P_MLE`, `tabpanel` can be set from a modeline without requiring `modelineexpr` to be enabled. The fix adds `P_MLE` to the flags:

```c
{"tabpanel",  "tpl",    P_STRING|P_VI_DEF|P_RALL|P_MLE,
```

### 2. Missing check_secure() in autocmd_add()

The `autocmd_add_or_delete()` function in `src/autocmd.c` does not call `check_secure()` or `check_restricted()` to prevent execution in sandbox mode. The fix adds these checks:

```c
if (check_restricted() || check_secure())
    return;
```

**Fix Commit:** https://github.com/vim/vim/commit/664701eb7576edb7c7c7d9f2d600815ec1f43459

## Reproduction Steps

The reproduction script (`repro/reproduction_steps.sh`) performs three tests:

### Test 1: Modeline Security Bypass
Creates a file with a modeline that sets `tabpanel` and verifies it succeeds without `modelineexpr`:
```
/* vim: set tabpanel=pwned_value: */
```

### Test 2: Sandbox Escape (autocmd_add)
Directly tests the core vulnerability by calling `autocmd_add()` from within Vim's sandbox:
```vim
sandbox call autocmd_add([{'event':'User','cmd':'call system("echo poc > /tmp/result")'}])
doautocmd User
```

### Test 3: Full Exploit Chain
Attempts to combine both vulnerabilities through a modeline that sets tabpanel to an expression calling `autocmd_add()`.

**Execution:**
```bash
cd /data/pruva/runs/2cdc80ca-2247-44e2-91bc-65b96f1563e7
bash repro/reproduction_steps.sh
```

## Evidence

### Test Results
```
[✓] Test 1: Modeline security bypass confirmed
    Output: tabpanel=pwned_value

[✓] Test 2: autocmd_add sandbox bypass confirmed  
    Output: poc_success
```

### Proof Files
- `/tmp/vim_poc_result` - Created by sandbox-escaped autocommand
- `logs/modeline_test.txt` - Crafted modeline test file
- `logs/t1.txt` - Verification that tabpanel was set via modeline

### Build Evidence
- Vim v9.2.0271 built from source with `+tabpanel` feature
- Build artifacts in `vim-repo/src/vim`

## Attack Chain

1. Attacker crafts a file with a malicious modeline
2. Victim opens the file in Vim (with default settings - `modeline` enabled)
3. Modeline sets `tabpanel` to an expression containing `autocmd_add()` call
4. When tabpanel is evaluated (on redraw), expression executes in sandbox
5. `autocmd_add()` registers malicious autocommand (no sandbox check)
6. After sandbox exits, autocommand triggers on next event
7. Arbitrary command executes with victim's privileges

## Recommendations

### Immediate Actions
1. **Upgrade** to Vim 9.2.0272 or later
2. **Workaround:** Disable modelines in `.vimrc`:
   ```vim
   set nomodeline
   ```

### Testing Recommendations
1. Verify `P_MLE` flag is set on all expression-evaluating options
2. Ensure all sandbox-escaping functions call `check_secure()`
3. Add regression tests for modeline sandbox escapes

## Additional Notes

### Surface Mismatch
The ticket claims surface `api_remote` but this is a **local CLI vulnerability** in Vim. The actual attack surface is `cli_local` (local file processing). Vim is a text editor, not a remote API service.

### Modeline Expression Limitations
Test 3 (full chain via modeline) has character encoding challenges because:
- `:` in dictionary syntax `{key: value}` is interpreted as modeline terminator
- `}` ends the `%{expr}` block prematurely
- `"` and `'` quotes conflict with modeline parsing

These limitations are parser-level constraints, not security controls. The core vulnerability (sandbox escape via `autocmd_add`) is confirmed through Test 2.

### Idempotency
The reproduction script is idempotent and can be run multiple times. Each run:
1. Builds Vim if not present
2. Cleans up previous test artifacts
3. Runs all three tests
4. Reports results
One Command

Verify with pruva-verify

Run the Pruva CLI to automatically fetch and execute the reproduction script.

pruva-verify REPRO-2026-00124
or pruva-verify GHSA-2GMJ-RPQF-PXVH
or pruva-verify CVE-2026-34714
Install: curl -fsSL https://pruva.dev/install.sh | sh

Or Run Manually

1

Download the script

curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00124/artifacts/reproduction_steps.sh
2

Make executable

chmod +x reproduction_steps.sh
3

Run the script

./reproduction_steps.sh
Run in a VM, container, or disposable environment. This exploits a real vulnerability.

How Pruva Reproduced This

Watch the AI agent's step-by-step process.

Loading session...

Artifacts

No artifacts available