From 21a2d05c609572fec979220064990f1732e6c7bb Mon Sep 17 00:00:00 2001 From: ozgen Date: Fri, 20 Feb 2026 15:08:56 +0100 Subject: [PATCH 1/2] fix: migration 268 to 269 enforce UNIQUE(name) for agent_groups and agent_groups_trash --- CMakeLists.txt | 2 +- src/manage_migrators.c | 81 +++++++++++++++++++++++++++++++++++++++++- src/manage_pg.c | 4 +-- src/sql.h | 3 ++ src/sql_pg.c | 26 ++++++++++++++ 5 files changed, 112 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55b28931f..973059d74 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,7 +96,7 @@ include(CPack) ## Variables -set(GVMD_DATABASE_VERSION 268) +set(GVMD_DATABASE_VERSION 269) set(GVMD_SCAP_DATABASE_VERSION 22) diff --git a/src/manage_migrators.c b/src/manage_migrators.c index 97049addd..255e7f1e5 100644 --- a/src/manage_migrators.c +++ b/src/manage_migrators.c @@ -3560,7 +3560,7 @@ migrate_266_to_267 () } /** - * @brief Migrate the database from version 265 to version 266. + * @brief Migrate the database from version 267 to version 268. * * @return 0 success, -1 error. */ @@ -3599,6 +3599,84 @@ migrate_267_to_268 () return 0; } +/** + * @brief Migrate the database from version 268 to version 269. + * + * @return 0 success, -1 error. + */ +int +migrate_268_to_269 () +{ + sql_begin_immediate (); + + /* Ensure that the database is currently version 268. */ + + if (manage_db_version () != 268) + { + sql_rollback (); + return -1; + } + /* Check table exists */ + const char *schema = sql_schema (); + int ag_exists = (sql_table_exists (schema, "agent_groups") == 1); + int ag_trash_exists = (sql_table_exists (schema, "agent_groups_trash") == 1); + + if (ag_exists) + { + /* Update the database. */ + // Before adding unique constraint migrate all agent group names + // name -> name - id + sql ( + "UPDATE agent_groups SET name = name || '-' || id::text;" + ); + + // According to the manual, ADD CONSTRAINT IF NOT EXISTS does not work + // Instead use drop and create new constraint + //ref: https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-DESC-DROP-CONSTRAINT + // Drop unique constraints if exists + sql ( + "ALTER TABLE agent_groups" + " DROP CONSTRAINT IF EXISTS agent_groups_name_key;" + ); + + // Add unique constraints for agent group name + sql ( + "ALTER TABLE agent_groups " + " ADD CONSTRAINT agent_groups_name_key UNIQUE (name);" + ); + } + + if (ag_trash_exists) + { + /* Update the database. */ + // Before adding unique constraint migrate all agent group names + // name -> name - id + sql ( + "UPDATE agent_groups_trash SET name = name || '-' || id::text;" + ); + + // Drop unique constraints if exists + sql ( + "ALTER TABLE agent_groups_trash" + " DROP CONSTRAINT IF EXISTS agent_groups_trash_name_key;" + ); + + // Add unique constraints for agent group name + sql ( + "ALTER TABLE agent_groups_trash " + " ADD CONSTRAINT agent_groups_trash_name_key UNIQUE (name);" + ); + } + + /* Set the database version to 269. */ + + set_db_version (269); + + sql_commit (); + + return 0; +} + #undef UPDATE_DASHBOARD_SETTINGS /** @@ -3673,6 +3751,7 @@ static migrator_t database_migrators[] = { {266, migrate_265_to_266}, {267, migrate_266_to_267}, {268, migrate_267_to_268}, + {269, migrate_268_to_269}, /* End marker. */ {-1, NULL}}; diff --git a/src/manage_pg.c b/src/manage_pg.c index 0a59bfb11..e491a5766 100644 --- a/src/manage_pg.c +++ b/src/manage_pg.c @@ -2882,7 +2882,7 @@ create_tables () sql ("CREATE TABLE IF NOT EXISTS agent_groups" " (id SERIAL PRIMARY KEY," " uuid TEXT NOT NULL UNIQUE," - " name TEXT NOT NULL," + " name TEXT UNIQUE NOT NULL," " scanner INTEGER NOT NULL REFERENCES scanners (id) ON DELETE RESTRICT," " owner INTEGER REFERENCES users (id) ON DELETE RESTRICT," " comment TEXT," @@ -2899,7 +2899,7 @@ create_tables () " uuid text UNIQUE NOT NULL," " owner integer REFERENCES users (id) ON DELETE RESTRICT," " scanner INTEGER NOT NULL REFERENCES scanners (id) ON DELETE RESTRICT," - " name text NOT NULL," + " name text UNIQUE NOT NULL," " comment text," " creation_time integer," " modification_time integer);"); diff --git a/src/sql.h b/src/sql.h index fa169fed9..73910206e 100644 --- a/src/sql.h +++ b/src/sql.h @@ -216,6 +216,9 @@ sql_int64_0_ps (const char *sql, ...); int sql_cancel_internal (); +int +sql_table_exists (const gchar *, const gchar *); + /* Transactions. */ void diff --git a/src/sql_pg.c b/src/sql_pg.c index 1e2bbb7e0..6b248b93c 100644 --- a/src/sql_pg.c +++ b/src/sql_pg.c @@ -942,6 +942,32 @@ sql_cancel_internal () return 0; } +/** + * @brief Check whether a table exists in a schema. + * + * @return 1 if exists, 0 if not exists, -1 on error. + */ +int +sql_table_exists (const gchar *schema, const gchar *table) +{ + const gchar *schema_name; + + if (table == NULL || *table == '\0') + return -1; + + schema_name = (schema && *schema) ? schema : sql_schema (); + + return sql_int_ps ( + "SELECT EXISTS (" + " SELECT 1" + " FROM pg_catalog.pg_tables" + " WHERE schemaname = $1 AND tablename = $2" + ");", + SQL_STR_PARAM (schema_name), + SQL_STR_PARAM (table), + NULL); +} + /** * @brief Tries to transfer data for a COPY ... FROM STDIN statement. * From 0810e45e53a2d3b5f00a57ca17bfa89fc506d15d Mon Sep 17 00:00:00 2001 From: ozgen Date: Fri, 20 Feb 2026 15:34:23 +0100 Subject: [PATCH 2/2] fix: reject duplicate names on create and modify the agent group --- src/gmp_agent_groups.c | 12 +++++ src/manage_agent_groups.h | 3 +- src/manage_sql_agent_groups.c | 88 +++++++++++++++++++++++------------ 3 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/gmp_agent_groups.c b/src/gmp_agent_groups.c index 600e512b7..ed00b86ee 100644 --- a/src/gmp_agent_groups.c +++ b/src/gmp_agent_groups.c @@ -465,6 +465,12 @@ create_agent_group_run (gmp_parser_t *gmp_parser, GError **error) log_event_fail ("agent_group", "Agent Group", NULL, "created"); break; + case AGENT_GROUP_RESP_GROUP_NAME_EXISTS: + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("create_agent_group", "Agent Group name exists")); + log_event_fail ("agent_group", "Agent Group", NULL, "created"); + break; + case AGENT_GROUP_RESP_INTERNAL_ERROR: default: SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("create_agent_group")); @@ -753,6 +759,12 @@ modify_agent_group_run (gmp_parser_t *gmp_parser, GError **error) log_event_fail ("agent_group", "Agent Group", NULL, "modified"); break; + case AGENT_GROUP_RESP_GROUP_NAME_EXISTS: + SEND_TO_CLIENT_OR_FAIL ( + XML_ERROR_SYNTAX ("modify_agent_group", "Agent Group name exists")); + log_event_fail ("agent_group", "Agent Group", NULL, "modified"); + break; + case AGENT_GROUP_RESP_INTERNAL_ERROR: default: SEND_TO_CLIENT_OR_FAIL (XML_INTERNAL_ERROR ("modify_agent_group")); diff --git a/src/manage_agent_groups.h b/src/manage_agent_groups.h index 2373c5705..cd3fe3a3f 100644 --- a/src/manage_agent_groups.h +++ b/src/manage_agent_groups.h @@ -47,7 +47,8 @@ typedef enum { AGENT_GROUP_RESP_INVALID_ARGUMENT = -5, ///< Failed invalid argument AGENT_GROUP_RESP_AGENT_NOT_FOUND = -6, ///< Failed getting agent id AGENT_GROUP_RESP_INTERNAL_ERROR = -7, ///< Internal error - AGENT_GROUP_RESP_AGENT_UNAUTHORIZED = -8 ///< Failed to create group with unauthorized agent + AGENT_GROUP_RESP_AGENT_UNAUTHORIZED = -8, ///< Failed to create group with unauthorized agent + AGENT_GROUP_RESP_GROUP_NAME_EXISTS = -9 ///< Failed to create group for name exists } agent_group_resp_t; agent_group_data_t diff --git a/src/manage_sql_agent_groups.c b/src/manage_sql_agent_groups.c index 44de6cb22..2adcf8880 100644 --- a/src/manage_sql_agent_groups.c +++ b/src/manage_sql_agent_groups.c @@ -112,6 +112,34 @@ agent_group_in_use_in_hidden_task (agent_group_t agent_group) agent_group); } +/** + * @brief Check if an agent group name already exists. + * in agent_groups and agent_groups_trash table. + * + * @param name The agent group name to check. + * + * @return 1 if the name exists in either agent_groups + * or agent_groups_trash, 0 otherwise. + */ +static int +agent_group_name_exists (const gchar *name) +{ + int count = sql_int_ps ( + "SELECT COUNT(*) FROM agent_groups WHERE name = $1;", + SQL_STR_PARAM (name), + NULL); + + if (count > 0) + return 1; + + count = sql_int_ps ( + "SELECT COUNT(*) FROM agent_groups_trash WHERE name = $1;", + SQL_STR_PARAM (name), + NULL); + + return (count > 0) ? 1 : 0; +} + /** * @brief Count the number of agent groups based on filter criteria. * @@ -215,30 +243,27 @@ create_agent_group (agent_group_data_t group_data, return AGENT_GROUP_RESP_INTERNAL_ERROR; } - gchar *quoted_uuid = sql_quote (group_data->uuid); - gchar *quoted_name = sql_quote (group_data->name); - gchar *quoted_comment = sql_quote (group_data->comment); - gchar *quoted_user_uuid = sql_quote (current_credentials.uuid); + if (agent_group_name_exists (group_data->name)) + { + g_debug ("%s: agent group name already exists", __func__); + return AGENT_GROUP_RESP_GROUP_NAME_EXISTS; + } sql_begin_immediate (); // Insert into agent_groups (scanner added) - sql ("INSERT INTO agent_groups (uuid, name, comment, scanner, owner, creation_time, modification_time) " - "VALUES ('%s', '%s', '%s', %llu, " - " (SELECT id FROM users WHERE uuid = '%s')," - " %ld, %ld);", - quoted_uuid, - quoted_name, - quoted_comment, - group_data->scanner, - quoted_user_uuid, - group_data->creation_time, - group_data->modification_time); - - g_free (quoted_uuid); - g_free (quoted_name); - g_free (quoted_comment); - g_free (quoted_user_uuid); + sql_ps ("INSERT INTO agent_groups (uuid, name, comment, scanner, owner, creation_time, modification_time) " + "VALUES ($1, $2, $3, $4, " + " (SELECT id FROM users WHERE uuid = $5)," + " $6, $7);", + SQL_STR_PARAM (group_data->uuid), + SQL_STR_PARAM (group_data->name), + SQL_STR_PARAM (group_data->comment), + SQL_RESOURCE_PARAM (group_data->scanner), + SQL_STR_PARAM (current_credentials.uuid), + SQL_INT_PARAM (group_data->creation_time), + SQL_INT_PARAM (group_data->modification_time), + NULL); agent_group_t new_agent_group = sql_last_insert_id (); if (new_agent_group == 0) @@ -300,20 +325,21 @@ modify_agent_group (agent_group_t agent_group, agent_group_data_t group_data, agent_uuid_list_t agent_uuids) { - gchar *quoted_name = sql_quote (group_data->name); - gchar *quoted_comment = sql_quote (group_data->comment); + if (agent_group_name_exists (group_data->name)) + { + g_debug ("%s: agent group name already exists", __func__); + return AGENT_GROUP_RESP_GROUP_NAME_EXISTS; + } sql_begin_immediate (); - sql ("UPDATE agent_groups SET name = '%s', comment = '%s', " - "modification_time = %ld WHERE id = %llu;", - quoted_name, - quoted_comment, - group_data->modification_time, - agent_group); - - g_free (quoted_name); - g_free (quoted_comment); + sql_ps ("UPDATE agent_groups SET name = $1, comment = $2, " + "modification_time = $3 WHERE id = $4;", + SQL_STR_PARAM (group_data->name), + SQL_STR_PARAM (group_data->comment), + SQL_INT_PARAM (group_data->modification_time), + SQL_RESOURCE_PARAM (agent_group), + NULL); if (!agent_uuids || agent_uuids->count == 0) {