Vulnerability Report for April 2026

Executive Summary

This monthly summary captures what our AI‑powered vulnerability analysis engine reviewed in April 2026, analyzing 450+ new or updated vulnerabilities across ecosystems and vendors.

This report highlights the 10 high‑severity vulnerabilities prioritized by severity, CVSS and potential business impact. Below are this month’s key stats: total analyzed, critical/high counts, average CVSS, and KEV (if present).

Σ
450+
Total Vulnerabilities
C
44
Critical
H
128
High
Avg
7.0
Avg CVSS
Prefer the full picture? Visit our vulnerability database to explore everything we analyzed this month.

Table of Contents

CVE-2026-42208: LiteLLM has SQL Injection in Proxy API key verification Critical

CVSS Score
9.3
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Severity
critical
Published Date
April 24, 2026

Affected Packages

  • litellm (pip) vulnerable: >= 1.81.16, < 1.83.7 patched in 1.83.7

Vulnerability Analysis

The vulnerability is a SQL injection in the LiteLLM proxy's API key verification logic. The advisory states that a database query mixed a caller-supplied key value directly into the query text. By analyzing the commits between the vulnerable version and the patched version, I identified commit 4dc416ee749122ca91e3bca095217478663419e7 as the security patch. This commit modifies the get_data function in litellm/proxy/utils.py. The diff clearly shows the change from an unsafe f-string-based query construction (WHERE v.token = '{token}') to a parameterized query (WHERE v.token = $1). The hashed_token is then passed as a separate argument, preventing the SQL injection. The primary vulnerable function is ProxyUtils.get_data, as it is responsible for creating the malicious query. The helper function _query_first_with_cached_plan_fallback was also updated to support passing these parameters, making it a relevant part of the fix.

Identified Vulnerable Functions

ProxyUtils.get_data
litellm/proxy/utils.py

The function get_data within the ProxyUtils class constructs a raw SQL query to validate an API token. The original code used an f-string to embed the hashed_token directly into the query string, which is a classic SQL injection vulnerability. An attacker could craft a malicious token that, when inserted into the query, could alter the query's logic to bypass authentication or exfiltrate data from the database.

ProxyUtils._query_first_with_cached_plan_fallback
litellm/proxy/utils.py

This function was modified to accept and pass query parameters (*args) to the underlying database driver. While not the source of the vulnerability itself, it was a necessary change to allow the get_data function to use parameterized queries, which is the fix for the SQL injection. This function is part of the vulnerable execution path.


CVE-2026-34976: Dgraph Affected by Pre-Auth Database Overwrite + SSRF + File Read via restoreTenant Missing Authorization Critical

Common Weakness Enumeration (CWE)
CWE-862 — Missing Authorization
CVSS Score
10.0
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Severity
critical
Published Date
April 06, 2026

Affected Packages

  • github.com/dgraph-io/dgraph/v25 (go) vulnerable: <= 25.3.0 patched in 25.3.1
  • github.com/dgraph-io/dgraph/v24 (go) vulnerable: <= 24.0.5 patched in None
  • github.com/dgraph-io/dgraph (go) vulnerable: <= 1.2.8 patched in None

Vulnerability Analysis

The root cause of the vulnerability is a missing authorization check on the restoreTenant GraphQL mutation. The adminMutationMWConfig map in graphql/admin/admin.go defines the middleware (including authentication and IP whitelisting) for each administrative mutation. The restoreTenant mutation was absent from this map, causing the middleware chain to be empty and allowing unauthenticated execution.

An attacker can send a crafted restoreTenant mutation to a vulnerable Dgraph instance. This call is processed without any authentication, directly invoking the admin.RestoreTenant resolver. This resolver, in turn, calls other functions like worker.ProcessRestoreRequest, which handle the backup and restore logic.

By controlling the parameters of the restoreTenant mutation, an attacker can: 1. Overwrite the database: By providing an S3 URL to a malicious backup. 2. Read local files: By using a file:// URL in the location parameter, which triggers fileHandler.Read and fileHandler.ListPaths to access the filesystem. Further file reads are possible via the encryptionKeyFile and vaultRoleIDFile parameters, which are processed by functions like worker.BuildEncFlag. 3. Perform SSRF: By providing an internal IP address as the S3 endpoint or in the vaultAddr parameter.

