From 4ccc81955e566c5a23f18935b56032c995731951 Mon Sep 17 00:00:00 2001 From: NguyenND Date: Thu, 21 May 2026 16:07:02 -0400 Subject: [PATCH] Fix high-severity issues: implement SSL and warn on raw-SQL filter SSL was wired through the UI and ConnectionProfile but never actually applied in any driver or passed from the connection builder. - main_window: pass ssl/ssl_ca/ssl_cert/ssl_key from profile into the driver config dict so drivers can act on them - MySQL: add ssl={ca,cert,key} to pymysql connect kwargs when enabled - PostgreSQL: set sslmode=verify-ca (with CA) or require, plus sslrootcert/sslcert/sslkey when provided - MSSQL: switch Encrypt=yes + TrustServerCertificate=no when SSL is on; Encrypt=no + TrustServerCertificate=yes otherwise - SQLite: no change needed (local file, no network layer) WHERE filter: add tooltip explicitly labelling the input as raw SQL so users understand arbitrary expressions are executed directly. Co-Authored-By: Claude Sonnet 4.6 --- app/drivers/mssql_driver.py | 10 +++++++++- app/drivers/mysql_driver.py | 6 ++++++ app/drivers/postgres_driver.py | 5 +++++ app/main_window.py | 4 ++++ app/ui/table_viewer.py | 6 +++++- 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/drivers/mssql_driver.py b/app/drivers/mssql_driver.py index d32a468..9a983c1 100644 --- a/app/drivers/mssql_driver.py +++ b/app/drivers/mssql_driver.py @@ -34,12 +34,20 @@ class MSSQLDriver(BaseDriver): installed = pyodbc.drivers() if PYODBC_AVAILABLE else [] driver = next((d for d in preferred if d in installed), preferred[0]) + if self.config.get("ssl"): + encrypt = "yes" + trust_cert = "no" + else: + encrypt = "no" + trust_cert = "yes" + return ( f"DRIVER={{{driver}}};" f"SERVER={host},{port};" f"DATABASE={db};" f"UID={user};PWD={pwd};" - f"TrustServerCertificate=yes;" + f"Encrypt={encrypt};" + f"TrustServerCertificate={trust_cert};" f"Connection Timeout={self.config.get('connection_timeout', 30)};" ) diff --git a/app/drivers/mysql_driver.py b/app/drivers/mysql_driver.py index 7242645..8729812 100644 --- a/app/drivers/mysql_driver.py +++ b/app/drivers/mysql_driver.py @@ -31,6 +31,12 @@ class MySQLDriver(BaseDriver): db = self.config.get("database", "") if db: kw["database"] = db + if self.config.get("ssl"): + ssl_opts = {} + if self.config.get("ssl_ca"): ssl_opts["ca"] = self.config["ssl_ca"] + if self.config.get("ssl_cert"): ssl_opts["cert"] = self.config["ssl_cert"] + if self.config.get("ssl_key"): ssl_opts["key"] = self.config["ssl_key"] + kw["ssl"] = ssl_opts return kw def connect(self) -> None: diff --git a/app/drivers/postgres_driver.py b/app/drivers/postgres_driver.py index 9a49b51..4aac641 100644 --- a/app/drivers/postgres_driver.py +++ b/app/drivers/postgres_driver.py @@ -26,6 +26,11 @@ class PostgreSQLDriver(BaseDriver): db = self.config.get("database", "") if db: kw["dbname"] = db + if self.config.get("ssl"): + kw["sslmode"] = "verify-ca" if self.config.get("ssl_ca") else "require" + if self.config.get("ssl_ca"): kw["sslrootcert"] = self.config["ssl_ca"] + if self.config.get("ssl_cert"): kw["sslcert"] = self.config["ssl_cert"] + if self.config.get("ssl_key"): kw["sslkey"] = self.config["ssl_key"] return kw def connect(self) -> None: diff --git a/app/main_window.py b/app/main_window.py index 3b5c271..5f8d528 100644 --- a/app/main_window.py +++ b/app/main_window.py @@ -246,6 +246,10 @@ class MainWindow(QMainWindow): "user": profile.username, "password": profile.password, "connection_timeout": profile.connection_timeout, + "ssl": profile.ssl, + "ssl_ca": profile.ssl_ca, + "ssl_cert": profile.ssl_cert, + "ssl_key": profile.ssl_key, } _log.info("Connecting to '%s' type=%s host=%s", profile.name, profile.db_type, profile.host) diff --git a/app/ui/table_viewer.py b/app/ui/table_viewer.py index daf1756..3daa226 100644 --- a/app/ui/table_viewer.py +++ b/app/ui/table_viewer.py @@ -331,9 +331,13 @@ class TableViewer(QWidget): self._filter_input = QLineEdit() self._filter_input.setPlaceholderText("WHERE … (e.g. id > 100)") self._filter_input.setFixedWidth(260) + self._filter_input.setToolTip( + "Raw SQL — injected directly into WHERE clause.\n" + "Examples: id > 100 name LIKE 'A%' status = 'active'" + ) self._filter_btn = QPushButton("🔍") self._filter_btn.setFixedWidth(34) - self._filter_btn.setToolTip("Apply filter") + self._filter_btn.setToolTip("Apply filter (raw SQL WHERE clause)") self._filter_btn.clicked.connect(self._apply_filter) self._filter_input.returnPressed.connect(self._apply_filter)