GitHub Security Advisory: GHSA-83xq-8jxj-4rxm
Package: wwbn/avideo (COMPOSER)
Severity: HIGH (CVSS 7.1)
CWE: CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
Vulnerable versions: <= 26.0
Patched in: unpatched

Summary: AVideo has a Path Traversal in import.json.php Allows Private Video Theft and Arbitrary File Read/Deletion via fileURI Parameter

Details:
## Summary

The `objects/import.json.php` endpoint accepts a user-controlled `fileURI` POST parameter with only a regex check that the value ends in `.mp4`. Unlike `objects/listFiles.json.php`, which was hardened with a `realpath()` + directory prefix check to restrict paths to the `videos/` directory, `import.json.php` performs no directory restriction. This allows an authenticated user with upload permission to: (1) steal any other user's private video files by importing them into their own account, (2) read `.txt`/`.html`/`.htm` files adjacent to any `.mp4` file on the filesystem, and (3) delete `.mp4` and adjacent text files if writable by the web server process.

## Details

### Missing path restriction in import.json.php

At `objects/import.json.php:12`, the only validation on the user-supplied `fileURI` is a regex ensuring it ends with `.mp4`:

```php
// objects/import.json.php:12
if (!preg_match("/.*\\.mp4$/i", $_POST['fileURI'])) {
    return false;
}
```

Compare this to the hardened `listFiles.json.php:16-28`, which was patched to restrict paths:

```php
// objects/listFiles.json.php:16-28
$allowedBase = realpath($global['systemRootPath'] . 'videos');
// ...
$resolvedPath = realpath($_POST['path']);
if ($resolvedPath === false || strpos($resolvedPath . '/', $allowedBase) !== 0) {
    http_response_code(403);
    echo json_encode(['error' => 'Path not allowed']);
    exit;
}
```

The same fix was never applied to `import.json.php`.

### Attack Primitive 1: File content disclosure (.txt/.html/.htm)

At lines 23-43, the endpoint strips the `.mp4` extension from `fileURI` and attempts to read adjacent `.txt`, `.html`, or `.htm` files via `file_get_contents()`:

```php
// objects/import.json.php:23-43
$filename = $obj->fileURI['dirname'] . DIRECTORY_SEPARATOR . $obj->fileURI['filename'];
$extensions = ['txt', 'html', 'htm'];
foreach ($extensions as $value) {
    if (file_exists("{$filename}.{$value}")) {
        $html = file_get_contents("{$filename}.{$value}");
        $_POST['description'] = $html;
        // ...
        break;
    }
}
```

The content flows into `$_POST['description']`, which is then saved as the video description by `upload.php:59-64`:

```php
// view/mini-upload-form/upload.php:59-64
if (!empty($_POST['description'])) {
    // ...
    $video->setDescription($_POST['description']);
}
```

The attacker then views the imported video to read the file contents in the description field. This works for any path where both a `.mp4` file and an adjacent `.txt`/`.html`/`.htm` file exist — which is the standard layout for every video in the `videos/` directory.

### Attack Primitive 2: Private video theft

At line 49, the endpoint copies the `.mp4` file to a temp directory and then imports it as the current user's video:

```php
// objects/import.json.php:47-49
$source = $obj->fileURI['dirname'] . DIRECTORY_SEPARATOR . $obj->fileURI['basename'];
if (!copy($source, $tmpFileName)) {
    // ...
}
```

An attacker who knows or can en