Feb 26, 2026·6 min read·30 visits
Unauthenticated SQL Injection in Parsl's monitoring dashboard allows attackers to read database contents via the `workflow_id` parameter. Patched in version 2026.01.05.
A classic SQL Injection vulnerability was discovered in the visualization component of Parsl (Python Parallel Scripting Library). The flaw exists within the monitoring dashboard, specifically where workflow IDs are processed. By manipulating the `workflow_id` parameter in URL routes, an unauthenticated attacker can inject arbitrary SQL commands. This allows for the exfiltration of monitoring data, potential Denial of Service (DoS) attacks against the backing SQLite database, and manipulation of the visualization interface.
Parsl is a heavy hitter in the world of scientific computing. It allows researchers to scale Python scripts from their laptop to massive supercomputers without rewriting code. It's the engine behind complex workflows in biology, physics, and astronomy. But even supercomputers need dashboards, and that's where parsl-visualize comes in.
This component provides a web-based interface (powered by Flask) to monitor the status of these massive distributed tasks. It tracks what ran, where it ran, and how long it took, storing all this juicy metadata in a local database (usually SQLite). It sounds benign—a local dashboard for local scientists.
However, developers often treat visualization tools like internal utilities, assuming they will never face the harsh light of the public internet. This assumption creates a blind spot. When you combine a web framework, a database, and user input, you have a recipe for disaster if you aren't careful. In this case, the disaster is a textbook SQL Injection that turns a monitoring tool into an open book.
The vulnerability here is so classic it belongs in a museum. It stems from the use of Python's string formatting operator (%) to construct SQL queries. If you have taken a 'Security 101' class in the last two decades, you know that string concatenation and SQL are mortal enemies.
The developers of the parsl-visualize component needed to fetch details for specific workflows. They took the workflow_id from the URL and plugged it directly into a query string using the %s placeholder. Note: In Python's database API (DB-API), %s can be used for parameterized queries if passed correctly as a second argument. But here, they used the Python string operator, effectively pasting the user input directly into the SQL command before sending it to the database driver.
This bypasses all automatic escaping mechanisms. The database engine doesn't see a variable; it sees a modified SQL command. Because Parsl uses Pandas read_sql_query to fetch this data, the result is immediately processed into a DataFrame, but the damage is done the moment the query hits the engine.
Let's look at the vulnerable code in parsl/monitoring/visualization/views.py. The offending function workflow_dag_details takes a workflow_id directly from the route decorator.
Vulnerable Implementation:
# The "Before" Code
# This is the digital equivalent of leaving your keys in the ignition.
def workflow_dag_details(workflow_id, path):
# ... snip ...
# DIRECT STRING INTERPOLATION - DANGER!
query = """SELECT task.task_id, task.task_func_name,
task.task_status_name, task.task_time_returned
FROM task
WHERE task.run_id='%s'""" % (workflow_id)
# The query is executed against the DB engine
df_tasks = pd.read_sql_query(query, db.engine)
# ... snip ...As you can see, the workflow_id is spliced into the string. If workflow_id is abc, the query is fine. If workflow_id is ' OR 1=1;--, the query becomes valid SQL that returns everything.
The Fix (Commit 013a928461e7...):
The patch introduces SQLAlchemy's text() construct and proper parameter binding. This ensures the database driver handles the input as data, not executable code.
# The "After" Code
import sqlalchemy
def workflow_dag_details(workflow_id, path):
# ... snip ...
# Using bind parameters via :run_id
query = """SELECT task.task_id, task.task_func_name,
task.task_status_name, task.task_time_returned
FROM task
WHERE task.run_id=:run_id"""
# Passing params separately to read_sql_query
df_tasks = pd.read_sql_query(
sqlalchemy.text(query),
db.engine,
params={"run_id": workflow_id}
)Exploiting this is trivial because the injection point is in the URL path, often a GET request. While the application expects a UUID-like string, we can feed it whatever we want.
The Attack Vector
The target endpoint follows the pattern /workflow/<workflow_id>/dag.
Payload Construction
Since the query is WHERE task.run_id='%s', we need to close the quote, inject our SQL, and comment out the rest.
' OR '1'='1SELECT ... FROM task WHERE task.run_id='' OR '1'='1'This returns every task in the database, potentially crashing the browser trying to render a DAG of a million nodes (a rudimentary DoS).
Data Exfiltration (UNION Based)
To steal data, we use a UNION attack. We need to match the number of columns in the original SELECT statement (4 columns based on the code snippet).
GET /workflow/' UNION SELECT 1, sql, 3, 4 FROM sqlite_master --/dag HTTP/1.1
Host: target-parsl-instanceThis payload would attempt to dump the schema of the database into the visualization table, revealing table names and structures. From there, an attacker can iterate to dump specific rows from sensitive tables.
You might argue, "It's just scientific metadata, who cares?" But in research environments, metadata is data.
task_func_name) and the parameters passed to them often reveal the nature of proprietary algorithms or sensitive datasets being processed.SELECT * forced by an injection can cause the SQLite process to consume all available memory or disk I/O, locking up the monitoring node and potentially interfering with the head node's ability to manage the actual computation.> [!WARNING]
> While SQLite limits the damage (no DROP TABLE via stacked queries in standard Python drivers), the ability to read the entire database state is a critical confidentiality breach.
The remediation is straightforward: Update Parsl to version 2026.01.05 or later.
The maintainers correctly identified that passing raw strings to Pandas is unsafe. They implemented SQLAlchemy's text() method, which creates a database-agnostic SQL expression, and used the params dictionary to safely bind user input. This shifts the burden of sanitization from the developer (who might forget) to the database driver (which is designed for this).
If you cannot upgrade immediately, you should restrict network access to the visualization port (typically mapped to localhost or an internal interface) using a firewall or SSH tunnel. Never expose the parsl-visualize interface to the public internet without an authentication proxy in front of it.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
Parsl Parsl Project | < 2026.01.05 | 2026.01.05 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-89 |
| Attack Vector | Network (Web) |
| CVSS v3.1 | 5.3 (Medium) |
| EPSS Score | 0.00068 |
| Impact | Confidentiality (High), Availability (Low) |
| Exploit Status | PoC Available |
Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
This report details CVE-2026-91127 (GHSA-3753-m2x2-q623), a high-severity DOM Cross-Site Scripting (DOM XSS) vulnerability in the file-viewer workspace developed by flyfish-dev. The legacy Word document (.doc) parser fails to restrict hyperlink URI schemes when rendering extracted document targets into generated HTML. As a result, a remote attacker can construct a malicious legacy DOC file containing scripts inside hyperlink properties. When a user previews the file and clicks the hyperlink, arbitrary JavaScript executes in the context of the hosting origin, enabling session hijacking, credential theft, or unauthorized API interaction.
An authorization bypass and tenant isolation vulnerability in Perses prior to version 0.54.0-beta.3 allows authenticated viewers to access unauthorized project resources by manipulating query parameters or querying unmapped ephemeral endpoints.
CVE-2026-63199 is a critical missing authorization vulnerability (CWE-862) in Perses versions 0.43.0 to 0.54.0-rc.0. It allows low-privileged attackers to retrieve and exfiltrate highly sensitive credentials (secrets) from different scopes by configuring a malicious datasource pointing to an attacker-controlled endpoint.
An arbitrary file read and path traversal vulnerability exists in Perses prior to version 0.54.0-rc.0. When configured with a file-system database backend, the application lacks input validation on the request-controlled project query parameter. An authenticated attacker with low privileges can supply directory traversal sequences to read arbitrary JSON or YAML files on the host file system.
CVE-2026-59163 is a critical authentication bypass vulnerability in the Mnemosyne sync server. In versions prior to v3.10.1, the server's authentication logic decoded incoming JSON Web Tokens (JWT) but completely skipped cryptographic signature verification. An unauthenticated remote attacker can exploit this vulnerability to bypass authentication, impersonate arbitrary users, read synchronized AI agent states, or write malicious database updates.
An authorization bypass vulnerability exists in the Moquette MQTT broker prior to version 0.18.1. When an MQTT client registers a Last Will and Testament (LWT) topic during its connection setup, the broker fails to perform write-access checks on that topic. Upon an abrupt client disconnection, the broker publishes the registered Will message to subscribers of the unauthorized topic, bypassing configured Access Control Lists (ACLs).