Technical Analysis: Avoiding System Path Leakage from pg_available_extensions
Core Problem
The thread identifies an information disclosure vulnerability in the recently added location column of the pg_available_extensions system view, introduced by the "Add paths of extensions to pg_available_extensions" feature.
The Bug
The extension_control_path GUC controls where PostgreSQL searches for extension control files. Its documentation states that when set to an empty string, the system should behave as if the default value '$system' is assumed. However, there's an inconsistency in how the pg_available_extensions view reports the location:
RESET extension_control_path→ location shows$system(correct, symbolic placeholder)SET extension_control_path=''→ location shows/usr/local/pgsql/share/extension(bug: leaks absolute filesystem path)
Why This Matters Architecturally
-
Information Leakage: Exposing absolute filesystem paths to SQL-level users reveals server installation details (directory structure, OS layout, custom installation prefixes). This is a security concern, particularly in shared hosting or multi-tenant environments where unprivileged database users should not learn server-side filesystem details.
-
Violation of Abstraction Contract: The
$systemtoken exists precisely to abstract away the physical location of the system extension directory. The documentation explicitly states that an empty string should be equivalent to'$system', so the view output should be consistent regardless of which equivalent representation the user sets. -
Inconsistency Between GUC Interpretation and View Output: The GUC parsing layer correctly treats
''as equivalent to'$system'for the purpose of finding extensions, but the view rendering code path apparently resolves the path before checking whether it should be displayed as the symbolic$systemtoken.
Root Cause Analysis
The likely root cause is in the code that generates the location column for pg_available_extensions. When extension_control_path is explicitly set (even to empty string), the code path that resolves the search path apparently expands $system to its physical directory before populating the view column. When the GUC is at its default (NULL/unset) value, a different code path correctly outputs the symbolic $system string.
The distinction is likely between:
- Default value handling: The GUC's default is literally the string
'$system', which gets passed through to the view output without resolution. - Empty string handling: When explicitly set to
'', the code resolves it to the system directory path for searching purposes but then uses that resolved path in the view output rather than mapping it back to$system.
Proposed Fix
The patch (mentioned but attached in the follow-up email) is described as "straightforward" — it ensures that when extension_control_path resolves to the system extension directory (whether via explicit '$system' token, empty string, or default), the location column consistently displays $system rather than the resolved absolute path.
The fix likely adds a check in the view-generating function that compares the resolved path against the known system extension directory and substitutes $system if they match, ensuring the abstraction is maintained regardless of how the GUC is configured.
Security Implications
While this is not a critical vulnerability (users with SET privileges already have some level of trust), it violates the principle of least information disclosure. The $system abstraction was deliberately designed to hide server internals, and any code path that bypasses it undermines that design intent. This is particularly relevant for:
- Cloud-managed PostgreSQL services where path disclosure could reveal infrastructure details
- Environments using
pg_available_extensionsin monitoring queries accessible to less-privileged roles - Compliance scenarios where information leakage must be minimized
Assessment
This is a clean, well-identified edge-case bug in a new feature. The fix is low-risk and narrowly scoped. It should be applied before the feature ships in a release to avoid establishing a behavior that would later require a compatibility-breaking change.