مراية لـ
https://github.com/postalserver/postal.git
تم المزامنة 2026-06-03 21:45:48 +00:00
fix(message-db): prevent SQL injection via condition keys (GHSA-x2hq-rfpg-3xr5)
The Legacy API message lookup endpoints parsed the request body as JSON and passed the `id` parameter straight through to the message database. A JSON object supplied for `id` arrived as a Ruby Hash and was used as a raw set of SQL `WHERE` conditions. `hash_to_sql` interpolated each Hash key directly inside backtick identifier quoting while escaping only the value, so a key containing a backtick could break out of the identifier and inject arbitrary SQL into the SELECT (blind, time-based) against the message database. Fixes: - Escape all identifiers (columns, tables, database names) through a new `escape_identifier` helper that wraps in backticks and doubles embedded backticks. Applied across hash_to_sql, select, insert, insert_multi, update and delete so no caller can inject via an identifier. - Validate the Legacy API `id` parameter at the controller boundary: reject any non-scalar value before it reaches the database and coerce it to an integer. Internal Hash-based lookups (e.g. tracking middleware) are unaffected. Adds regression tests at the unit (hash_to_sql / escape_identifier) and request (legacy messages/deliveries) levels.
هذا الالتزام موجود في:
@@ -15,12 +15,9 @@ module LegacyAPI
|
||||
# OR an error if the message does not exist.
|
||||
#
|
||||
def message
|
||||
if api_params["id"].blank?
|
||||
render_parameter_error "`id` parameter is required but is missing"
|
||||
return
|
||||
end
|
||||
message = find_message
|
||||
return if performed?
|
||||
|
||||
message = @current_credential.server.message(api_params["id"])
|
||||
message_hash = { id: message.id, token: message.token }
|
||||
expansions = api_params["_expansions"]
|
||||
|
||||
@@ -111,12 +108,9 @@ module LegacyAPI
|
||||
# OR an error if the message does not exist.
|
||||
#
|
||||
def deliveries
|
||||
if api_params["id"].blank?
|
||||
render_parameter_error "`id` parameter is required but is missing"
|
||||
return
|
||||
end
|
||||
message = find_message
|
||||
return if performed?
|
||||
|
||||
message = @current_credential.server.message(api_params["id"])
|
||||
deliveries = message.deliveries.map do |d|
|
||||
{
|
||||
id: d.id,
|
||||
@@ -136,5 +130,37 @@ module LegacyAPI
|
||||
id: api_params["id"]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Look up the message referenced by the request's `id` parameter.
|
||||
#
|
||||
# The legacy API only ever identifies a message by its integer ID. The
|
||||
# request body is parsed as JSON, so without validation a JSON object or
|
||||
# array supplied for `id` would arrive as a Ruby Hash/Array and be passed
|
||||
# straight through to the message database as a raw set of SQL conditions.
|
||||
# We therefore reject anything that is not a simple scalar before it can
|
||||
# reach the database and coerce the value to an integer ID.
|
||||
#
|
||||
# Renders an error and returns nil when the parameter is missing or is not
|
||||
# a scalar; otherwise returns the matched message (raising NotFound when no
|
||||
# message matches, which the actions rescue).
|
||||
#
|
||||
# @return [Postal::MessageDB::Message, nil]
|
||||
def find_message
|
||||
id = api_params["id"]
|
||||
|
||||
if id.blank?
|
||||
render_parameter_error "`id` parameter is required but is missing"
|
||||
return
|
||||
end
|
||||
|
||||
unless id.is_a?(String) || id.is_a?(Integer)
|
||||
render_parameter_error "`id` parameter must be a string or integer"
|
||||
return
|
||||
end
|
||||
|
||||
@current_credential.server.message(id.to_i)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
المرجع في مشكلة جديدة
حظر مستخدم