The identified vulnerable functions are the key components in this exploit chain, starting from the unauthenticated entry point (admin.RestoreTenant) to the functions that perform the malicious actions (worker.ProcessRestoreRequest, fileHandler.Read, etc.).

Identified Vulnerable Functions

admin.RestoreTenant
graphql/admin/restore.go

This is the GraphQL resolver for the restoreTenant mutation. It was vulnerable because it was missing from the adminMutationMWConfig map in graphql/admin/admin.go, which meant it had no authentication or authorization middleware. This allowed any unauthenticated user to call it and access powerful features like restoring backups from arbitrary locations.

worker.ProcessRestoreRequest
worker/online_restore.go

This function is called by the unauthenticated RestoreTenant resolver. It takes attacker-controlled parameters, including the backup location (S3, file URI), credentials, and other sensitive information. This allows for database overwrites, Server-Side Request Forgery (SSRF) by pointing to internal endpoints, and local file system access.

fileHandler.ListPaths
worker/backup_handler.go

When the restoreTenant mutation is called with a file:// URI, this function is used to list the contents of directories on the server's local filesystem. This allows an unauthenticated attacker to probe the filesystem structure and identify interesting files.

fileHandler.Read
worker/backup_handler.go

When the restoreTenant mutation is called with a file:// URI pointing to a file, this function is used to read the contents of that file. This allows an unauthenticated attacker to read arbitrary files on the server, limited only by file system permissions.

worker.BuildEncFlag
worker/online_restore.go

This function is called as part of the restore process and reads a file from the path provided in the encryptionKeyFile parameter of the restoreTenant mutation. An unauthenticated attacker can abuse this to read arbitrary files from the server's filesystem.


CVE-2026-41070: openvpn-auth-oauth2 returns FUNC_SUCCESS on client-deny, allowing unauthenticated VPN access Critical

Common Weakness Enumeration (CWE)
CWE-287 — Improper Authentication
CVSS Score
10.0
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:N
Severity
critical
Published Date
April 22, 2026

Affected Packages

  • github.com/jkroepke/openvpn-auth-oauth2 (go) vulnerable: >= 1.26.3, < 1.27.3 patched in 1.27.3

Vulnerability Analysis

The vulnerability lies in the incorrect handling of authentication denial within the openvpn-auth-oauth2 plugin's experimental mode. The root cause was identified in the handleAuthUserPassVerify function located in lib/openvpn-auth-oauth2/openvpn/handle.go. When a client that does not support web authentication attempts to connect, the plugin's logic correctly determines that the client should be denied. However, instead of returning an error code to the OpenVPN server, it returned OPENVPN_PLUGIN_FUNC_SUCCESS. The OpenVPN server treats this success code as an approval for connection, thus bypassing the authentication mechanism entirely. The patch in commit 36f69a6c67c1054da7cbfa04ced3f0555127c8f2 rectifies this by changing the return value to OPENVPN_PLUGIN_FUNC_ERROR in the case of a client denial, ensuring that the OpenVPN server correctly terminates the connection attempt. This makes handleAuthUserPassVerify the direct location of the vulnerability.

Identified Vulnerable Functions

openvpn.PluginHandle.handleAuthUserPassVerify
lib/openvpn-auth-oauth2/openvpn/handle.go

The function handleAuthUserPassVerify incorrectly returned OPENVPN_PLUGIN_FUNC_SUCCESS when client authentication was denied for clients not supporting web authentication. The OpenVPN server interprets this status as a successful authentication, granting the client network access despite the intended denial. The fix changes the return value to OPENVPN_PLUGIN_FUNC_ERROR, ensuring the OpenVPN server correctly rejects the unauthenticated client.


GHSA-3xx2-mqjm-hg9x: Paperclip: Cross-tenant agent API key IDOR in `/agents/:id/keys` routes allows full victim-company compromise Critical

CVSS Score
10.0
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
Severity
critical
Published Date
April 16, 2026

Affected Packages

  • @paperclipai/server (npm) vulnerable: < 2026.416.0 patched in 2026.416.0

Vulnerability Analysis

