Split from #3651 as suggested by @charmander.
Summary
When a PostgreSQL server returns column names like __proto__, constructor, or toString, these are used as object keys in result rows without any sanitization. A rogue or compromised PostgreSQL server can exploit this to pollute Object.prototype on the client.
Affected code
packages/pg/lib/result.js — both addFields() and parseRow():
// addFields() builds the pre-built empty result object:
const row = {}
for (let i = 0; i < fieldDescriptions.length; i++) {
const desc = fieldDescriptions[i]
row[desc.name] = null // desc.name comes from server
}
this._prebuiltEmptyResultObject = { ...row }
// parseRow() assigns parsed values using server-supplied field names:
parseRow(rowData) {
const row = { ...this._prebuiltEmptyResultObject }
for (let i = 0, len = rowData.length; i < len; i++) {
const field = this.fields[i].name // server-controlled
row[field] = this._parsers[i](v) // value also server-controlled
}
return row
}
Threat model
This is a rogue server scenario, not a trusted-config issue. If a client connects to a malicious PostgreSQL server (e.g. via DNS hijack, MITM without TLS verification, or a compromised host), the server controls the column names in RowDescription messages. A column named __proto__ with a crafted value can pollute the prototype chain of all objects in the Node.js process.
Steps to reproduce
- Set up a mock PostgreSQL server that returns a
RowDescription with a field named __proto__ and a DataRow with the value {"polluted": true} (as a JSON/text column)
- Connect a
pg client to this server and execute any query
- After the result is parsed, check
({}).polluted — it will be true
Impact
- Denial of service (crash or undefined behavior in downstream code)
- Potential RCE depending on how the application uses objects after pollution
Suggested fix
Use Object.create(null) instead of {} for result rows, or reject/rename column names that collide with Object.prototype properties:
const row = Object.create(null)
Split from #3651 as suggested by @charmander.
Summary
When a PostgreSQL server returns column names like
__proto__,constructor, ortoString, these are used as object keys in result rows without any sanitization. A rogue or compromised PostgreSQL server can exploit this to polluteObject.prototypeon the client.Affected code
packages/pg/lib/result.js— bothaddFields()andparseRow():Threat model
This is a rogue server scenario, not a trusted-config issue. If a client connects to a malicious PostgreSQL server (e.g. via DNS hijack, MITM without TLS verification, or a compromised host), the server controls the column names in
RowDescriptionmessages. A column named__proto__with a crafted value can pollute the prototype chain of all objects in the Node.js process.Steps to reproduce
RowDescriptionwith a field named__proto__and aDataRowwith the value{"polluted": true}(as a JSON/text column)pgclient to this server and execute any query({}).polluted— it will betrueImpact
Suggested fix
Use
Object.create(null)instead of{}for result rows, or reject/rename column names that collide withObject.prototypeproperties: