CVE-2026-12044: pgAdmin 4: SQL injection in COMMENT ON ... IS '<description>' rendering across dialog templates

Published Jun 18, 2026
·
Updated

SQL injection in pgAdmin 4 across every dialog template that renders COMMENT ON ... IS '<description>' for a user-supplied description field. The Jinja templates for Domains (and their constraints), Foreign Tables, Languages, and Event Triggers, plus the Views OID-lookup query, interpolated the description directly inside a single-quoted SQL literal -- '{{ data.description }}' -- instead of passing it through the qtLiteral escape filter. An authenticated pgAdmin user with permission to create or alter the affected object types could submit a description containing an apostrophe, break out of the literal and chain arbitrary SQL. The injected SQL runs under the PostgreSQL role the user is already authenticated as; for a connected role with COPY ... TO/FROM PROGRAM (typically PostgreSQL superuser), this chains to OS command execution on the PostgreSQL host. The defect does not cross a privilege boundary -- the user already has direct SQL access to that role through pgAdmin's Query Tool -- so the attacker gains no capability beyond what their database role already grants. The marginal impact captures bypass of any application-layer Query Tool gating an operator may have configured.

The defect was originally reported against the Domain Dialog description field; a code-wide audit identified sixteen sites of the same pattern across the templates listed above. The same review also surfaced ten related sinks in the pgstattuple/pgstatindex stats templates -- pgstattuple('{{schema}}.{{table}}') and the matching pgstatindex shape -- where qtIdent escapes embedded double quotes inside the identifier but not apostrophes, so a user with CREATE privilege on a schema could plant a table or index named foo'bar and a later stats viewer would render an unbalanced literal.

Fix is layered:

1. Sites: replace every '{{ x.description }}' with {{ x.description|qtLiteral(conn) }} (no surrounding quotes -- the filter wraps the value in escaped quotes itself). Plumb conn=self.conn through every rendertemplate call that loads one of these templates. Also corrects a { % elif Jinja typo in the foreign-table schema diff (dead branch). Rewrite the ten pgstattuple/pgstatindex stats sites to address the relation via OID + ::oid::regclass cast (e.g. pgstattuple({{ tid }}::oid::regclass)), eliminating the embedded literal-call form entirely so that bug-class can no longer recur there.

2. Driver hardening: qtLiteral (in utils/driver/psycopg3/init.py) used to silently return the raw unescaped value when its conn argument was falsy. It now raises ValueError -- surfacing the entire bug class going forward. The change immediately uncovered eight latent plumbing bugs (in schemas/init.py, schemas/functions/init.py, schemas/tables/utils.py, foreignservers/init.py, and seven sites in roles/init.py) -- all fixed as part of this patch. The inner except block that swallowed adapter-level failures and returned the raw value is also removed, so unadaptable inputs raise instead of leaking unescaped values.

3. Regression tests: a per-template behavioural test renders each previously-vulnerable template with an apostrophe-injection payload and asserts the escaped fragment is present and the vulnerable fragment absent; a lint test walks every .sql template flagging any '{{ ... }}' single-quote-wrapped interpolation against an explicit allowlist; unit tests cover the new qtLiteral fail-fast and inner-except raise paths.

This issue affects pgAdmin 4: from 1.0 before 9.16.

Affected Software

2 affected components
pgAdmin pgAdmin 4>=1.0<9.16
pgAdmin Pgadmin 4 Postgresql>=1.0<9.16

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pgAdmin 4 to a version that resolves this vulnerability.

    Fixed in 9.16
  2. Configuration

    In the templates for Domains (and constraints), Foreign Tables, Languages, and Event Triggers, and in all dialog templates that render COMMENT ON ... with user-supplied descriptions, replace single-quoted SQL-literal interpolation such as ' {{ data.description }} ' / ' {{ x.description }} ' with qtLiteral escaping: use {{ x.description|qtLiteral(conn) }} (and do not add surrounding quotes because qtLiteral wraps the value in escaped quotes itself).

    pgAdmin 4 Jinja templates (description fields) description interpolation pattern = Replace all occurrences of '{{ data.description }}' and '{{ x.description }}' (single-quote-wrapped) with escaped rendering using qtLiteral
  3. Configuration

    Plumb conn=self.conn through every render_template call that loads these vulnerable templates, so the qtLiteral filter can access the connection and properly escape/validate.

    pgAdmin 4 template rendering plumbing conn argument passed to render_template = conn=self.conn
  4. Configuration

    Change qtLiteral so that when its conn argument is falsy it raises ValueError (fail-fast) rather than silently returning the raw unescaped value. Also remove the inner except block that swallowed adapter-level failures and returned the raw value; unadaptable inputs must raise instead of leaking unescaped values.

    pgAdmin 4 qtLiteral filter (utils/driver/psycopg3/__init__.py) qtLiteral behavior when conn is falsy = Raise ValueError instead of returning raw value
  5. Configuration

    Rewrite the ten pgstattuple/pgstatindex stats sites to address the relation via OID + ::oid::regclass cast (e.g., pgstattuple({{ tid }}::oid::regclass)), eliminating the embedded literal-call form entirely so the same apostrophe-literal issue cannot recur.

    pgAdmin 4 pgstattuple/pgstatindex stats templates stats site SQL construction = Rewrite using OID + ::oid::regclass cast

Event History

Jun 18, 2026
CVE Published
via MITRE·11:37 PM
Data Sourced
via MITRE·11:37 PM
DescriptionSeverityWeakness
Jun 19, 2026
Data Sourced
via NVD·12:16 AM
RemedyDescriptionSeverityWeaknessAffected Software
Free Weekly Intel

Don't miss critical vulnerabilities

Join thousands of security professionals who receive our weekly digest of trending CVEs, zero-days, and exploited vulnerabilities.

No spam. Unsubscribe anytime.

Frequently Asked Questions

1

What is the severity of CVE-2026-12044?

CVE-2026-12044 has a high severity rating of 8.8.

2

How do I fix CVE-2026-12044?

To fix CVE-2026-12044, update pgAdmin 4 to the latest version that addresses the SQL injection vulnerability.

3

What vulnerabilities are associated with CVE-2026-12044?

CVE-2026-12044 is associated with SQL injection vulnerabilities that can be exploited through user-supplied inputs in dialog templates.

4

What impact does CVE-2026-12044 have on data security?

CVE-2026-12044 can lead to unauthorized access to sensitive data through SQL injection attacks.

5

Which software is affected by CVE-2026-12044?

CVE-2026-12044 affects pgAdmin 4 that utilizes Jinja templates for rendering user inputs.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203