The vulnerability lies in the lack of proper authorization checks in the API routes responsible for managing agent API keys. The GET, POST, and DELETE handlers under /agents/:id/keys in server/src/routes/agents.ts only verify that the caller has a board-type session (assertBoard(req)) but fail to check if the caller has access to the company that owns the target agent. This allows a user from one company to list, create, or revoke API keys for agents in any other company by simply providing the victim agent's UUID in the URL. The POST handler exacerbates the issue by returning the newly created API key in cleartext, which grants the attacker full agent-level access within the victim's tenant, resulting in a complete cross-tenant compromise. The service layer functions createApiKey, listKeys, and revokeKey in server/src/services/agents.ts are also insecure as they do not perform any authorization checks, relying entirely on the calling route handlers for security.

Identified Vulnerable Functions

router.get("/agents/:id/keys", ...)
server/src/routes/agents.ts

This function handles GET requests to /agents/:id/keys. It is vulnerable because it only checks if the user has a "board" type session (assertBoard(req)) but does not verify if the user has access to the company associated with the agent ID provided in the URL. This allows an attacker from one company to list the API keys of an agent in another company.

router.post("/agents/:id/keys", ...)
server/src/routes/agents.ts

This function handles POST requests to /agents/:id/keys. It is vulnerable because it only checks if the user has a "board" type session (assertBoard(req)) but does not verify if the user has access to the company associated with the agent ID provided in the URL. This allows an attacker from one company to create a new API key for an agent in another company and receive the key in cleartext, leading to a full cross-tenant compromise.

router.delete("/agents/:id/keys/:keyId", ...)
server/src/routes/agents.ts

This function handles DELETE requests to /agents/:id/keys/:keyId. It is vulnerable because it only checks if the user has a "board" type session (assertBoard(req)) but does not verify if the user has access to the company associated with the key ID. This allows an attacker from one company to revoke an API key of an agent in another company, causing a denial of service.

createApiKey
server/src/services/agents.ts

This service layer function is responsible for creating an API key. It is vulnerable because it does not perform any authorization checks and blindly trusts the provided agent ID. It copies the companyId from the victim agent, which is then used to create a new API key associated with the victim's company.

listKeys
server/src/services/agents.ts

This service layer function is responsible for listing API keys for a given agent ID. It is vulnerable because it does not perform any authorization checks and directly queries the database based on the provided agent ID, allowing an attacker to list keys of any agent.

revokeKey
server/src/services/agents.ts

This service layer function is responsible for revoking an API key. It is vulnerable because it does not perform any authorization checks and directly updates the database based on the provided key ID, allowing an attacker to revoke any key.


GHSA-47wq-cj9q-wpmp: Paperclip: Cross-tenant agent API token minting via missing assertCompanyAccess on /api/agents/:id/keys Critical

CVSS Score
10.0
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
Severity
critical
Published Date
April 16, 2026

Affected Packages

  • @paperclipai/server (npm) vulnerable: < 2026.416.0 patched in 2026.416.0

Vulnerability Analysis

The vulnerability is a classic Broken Access Control issue (CWE-862: Missing Authorization). Multiple API endpoints in server/src/routes/agents.ts that manage agents and their API keys failed to perform a critical authorization check. The code only verified that a request came from an authenticated user (via assertBoard) but did not verify that the user belonged to the same company as the agent they were attempting to manage (which should be done by assertCompanyAccess).

This allowed any authenticated user, even a newly registered one with no permissions or company affiliations, to perform sensitive actions on any agent in any company on the platform. The most critical impact comes from the POST /api/agents/:id/keys endpoint, which allowed an attacker to mint a new, valid API token for an agent in a victim's company. This token would then grant the attacker access to the victim company's data and resources, leading to a full cross-tenant information disclosure and integrity compromise.

Other affected endpoints for agent lifecycle management (/pause, /resume, /terminate, /delete) allowed for a denial-of-service attack against any agent on the instance.

The analysis of the patch commit 32a9165ddf6308f3b46eae0653b6f583e502e538 confirms this. The addition of the test file server/src/__tests__/agent-cross-tenant-authz-routes.test.ts provides clear evidence of the fix. This file contains specific tests to ensure that cross-tenant requests to the vulnerable endpoints are rejected, confirming that the missing assertCompanyAccess checks were added. The vulnerable functions are the anonymous Express route handlers for the affected API paths, as well as the service function agents.createApiKey which is instrumental in the exploit.

