## Ticket: CVE-2026-39850 — Yii2 Local File Inclusion in `View::renderPhpFile`

**Advisory**: GHSA-5vpg-rj7q-qpw2 — https://github.com/yiisoft/yii2/security/advisories/GHSA-5vpg-rj7q-qpw2
**CVE**: CVE-2026-39850 | **CWE-98 / CWE-94** (LFI / Code Injection)
**Severity**: High — CVSS 3.1 base 7.4
**Package**: `yiisoft/yii2` (Packagist) | **Repository**: https://github.com/yiisoft/yii2

### Impact

`yii\base\View::renderPhpFile($_file_, $_params_)` calls
`extract($_params_, EXTR_OVERWRITE)` **before** the `require $_file_` that
loads the template. Because `EXTR_OVERWRITE` is allowed to clobber existing
local variables, a `_file_` key in the params array overwrites the local
`$_file_` immediately before the `require` — redirecting the include to any
path on disk the PHP process can read.

This is local file inclusion: an attacker who controls (any subset of) the
params array passed to `renderPhpFile` can read arbitrary files, and execute
arbitrary PHP if the included file's contents are PHP.

### Affected / fixed versions

Affected: `yiisoft/yii2` `<= 2.0.54`.
Fixed: **`2.0.55`**.

Reproduce on a vulnerable build (**`2.0.54`**) and verify the fix on
**`2.0.55`**.

### Where to look

The fix ships in commit
[`109878b491dbffa541032bc99fb5e26d12cd0375`](https://github.com/yiisoft/yii2/commit/109878b491dbffa541032bc99fb5e26d12cd0375).
Inspect the patch to confirm the root cause and the guard:

```bash
git clone https://github.com/yiisoft/yii2.git
cd yii2 && git show 109878b491dbffa541032bc99fb5e26d12cd0375
```

The change reworks how `renderPhpFile` imports params so the local
`$_file_` can no longer be overwritten by a `_file_` key in the params array.

### Reproduction approach

No web server, database, or browser is needed — the bug is observable from a
single PHP CLI invocation.

1. In a scratch composer project, install the **vulnerable** build:
   `composer require yiisoft/yii2:2.0.54`
2. Place a benign template `safe.php` (e.g. `<?php echo "SAFE\n";`).
3. Run a small PHP script that:
   - instantiates `yii\web\View`,
   - calls `$view->renderPhpFile('safe.php', ['_file_' => '/etc/passwd'])`,
   - prints the returned content.
4. Repeat with the **fixed** build: `composer require yiisoft/yii2:2.0.55`.

### Expected result

| Build | `renderPhpFile('safe.php', ['_file_' => '/etc/passwd'])` | Output |
|-------|----------------------------------------------------------|--------|
| `yiisoft/yii2:2.0.54` (vulnerable) | `_file_` key overwrites local `$_file_` before `require` | contents of **`/etc/passwd`** (LFI confirmed) |
| `yiisoft/yii2:2.0.55` (fixed) | local `$_file_` no longer clobberable from params | contents of **`safe.php`** |

- **Vulnerable indicator**: output contains `/etc/passwd` content
  (e.g. a `root:` line) — the attacker-chosen file was included.
- **Fixed indicator**: output is the unmodified content of `safe.php`.

### Expected artifacts

- `reproduction_steps.sh` — installs both versions, runs the PHP PoC for
  each, and captures the printed output.
- `validation_verdict.json` — `verdict: confirmed`, with the vulnerable and
  fixed indicators captured.
- Logs capturing the PHP CLI output for `2.0.54` and `2.0.55`.
