CWE-703
DiscouragedImproper Check or Handling of Exceptional Conditions
Abstraction: Pillar · Status: Incomplete
The product does not properly anticipate or handle exceptional conditions that rarely occur during normal operation of the product.
243 vulnerabilities reference this CWE, most recent first.
GHSA-22QQ-3XWM-R5X4
Vulnerability from github – Published: 2025-02-03 15:55 – Updated: 2026-03-09 20:46Name: ASA-2025-001: Malicious peer can disrupt node's ability to sync via blocksync Component: CometBFT [OUTDATED] Criticality: Medium (Considerable Impact; Possible Likelihood per ACMv1.2) Update of Criticality on 2026-03-06: We've made a mistake and over-rated the criticality of this bug in our initial triage. We have calibrated our vulnerability rating internally and updated the criticality of this bug to be Informational (Negligible Impact, Possible Likelihood) Affected versions: <= v0.38.16, v1.0.0 Affected users: Validators, Full nodes
Impact
A malicious peer may be able to interfere with a node's ability to sync blocks with peers via the blocksync mechanism.
In the blocksync protocol peers send their base and latest heights when they connect to a new node (A), which is syncing to the tip of a network. base acts as a lower ground and informs A that the peer only has blocks starting from height base. latest height informs A about the latest block in a network. Normally, nodes would only report increasing heights:
B: {base: 100, latest: 1000}
B: {base: 100, latest: 1001}
B: {base: 100, latest: 1002}
...
If B fails to provide the latest block, B is removed and the latest height (target height) is recalculated based on other nodes latest heights.
The existing code hovewer doesn't check for the case where B first reports latest height X and immediately after height Y, where X > Y. For example:
B: {base: 100, latest: 2000}
B: {base: 100, latest: 1001}
B: {base: 100, latest: 1002}
...
A will be trying to catch up to 2000 indefinitely. Even if B disconnects, the latest height (target height) won't be recalculated because A "doesn't know where 2000" came from per see.
Impact Qualification
This condition requires the introduction of malicious code in the full node first reporting a non-existing latest height, then reporting lower latest height and nodes which are syncing using blocksync protocol.
Patches
The new CometBFT releases v1.0.1 and v0.38.17 fix this issue.
Unreleased code in the main is patched as well.
Workarounds
When the operator notices blocksync is stuck, they can identify the peer from which that message with "invalid" height was received. This may require increasing the logging level of the blocksync module. This peer can then be subsequently banned at the p2p layer as a temporary mitigation.
References
If you have questions about Interchain security efforts, please reach out to our official communication channel at security@interchain.io. For more information about the Interchain Foundation’s engagement with Amulet, and to sign up for security notification emails, please see https://github.com/interchainio/security.
A Github Security Advisory for this issue is available in the CometBFT repository. For more information about CometBFT, see https://docs.cometbft.com/.
EDIT:
Please notice that this has been updated to be informational severity. This can be avoided by ensuring that one is not connected to a malicious peer during blocksync.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/cometbft/cometbft"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.0-alpha.1"
},
{
"fixed": "1.0.1"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/cometbft/cometbft"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.38.17"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-24371"
],
"database_specific": {
"cwe_ids": [
"CWE-703"
],
"github_reviewed": true,
"github_reviewed_at": "2025-02-03T15:55:28Z",
"nvd_published_at": "2025-02-03T22:15:28Z",
"severity": "MODERATE"
},
"details": "Name: ASA-2025-001: Malicious peer can disrupt node\u0027s ability to sync via blocksync\nComponent: CometBFT\n[OUTDATED] Criticality: Medium (Considerable Impact; Possible Likelihood per [ACMv1.2](https://github.com/interchainio/security/blob/main/resources/CLASSIFICATION_MATRIX.md))\n**Update of Criticality on 2026-03-06**: We\u0027ve made a mistake and over-rated the criticality of this bug in our initial triage. We have calibrated our vulnerability rating internally and updated the criticality of this bug to be Informational (Negligible Impact, Possible Likelihood)\nAffected versions: \u003c= v0.38.16, v1.0.0\nAffected users: Validators, Full nodes\n\n### Impact\n\nA malicious peer may be able to interfere with a node\u0027s ability to sync blocks with peers via the blocksync mechanism. \n\nIn the `blocksync` protocol peers send their `base` and `latest` heights when they connect to a new node (`A`), which is syncing to the tip of a network. `base` acts as a lower ground and informs `A` that the peer only has blocks starting from height `base`. `latest` height informs `A` about the latest block in a network. Normally, nodes would only report increasing heights:\n\n```\nB: {base: 100, latest: 1000}\nB: {base: 100, latest: 1001}\nB: {base: 100, latest: 1002}\n...\n```\n\nIf `B` fails to provide the latest block, `B` is removed and the `latest` height (target height) is recalculated based on other nodes `latest` heights.\n\nThe existing code hovewer doesn\u0027t check for the case where `B` first reports `latest` height `X` and immediately after height `Y`, where `X \u003e Y`. For example:\n\n```\nB: {base: 100, latest: 2000}\nB: {base: 100, latest: 1001}\nB: {base: 100, latest: 1002}\n...\n```\n\n`A` will be trying to catch up to 2000 indefinitely. Even if `B` disconnects, the `latest` height (target height) won\u0027t be recalculated because `A` \"doesn\u0027t know where 2000\" came from per see.\n\n#### Impact Qualification\n\nThis condition requires the introduction of malicious code in the full node first reporting a non-existing `latest` height, then reporting lower `latest` height and nodes which are syncing using `blocksync` protocol.\n\n### Patches\n\nThe new CometBFT releases [v1.0.1](https://github.com/cometbft/cometbft/releases/tag/v1.0.1) and [v0.38.17](https://github.com/cometbft/cometbft/releases/tag/v0.38.17) fix this issue.\n\nUnreleased code in the main is patched as well.\n\n### Workarounds\n\nWhen the operator notices `blocksync` is stuck, they can identify the peer from which that message with \"invalid\" height was received. This may require increasing the logging level of the `blocksync` module. This peer can then be subsequently banned at the p2p layer as a temporary mitigation.\n\n### References\n\nIf you have questions about Interchain security efforts, please reach out to our official communication channel at [security@interchain.io](mailto:security@interchain.io). For more information about the Interchain Foundation\u2019s engagement with Amulet, and to sign up for security notification emails, please see https://github.com/interchainio/security. \n\nA Github Security Advisory for this issue is available in the CometBFT [repository](https://github.com/cometbft/cometbft/security/advisories/GHSA-22qq-3xwm-r5x4). For more information about CometBFT, see https://docs.cometbft.com/.\n\nEDIT:\n\nPlease notice that this has been updated to be `informational` severity. This can be avoided by ensuring that one is not connected to a malicious peer during blocksync.",
"id": "GHSA-22qq-3xwm-r5x4",
"modified": "2026-03-09T20:46:44Z",
"published": "2025-02-03T15:55:28Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/cometbft/cometbft/security/advisories/GHSA-22qq-3xwm-r5x4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-24371"
},
{
"type": "WEB",
"url": "https://github.com/cometbft/cometbft/commit/0ee80cd609c7ae9fe856bdd1c6d38553fdae90ce"
},
{
"type": "WEB",
"url": "https://github.com/cometbft/cometbft/commit/2cebfde06ae5073c0b296a9d2ca6ab4b95397ea5"
},
{
"type": "PACKAGE",
"url": "https://github.com/cometbft/cometbft"
},
{
"type": "WEB",
"url": "https://github.com/cometbft/cometbft/releases/tag/v0.38.17"
},
{
"type": "WEB",
"url": "https://github.com/cometbft/cometbft/releases/tag/v1.0.1"
},
{
"type": "WEB",
"url": "https://pkg.go.dev/vuln/GO-2025-3442"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:L/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "CometBFT allows a malicious peer to make node stuck in blocksync"
}
GHSA-27JF-22P2-6VQ4
Vulnerability from github – Published: 2023-08-29 09:30 – Updated: 2024-04-04 07:15Motorola EBTS/MBTS Site Controller drops to debug prompt on unhandled exception. The Motorola MBTS Site Controller exposes a debug prompt on the device's serial port in case of an unhandled exception. This allows an attacker with physical access that is able to trigger such an exception to extract secret key material and/or gain arbitrary code execution on the device.
{
"affected": [],
"aliases": [
"CVE-2023-23774"
],
"database_specific": {
"cwe_ids": [
"CWE-248",
"CWE-703",
"CWE-755"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-08-29T09:15:09Z",
"severity": "HIGH"
},
"details": "Motorola EBTS/MBTS Site Controller drops to debug prompt on unhandled exception. The Motorola MBTS Site Controller exposes a debug prompt on the device\u0027s serial port in case of an unhandled exception. This allows an attacker with physical access that is able to trigger such an exception to extract secret key material and/or gain arbitrary code execution on the device.",
"id": "GHSA-27jf-22p2-6vq4",
"modified": "2024-04-04T07:15:16Z",
"published": "2023-08-29T09:30:24Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-23774"
},
{
"type": "WEB",
"url": "https://tetraburst.com"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-27WG-99G8-2V4V
Vulnerability from github – Published: 2024-01-03 21:48 – Updated: 2024-01-03 21:48Impact
In rust-evm, a feature called record_external_operation was introduced, allowing library users to record custom gas changes. This feature can have some bogus interactions with the call stack.
In particular, during finalization of a CREATE or CREATE2, in the case that the substack execution happens successfully, rust-evm will first commit the substate, and then call record_external_operation(Write(out_code.len())). If record_external_operation later fails, this error is returned to the parent call stack, instead of Succeeded. Yet, the substate commitment already happened. This causes smart contracts able to commit state changes, when the parent caller contract receives zero address (which usually indicates that the execution has failed).
This issue only impacts library users with custom record_external_operation that returns errors.
Patches
The issue is patched in release 0.41.1. The commit can be seem here.
Workarounds
None.
References
Patch PR #264.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.41.0"
},
"package": {
"ecosystem": "crates.io",
"name": "evm"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.41.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-21629"
],
"database_specific": {
"cwe_ids": [
"CWE-703"
],
"github_reviewed": true,
"github_reviewed_at": "2024-01-03T21:48:34Z",
"nvd_published_at": "2024-01-02T22:15:09Z",
"severity": "MODERATE"
},
"details": "### Impact\n\nIn `rust-evm`, a feature called `record_external_operation` was introduced, allowing library users to record custom gas changes. This feature can have some bogus interactions with the call stack.\n\nIn particular, during finalization of a `CREATE` or `CREATE2`, in the case that [the substack execution happens successfully](https://github.com/rust-ethereum/evm/blob/release-v041/src/executor/stack/executor.rs#L1012C25-L1012C69), `rust-evm` will first commit the substate, and then call `record_external_operation(Write(out_code.len()))`. If `record_external_operation` later fails, this error is returned to the parent call stack, instead of `Succeeded`. Yet, the substate commitment already happened. This causes smart contracts able to commit state changes, when the parent caller contract receives zero address (which usually indicates that the execution has failed).\n\nThis issue only impacts library users with custom `record_external_operation` that returns errors.\n\n### Patches\n\nThe issue is patched in release 0.41.1. The commit can be seem [here](https://github.com/rust-ethereum/evm/commit/d8991ec727ad0fb64fe9957a3cd307387a6701e4).\n\n### Workarounds\n\nNone.\n\n### References\n\nPatch PR [#264](https://github.com/rust-ethereum/evm/pull/264).\n",
"id": "GHSA-27wg-99g8-2v4v",
"modified": "2024-01-03T21:48:34Z",
"published": "2024-01-03T21:48:34Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/rust-ethereum/evm/security/advisories/GHSA-27wg-99g8-2v4v"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-21629"
},
{
"type": "WEB",
"url": "https://github.com/rust-ethereum/evm/pull/264"
},
{
"type": "WEB",
"url": "https://github.com/rust-ethereum/evm/commit/d8991ec727ad0fb64fe9957a3cd307387a6701e4"
},
{
"type": "PACKAGE",
"url": "https://github.com/rust-ethereum/evm"
},
{
"type": "WEB",
"url": "https://github.com/rust-ethereum/evm/blob/release-v041/src/executor/stack/executor.rs#L1012C25-L1012C69"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "Rust EVM erroneousle handles `record_external_operation` error return"
}
GHSA-2C58-JP5Q-Q38F
Vulnerability from github – Published: 2025-07-30 00:32 – Updated: 2025-11-05 00:31A logic issue was addressed with improved checks. This issue is fixed in macOS Sequoia 15.6. A download's origin may be incorrectly associated.
{
"affected": [],
"aliases": [
"CVE-2025-43240"
],
"database_specific": {
"cwe_ids": [
"CWE-703"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-07-30T00:15:35Z",
"severity": "MODERATE"
},
"details": "A logic issue was addressed with improved checks. This issue is fixed in macOS Sequoia 15.6. A download\u0027s origin may be incorrectly associated.",
"id": "GHSA-2c58-jp5q-q38f",
"modified": "2025-11-05T00:31:23Z",
"published": "2025-07-30T00:32:22Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-43240"
},
{
"type": "WEB",
"url": "https://lists.debian.org/debian-lts-announce/2025/08/msg00015.html"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/124149"
},
{
"type": "WEB",
"url": "https://support.apple.com/en-us/124152"
},
{
"type": "WEB",
"url": "http://seclists.org/fulldisclosure/2025/Aug/0"
},
{
"type": "WEB",
"url": "http://seclists.org/fulldisclosure/2025/Jul/32"
},
{
"type": "WEB",
"url": "http://www.openwall.com/lists/oss-security/2025/08/02/1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-2M4X-4Q9J-W97G
Vulnerability from github – Published: 2022-07-01 00:01 – Updated: 2024-05-20 21:27An issue in the AST parser (ast/compile.go) of Open Policy Agent v0.10.2 allows attackers to cause a Denial of Service (DoS) via a crafted input.
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/open-policy-agent/opa"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.42.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2022-33082"
],
"database_specific": {
"cwe_ids": [
"CWE-703"
],
"github_reviewed": true,
"github_reviewed_at": "2022-07-06T19:52:00Z",
"nvd_published_at": "2022-06-30T22:15:00Z",
"severity": "HIGH"
},
"details": "An issue in the AST parser (ast/compile.go) of Open Policy Agent v0.10.2 allows attackers to cause a Denial of Service (DoS) via a crafted input.",
"id": "GHSA-2m4x-4q9j-w97g",
"modified": "2024-05-20T21:27:56Z",
"published": "2022-07-01T00:01:03Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-33082"
},
{
"type": "WEB",
"url": "https://github.com/open-policy-agent/opa/issues/4761"
},
{
"type": "WEB",
"url": "https://github.com/open-policy-agent/opa/issues/4762"
},
{
"type": "WEB",
"url": "https://github.com/open-policy-agent/opa/pull/4701"
},
{
"type": "WEB",
"url": "https://github.com/open-policy-agent/opa/commit/064f6168a8dfebdeb2ea147f7882bb9f5d2b7f67"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-policy-agent/opa"
},
{
"type": "WEB",
"url": "https://github.com/open-policy-agent/opa/blob/598176de326025451025225aca53e85708d5f1db/ast/compile.go#L1224"
},
{
"type": "WEB",
"url": "https://pkg.go.dev/vuln/GO-2022-0574"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Denial of service in Open Policy Agent "
}
GHSA-2QMW-PVF7-4MW6
Vulnerability from github – Published: 2024-07-11 21:31 – Updated: 2024-09-06 21:38Vault and Vault Enterprise did not properly handle requests originating from unauthorized IP addresses when the TCP listener option, proxy_protocol_behavior, was set to deny_unauthorized. When receiving a request from a source IP address that was not listed in proxy_protocol_authorized_addrs, the Vault API server would shut down and no longer respond to any HTTP requests, potentially resulting in denial of service.
While this bug also affected versions of Vault up to 1.17.1 and 1.16.5, a separate regression in those release series did not allow Vault operators to configure the deny_unauthorized option, thus not allowing the conditions for the denial of service to occur.
Fixed in Vault and Vault Enterprise 1.17.2, 1.16.6, and 1.15.12
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/hashicorp/vault"
},
"ranges": [
{
"events": [
{
"introduced": "1.10.0"
},
{
"fixed": "1.15.12"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/hashicorp/vault"
},
"ranges": [
{
"events": [
{
"introduced": "1.16.0-rc1"
},
{
"fixed": "1.16.3"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Go",
"name": "github.com/hashicorp/vault"
},
"ranges": [
{
"events": [
{
"introduced": "1.17.0-rc1"
},
{
"fixed": "1.17.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-6468"
],
"database_specific": {
"cwe_ids": [
"CWE-703"
],
"github_reviewed": true,
"github_reviewed_at": "2024-07-12T14:02:32Z",
"nvd_published_at": "2024-07-11T21:15:12Z",
"severity": "HIGH"
},
"details": "Vault and Vault Enterprise did not properly handle requests originating from unauthorized IP addresses when the TCP listener option, proxy_protocol_behavior, was set to deny_unauthorized. When receiving a request from a source IP address that was not listed in proxy_protocol_authorized_addrs, the Vault API server would shut down and no longer respond to any HTTP requests, potentially resulting in denial of service.\n\nWhile this bug also affected versions of Vault up to 1.17.1 and 1.16.5, a separate regression in those release series did not allow Vault operators to configure the deny_unauthorized option, thus not allowing the conditions for the denial of service to occur.\n\nFixed in Vault and Vault Enterprise 1.17.2, 1.16.6, and 1.15.12",
"id": "GHSA-2qmw-pvf7-4mw6",
"modified": "2024-09-06T21:38:47Z",
"published": "2024-07-11T21:31:13Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-6468"
},
{
"type": "WEB",
"url": "https://discuss.hashicorp.com/t/hcsec-2024-14-vault-vulnerable-to-denial-of-service-when-setting-a-proxy-protocol-behavior/68518"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-2qmw-pvf7-4mw6"
},
{
"type": "PACKAGE",
"url": "https://github.com/hashicorp/vault"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Hashicorp Vault vulnerable to Improper Check or Handling of Exceptional Conditions "
}
GHSA-2V2P-6J97-CJG9
Vulnerability from github – Published: 2026-10-05 23:19 – Updated: 2026-10-05 23:19Summary
An untrusted script run by VM.run can construct an embedder-exposed host function that returns a rejected native Promise and ignore the result. The sandbox-to-host construct trap forwards that Promise without applying the host-side rejection handling already used by the neighboring apply trap, so Node's strict unhandled-rejection policy terminates the host process.
Technical Details
The precondition is an application-supplied constructable host function in the VM sandbox whose constructor body returns a native rejected Promise. In JavaScript, an object explicitly returned by a constructor replaces the newly allocated instance, so new HostReject() produces that Promise.
VM.run executes the attacker-controlled source. For an ordinary host-function call, BaseHandler.apply invokes the host function, calls markHostPromiseHandled(ret), and then wraps the result. The adjacent BaseHandler.construct path instead calls Reflect.construct and returns thisFromOtherWithFactory(...) without calling the same sanitizer. The rejected host Promise therefore crosses the bridge still unhandled. With Node's strict unhandled-rejection behavior, the rejection is promoted to an uncaught exception and kills the host process.
The shortest path is VM.run → the sandbox bridge → BaseHandler.construct. The control changes only the guest expression: it attaches .catch(function () {}) to the constructed Promise before ignoring it. The host function, VM configuration, rejection, and Node policy remain the same, and the control process survives.
This is unintended because the repository's GHSA-gjq8 hardening explicitly marks host Promises handled at the apply boundary, while no equivalent handling exists in the adjacent construct return path. The construct path is a distinct boundary and fix surface, not a second invocation of the already-fixed apply path.
PoV
Save the following as construct-promise-poc.js:
'use strict';
const { VM } = require(process.cwd() + '/lib/main.js');
function HostReject() {
return Promise.reject(new Error('constructed-host-boom'));
}
const mode = process.argv[2];
const vm = new VM({ sandbox: { HostReject } });
if (mode === 'vulnerable') {
console.log('VULNERABLE_STARTED');
vm.run('new HostReject(); 1');
setTimeout(() => console.log('ALIVE'), 300);
} else if (mode === 'control') {
vm.run('new HostReject().catch(function () {}); 1');
setTimeout(() => console.log('CONTROL_ALIVE'), 300);
} else {
throw new Error('usage: node construct-promise-poc.js vulnerable|control');
}
PoC
Check out vm2 revision 91034466bfb7f56b95fd48083ec6ca36d058f164, install its declared dependencies with npm ci --ignore-scripts, and save construct-promise-poc.js in that checkout's module root (the directory containing package.json and lib/). Run the script and both commands below from that same module-root directory. The script resolves lib/main.js from the current directory, so the test uses the checked-out vm2 source.
Tested with vm2 3.11.8 at that revision on Node.js v26.8.1, with NODE_OPTIONS=--unhandled-rejections=strict. The abort flag makes the process crash signal explicit:
NODE_OPTIONS=--unhandled-rejections=strict node --abort-on-uncaught-exception construct-promise-poc.js vulnerable
Abridged vulnerable output:
VULNERABLE_STARTED
Error: constructed-host-boom
at VM2 Wrapper.construct (.../lib/bridge.js:2340:11)
at VM.run (.../lib/vm.js:613:16)
The process exits before printing ALIVE (exit status 139 in the tested execution). The stack paths are environment-dependent; the construct and VM.run frames identify the relevant target functions.
The otherwise identical control is:
NODE_OPTIONS=--unhandled-rejections=strict node --abort-on-uncaught-exception construct-promise-poc.js control
Control output:
CONTROL_ALIVE
The control exits successfully. The crash is a process-level availability failure, not a guest exception caught and returned by VM.run.
Impact
An attacker who can submit JavaScript to a VM and who receives a constructable Promise-returning host API can terminate the Node.js process hosting the sandbox with one expression. This can take down a plugin worker, notebook kernel, queue consumer, or multi-tenant execution worker serving other users. The exploit does not require filesystem access, a Node builtin, nested VMs, or host compromise. It requires the embedder to expose the host constructor and the host to use strict unhandled-rejection handling.
The impact is host availability only; this report does not claim confidentiality or integrity impact. The typed classification is CWE-248 (Uncaught Exception) and CWE-703 (Improper Check or Handling of Exceptional Conditions), with CVSS 3.1 AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H (8.6, High) for a deployment that accepts untrusted code over a network boundary.
Suggested Fix
Restore the bridge invariant that every host-native Promise crossing into the sandbox is marked handled before it can be ignored. In BaseHandler.construct, add the same markHostPromiseHandled(ret) call used by BaseHandler.apply, after the host result is produced and before it is converted and returned:
stripDangerousSymbolsFromHostResult(ret);
markHostPromiseHandled(ret);
return thisFromOtherWithFactory(getHandlerFactory(this), ret, thisFromOther(object));
The call should attach the benign host-side rejection reaction without changing the Promise value or preventing sandbox code that later attaches .catch() or .then(..., onRejected) from observing the rejection. Regression tests should cover an ignored rejected Promise returned through new, the caught control, ordinary non-Promise constructor returns, and the existing apply-path behavior.
Affected Package/Versions
- Package:
vm2(npm). - Confirmed vulnerable:
3.11.8, including revision91034466bfb7f56b95fd48083ec6ca36d058f164. - The exact tested affected range is
3.11.8; no broader version range is inferred from this test.
Advisory History
The public GHSA-gjq8-xm47-88rc advisory describes host-returned Promise rejection termination and records 3.11.8 as its patched version. Its fix and reproduction cover a host function invoked through the bridge apply route. This report uses the distinct construct route reached by new, where the tested 3.11.8 source still omits markHostPromiseHandled(ret). A fix for apply does not automatically fix this adjacent return path.
The checked related public history includes GHSA-hw58-p9xv-2mjh, the earlier sandbox-Promise constructor issue. GHSA-hw58 concerns an error raised by a Promise executor for a Promise created inside the sandbox; it is the sandbox-native localPromise/executor path, not GHSA-gjq8's host-returned Promise through BaseHandler.apply and not this host-constructor result through BaseHandler.construct.
The checked search also found PR #421, “Handle errors thrown in async functions”. That is a separate async-error history item concerning errors from async functions and timer callbacks, with general unhandled-rejection handling, rather than a host Promise returned through the GHSA-gjq8 apply boundary or a Promise returned by an exposed constructor through this construct trap.
The bound prior local report, titled “NodeVM crypto sanitizer exposes process-wide crypto.setFips,” covers a different root cause: an allowlisted crypto builtin exposes setFips, allowing guest code to mutate host-wide cryptographic state. Its boundary and fix surface are builtin sanitization and process-state mutation, not host-Promise rejection handling; it therefore differs from both the GHSA-gjq8 apply route and this BaseHandler.construct route. No prior report covering this construct-trap root cause was found.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 3.12.1"
},
"package": {
"ecosystem": "npm",
"name": "vm2"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.12.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-100722"
],
"database_specific": {
"cwe_ids": [
"CWE-248",
"CWE-703"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-05T23:19:55Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Summary\n\nAn untrusted script run by `VM.run` can construct an embedder-exposed host function that returns a rejected native Promise and ignore the result. The sandbox-to-host `construct` trap forwards that Promise without applying the host-side rejection handling already used by the neighboring `apply` trap, so Node\u0027s strict unhandled-rejection policy terminates the host process.\n\n## Technical Details\n\nThe precondition is an application-supplied constructable host function in the VM sandbox whose constructor body returns a native rejected Promise. In JavaScript, an object explicitly returned by a constructor replaces the newly allocated instance, so `new HostReject()` produces that Promise.\n\n`VM.run` executes the attacker-controlled source. For an ordinary host-function call, `BaseHandler.apply` invokes the host function, calls `markHostPromiseHandled(ret)`, and then wraps the result. The adjacent `BaseHandler.construct` path instead calls `Reflect.construct` and returns `thisFromOtherWithFactory(...)` without calling the same sanitizer. The rejected host Promise therefore crosses the bridge still unhandled. With Node\u0027s strict unhandled-rejection behavior, the rejection is promoted to an uncaught exception and kills the host process.\n\nThe shortest path is `VM.run` \u2192 the sandbox bridge \u2192 `BaseHandler.construct`. The control changes only the guest expression: it attaches `.catch(function () {})` to the constructed Promise before ignoring it. The host function, VM configuration, rejection, and Node policy remain the same, and the control process survives.\n\nThis is unintended because the repository\u0027s GHSA-gjq8 hardening explicitly marks host Promises handled at the apply boundary, while no equivalent handling exists in the adjacent construct return path. The construct path is a distinct boundary and fix surface, not a second invocation of the already-fixed apply path.\n\n## PoV\n\nSave the following as `construct-promise-poc.js`:\n\n```js\n\u0027use strict\u0027;\n\nconst { VM } = require(process.cwd() + \u0027/lib/main.js\u0027);\n\nfunction HostReject() {\n return Promise.reject(new Error(\u0027constructed-host-boom\u0027));\n}\n\nconst mode = process.argv[2];\nconst vm = new VM({ sandbox: { HostReject } });\n\nif (mode === \u0027vulnerable\u0027) {\n console.log(\u0027VULNERABLE_STARTED\u0027);\n vm.run(\u0027new HostReject(); 1\u0027);\n setTimeout(() =\u003e console.log(\u0027ALIVE\u0027), 300);\n} else if (mode === \u0027control\u0027) {\n vm.run(\u0027new HostReject().catch(function () {}); 1\u0027);\n setTimeout(() =\u003e console.log(\u0027CONTROL_ALIVE\u0027), 300);\n} else {\n throw new Error(\u0027usage: node construct-promise-poc.js vulnerable|control\u0027);\n}\n```\n\n## PoC\n\nCheck out vm2 revision `91034466bfb7f56b95fd48083ec6ca36d058f164`, install its declared dependencies with `npm ci --ignore-scripts`, and save `construct-promise-poc.js` in that checkout\u0027s module root (the directory containing `package.json` and `lib/`). Run the script and both commands below from that same module-root directory. The script resolves `lib/main.js` from the current directory, so the test uses the checked-out vm2 source.\n\nTested with vm2 `3.11.8` at that revision on Node.js `v26.8.1`, with `NODE_OPTIONS=--unhandled-rejections=strict`. The abort flag makes the process crash signal explicit:\n\n```text\nNODE_OPTIONS=--unhandled-rejections=strict node --abort-on-uncaught-exception construct-promise-poc.js vulnerable\n```\n\nAbridged vulnerable output:\n\n```text\nVULNERABLE_STARTED\nError: constructed-host-boom\n at VM2 Wrapper.construct (.../lib/bridge.js:2340:11)\n at VM.run (.../lib/vm.js:613:16)\n```\n\nThe process exits before printing `ALIVE` (exit status 139 in the tested execution). The stack paths are environment-dependent; the `construct` and `VM.run` frames identify the relevant target functions.\n\nThe otherwise identical control is:\n\n```text\nNODE_OPTIONS=--unhandled-rejections=strict node --abort-on-uncaught-exception construct-promise-poc.js control\n```\n\nControl output:\n\n```text\nCONTROL_ALIVE\n```\n\nThe control exits successfully. The crash is a process-level availability failure, not a guest exception caught and returned by `VM.run`.\n\n## Impact\n\nAn attacker who can submit JavaScript to a VM and who receives a constructable Promise-returning host API can terminate the Node.js process hosting the sandbox with one expression. This can take down a plugin worker, notebook kernel, queue consumer, or multi-tenant execution worker serving other users. The exploit does not require filesystem access, a Node builtin, nested VMs, or host compromise. It requires the embedder to expose the host constructor and the host to use strict unhandled-rejection handling.\n\nThe impact is host availability only; this report does not claim confidentiality or integrity impact. The typed classification is CWE-248 (Uncaught Exception) and CWE-703 (Improper Check or Handling of Exceptional Conditions), with CVSS 3.1 `AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H` (8.6, High) for a deployment that accepts untrusted code over a network boundary.\n\n## Suggested Fix\n\nRestore the bridge invariant that every host-native Promise crossing into the sandbox is marked handled before it can be ignored. In `BaseHandler.construct`, add the same `markHostPromiseHandled(ret)` call used by `BaseHandler.apply`, after the host result is produced and before it is converted and returned:\n\n```js\nstripDangerousSymbolsFromHostResult(ret);\nmarkHostPromiseHandled(ret);\nreturn thisFromOtherWithFactory(getHandlerFactory(this), ret, thisFromOther(object));\n```\n\nThe call should attach the benign host-side rejection reaction without changing the Promise value or preventing sandbox code that later attaches `.catch()` or `.then(..., onRejected)` from observing the rejection. Regression tests should cover an ignored rejected Promise returned through `new`, the caught control, ordinary non-Promise constructor returns, and the existing apply-path behavior.\n\n## Affected Package/Versions\n\n- Package: `vm2` (npm).\n- Confirmed vulnerable: `3.11.8`, including revision `91034466bfb7f56b95fd48083ec6ca36d058f164`.\n- The exact tested affected range is `3.11.8`; no broader version range is inferred from this test.\n\n## Advisory History\n\nThe public [GHSA-gjq8-xm47-88rc advisory](https://github.com/patriksimek/vm2/security/advisories/GHSA-gjq8-xm47-88rc) describes host-returned Promise rejection termination and records `3.11.8` as its patched version. Its fix and reproduction cover a host function invoked through the bridge `apply` route. This report uses the distinct `construct` route reached by `new`, where the tested `3.11.8` source still omits `markHostPromiseHandled(ret)`. A fix for `apply` does not automatically fix this adjacent return path.\n\nThe checked related public history includes [GHSA-hw58-p9xv-2mjh](https://github.com/patriksimek/vm2/security/advisories/GHSA-hw58-p9xv-2mjh), the earlier sandbox-Promise constructor issue. GHSA-hw58 concerns an error raised by a Promise executor for a Promise created inside the sandbox; it is the sandbox-native `localPromise`/executor path, not GHSA-gjq8\u0027s host-returned Promise through `BaseHandler.apply` and not this host-constructor result through `BaseHandler.construct`.\n\nThe checked search also found [PR #421, \u201cHandle errors thrown in async functions\u201d](https://github.com/patriksimek/vm2/pull/421). That is a separate async-error history item concerning errors from async functions and timer callbacks, with general unhandled-rejection handling, rather than a host Promise returned through the GHSA-gjq8 `apply` boundary or a Promise returned by an exposed constructor through this `construct` trap.\n\nThe bound prior local report, titled \u201cNodeVM crypto sanitizer exposes process-wide crypto.setFips,\u201d covers a different root cause: an allowlisted `crypto` builtin exposes `setFips`, allowing guest code to mutate host-wide cryptographic state. Its boundary and fix surface are builtin sanitization and process-state mutation, not host-Promise rejection handling; it therefore differs from both the GHSA-gjq8 `apply` route and this `BaseHandler.construct` route. No prior report covering this construct-trap root cause was found.",
"id": "GHSA-2v2p-6j97-cjg9",
"modified": "2026-10-05T23:19:55Z",
"published": "2026-10-05T23:19:55Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/security/advisories/GHSA-2v2p-6j97-cjg9"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-100722"
},
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/commit/6e487df9518e455e5e60faec3df3a0e2e27f9940"
},
{
"type": "PACKAGE",
"url": "https://github.com/patriksimek/vm2"
},
{
"type": "WEB",
"url": "https://github.com/patriksimek/vm2/releases/tag/v3.12.2"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/vm2-before-3.12.2-host-process-termination-via-construct-trap"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:N/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:H",
"type": "CVSS_V4"
}
],
"summary": "vm2: Host Promise rejection from an exposed constructor can terminate the vm2 host process"
}
GHSA-2W9W-FGQ6-2VMC
Vulnerability from github – Published: 2025-11-11 18:30 – Updated: 2025-11-19 21:31Sandbox escape due to incorrect boundary conditions in the Graphics: WebGPU component. This vulnerability affects Firefox < 145.
{
"affected": [],
"aliases": [
"CVE-2025-13023"
],
"database_specific": {
"cwe_ids": [
"CWE-703"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-11-11T16:15:39Z",
"severity": "CRITICAL"
},
"details": "Sandbox escape due to incorrect boundary conditions in the Graphics: WebGPU component. This vulnerability affects Firefox \u003c 145.",
"id": "GHSA-2w9w-fgq6-2vmc",
"modified": "2025-11-19T21:31:18Z",
"published": "2025-11-11T18:30:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-13023"
},
{
"type": "WEB",
"url": "https://bugzilla.mozilla.org/show_bug.cgi?id=1992032"
},
{
"type": "WEB",
"url": "https://www.mozilla.org/security/advisories/mfsa2025-87"
},
{
"type": "WEB",
"url": "https://www.mozilla.org/security/advisories/mfsa2025-90"
}
],
"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-32J9-6QQM-MQ9G
Vulnerability from github – Published: 2022-03-17 00:00 – Updated: 2022-03-30 20:16The package node-lmdb before 0.9.7 is vulnerable to Denial of Service (DoS) when defining a non-invokable ToString value, which will cause a crash during type check.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "node-lmdb"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.9.7"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2022-21164"
],
"database_specific": {
"cwe_ids": [
"CWE-241",
"CWE-703"
],
"github_reviewed": true,
"github_reviewed_at": "2022-03-19T00:10:41Z",
"nvd_published_at": "2022-03-16T16:15:00Z",
"severity": "HIGH"
},
"details": "The package node-lmdb before 0.9.7 is vulnerable to Denial of Service (DoS) when defining a non-invokable `ToString` value, which will cause a crash during type check.",
"id": "GHSA-32j9-6qqm-mq9g",
"modified": "2022-03-30T20:16:16Z",
"published": "2022-03-17T00:00:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2022-21164"
},
{
"type": "WEB",
"url": "https://github.com/Venemo/node-lmdb/commit/97760104c0fd311206b88aecd91fa1f59fe2b85a"
},
{
"type": "PACKAGE",
"url": "https://github.com/Venemo/node-lmdb"
},
{
"type": "WEB",
"url": "https://snyk.io/vuln/SNYK-JS-NODELMDB-2400723"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "Unhandled case in node-lmdb"
}
GHSA-3CX6-J9J4-54MP
Vulnerability from github – Published: 2026-02-03 17:21 – Updated: 2026-02-08 01:01Impact
Private data exports can lead to data leaks in cases where the UUID generation causes collisions for the generated UUIDs.
The bug was introduced by #13571 and affects Decidim versions 0.30.0 or newer (currently 2025-09-23).
This issue was discovered by running the following spec several times in a row, as it can randomly fail due to this bug:
$ cd decidim-core
$ for i in {1..10}; do bundle exec rspec spec/jobs/decidim/download_your_data_export_job_spec.rb -e "deletes the" || break ; done
Run the spec as many times as needed to hit a UUID that converts to 0 through .to_i.
The UUID to zero conversion does not cause a security issue but the security issue is demonstrated with the following example.
The following code regenerates the issue by assigning a predefined UUID that will generate a collision (example assumes there are already two existing users in the system):
# Create the ZIP buffers to be stored
buffer1 = Zip::OutputStream.write_buffer do |out|
out.put_next_entry("admin.txt")
out.write "Hello, admin!"
end
buffer1.rewind
buffer2 = Zip::OutputStream.write_buffer do |out|
out.put_next_entry("user.txt")
out.write "Hello, user!"
end
buffer2.rewind
# Create the private exports with a predefined IDs
user1 = Decidim::User.find(1)
export = user1.private_exports.build
export.id = "0210ae70-482b-4671-b758-35e13e0097a9"
export.export_type = "download_your_data"
export.file.attach(io: buffer1, filename: "foobar.zip", content_type: "application/zip")
export.expires_at = Decidim.download_your_data_expiry_time.from_now
export.metadata = {}
export.save!
user2 = Decidim::User.find(2)
export = user2.private_exports.build
export.id = "0210d2df-a0c7-40aa-ad97-2dae5083e3b8"
export.export_type = "download_your_data"
export.file.attach(io: buffer2, filename: "foobar.zip", content_type: "application/zip")
export.expires_at = Decidim.download_your_data_expiry_time.from_now
export.metadata = {}
export.save!
Expect to see an error in the situation.
Now, login as user with ID 1, go to /download_your_data, click "Download file" from the export and expect to see the data that should be attached to user with ID 2. This is an artificially replicated situation with the predefined UUIDs but it can easily happen in real situations.
The reason for the test case failure can be replicated in case you change the export ID to export.id = "e9540f96-9e3d-4abe-8c2a-6c338d85a684". This would return 0 through .to_s
After attaching that ID, you can test if the file is available for the export:
user.private_exports.last.file.attached?
=> false
user.private_exports.last.file.blob
=> nil
Note that this fails with such UUID as shown in the example and could easily lead to collisions in case the UUID starts with a number. E.g. UUID "0210ae70-482b-4671-b758-35e13e0097a9" would convert to 210 through .to_s. Therefore, if someone else has a "private" export with the prefixes "00000210", "0000210", "000210", "00210", "0210" or "210", that would cause a collision and the file could be attached to the wrong private export.
Theoretical chance of collision (the reality depends on the UUID generation algorithm):
- Potential combinations of the UUID first part (8 characters hex): 16^8
- Potentially colliding character combinations (8 numbers characters in the range of 0-9): 10^8
- 10^8 / 16^8 ≈ 2.3% (23 / 1000 users)
The root cause is that the class Decidim::PrivateExport defines an ActiveStorage relation to file and the table active_storage_attachments stores the related record_id as bigint which causes the conversion to happen.
Workarounds
Fully disable the private exports feature until a patch is available.
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "decidim-core"
},
"ranges": [
{
"events": [
{
"introduced": "0.30.0"
},
{
"fixed": "0.30.4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "decidim"
},
"ranges": [
{
"events": [
{
"introduced": "0.30.0"
},
{
"fixed": "0.30.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-65017"
],
"database_specific": {
"cwe_ids": [
"CWE-200",
"CWE-703"
],
"github_reviewed": true,
"github_reviewed_at": "2026-02-03T17:21:17Z",
"nvd_published_at": "2026-02-03T15:16:12Z",
"severity": "HIGH"
},
"details": "### Impact\nPrivate data exports can lead to data leaks in cases where the UUID generation causes collisions for the generated UUIDs.\n\nThe bug was introduced by #13571 and affects Decidim versions 0.30.0 or newer (currently 2025-09-23).\n\nThis issue was discovered by running the following spec several times in a row, as it can randomly fail due to this bug:\n\n```bash\n$ cd decidim-core\n$ for i in {1..10}; do bundle exec rspec spec/jobs/decidim/download_your_data_export_job_spec.rb -e \"deletes the\" || break ; done\n```\n\nRun the spec as many times as needed to hit a UUID that converts to `0` through `.to_i`.\n\nThe UUID to zero conversion does not cause a security issue but the security issue is demonstrated with the following example.\n\nThe following code regenerates the issue by assigning a predefined UUID that will generate a collision (example assumes there are already two existing users in the system):\n\n```ruby\n# Create the ZIP buffers to be stored\nbuffer1 = Zip::OutputStream.write_buffer do |out|\n out.put_next_entry(\"admin.txt\")\n out.write \"Hello, admin!\"\nend\nbuffer1.rewind\nbuffer2 = Zip::OutputStream.write_buffer do |out|\n out.put_next_entry(\"user.txt\")\n out.write \"Hello, user!\"\nend\nbuffer2.rewind\n\n# Create the private exports with a predefined IDs\nuser1 = Decidim::User.find(1)\nexport = user1.private_exports.build\nexport.id = \"0210ae70-482b-4671-b758-35e13e0097a9\"\nexport.export_type = \"download_your_data\"\nexport.file.attach(io: buffer1, filename: \"foobar.zip\", content_type: \"application/zip\")\nexport.expires_at = Decidim.download_your_data_expiry_time.from_now\nexport.metadata = {}\nexport.save!\n\n\nuser2 = Decidim::User.find(2)\nexport = user2.private_exports.build\nexport.id = \"0210d2df-a0c7-40aa-ad97-2dae5083e3b8\"\nexport.export_type = \"download_your_data\"\nexport.file.attach(io: buffer2, filename: \"foobar.zip\", content_type: \"application/zip\")\nexport.expires_at = Decidim.download_your_data_expiry_time.from_now\nexport.metadata = {}\nexport.save!\n```\n\nExpect to see an error in the situation.\n\nNow, login as user with ID 1, go to `/download_your_data`, click \"Download file\" from the export and expect to see the data that should be attached to user with ID 2. This is an artificially replicated situation with the predefined UUIDs but it can easily happen in real situations.\n\nThe reason for the test case failure can be replicated in case you change the export ID to `export.id = \"e9540f96-9e3d-4abe-8c2a-6c338d85a684\"`. This would return `0` through `.to_s`\n\nAfter attaching that ID, you can test if the file is available for the export:\n\n```ruby\nuser.private_exports.last.file.attached?\n=\u003e false\nuser.private_exports.last.file.blob\n=\u003e nil\n```\n\nNote that this fails with such UUID as shown in the example and could easily lead to collisions in case the UUID starts with a number. E.g. UUID `\"0210ae70-482b-4671-b758-35e13e0097a9\"` would convert to `210` through `.to_s`. Therefore, if someone else has a \"private\" export with the prefixes \"00000210\", \"0000210\", \"000210\", \"00210\", \"0210\" or \"210\", that would cause a collision and the file could be attached to the wrong private export.\n\nTheoretical chance of collision (the reality depends on the UUID generation algorithm):\n\n- Potential combinations of the UUID first part (8 characters hex): 16^8\n- Potentially colliding character combinations (8 numbers characters in the range of 0-9): 10^8\n- 10^8 / 16^8 \u2248 2.3% (23 / 1000 users)\n\nThe root cause is that the class `Decidim::PrivateExport` defines an ActiveStorage relation to `file` and the table `active_storage_attachments` stores the related `record_id` as `bigint` which causes the conversion to happen.\n\n### Workarounds\nFully disable the private exports feature until a patch is available.",
"id": "GHSA-3cx6-j9j4-54mp",
"modified": "2026-02-08T01:01:36Z",
"published": "2026-02-03T17:21:17Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/decidim/decidim/security/advisories/GHSA-3cx6-j9j4-54mp"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-65017"
},
{
"type": "WEB",
"url": "https://github.com/decidim/decidim/pull/13571"
},
{
"type": "PACKAGE",
"url": "https://github.com/decidim/decidim"
},
{
"type": "WEB",
"url": "https://github.com/decidim/decidim/releases/tag/v0.30.4"
},
{
"type": "WEB",
"url": "https://github.com/decidim/decidim/releases/tag/v0.31.0"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/decidim-core/CVE-2025-65017.yml"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/decidim/CVE-2025-65017.yml"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:P/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Decidim\u0027s private data exports can lead to data leaks"
}
No mitigation information available for this CWE.
No CAPEC attack patterns related to this CWE.