Identified Vulnerable Functions

router.get("/agents/:id/keys")
server/src/routes/agents.ts

This function, which handles GET requests to /api/agents/:id/keys, only checked if a user was authenticated (assertBoard) but failed to verify if the user belonged to the company that owned the agent. This allowed any authenticated user on the platform to list the API keys of any agent in any company, leading to a cross-tenant information disclosure vulnerability.

router.post("/agents/:id/keys")
server/src/routes/agents.ts

This function, which handles POST requests to /api/agents/:id/keys, was the most critical part of the vulnerability. It allowed any authenticated user to mint a new API token for any agent in any company. The function only performed an authentication check (assertBoard) without an authorization check (assertCompanyAccess), enabling a complete bypass of the tenancy model.

router.delete("/agents/:id/keys/:keyId")
server/src/routes/agents.ts

This function, which handles DELETE requests to /api/agents/:id/keys/:keyId, only checked for user authentication (assertBoard) and not for company membership. This allowed any authenticated user to revoke/delete the API keys of any agent in any company, potentially causing a denial of service.

router.post("/agents/:id/pause")
server/src/routes/agents.ts

This function, handling agent pausing, was vulnerable due to a missing assertCompanyAccess check. It allowed any authenticated user to pause any agent in any company, leading to a cross-tenant denial of service.

router.post("/agents/:id/resume")
server/src/routes/agents.ts

This function, handling agent resuming, was vulnerable due to a missing assertCompanyAccess check. It allowed any authenticated user to resume any agent in any company, enabling unauthorized cross-tenant actions.

router.post("/agents/:id/terminate")
server/src/routes/agents.ts

This function, handling agent termination, was vulnerable due to a missing assertCompanyAccess check. It allowed any authenticated user to terminate any agent in any company, leading to a cross-tenant denial of service.

router.delete("/agents/:id")
server/src/routes/agents.ts

This function, handling agent deletion, was vulnerable due to a missing assertCompanyAccess check. It allowed any authenticated user to delete any agent in any company, leading to a severe cross-tenant availability and integrity impact.

agents.createApiKey
server/src/services/agents.ts

While the primary vulnerability was the missing authorization check in the route handler, this service function is a key part of the exploit chain. It is called by the vulnerable POST /api/agents/:id/keys endpoint and is responsible for creating the API token. Crucially, it associates the new token with the victim agent's companyId, which is what allows the attacker's token to gain access to the victim's tenant. This function would appear in a runtime profile during exploitation.


GHSA-68qg-g8mg-6pr7: paperclip Vulnerable to Unauthenticated Remote Code Execution via Import Authorization Bypass Critical

CVSS Score
10.0
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Severity
critical
Published Date
April 10, 2026

Affected Packages

  • paperclipai (npm) vulnerable: < 2026.410.0 patched in 2026.410.0
  • @paperclipai/server (npm) vulnerable: < 2026.410.0 patched in 2026.410.0

Vulnerability Analysis

The root cause of the remote code execution vulnerability is a missing authorization check in the POST /api/companies/import endpoint. The vulnerability description details a chain of weaknesses, starting with open user registration and self-approval of API keys, which grants an attacker the initial authenticated access. The critical flaw, however, is within the companyRoutes.post(/import) function in server/src/routes/companies.ts. This function failed to verify if the user had instance administrator privileges before creating a new company via an import bundle. An attacker could exploit this by supplying a malicious .paperclip.yaml file within the import data, which defines an agent using the 'process' adapter to execute arbitrary commands on the server's operating system. The exploit is triggered by calling the /api/agents/<agent-id>/wakeup endpoint.

The patch, identified in commit ac664df8e48326135a913e97ee7ed937d913586b, addresses this primary vulnerability by adding the assertInstanceAdmin check for any 'new_company' import. Additionally, the same commit fixes several other related, but less severe, authorization bypasses in other routes (/approvals, /activity, /agents) where company-level access was not properly enforced, preventing authenticated users from performing actions outside of their own company's scope.

Identified Vulnerable Functions

companyRoutes.post(/import)
server/src/routes/companies.ts

This route handler is the core of the remote code execution vulnerability. It was missing an authorization check to ensure that only instance administrators could create new companies via the import functionality. An attacker, after creating a basic account, could call this endpoint with a specially crafted payload to create a new company and define an agent with a 'process' adapter, which executes arbitrary shell commands on the server.

