Files
pgadmin4/web/pgadmin/utils/validation_utils.py
T
Ashesh Vashi d57acce354 fix: harden validation/preference/connection-params paths against pre-existing edge cases
Five small defensive fixes that were exposed by running the full
regression suite end-to-end:

1. utils/validation_utils.py: validate_email() now returns False
   instead of raising TypeError when passed a non-str/bytes value
   (e.g. None from a missing form field). Matches the wrapper's
   contract that it only ever returns bool.

2. tools/user_management/__init__.py: list endpoint guarded against
   users with no roles. u.roles[0].id -> u.roles[0].id if u.roles
   else None. Triggered by ChangePasswordTestCase fixtures.

3. utils/preferences.py: control_props['tags'] / ['creatable']
   replaced with .get(...) so preferences whose control_props omit
   these keys do not raise KeyError on update.

4. browser/server_groups/servers/__init__.py (create endpoint):
   convert_connection_parameter() is bidirectional (list<->dict).
   The save path always wants the storage shape (dict). When input
   is already a dict (internal callers / tests mimicking storage
   form), skip conversion to avoid the dict->list round-trip that
   breaks the MutableDict column. Same fix applied to the workspaces
   save path.

5. misc/workspaces/__init__.py: same defensive handling for
   convert_connection_parameter() on the save path.

These are all pre-existing master bugs surfaced by edge-case test
data; none are introduced by the 9.15 CVE work.
2026-05-01 23:29:43 +05:30

60 lines
2.1 KiB
Python

##########################################################################
#
# pgAdmin 4 - PostgreSQL Tools
#
# Copyright (C) 2013 - 2026, The pgAdmin Development Team
# This software is released under the PostgreSQL Licence
#
##########################################################################
import email_validator
from email_validator import validate_email as email_validate, \
EmailNotValidError
def validate_email(email, email_config=None):
# email_validator raises TypeError (not EmailNotValidError) when the
# input is not str/bytes. Treat anything non-string as invalid so
# callers see a clean False, matching the contract that this wrapper
# never raises.
if not isinstance(email, (str, bytes)):
return False
try:
if email_config is None:
email_config = {}
import config
email_config['CHECK_EMAIL_DELIVERABILITY'] = \
config.CHECK_EMAIL_DELIVERABILITY
email_config['ALLOW_SPECIAL_EMAIL_DOMAINS'] = \
config.ALLOW_SPECIAL_EMAIL_DOMAINS
email_config["GLOBALLY_DELIVERABLE"] = \
config.GLOBALLY_DELIVERABLE
# Allow special email domains
if isinstance(email_config['ALLOW_SPECIAL_EMAIL_DOMAINS'], str):
email_config['ALLOW_SPECIAL_EMAIL_DOMAINS'] = \
email_config['ALLOW_SPECIAL_EMAIL_DOMAINS'].split(',')
try:
email_validator.SPECIAL_USE_DOMAIN_NAMES = [
d for d in email_validator.SPECIAL_USE_DOMAIN_NAMES
if d not in email_config['ALLOW_SPECIAL_EMAIL_DOMAINS']
]
except Exception:
pass
email_validator.GLOBALLY_DELIVERABLE = \
email_config["GLOBALLY_DELIVERABLE"]
# Validate.
_ = email_validate(
email,
check_deliverability=email_config['CHECK_EMAIL_DELIVERABILITY'])
# Update with the normalized form.
return True
except EmailNotValidError as e:
# email is not valid, exception message is human-readable
print(str(e))
return False