From f5b7cd6fb1c938d578604dec4bb5eb55a87bff15 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 20:27:45 +0000 Subject: [PATCH 1/5] Initial plan From 6403573e36a9a632daaf749816c6cc95f0e28d17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 20:32:21 +0000 Subject: [PATCH 2/5] Fix SQL injection vulnerabilities using parameterized queries Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com> --- .../Sql/SqlDatabaseMetaDataRetriever.cs | 63 ++++++++++++++++--- 1 file changed, 56 insertions(+), 7 deletions(-) diff --git a/mRemoteNG/Config/Serializers/ConnectionSerializers/Sql/SqlDatabaseMetaDataRetriever.cs b/mRemoteNG/Config/Serializers/ConnectionSerializers/Sql/SqlDatabaseMetaDataRetriever.cs index d900a959..27fe95bc 100644 --- a/mRemoteNG/Config/Serializers/ConnectionSerializers/Sql/SqlDatabaseMetaDataRetriever.cs +++ b/mRemoteNG/Config/Serializers/ConnectionSerializers/Sql/SqlDatabaseMetaDataRetriever.cs @@ -99,9 +99,22 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Sql if (rootTreeNode != null) { cmd = databaseConnector.DbCommand( - "INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES('" + - MiscTools.PrepareValueForDB(rootTreeNode.Name) + "', 0, '" + strProtected + "','" + - ConnectionsFileInfo.ConnectionFileVersion + "')"); + "INSERT INTO tblRoot (Name, Export, Protected, ConfVersion) VALUES(@Name, 0, @Protected, @ConfVersion)"); + + DbParameter nameParam = cmd.CreateParameter(); + nameParam.ParameterName = "@Name"; + nameParam.Value = rootTreeNode.Name; + cmd.Parameters.Add(nameParam); + + DbParameter protectedParam = cmd.CreateParameter(); + protectedParam.ParameterName = "@Protected"; + protectedParam.Value = strProtected; + cmd.Parameters.Add(protectedParam); + + DbParameter confVersionParam = cmd.CreateParameter(); + confVersionParam.ParameterName = "@ConfVersion"; + confVersionParam.Value = ConnectionsFileInfo.ConnectionFileVersion.ToString(); + cmd.Parameters.Add(confVersionParam); cmd.ExecuteNonQuery(); } @@ -111,6 +124,22 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Sql } } + private bool IsValidTableName(string tableName) + { + // Table names should only contain alphanumeric characters and underscores + // This prevents SQL injection when table names must be used directly in queries + if (string.IsNullOrWhiteSpace(tableName)) + return false; + + foreach (char c in tableName) + { + if (!char.IsLetterOrDigit(c) && c != '_') + return false; + } + + return true; + } + private bool DoesDbTableExist(IDatabaseConnector databaseConnector, string tableName) { bool exists; @@ -119,7 +148,18 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Sql { // ANSI SQL way. Works in PostgreSQL, MSSQL, MySQL. string database_name = Properties.OptionsDBsPage.Default.SQLDatabaseName; - DbCommand cmd = databaseConnector.DbCommand("select case when exists((select * from information_schema.tables where table_name = '" + tableName + "' and table_schema='"+ database_name + "')) then 1 else 0 end"); + DbCommand cmd = databaseConnector.DbCommand("select case when exists((select * from information_schema.tables where table_name = @TableName and table_schema = @DatabaseName)) then 1 else 0 end"); + + DbParameter tableNameParam = cmd.CreateParameter(); + tableNameParam.ParameterName = "@TableName"; + tableNameParam.Value = tableName; + cmd.Parameters.Add(tableNameParam); + + DbParameter databaseNameParam = cmd.CreateParameter(); + databaseNameParam.ParameterName = "@DatabaseName"; + databaseNameParam.Value = database_name; + cmd.Parameters.Add(databaseNameParam); + short cmdResult = Convert.ToInt16(cmd.ExecuteScalar()); exists = (cmdResult == 1); } @@ -128,9 +168,18 @@ namespace mRemoteNG.Config.Serializers.ConnectionSerializers.Sql try { // Other RDBMS. Graceful degradation - exists = true; - DbCommand cmdOthers = databaseConnector.DbCommand("select 1 from " + tableName + " where 1 = 0"); - cmdOthers.ExecuteNonQuery(); + // Note: Table names cannot be parameterized in standard SQL. + // Validate tableName to prevent SQL injection + if (!IsValidTableName(tableName)) + { + exists = false; + } + else + { + exists = true; + DbCommand cmdOthers = databaseConnector.DbCommand($"select 1 from {tableName} where 1 = 0"); + cmdOthers.ExecuteNonQuery(); + } } catch { From 5e2fc8b0dc6acb0fdd1c77c08452b16d1af67445 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 20:33:47 +0000 Subject: [PATCH 3/5] Fix SQL injection in SqlConnectionsSaver UpdateUpdatesTable method Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com> --- mRemoteNG/Config/Connections/SqlConnectionsSaver.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs b/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs index 8dc8a287..955708f5 100644 --- a/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs @@ -168,7 +168,13 @@ namespace mRemoteNG.Config.Connections // TODO: use transaction System.Data.Common.DbCommand dbQuery = databaseConnector.DbCommand("TRUNCATE TABLE tblUpdate"); dbQuery.ExecuteNonQuery(); - dbQuery = databaseConnector.DbCommand("INSERT INTO tblUpdate (LastUpdate) VALUES('" + MiscTools.DBDate(DateTime.Now.ToUniversalTime()) + "')"); + dbQuery = databaseConnector.DbCommand("INSERT INTO tblUpdate (LastUpdate) VALUES(@LastUpdate)"); + + DbParameter lastUpdateParam = dbQuery.CreateParameter(); + lastUpdateParam.ParameterName = "@LastUpdate"; + lastUpdateParam.Value = MiscTools.DBTimeStampNow(); + dbQuery.Parameters.Add(lastUpdateParam); + dbQuery.ExecuteNonQuery(); } From fe4e2057722e0614810c1389d936371936ab4caf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 20:36:32 +0000 Subject: [PATCH 4/5] Add clarifying comment about datetime parameter usage Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com> --- mRemoteNG/Config/Connections/SqlConnectionsSaver.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs b/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs index 955708f5..d1c3ec0c 100644 --- a/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs @@ -172,6 +172,8 @@ namespace mRemoteNG.Config.Connections DbParameter lastUpdateParam = dbQuery.CreateParameter(); lastUpdateParam.ParameterName = "@LastUpdate"; + // Use DBTimeStampNow() instead of DBDate() - the column is datetime type, not string + // DBTimeStampNow() returns the appropriate .NET type (DateTime or MySqlDateTime) for parameterized queries lastUpdateParam.Value = MiscTools.DBTimeStampNow(); dbQuery.Parameters.Add(lastUpdateParam); From 2d3830b74762d42fc659403eb768858ab59330d4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 18 Oct 2025 20:38:22 +0000 Subject: [PATCH 5/5] Clarify comment about database-specific datetime types Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com> --- mRemoteNG/Config/Connections/SqlConnectionsSaver.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs b/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs index d1c3ec0c..c10bdcff 100644 --- a/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs +++ b/mRemoteNG/Config/Connections/SqlConnectionsSaver.cs @@ -173,7 +173,7 @@ namespace mRemoteNG.Config.Connections DbParameter lastUpdateParam = dbQuery.CreateParameter(); lastUpdateParam.ParameterName = "@LastUpdate"; // Use DBTimeStampNow() instead of DBDate() - the column is datetime type, not string - // DBTimeStampNow() returns the appropriate .NET type (DateTime or MySqlDateTime) for parameterized queries + // DBTimeStampNow() returns the database-specific .NET type: DateTime for MSSQL, MySqlDateTime for MySQL lastUpdateParam.Value = MiscTools.DBTimeStampNow(); dbQuery.Parameters.Add(lastUpdateParam);