companyRoutes.post(/import/preview)
server/src/routes/companies.ts

Similar to the '/import' route, this route handler for previewing an import was missing an instance administrator check for the 'new_company' mode. This allowed any authenticated user to probe and prepare for the exploitation of the main import vulnerability.

approvalRoutes.post(/approvals/:id/approve)
server/src/routes/approvals.ts

This function was vulnerable to a cross-company authorization bypass. It only checked if the caller was a board user (assertBoard) but did not verify if the user had access to the company associated with the approval ID. This allowed an attacker to approve requests in any company on the instance.

approvalRoutes.post(/approvals/:id/reject)
server/src/routes/approvals.ts

This function was vulnerable to a cross-company authorization bypass. It only checked if the caller was a board user (assertBoard) but did not verify if the user had access to the company associated with the approval ID. This allowed an attacker to reject requests in any company on the instance.

activityRoutes.get(/heartbeat-runs/:runId/issues)
server/src/routes/activity.ts

This route handler was missing an authorization check. It allowed any authenticated board user to list issues related to a heartbeat run from any company, potentially exposing sensitive information.

agentRoutes.post(/heartbeat-runs/:runId/cancel)
server/src/routes/agents.ts

This function was vulnerable to a cross-company authorization bypass. It allowed any authenticated board user to cancel an agent run in any company, leading to a potential denial of service.


GHSA-jp74-mfrx-3qvh: Saltcorn: SQL Injection via Unparameterized Sync Endpoints (maxLoadedId) Critical

CVSS Score
10.0
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
Severity
critical
Published Date
April 16, 2026

Affected Packages

  • @saltcorn/server (npm) vulnerable: < 1.4.6 patched in 1.4.6
  • @saltcorn/server (npm) vulnerable: >= 1.5.0-beta.0, < 1.5.6 patched in 1.5.6
  • @saltcorn/server (npm) vulnerable: >= 1.6.0-alpha.0, < 1.6.0-beta.5 patched in 1.6.0-beta.5

Vulnerability Analysis

The vulnerability is a classic SQL injection caused by the direct interpolation of user-controlled input into SQL query strings. The primary attack vector is through the /sync/load_changes and /sync/deletes API endpoints, which are accessible to any authenticated user. The maxLoadedId, syncFrom, and syncUntil parameters were not properly sanitized or validated, allowing an attacker to inject arbitrary SQL code.

The patch addresses this vulnerability at its root by implementing two key changes. First, it adds validation to ensure that the input parameters are of the expected type (e.g., maxLoadedId must be an integer). Second, and more critically, it replaces all instances of string interpolation for these values in SQL queries with parameterized queries. This is the standard and most effective defense against SQL injection, as it ensures that user input is treated as data and not as executable code.

The fix was comprehensively applied not only in the high-level route handlers in packages/server/routes/sync.js but also in several lower-level data access methods within the Table class in packages/saltcorn-data/models/table.ts, such as deleteRows, updateRow, and insertRow. This ensures that the vulnerability is mitigated throughout the application's data layer.

Identified Vulnerable Functions

getSyncRows
packages/server/routes/sync.js

The function was vulnerable to SQL injection because it directly interpolated the maxLoadedId parameter from the request body into an SQL query string without sanitization or parameterization. An attacker could provide a malicious string for maxLoadedId to execute arbitrary SQL commands. The patch adds validation to ensure maxLoadedId is an integer and uses parameterized queries to prevent injection.

getDelRows
packages/server/routes/sync.js

The function was vulnerable to SQL injection because it directly interpolated the syncFrom and syncUntil date values from the request body into an SQL query string. Although these were expected to be dates, they were not properly validated, allowing an attacker to inject SQL expressions. The patch adds validation and uses parameterized queries.

router.post
packages/server/routes/sync.js

This route handler for /sync/load_changes is the entry point for the vulnerability. It takes the syncInfos object from the request body and passes it to the getSyncRows function. The maxLoadedId within syncInfos was not sanitized, leading to the SQL injection vulnerability in getSyncRows.

Table.deleteRows
packages/saltcorn-data/models/table.ts

