GitHub Security Advisory: GHSA-g9cm-rxp7-6gv5
Package: wwbn/avideo (COMPOSER)
Severity: MODERATE (CVSS 6.4)
CWE: CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Vulnerable versions: <= 29.0
Patched in: unpatched

Summary: AVideo: HTML Injection in notifySubscribers.json.php Allows Platform-Branded Phishing Emails to Channel Subscribers

Details:
## Summary

`objects/notifySubscribers.json.php` takes the raw `message` POST parameter and passes it into `sendSiteEmail()`, which substitutes it directly into an HTML email template (via `str_replace` on the `{message}` placeholder) and renders it with `PHPMailer::msgHTML()`. There is no HTML sanitization, character escaping, or output encoding on the attacker-controlled `message` between `$_POST['message']` and the rendered email. Any authenticated user with upload permission can therefore broadcast arbitrary HTML — phishing links, tracking pixels, CSS/UI spoofing — to every subscriber on their channel (up to 10,000 recipients per invocation). The email is sent From: the platform's configured contact address and wrapped in the site's official logo and title, so attacker-supplied HTML arrives with the appearance of an official platform communication.

## Details

**File:** `objects/notifySubscribers.json.php`

```php
10: if (!User::canUpload()) {
11:     forbiddenPage('You can not notify');
12: }
13: forbidIfIsUntrustedRequest('notifySubscribers');
14: $user_id = User::getId();
15: // if admin bring all subscribers
16: if (User::isAdmin()) {
17:     $user_id = '';
18: }
19:
20: require_once 'subscribe.php';
21: setRowCount(10000);
...
23: $Subscribes = Subscribe::getAllSubscribes($user_id);
...
34: $subject = 'Message From Site ' . $config->getWebSiteTitle();
35: $message = $_POST['message'];
36:
37: $resp = sendSiteEmail($to, $subject, $message);
```

Controls present at the entry point:

- `User::canUpload()` — gates access to any account that can upload (a baseline authenticated uploader role; in typical AVideo configurations where `authCanUploadVideos` is enabled, this is any logged-in user with a verified email).
- `forbidIfIsUntrustedRequest('notifySubscribers')` — in `objects/functionsSecurity.php:138-165`, this delegates to `isUntrustedRequest()` which only validates same-origin via `requestComesFromSameDomainAsMyAVideo()` (`objects/functionsAVideo.php:199-206`), i.e. a Referer/Origin header check. It is **not** a CSRF token. An attacker acting on their own authenticated browser session trivially satisfies the Referer check.

There is no CAPTCHA, no rate limit, no per-recipient quota, and no unsubscribe link. `setRowCount(10000)` allows up to 10,000 subscriber rows to be pulled and mailed in a single request. For admin callers (`User::isAdmin()` → `$user_id = ''`), `Subscribe::getAllSubscribes('')` returns the entire subscriber set for the platform rather than the caller's channel.

**File:** `objects/functionsMail.php`

```php
 59: function sendSiteEmail($to, $subject, $message, $fromEmail = '', $fromName = '')
 60: {
 ...
 78:     $subject = UTF8encode($subject);
 79:     $message = UTF8encode($message);            // UTF-8 normalization, no HTML handling
 80:     $message = createEmailMessageFromTemplate($message);
 ...
119:             $mail = new \PHPMailer\PHPMailer\PHPMailer();
120:             setSiteSendMessage($mail);
...
125: