GitHub Security Advisory: GHSA-ghx5-7jjg-q2j7
Package: wwbn/avideo (COMPOSER)
Severity: MODERATE (CVSS 5.4)
CWE: CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Vulnerable versions: <= 26.0
Patched in: unpatched

Summary: AVideo vulnerable to Stored XSS via html_entity_decode() Reversing xss_esc() Sanitization in Channel About Field

Details:
## Summary

A sanitization order-of-operations flaw in the user profile "about" field allows any registered user to inject arbitrary JavaScript that executes when other users visit their channel page. The `xss_esc()` function entity-encodes input before `strip_specific_tags()` can match dangerous HTML tags, and `html_entity_decode()` on output reverses the encoding, restoring the raw malicious HTML.

## Details

**Input sanitization** in `objects/user.php:156`:

```php
public function setAbout($about)
{
    $this->about = strip_specific_tags(xss_esc($about));
}
```

The call order is `strip_specific_tags(xss_esc($about))`. The inner `xss_esc()` function (`objects/functionsSecurity.php:233`) calls `htmlspecialchars()`:

```php
$result = @htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
```

This encodes `<script>alert(1)</script>` to `&lt;script&gt;alert(1)&lt;/script&gt;`.

Then `strip_specific_tags()` (`objects/functions.php:6623-6636`) runs regex patterns to remove dangerous tags:

```php
$string = preg_replace('/<' . $tag . '[^>]*>(.*?)<\/' . $tag . '>/s', $replacement, $string);
```

But the regex looks for literal `<script>` — it can never match the entity-encoded `&lt;script&gt;`. The sanitizer is completely neutralized by the encoding that precedes it.

**Output** in `view/channelBody.php:239-246`:

```php
$about = html_entity_decode($user->getAbout());
if (!empty($advancedCustomUser->showAllAboutTextOnChannel)) {
    echo $about;
} else {
?>
    <div id="aboutAreaPreContent">
        <div id="aboutAreaContent">
            <?php echo $about; ?>
        </div>
    </div>
```

The `html_entity_decode()` call reverses the `htmlspecialchars()` encoding, restoring the original raw HTML including `<script>` tags. The result is echoed directly into the page without any further escaping.

**Secondary vector:** The `<img>` tag is not in the `strip_specific_tags` blocklist (`['script', 'style', 'iframe', 'object', 'applet', 'link']`), so payloads like `<img src=x onerror=...>` bypass even the intended tag stripping entirely.

The about field is set via `objects/userUpdate.json.php:28`, accessible to any logged-in user:

```php
$user->setAbout($_POST['about']);
```

The channel page (`view/channelBody.php`) is publicly accessible — no authentication is required to view it.

## PoC

**Step 1:** Log in as any registered user and update the "about" field:

```bash
curl -X POST 'https://TARGET/objects/userUpdate.json.php' \
  -H 'Cookie: PHPSESSID=ATTACKER_SESSION' \
  -d 'about=<img src=x onerror=alert(document.cookie)>&user=attacker&pass=password123&email=attacker@example.com&name=Attacker&analyticsCode=&donationLink=&phone='
```

**Step 2:** Any user (including unauthenticated visitors) navigates to the attacker's channel page:

```
https://TARGET/channel/attacker
```

**Expected result:** The JavaScript in the `onerror` handler executes in the visitor's browser, displaying their session cookie.

**Alternative payload using `<script>` tag (also works du