The function used string concatenation to build a DELETE query with a list of IDs, making it vulnerable to SQL injection if the ids array contained malicious values. The patch fixes this by using a parameterized query.

Table.updateRow
packages/saltcorn-data/models/table.ts

The function used string interpolation for id and oldLastModified in an UPDATE query. This could lead to SQL injection if these parameters were not properly sanitized before being passed to the function. The patch fixes this by using parameterized queries.

Table.insertRow
packages/saltcorn-data/models/table.ts

The function used string interpolation for id and a timestamp value in an INSERT query for the sync info table. This could lead to SQL injection. The patch fixes this by using parameterized queries.


GHSA-wpqr-6v78-jr5g: Gemini CLI: Remote Code Execution via workspace trust and tool allowlisting bypasses Critical

CVSS Score
10.0
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Severity
critical
Published Date
April 24, 2026

Affected Packages

  • @google/gemini-cli (npm) vulnerable: < 0.39.1 patched in 0.39.1
  • @google/gemini-cli (npm) vulnerable: = 0.40.0-preview.2 patched in 0.40.0-preview.3
  • google-github-actions/run-gemini-cli (actions) vulnerable: < 0.1.22 patched in 0.1.22

Vulnerability Analysis

The vulnerability is a supply chain issue where the run-gemini-cli GitHub Action uses the @google/gemini-cli tool, which had two critical security flaws: automatic workspace trust in headless mode and a tool allowlisting bypass. The run-gemini-cli action was deemed vulnerable because it provided a direct way to use the flawed gemini-cli tool without necessary safeguards.

The central function in the run-gemini-cli action is run in src/main.ts. This function manages the entire lifecycle of the action's execution, including downloading and running the gemini-cli tool. In vulnerable versions, this run function would execute the CLI in a way that was susceptible to the trust and allowlist vulnerabilities.

The fix for the action, released in version 0.1.22, was primarily to provide guidance and rely on the patched gemini-cli. The commit d7a38ece760f4f74f733607b503ec3ec9cfee2c4 is a key part of this fix, as it introduces documentation on the new, mandatory trust model. This indicates that the solution was to enforce explicit user consent for trusting a workspace, which is a configuration change managed by the user but facilitated by the action.

Consequently, the run function is identified as the vulnerable function. It is the function that would be active during a potential exploit, as it is responsible for executing the gemini-cli tool with the settings that made it vulnerable. Any runtime analysis or profiling during an exploit would point to this run function as the initiator of the vulnerable process within the run-gemini-cli action.

Identified Vulnerable Functions

run
src/main.ts

The run function in src/main.ts is the main entry point for the run-gemini-cli GitHub Action. This function is responsible for orchestrating the execution of the gemini-cli tool. The vulnerability lies in the fact that this function would execute a vulnerable version of gemini-cli in an environment that the CLI would implicitly trust, leading to potential remote code execution. The patch for the action involves updating the documentation to guide users on how to configure the new trust settings, as evidenced by the commit that adds trust-guidance.md. Therefore, the run function is the function that would appear in a runtime profile during the exploitation of this vulnerability within the action.


CVE-2026-35490: changedetection.io has an Authentication Bypass via Decorator Ordering Critical

Common Weakness Enumeration (CWE)
CWE-863 — Incorrect Authorization
CVSS Score
9.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Severity
critical
Published Date
April 07, 2026

Affected Packages

  • changedetection.io (pip) vulnerable: <= 0.54.7 patched in 0.54.8

Vulnerability Analysis

The vulnerability is an authentication bypass caused by incorrect decorator ordering in the Flask application. In several blueprints, the @login_optionally_required decorator was applied before the @blueprint.route() decorator. According to Flask's design, the @route() decorator must be the outermost one, as it registers the function it wraps as a request handler. When the order is reversed, @route() registers the original, undecorated function, effectively ignoring the authentication check provided by @login_optionally_required. This allowed unauthenticated access to sensitive endpoints. The analysis of the provided patch commit reveals several functions across different blueprints where this decorator order was corrected, thus fixing the vulnerability for those routes. The identified vulnerable functions were responsible for handling backups (creation, download, deletion), backup restoration, and browser-step operations, all of which could be triggered by an unauthenticated attacker.

Identified Vulnerable Functions

request_backup
changedetectionio/blueprint/backups/__init__.py

The @login_optionally_required decorator was incorrectly placed before the @backups_blueprint.route decorator. In Flask, the @route decorator must be the outermost to correctly register the wrapped function with all its decorators. Due to this incorrect ordering, the request_backup function was registered without the authentication check, allowing unauthenticated users to trigger a backup creation.

download_backup
changedetectionio/blueprint/backups/__init__.py

The incorrect ordering of the @login_optionally_required and @backups_blueprint.route decorators caused the authentication check for the download_backup function to be bypassed. This allowed an unauthenticated attacker to download sensitive backup files.

create
changedetectionio/blueprint/backups/__init__.py

The create function, responsible for rendering the backup creation page, was left unprotected due to the decorator ordering issue. This exposed two routes ('/' and '/create' under the backups blueprint) to unauthenticated access.

remove_backups
changedetectionio/blueprint/backups/__init__.py

Authentication was bypassed for the remove_backups function due to the incorrect decorator order. This allowed an unauthenticated attacker to delete all existing backups.

restore
changedetectionio/blueprint/backups/restore.py

The restore function, which displays the backup restoration page, was accessible to unauthenticated users because the @login_optionally_required decorator was not correctly applied.

backups_restore_start
changedetectionio/blueprint/backups/restore.py

The backups_restore_start function, which initiates the backup restore process, was vulnerable to unauthenticated access due to the incorrect decorator ordering, allowing an attacker to upload and restore a potentially malicious backup.

browsersteps_start_session
changedetectionio/blueprint/browser_steps/__init__.py

The browsersteps_start_session function was unprotected due to the decorator ordering flaw, enabling an unauthenticated user to start a new browser steps session, potentially leading to resource exhaustion or other issues.

browser_steps_fetch_screenshot_image
changedetectionio/blueprint/browser_steps/__init__.py

Authentication was bypassed for the browser_steps_fetch_screenshot_image function, allowing an unauthenticated user to fetch screenshot images related to browser automation steps, potentially leaking sensitive information.

browsersteps_ui_update
changedetectionio/blueprint/browser_steps/__init__.py

The browsersteps_ui_update function was left unprotected due to the decorator ordering issue, allowing unauthenticated POST requests. This could lead to unauthorized modifications of browser step configurations.


CVE-2026-41501: electurm has Command Injection via runLinux funtion Critical

CVSS Score
9.8
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Severity
critical
Published Date
April 24, 2026

Affected Packages

  • electerm (npm) vulnerable: < 3.3.8 patched in 3.3.8

Vulnerability Analysis

The vulnerability lies in the npm/install.js script, which is executed during the npm install -g electerm process. The script fetches release information from GitHub, including filenames, and uses these filenames to construct shell commands for different operating systems (runLinux, runWin, runMac).

The core of the vulnerability is the lack of input sanitization on the filenames received from the remote server. An attacker who can control the release metadata on the project's update server (or intercept the traffic) can craft a malicious filename containing shell metacharacters. When this filename is used in the exec calls within the runLinux, runWin, or runMac functions, it leads to arbitrary command execution on the user's machine.

The patch addresses this by completely rewriting the installation logic. It introduces sanitizeFilename and sanitizeVersion functions to validate the data fetched from the remote server. It also replaces the vulnerable exec calls with safer alternatives like execFile and ensures that any path passed to a shell command is properly quoted or sanitized. The vulnerable functions runLinux, runWin, and runMac were removed and replaced with a more secure implementation.

Identified Vulnerable Functions

runLinux
npm/install.js

The runLinux function is vulnerable to command injection. It constructs a shell command using the name parameter, which is derived from a remote source (GitHub releases), without proper sanitization. An attacker controlling the release filename could inject arbitrary commands that would be executed by the exec function.

runWin
npm/install.js

The runWin function is vulnerable to command injection. It constructs a shell command using the name parameter, which is derived from a remote source (GitHub releases), without proper sanitization. An attacker controlling the release filename could inject arbitrary commands that would be executed by the exec function.

runMac
npm/install.js

The runMac function is vulnerable to command injection. It constructs a shell command using the name parameter, which is derived from a remote source (GitHub releases), without proper sanitization. An attacker controlling the release filename could inject arbitrary commands that would be executed by the exec function.