Parsl Tongue: SQL Injection in High-Performance Computing Visualization
Jan 6, 2026·6 min read
Executive Summary (TL;DR)
The 'parsl-visualize' dashboard fails to sanitize the 'workflow_id' URL parameter before passing it to a raw SQL query. This allows unauthenticated remote attackers to execute arbitrary SQL commands via Boolean-based blind injection. The vulnerability affects all versions prior to the January 5, 2026 patch.
Parsl, a parallel scripting library for Python often used in academic and high-performance computing (HPC) environments, contains a critical SQL injection vulnerability in its monitoring dashboard. The flaw allows unauthenticated attackers to manipulate database queries via the visualization interface, potentially exposing sensitive workflow metadata and environment configurations.
The Hook: Visualizing the Disaster
Parsl is the glue that holds together massive scientific workflows. It lets researchers write Python scripts that scale across thousands of cores, managing dependencies and data movement like a high-performance traffic cop. To keep track of these complex jobs, Parsl provides a monitoring tool: the parsl-visualize dashboard. It renders beautiful DAGs (Directed Acyclic Graphs) showing task dependencies and statuses.
But here is the problem: visualization tools are often treated like second-class citizens in the security world. Developers assume they will only run on safe, internal networks—a dangerous assumption in the age of perimeter-less networks. In this case, the code responsible for fetching those graph details from the database was trusting user input implicitly.
This vulnerability lives inside parsl/monitoring/visualization/views.py. While the dashboard looks like a harmless read-only interface, it is actually a direct pipe to the underlying database (typically SQLite or PostgreSQL). An attacker doesn't need a username or password; they just need network access to the visualization port (default 8080) and a little bit of creativity with URL parameters.
The Flaw: Python's Classic String Sin
The root cause here is as old as the hills, yet we see it in Python codebases every single day. The developer used Python's string formatting operator (%) or f-strings to build a SQL query dynamically. In the world of database security, this is the cardinal sin. It mixes the control plane (the SQL commands) with the data plane (the user input).
The application defines routes that accept a workflow_id (also known as a run_id). This ID is supposed to be a UUID or a specific identifier string. However, the application code grabs this ID directly from the URL and drops it straight into a SELECT statement string. It does not validate that the ID is actually an ID, nor does it escape special characters.
Once that string is constructed, it gets handed off to pandas.read_sql_query. Pandas is a powerful data analysis library, but it isn't a security tool. If you hand it a raw string containing malicious SQL syntax, it will happily execute it against the database engine. It assumes you know what you are doing. In this case, the developers didn't.
The Code: The Smoking Gun
Let's look at the vulnerable pattern found in parsl/monitoring/visualization/views.py. This is textbook insecure coding. The code takes workflow_id and effectively interpolates it into the WHERE clause.
# VULNERABLE CODE
query = """SELECT task.task_id, ...
FROM task ...
WHERE task.run_id='%s'""" % (workflow_id)
df_tasks = pd.read_sql_query(query, db.engine)If workflow_id is just 12345, the query works fine. But if workflow_id is 12345' OR '1'='1, the logic breaks. The single quote closes the run_id string literal, and the rest is interpreted as SQL commands.
The fix involves switching to parameterized queries. This tells the database driver to treat the input strictly as data, not executable code. Here is how the patch handles it using SQLAlchemy's text() construct:
# PATCHED CODE
import sqlalchemy
query = """SELECT task.task_id, ...
FROM task ...
WHERE task.run_id=:run_id"""
# The input is passed separately in the 'params' dictionary
df_tasks = pd.read_sql_query(
sqlalchemy.text(query),
db.engine,
params={"run_id": workflow_id}
)Notice the use of :run_id placeholder and the params dictionary. The database driver now handles the escaping automatically.
The Exploit: Asking the Oracle
Since this is a visualization tool, we can exploit this using Boolean-based Blind SQL Injection. We might not see the database errors directly in the browser, but we can see the side effects: does the graph render, or does it vanish?
Scenario 1: The TRUE Condition
We inject a condition that is always true. We append ' AND '1'='1 to the valid ID.
- URL:
/workflow/valid-id' AND '1'='1/dag_group_by_states - Query becomes:
SELECT ... WHERE run_id='valid-id' AND '1'='1' - Result: The database returns the rows because
True AND TrueisTrue. The dashboard renders the graph normally.
Scenario 2: The FALSE Condition
We inject a condition that is always false. We append ' AND '1'='0.
- URL:
/workflow/valid-id' AND '1'='0/dag_group_by_states - Query becomes:
SELECT ... WHERE run_id='valid-id' AND '1'='0' - Result: The database returns zero rows. The dashboard tries to render an empty dataset and likely shows nothing or an empty grid.
By automating this process (changing the condition to ask questions like "Is the first letter of the admin password 'A'?"), an attacker can slowly extract the entire database, character by character.
The Impact: Why Should We Panic?
You might think, "So what? It's just workflow logs." But in HPC environments, these logs are gold mines. The database often contains environment variables used during task execution. These variables frequently hold API keys, AWS credentials, or database connection strings for other systems.
Furthermore, scientific workflows often process sensitive data—genomic sequences, proprietary algorithms, or financial models. Access to the metadata can reveal the structure of this proprietary research or even leak input parameters that are confidential.
Finally, there is the risk of Denial of Service (DoS). SQL injection allows an attacker to execute resource-intensive functions. In SQLite, functions like randomblob() can consume massive amounts of memory. An attacker could craft a query that causes the visualization server—or the database itself—to hang or crash, disrupting the monitoring capabilities for the entire cluster.
The Fix: Parameterize or Perish
The immediate fix is to upgrade Parsl. Specifically, you need the version containing commit 013a928461e70f38a33258bd525a351ed828e974 (merged in January 2026). If upgrading is not an option, you must apply the patch manually by editing parsl/monitoring/visualization/views.py to use sqlalchemy.text and bind parameters.
Beyond patching, this vulnerability highlights the need for Defense in Depth. Visualization dashboards should never be exposed to the public internet without authentication. If you are running parsl-visualize, ensure it is behind a VPN or an authenticating reverse proxy (like Nginx with Basic Auth or OAuth).
Finally, for developers: stop using f-strings or % formatting for SQL. Just stop. Every modern database library in Python supports parameterization. It is cleaner, safer, and often faster because the database can cache the query execution plan.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:LAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Parsl Parsl | < Commit 013a928461e70f38a33258bd525a351ed828e974 | Commit 013a928461e70f38a33258bd525a351ed828e974 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-89 (SQL Injection) |
| Attack Vector | Network (Web Dashboard) |
| CVSS Score | 8.6 (High) |
| Affected Component | parsl.monitoring.visualization |
| Exploit Status | PoC Available |
| Authentication | None Required |
MITRE ATT&CK Mapping
The software constructs all or part of an SQL command using an input that might be influenced by an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the intended SQL command when it is sent to a downstream component.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.