CWE-470
AllowedUse of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection')
Abstraction: Base · Status: Draft
The product uses external input with reflection to select which classes or code to use, but it does not sufficiently prevent the input from selecting improper classes or code.
113 vulnerabilities reference this CWE, most recent first.
GHSA-CX4M-2P55-RW7J
Vulnerability from github – Published: 2026-05-04 18:30 – Updated: 2026-05-08 17:53Arbitrary Class Instantiation via Model Manifest in Apache OpenNLP ExtensionLoader
Versions Affected: before 2.5.9, before 3.0.0-M3
Description:
The ExtensionLoader.instantiateExtension(Class, String) method loads a class by its fully-qualified name via Class.forName() and invokes its no-arg constructor, with the class name sourced from the manifest.properties entry of a model archive. The existing isAssignableFrom check correctly rejects classes that are not subtypes of the expected extension interface (BaseToolFactory for factory=, ArtifactSerializer for serializer-class-*), but the check runs after Class.forName() has already loaded and initialized the named class.
Class.forName() with default initialization semantics executes the target class's static initializer before returning, so an attacker who can supply a crafted model archive can cause the static initializer of any class on the classpath to run during model loading, regardless of whether that class passes the subsequent type check.
Exploitation requires a class with attacker-useful side effects in its static initializer (for example, JNDI lookup, outbound network I/O, or filesystem access) to be present on the classpath, so this is not a drop-in remote code execution; however, the attack surface grows as third-party model distribution becomes more common (community model repositories, Hugging Face-style sharing), where users routinely load model files from origins they do not control. A secondary, narrower vector affects deployments that ship legitimate BaseToolFactory or ArtifactSerializer subclasses with side-effecting no-arg constructors: a malicious manifest can name such a class and force its constructor to run during model load.
Mitigation:
- 2.x users should upgrade to 2.5.9.
- 3.x users should upgrade to 3.0.0-M3.
Note: The fix introduces a package-prefix allowlist that is consulted before Class.forName() is invoked, so the static initializer of a disallowed class is never executed. Classes under the opennlp. prefix remain permitted by default. Deployments that load models referencing factories or serializers outside opennlp.* must opt those packages in, either programmatically via ExtensionLoader.registerAllowedPackage(String) before the first model load, or by setting the OPENNLP_EXT_ALLOWED_PACKAGES system property to a comma-separated list of allowed package prefixes.
Users who cannot upgrade immediately should ensure that all model files are sourced from trusted origins and should audit their classpath for classes with side-effecting static initializers or constructors, particularly any that perform JNDI lookups, network requests, or filesystem operations during class initialization.
{
"affected": [
{
"package": {
"ecosystem": "Maven",
"name": "org.apache.opennlp:opennlp-tools"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2.5.9"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Maven",
"name": "org.apache.opennlp:opennlp-tools"
},
"ranges": [
{
"events": [
{
"introduced": "3.0.0-M1"
},
{
"fixed": "3.0.0-M3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42027"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-08T17:53:14Z",
"nvd_published_at": "2026-05-04T17:16:24Z",
"severity": "CRITICAL"
},
"details": "Arbitrary Class Instantiation via Model Manifest in Apache OpenNLP ExtensionLoader\n\n\n\n\n\nVersions Affected: before 2.5.9, before 3.0.0-M3\n\n\n\n\n\nDescription:\u00a0\n\nThe ExtensionLoader.instantiateExtension(Class, String)\u00a0method loads a class by its fully-qualified name via Class.forName()\u00a0and invokes its no-arg constructor, with the class name sourced from the manifest.properties\u00a0entry of a model archive. The existing isAssignableFrom\u00a0check correctly rejects classes that are not subtypes of the expected extension interface (BaseToolFactory\u00a0for factory=, ArtifactSerializer\u00a0for serializer-class-*), but the check runs after\u00a0Class.forName()\u00a0has already loaded and initialized the named class. \n\nClass.forName()\u00a0with default initialization semantics executes the target class\u0027s static initializer before returning, so an attacker who can supply a crafted model archive can cause the static initializer of any class on the classpath to run during model loading, regardless of whether that class passes the subsequent type check. \n\nExploitation requires a class with attacker-useful side effects in its static initializer (for example, JNDI lookup, outbound network I/O, or filesystem access) to be present on the classpath, so this is not a drop-in remote code execution; however, the attack surface grows as third-party model distribution becomes more common (community model repositories, Hugging Face-style sharing), where users routinely load model files from origins they do not control. A secondary, narrower vector affects deployments that ship legitimate BaseToolFactory\u00a0or ArtifactSerializer\u00a0subclasses with side-effecting no-arg constructors: a malicious manifest can name such a class and force its constructor to run during model load.\n\n\n\n\n\nMitigation:\u00a0\n\n\n\n * 2.x users should upgrade to 2.5.9. \n * 3.x users should upgrade to 3.0.0-M3. \n\n\n\n\nNote: The fix introduces a package-prefix allowlist that is consulted before Class.forName()\u00a0is invoked, so the static initializer of a disallowed class is never executed. Classes under the opennlp.\u00a0prefix remain permitted by default. Deployments that load models referencing factories or serializers outside opennlp.*\u00a0must opt those packages in, either programmatically via ExtensionLoader.registerAllowedPackage(String)\u00a0before the first model load, or by setting the OPENNLP_EXT_ALLOWED_PACKAGES\u00a0system property to a comma-separated list of allowed package prefixes. \n\nUsers who cannot upgrade immediately should ensure that all model files are sourced from trusted origins\u00a0and should audit their classpath for classes with side-effecting static initializers or constructors, particularly any that perform JNDI lookups, network requests, or filesystem operations during class initialization.",
"id": "GHSA-cx4m-2p55-rw7j",
"modified": "2026-05-08T17:53:14Z",
"published": "2026-05-04T18:30:31Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42027"
},
{
"type": "PACKAGE",
"url": "https://github.com/apache/opennlp"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread/ltlo4powjfc0w2w2yyl1o5tc7q1gcb2y"
},
{
"type": "WEB",
"url": "http://www.openwall.com/lists/oss-security/2026/05/01/20"
}
],
"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": "Apache OpenNLP ExtensionLoader Vulnerable to Arbitrary Class Instantiation via Model Manifest"
}
GHSA-F78J-4W3G-4Q65
Vulnerability from github – Published: 2024-03-12 15:44 – Updated: 2024-09-25 20:56Summary
More methods than expected can be called on reflex instances. Being able to call some of them has security implications.
Details
To invoke a reflex a websocket message of the following shape is sent:
{
"target": "[class_name]#[method_name]",
"args": []
}
The server will proceed to instantiate reflex using the provided class_name as long as it extends StimulusReflex::Reflex.
It then attempts to call method_name on the instance with the provided arguments ref:
method = reflex.method method_name
required_params = method.parameters.select { |(kind, _)| kind == :req }
optional_params = method.parameters.select { |(kind, _)| kind == :opt }
if arguments.size >= required_params.size && arguments.size <= required_params.size + optional_params.size
reflex.public_send(method_name, *arguments)
end
This is problematic as reflex.method(method_name) can be more methods than those explicitly specified by the developer in their reflex class. A good example is the instance_variable_set method.
{
"target": "ChatReflex#instance_variable_set",
"args": ["@user", "<admin-id>"]
}
Here are other interesting methods that were found to be available for the [ChatReflex sample reflex](https://github.com/hopsoft/stimulus_reflex_expo/blob/dcce8c36a6782d1e7f57f0e2766a3f6fd770b3b1/app/reflexes/chat_reflex.rb)
- `remote_byebug`: bind a debugging server
- `pry`: drop the process in a REPL session
All in all, only counting `:req` and `:opt` parameters helps.
For example around [version 1.0](https://github.com/stimulusreflex/stimulus_reflex/blob/1f610b636abfed27de2c61104aebd1ac98180d5b/lib/stimulus_reflex/channel.rb#L41) only `.arity` was checked which allowed access to the `system` method (`.arity == -1`)
{
"target": "ChatReflex#system",
"args": ["[command here]"]
}
Using `public_send` instead of `send` does not help but the following payloads **do not** work since `:rest` parameters are not counted in the current version
{
"target": "ChatReflex#send",
"args": ["system", "[command here]"]
}
{
"target": "ChatReflex#instance_eval",
"args": ["system('[command here]')"]
}
Pre-versions of 3.5.0 added a render_collection method on reflexes with a :req parameter. Calling this method could lead to arbitrary code execution:
{
"target": "StimulusReflex::Reflex#render_collection",
"args": [
{ "inline": "<% system('[command here]') %>" }
]
}
Patches
Patches are available on RubyGems and on NPM.
The patched versions are:
- 3.4.2
- 3.5.0.rc4
Workaround
You can add this guard to mitigate the issue if running an unpatched version of the library.
1.) Make sure all your reflexes inherit from the ApplicationReflex class
2.) Add this before_reflex callback to your app/reflexes/application_reflex.rb file:
class ApplicationReflex < StimulusReflex::Reflex
before_reflex do
ancestors = self.class.ancestors[0..self.class.ancestors.index(StimulusReflex::Reflex) - 1]
allowed = ancestors.any? { |a| a.public_instance_methods(false).any?(method_name.to_sym) }
raise ArgumentError.new("Reflex method '#{method_name}' is not defined on class '#{self.class.name}' or on any of its ancestors") if !allowed
end
end
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "stimulus_reflex"
},
"ranges": [
{
"events": [
{
"introduced": "3.5.0.pre0"
},
{
"fixed": "3.5.0.rc4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "stimulus_reflex"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.4.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "stimulus_reflex"
},
"ranges": [
{
"events": [
{
"introduced": "3.5.0-pre0"
},
{
"fixed": "3.5.0-rc4"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "npm",
"name": "stimulus_reflex"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.4.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2024-28121"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": true,
"github_reviewed_at": "2024-03-12T15:44:49Z",
"nvd_published_at": "2024-03-12T20:15:08Z",
"severity": "HIGH"
},
"details": "### Summary\nMore methods than expected can be called on reflex instances. Being able to call some of them has security implications.\n\n### Details\nTo invoke a reflex a websocket message of the following shape is sent:\n```json\n{ \n \"target\": \"[class_name]#[method_name]\", \n \"args\": [] \n}\n```\nThe server will proceed to instantiate `reflex` using the provided `class_name` as long as it extends `StimulusReflex::Reflex`.\nIt then attempts to call `method_name` on the instance with the provided arguments [ref](https://github.com/stimulusreflex/stimulus_reflex/blob/0211cad7d60fe96838587f159d657e44cee51b9b/app/channels/stimulus_reflex/channel.rb#L83):\n\n```ruby\nmethod = reflex.method method_name\nrequired_params = method.parameters.select { |(kind, _)| kind == :req }\noptional_params = method.parameters.select { |(kind, _)| kind == :opt }\n\nif arguments.size \u003e= required_params.size \u0026\u0026 arguments.size \u003c= required_params.size + optional_params.size\n reflex.public_send(method_name, *arguments)\nend\n```\n\nThis is problematic as `reflex.method(method_name)` can be more methods than those explicitly specified by the developer in their reflex class. A good example is the `instance_variable_set` method.\n\n\u003cdetails\u003e\n\n\u003csummary\u003eRead more\u003c/summary\u003e\nLet\u0027s imagine a reflex that uses `@user` as a trusted variable in an `after_reflex` callback.\n\nThis variable can be overwritten using the following message:\n```json\n{\n \"target\": \"ChatReflex#instance_variable_set\", \n \"args\": [\"@user\", \"\u003cadmin-id\u003e\"]\n}\n```\n\nHere are other interesting methods that were found to be available for the [ChatReflex sample reflex](https://github.com/hopsoft/stimulus_reflex_expo/blob/dcce8c36a6782d1e7f57f0e2766a3f6fd770b3b1/app/reflexes/chat_reflex.rb)\n- `remote_byebug`: bind a debugging server\n- `pry`: drop the process in a REPL session\n\nAll in all, only counting `:req` and `:opt` parameters helps.\nFor example around [version 1.0](https://github.com/stimulusreflex/stimulus_reflex/blob/1f610b636abfed27de2c61104aebd1ac98180d5b/lib/stimulus_reflex/channel.rb#L41) only `.arity` was checked which allowed access to the `system` method (`.arity == -1`)\n```json\n{\n \"target\": \"ChatReflex#system\", \n \"args\": [\"[command here]\"]\n}\n```\nUsing `public_send` instead of `send` does not help but the following payloads **do not** work since `:rest` parameters are not counted in the current version\n```json\n{\n \"target\": \"ChatReflex#send\", \n \"args\": [\"system\", \"[command here]\"] \n}\n```\n```json\n{ \n \"target\": \"ChatReflex#instance_eval\", \n \"args\": [\"system(\u0027[command here]\u0027)\"]\n}\n```\n\n\u003c/details\u003e\n\nPre-versions of 3.5.0 added a `render_collection` method on reflexes with a `:req` parameter. Calling this method could lead to arbitrary code execution:\n```json\n{\n \"target\": \"StimulusReflex::Reflex#render_collection\", \n \"args\": [\n { \"inline\": \"\u003c% system(\u0027[command here]\u0027) %\u003e\" }\n ]\n}\n```\n\n### Patches\n\nPatches are [available on RubyGems](https://rubygems.org/gems/stimulus_reflex) and on [NPM](https://npmjs.org/package/stimulus_reflex). \n\nThe patched versions are: \n- [`3.4.2`](https://github.com/stimulusreflex/stimulus_reflex/releases/tag/v3.4.2)\n- [`3.5.0.rc4`](https://github.com/stimulusreflex/stimulus_reflex/releases/tag/v3.5.0.rc4)\n\n### Workaround\n\nYou can add this guard to mitigate the issue if running an unpatched version of the library. \n\n1.) Make sure all your reflexes inherit from the `ApplicationReflex` class\n2.) Add this `before_reflex` callback to your `app/reflexes/application_reflex.rb` file:\n\n```ruby\nclass ApplicationReflex \u003c StimulusReflex::Reflex\n before_reflex do\n ancestors = self.class.ancestors[0..self.class.ancestors.index(StimulusReflex::Reflex) - 1]\n allowed = ancestors.any? { |a| a.public_instance_methods(false).any?(method_name.to_sym) }\n\n raise ArgumentError.new(\"Reflex method \u0027#{method_name}\u0027 is not defined on class \u0027#{self.class.name}\u0027 or on any of its ancestors\") if !allowed\n end\nend\n```",
"id": "GHSA-f78j-4w3g-4q65",
"modified": "2024-09-25T20:56:24Z",
"published": "2024-03-12T15:44:49Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/stimulusreflex/stimulus_reflex/security/advisories/GHSA-f78j-4w3g-4q65"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-28121"
},
{
"type": "WEB",
"url": "https://github.com/stimulusreflex/stimulus_reflex/commit/538582d240439aab76066c72335ea92096cd0c7f"
},
{
"type": "WEB",
"url": "https://github.com/stimulusreflex/stimulus_reflex/commit/d823d7348f9ca42eb6df25574f11974e4f5bc88c"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/stimulus_reflex/CVE-2024-28121.yml"
},
{
"type": "PACKAGE",
"url": "https://github.com/stimulusreflex/stimulus_reflex"
},
{
"type": "WEB",
"url": "https://github.com/stimulusreflex/stimulus_reflex/blob/0211cad7d60fe96838587f159d657e44cee51b9b/app/channels/stimulus_reflex/channel.rb#L83"
},
{
"type": "WEB",
"url": "https://github.com/stimulusreflex/stimulus_reflex/releases/tag/v3.4.2"
},
{
"type": "WEB",
"url": "https://github.com/stimulusreflex/stimulus_reflex/releases/tag/v3.5.0.rc4"
},
{
"type": "WEB",
"url": "http://seclists.org/fulldisclosure/2024/Mar/16"
}
],
"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"
}
],
"summary": "StimulusReflex arbitrary method call"
}
GHSA-FXCW-5G46-3PJV
Vulnerability from github – Published: 2024-10-09 15:32 – Updated: 2025-11-03 21:31In Progress Telerik Reporting versions prior to 2024 Q3 (18.2.24.924), a code execution attack is possible using object injection via insecure expression evaluation.
{
"affected": [],
"aliases": [
"CVE-2024-8048"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-10-09T15:15:17Z",
"severity": "HIGH"
},
"details": "In Progress Telerik Reporting versions prior to 2024 Q3 (18.2.24.924), a code execution attack is possible using object injection via insecure expression evaluation.",
"id": "GHSA-fxcw-5g46-3pjv",
"modified": "2025-11-03T21:31:15Z",
"published": "2024-10-09T15:32:21Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-8048"
},
{
"type": "WEB",
"url": "https://docs.telerik.com/reporting/knowledge-base/insecure-expression-evaluation-cve-2024-8048"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20250425-0004"
}
],
"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:H",
"type": "CVSS_V3"
}
]
}
GHSA-G2M5-6X74-9MV6
Vulnerability from github – Published: 2024-07-24 15:31 – Updated: 2025-04-26 00:30In Progress® Telerik® Reporting versions prior to 18.1.24.709, a code execution attack is possible through object injection via an insecure type resolution vulnerability.
{
"affected": [],
"aliases": [
"CVE-2024-6096"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-07-24T14:15:06Z",
"severity": "HIGH"
},
"details": "In Progress\u00ae Telerik\u00ae Reporting versions prior to 18.1.24.709, a code execution attack is possible through object injection via an insecure type resolution vulnerability.",
"id": "GHSA-g2m5-6x74-9mv6",
"modified": "2025-04-26T00:30:23Z",
"published": "2024-07-24T15:31:27Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-6096"
},
{
"type": "WEB",
"url": "https://docs.telerik.com/reporting/knowledge-base/unsafe-reflection-CVE-2024-6096"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20250425-0003"
}
],
"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"
}
]
}
GHSA-G39R-HH73-78XJ
Vulnerability from github – Published: 2024-01-16 21:31 – Updated: 2024-01-23 21:30An unsafe reflection vulnerability was identified in GitHub Enterprise Server that could lead to reflection injection. This vulnerability could lead to the execution of user-controlled methods and remote code execution. To exploit this bug, an actor would need to be logged into an account on the GHES instance with the organization owner role. This vulnerability affected all versions of GitHub Enterprise Server prior to 3.12 and was fixed in versions 3.8.13, 3.9.8, 3.10.5, and 3.11.3. This vulnerability was reported via the GitHub Bug Bounty program.
{
"affected": [],
"aliases": [
"CVE-2024-0200"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-01-16T19:15:08Z",
"severity": "HIGH"
},
"details": "An unsafe reflection vulnerability was identified in GitHub Enterprise Server that could lead to reflection injection. This vulnerability\u00a0could lead to the execution of user-controlled methods and remote code execution. To\u00a0exploit this bug, an actor would need to be logged into an account on the GHES instance with the organization owner role.\u00a0This vulnerability affected all versions of GitHub Enterprise Server prior to 3.12 and was fixed in versions 3.8.13, 3.9.8, 3.10.5, and 3.11.3. This vulnerability was reported via the GitHub Bug Bounty program.\n\n",
"id": "GHSA-g39r-hh73-78xj",
"modified": "2024-01-23T21:30:20Z",
"published": "2024-01-16T21:31:22Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-0200"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.10/admin/release-notes#3.10.5"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.11/admin/release-notes#3.11.3"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.8/admin/release-notes#3.8.13"
},
{
"type": "WEB",
"url": "https://docs.github.com/en/enterprise-server@3.9/admin/release-notes#3.9.8"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:L/A:L",
"type": "CVSS_V3"
}
]
}
GHSA-GMJG-HV98-QGGQ
Vulnerability from github – Published: 2026-05-11 13:59 – Updated: 2026-05-11 13:59Summary
praisonaiagents resolves unresolved tool names against module globals and __main__ after it fails to match the declared tool list and the registry. With the default agent configuration, _perm_allow is None, so undeclared non-dangerous tool names are not rejected by the permission gate. An attacker who can influence tool-call names can therefore invoke unintended application callables that were never declared as tools.
Details
The vulnerable resolution path is in [tool_execution.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/tool_execution.py:734). After searching declared tools and the registry, execution falls back to globals() and then __main__:
func = None
for tool in self.tools if isinstance(self.tools, (list, tuple)) else []:
...
if func is None:
try:
from ..tools.registry import get_registry
registry = get_registry()
func = registry.get(function_name)
except ImportError:
pass
if func is None:
func = globals().get(function_name)
if not func:
import __main__
func = getattr(__main__, function_name, None)
If a callable is found, it is executed directly:
elif callable(func):
casted_arguments = self._cast_arguments(func, arguments)
return func(**casted_arguments)
The permission gate does not enforce a declared-tool allowlist by default. In [tool_execution.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/tool_execution.py:550), execution is only rejected if _perm_allow is non-None:
if self._perm_deny and function_name in self._perm_deny:
return {"error": f"Tool '{function_name}' blocked by permission policy", "permission_denied": True}
if self._perm_allow is not None and function_name not in self._perm_allow:
return {"error": f"Tool '{function_name}' not in allowed tools list", "permission_denied": True}
Default agent initialization sets _perm_allow = None, which means "allow all" rather than "allow only declared tools" in [agent.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/agent.py:1749):
self._perm_deny = frozenset() # Permission tier deny set (empty = no denials)
self._perm_allow = None # Permission tier allow set (None = allow all)
The project's own tests confirm that default agents have no allowlist and that undeclared custom tool names pass approval:
[test_permissions.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/tests/unit/test_permissions.py:56)asserts that a defaultAgenthas_perm_allow is None.test_permissions.pyexplicitly checks thatagent._check_tool_approval_sync("my_custom_tool", {})passes for an undeclared tool name.
Empirical verification:
I verified the bypass locally on commit d8a8a786915dc67a7c3021e24f72458f2eac5d9c (v4.6.35) by defining a callable only in __main__, giving the agent an empty tools list, and invoking execute_tool() with that undeclared name. The tool executor ran the __main__ function anyway.
PoC
Environment
- Repo: MervinPraison/PraisonAI
- Commit: d8a8a786915dc67a7c3021e24f72458f2eac5d9c
- Verified against PyPI package versions available on May 3, 2026:
- praisonaiagents 1.6.35
- PraisonAI 4.6.35
- Python 3
Steps 1. From the repository root, run:
python3 - <<'PY'
import sys
from unittest.mock import MagicMock, patch
sys.path.insert(0, '/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents')
from praisonaiagents.agent.tool_execution import ToolExecutionMixin
def sneaky(msg='ok'):
return {'ran': msg}
class HookRunner:
def execute_sync(self, *args, **kwargs):
return []
def is_blocked(self, results):
return False
class Dummy(ToolExecutionMixin):
def __init__(self):
self.name = 'demo'
self.tools = []
self.chat_history = []
self._hook_runner = HookRunner()
self.context_manager = None
self._doom_loop_tracker = None
self._perm_deny = frozenset()
self._perm_allow = None
self._approval_backend = None
mock_registry = MagicMock()
mock_registry.approve_sync.return_value = MagicMock(approved=True, reason='mock', modified_args=None)
mock_registry.mark_approved = MagicMock()
with patch('praisonaiagents.approval.get_approval_registry', return_value=mock_registry):
agent = Dummy()
print(agent.execute_tool('sneaky', {'msg': 'hello'}))
print(mock_registry.approve_sync.call_args)
PY
Expected output
{'ran': 'hello'}
call('demo', 'sneaky', {'msg': 'hello'})
The important point is that sneaky was never declared in self.tools and was only present in __main__.
Impact
- Any deployment that lets an untrusted party influence tool-call names: undeclared application callables can run even though they were never registered as tools.
- Operators who rely on the declared tool list as a security boundary: that boundary is broken because unresolved names fall through to
globals()and__main__. - Applications that keep privileged helper functions in process scope: the attacker can reuse those helpers with the application's own privileges, which can lead to unauthorized state changes and, depending on what is loaded, data exposure or command execution.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 1.6.36"
},
"package": {
"ecosystem": "PyPI",
"name": "praisonaiagents"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.6.37"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.6.36"
},
"package": {
"ecosystem": "PyPI",
"name": "PraisonAI"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.6.37"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-44339"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-11T13:59:17Z",
"nvd_published_at": "2026-05-08T14:16:46Z",
"severity": "HIGH"
},
"details": "### Summary\n`praisonaiagents` resolves unresolved tool names against module globals and `__main__` after it fails to match the declared tool list and the registry. With the default agent configuration, `_perm_allow` is `None`, so undeclared non-dangerous tool names are not rejected by the permission gate. An attacker who can influence tool-call names can therefore invoke unintended application callables that were never declared as tools.\n\n### Details\nThe vulnerable resolution path is in [`[tool_execution.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/tool_execution.py:734)`](/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/tool_execution.py:734). After searching declared tools and the registry, execution falls back to `globals()` and then `__main__`:\n\n```python\nfunc = None\nfor tool in self.tools if isinstance(self.tools, (list, tuple)) else []:\n ...\n\nif func is None:\n try:\n from ..tools.registry import get_registry\n registry = get_registry()\n func = registry.get(function_name)\n except ImportError:\n pass\n\nif func is None:\n func = globals().get(function_name)\n if not func:\n import __main__\n func = getattr(__main__, function_name, None)\n```\n\nIf a callable is found, it is executed directly:\n\n```python\nelif callable(func):\n casted_arguments = self._cast_arguments(func, arguments)\n return func(**casted_arguments)\n```\n\nThe permission gate does not enforce a declared-tool allowlist by default. In [`[tool_execution.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/tool_execution.py:550)`](/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/tool_execution.py:550), execution is only rejected if `_perm_allow` is non-`None`:\n\n```python\nif self._perm_deny and function_name in self._perm_deny:\n return {\"error\": f\"Tool \u0027{function_name}\u0027 blocked by permission policy\", \"permission_denied\": True}\nif self._perm_allow is not None and function_name not in self._perm_allow:\n return {\"error\": f\"Tool \u0027{function_name}\u0027 not in allowed tools list\", \"permission_denied\": True}\n```\n\nDefault agent initialization sets `_perm_allow = None`, which means \"allow all\" rather than \"allow only declared tools\" in [`[agent.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/agent.py:1749)`](/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/praisonaiagents/agent/agent.py:1749):\n\n```python\nself._perm_deny = frozenset() # Permission tier deny set (empty = no denials)\nself._perm_allow = None # Permission tier allow set (None = allow all)\n```\n\nThe project\u0027s own tests confirm that default agents have no allowlist and that undeclared custom tool names pass approval:\n\n- [`[test_permissions.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/tests/unit/test_permissions.py:56)`](/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/tests/unit/[test_permissions.py](https://github.com/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/tests/unit/test_permissions.py:142):56) asserts that a default `Agent` has `_perm_allow is None`.\n- [`test_permissions.py`](/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents/tests/unit/test_permissions.py:142) explicitly checks that `agent._check_tool_approval_sync(\"my_custom_tool\", {})` passes for an undeclared tool name.\n\n**Empirical verification:**\n\nI verified the bypass locally on commit `d8a8a786915dc67a7c3021e24f72458f2eac5d9c` (`v4.6.35`) by defining a callable only in `__main__`, giving the agent an empty `tools` list, and invoking `execute_tool()` with that undeclared name. The tool executor ran the `__main__` function anyway.\n\n### PoC\n**Environment**\n- Repo: `MervinPraison/PraisonAI`\n- Commit: `d8a8a786915dc67a7c3021e24f72458f2eac5d9c`\n- Verified against PyPI package versions available on May 3, 2026:\n - `praisonaiagents` `1.6.35`\n - `PraisonAI` `4.6.35`\n- Python 3\n\n**Steps**\n1. From the repository root, run:\n\n```bash\npython3 - \u003c\u003c\u0027PY\u0027\nimport sys\nfrom unittest.mock import MagicMock, patch\n\nsys.path.insert(0, \u0027/Users/shmulc/Documents/Codex/2026-05-03/please-go-over-tmp-tp-advisories/repos/PraisonAI/src/praisonai-agents\u0027)\nfrom praisonaiagents.agent.tool_execution import ToolExecutionMixin\n\ndef sneaky(msg=\u0027ok\u0027):\n return {\u0027ran\u0027: msg}\n\nclass HookRunner:\n def execute_sync(self, *args, **kwargs):\n return []\n def is_blocked(self, results):\n return False\n\nclass Dummy(ToolExecutionMixin):\n def __init__(self):\n self.name = \u0027demo\u0027\n self.tools = []\n self.chat_history = []\n self._hook_runner = HookRunner()\n self.context_manager = None\n self._doom_loop_tracker = None\n self._perm_deny = frozenset()\n self._perm_allow = None\n self._approval_backend = None\n\nmock_registry = MagicMock()\nmock_registry.approve_sync.return_value = MagicMock(approved=True, reason=\u0027mock\u0027, modified_args=None)\nmock_registry.mark_approved = MagicMock()\n\nwith patch(\u0027praisonaiagents.approval.get_approval_registry\u0027, return_value=mock_registry):\n agent = Dummy()\n print(agent.execute_tool(\u0027sneaky\u0027, {\u0027msg\u0027: \u0027hello\u0027}))\n print(mock_registry.approve_sync.call_args)\nPY\n```\n\n**Expected output**\n```text\n{\u0027ran\u0027: \u0027hello\u0027}\ncall(\u0027demo\u0027, \u0027sneaky\u0027, {\u0027msg\u0027: \u0027hello\u0027})\n```\n\nThe important point is that `sneaky` was never declared in `self.tools` and was only present in `__main__`.\n\n### Impact\n- **Any deployment that lets an untrusted party influence tool-call names**: undeclared application callables can run even though they were never registered as tools.\n- **Operators who rely on the declared tool list as a security boundary**: that boundary is broken because unresolved names fall through to `globals()` and `__main__`.\n- **Applications that keep privileged helper functions in process scope**: the attacker can reuse those helpers with the application\u0027s own privileges, which can lead to unauthorized state changes and, depending on what is loaded, data exposure or command execution.",
"id": "GHSA-gmjg-hv98-qggq",
"modified": "2026-05-11T13:59:17Z",
"published": "2026-05-11T13:59:17Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-gmjg-hv98-qggq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44339"
},
{
"type": "PACKAGE",
"url": "https://github.com/MervinPraison/PraisonAI"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "PraisonAI has unsafe tool resolution in `ToolExecutionMixin.execute_tool`: undeclared `__main__` callables execute"
}
GHSA-GWR9-Q5W7-G798
Vulnerability from github – Published: 2026-04-04 15:30 – Updated: 2026-04-04 21:30Microsoft Smart VPN 1.1.3.0 contains a denial of service vulnerability that allows local attackers to crash the application by submitting oversized input through the search interface. Attackers can paste a buffer of 2100 characters into the top right search bar to trigger an unhandled exception that crashes the application.
{
"affected": [],
"aliases": [
"CVE-2018-25239"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-04-04T14:16:19Z",
"severity": "MODERATE"
},
"details": "Microsoft Smart VPN 1.1.3.0 contains a denial of service vulnerability that allows local attackers to crash the application by submitting oversized input through the search interface. Attackers can paste a buffer of 2100 characters into the top right search bar to trigger an unhandled exception that crashes the application.",
"id": "GHSA-gwr9-q5w7-g798",
"modified": "2026-04-04T21:30:26Z",
"published": "2026-04-04T15:30:20Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2018-25239"
},
{
"type": "WEB",
"url": "https://www.exploit-db.com/exploits/46272"
},
{
"type": "WEB",
"url": "https://www.microsoft.com/store/productId/9NH1G93D4HKR"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/microsoft-smart-vpn-denial-of-service-via-search"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/smart-vpn-denial-of-service-via-search"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/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-GWWH-WV9X-J72W
Vulnerability from github – Published: 2023-07-06 19:24 – Updated: 2024-04-04 05:33The YouTube Embedded 1.2 SDK binds to a service within the YouTube Main App. After binding, a remote context is created with the flags Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY. This allows the client app to remotely load code from YouTube Main App by retrieving the Main App’s ClassLoader. A potential vulnerability in the binding logic used by the client SDK where the SDK ends up calling bindService() on a malicious app rather than YT Main App. This creates a vulnerability where the SDK can load the malicious app’s ClassLoader instead, allowing the malicious app to load arbitrary code into the calling app whenever the embedded SDK is invoked. In order to trigger this vulnerability, an attacker must masquerade the Youtube app and install it on a device, have a second app that uses the Embedded player and typically distribute both to the victim outside of the Play Store.
{
"affected": [],
"aliases": [
"CVE-2023-0460"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-03-01T17:15:00Z",
"severity": "HIGH"
},
"details": "The YouTube Embedded 1.2 SDK binds to a service within the YouTube Main App. After binding, a remote context is created with the flags Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY. This allows the client app to remotely load code from YouTube Main App by retrieving the Main App\u2019s ClassLoader. A potential vulnerability in the binding logic used by the client SDK where the SDK ends up calling bindService() on a malicious app rather than YT Main App. This creates a vulnerability where the SDK can load the malicious app\u2019s ClassLoader instead, allowing the malicious app to load arbitrary code into the calling app whenever the embedded SDK is invoked. In order to trigger this vulnerability, an attacker must masquerade the Youtube app and install it on a device, have a second app that uses the Embedded player and typically distribute both to the victim outside of the Play Store.",
"id": "GHSA-gwwh-wv9x-j72w",
"modified": "2024-04-04T05:33:48Z",
"published": "2023-07-06T19:24:11Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-0460"
},
{
"type": "WEB",
"url": "https://developers.google.com/youtube/android/player/downloads"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-H47X-2J37-FW5M
Vulnerability from github – Published: 2022-05-24 17:01 – Updated: 2022-06-29 13:25A vulnerability was found in Infinispan such that the invokeAccessibly method from the public class ReflectionUtil allows any application class to invoke private methods in any class with Infinispan's privileges. The attacker can use reflection to introduce new, malicious behavior into the application.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 8.2.11.Final"
},
"package": {
"ecosystem": "Maven",
"name": "org.infinispan:infinispan-core"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "8.2.12.Final"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 9.4.16.Final"
},
"package": {
"ecosystem": "Maven",
"name": "org.infinispan:infinispan-core"
},
"ranges": [
{
"events": [
{
"introduced": "9.0.0.Final"
},
{
"fixed": "9.4.17.Final"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2019-10174"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": true,
"github_reviewed_at": "2022-06-29T13:25:50Z",
"nvd_published_at": "2019-11-25T11:15:00Z",
"severity": "HIGH"
},
"details": "A vulnerability was found in Infinispan such that the invokeAccessibly method from the public class ReflectionUtil allows any application class to invoke private methods in any class with Infinispan\u0027s privileges. The attacker can use reflection to introduce new, malicious behavior into the application.",
"id": "GHSA-h47x-2j37-fw5m",
"modified": "2022-06-29T13:25:50Z",
"published": "2022-05-24T17:01:50Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2019-10174"
},
{
"type": "WEB",
"url": "https://github.com/infinispan/infinispan/commit/5dbb05cfaca01a1a66732b82a0f5ba615ccbd214"
},
{
"type": "WEB",
"url": "https://github.com/infinispan/infinispan/commit/7bdc2822ccf79127a488130239c49a5e944e3ca2"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2020:0481"
},
{
"type": "WEB",
"url": "https://access.redhat.com/errata/RHSA-2020:0727"
},
{
"type": "WEB",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2019-10174"
},
{
"type": "PACKAGE",
"url": "https://github.com/infinispan/infinispan"
},
{
"type": "WEB",
"url": "https://security.netapp.com/advisory/ntap-20220210-0018"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Use of Externally-Controlled Input to Select Classes or Code in Infinispan"
}
GHSA-H4JP-RW7W-8X59
Vulnerability from github – Published: 2024-11-05 15:30 – Updated: 2024-11-10 00:35A high-severity vulnerability that can lead to arbitrary code execution on the system hosting the Web SDK role was found in the Genetec Security Center product line.
{
"affected": [],
"aliases": [
"CVE-2024-7059"
],
"database_specific": {
"cwe_ids": [
"CWE-470"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2024-11-05T13:15:03Z",
"severity": "HIGH"
},
"details": "A high-severity vulnerability that can lead to arbitrary code execution on the system hosting the Web SDK role was found in the Genetec Security Center product line.",
"id": "GHSA-h4jp-rw7w-8x59",
"modified": "2024-11-10T00:35:34Z",
"published": "2024-11-05T15:30:34Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-7059"
},
{
"type": "WEB",
"url": "https://resources.genetec.com/security-advisories"
},
{
"type": "WEB",
"url": "https://resources.genetec.com/security-advisories/high-severity-vulnerability-affecting-security-center-web-sdk-role"
},
{
"type": "WEB",
"url": "https://ressources.genetec.com/bulletins-de-securite/vulnerabilite-de-haute-severite-affectant-le-role-sdk-web-de-security-center"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:H/AT:P/PR:H/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"
}
]
}
Mitigation
Refactor your code to avoid using reflection.
Mitigation
Do not use user-controlled inputs to select and load classes or code.
Mitigation
Apply strict input validation by using allowlists or indirect selection to ensure that the user is only selecting allowable classes or code.
CAPEC-138: Reflection Injection
An adversary supplies a value to the target application which is then used by reflection methods to identify a class, method, or field. For example, in the Java programming language the reflection libraries permit an application to inspect, load, and invoke classes and their components by name. If an adversary can control the input into these methods including the name of the class/method/field or the parameters passed to methods, they can cause the targeted application to invoke incorrect methods, read random fields, or even to load and utilize malicious classes that the adversary created. This can lead to the application revealing sensitive information, returning incorrect results, or even having the adversary take control of the targeted application.