diff --git a/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-01.patch b/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-01.patch
new file mode 100644
index 0000000000..3ef6ddcca1
--- /dev/null
+++ b/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-01.patch
@@ -0,0 +1,65 @@
+From 1e872e301436efa5d3fcd54e7628ac82c57698d2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?B=C3=A1lint=20Horv=C3=A1th?= <bal.horv.98@gmail.com>
+Date: Tue, 7 Apr 2026 14:40:17 +0200
+Subject: [PATCH] afsql: Fixed SQL injection bug
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Bálint Horváth <bal.horv.98@gmail.com>
+Signed-off-by: Hofi <hofione@gmail.com>
+
+CVE: CVE-2026-39879
+Upstream-Status: Backport [https://github.com/syslog-ng/syslog-ng/commit/1e872e301436efa5d3fcd54e7628ac82c57698d2]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ modules/afsql/afsql.c | 12 +++++++++---
+ 1 file changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/modules/afsql/afsql.c b/modules/afsql/afsql.c
+index be59a2352..fb7ef3b32 100644
+--- a/modules/afsql/afsql.c
++++ b/modules/afsql/afsql.c
+@@ -36,6 +36,8 @@
+ 
+ #include <string.h>
+ #include <errno.h>
++#include <utf8utils.h>
++
+ #include "compat/openssl_support.h"
+ #include <openssl/evp.h>
+ 
+@@ -219,11 +221,12 @@ static gboolean
+ afsql_dd_run_query(AFSqlDestDriver *self, const gchar *query, gboolean silent, dbi_result *result)
+ {
+   dbi_result db_res;
++  gchar* escaped_query = convert_unsafe_utf8_to_escaped_text(query, -1, 0);
+ 
+   msg_debug("Running SQL query",
+-            evt_tag_str("query", query));
++            evt_tag_str("query", escaped_query));
+ 
+-  db_res = dbi_conn_query(self->dbi_ctx, query);
++  db_res = dbi_conn_query(self->dbi_ctx, escaped_query);
+   if (!db_res)
+     {
+       const gchar *dbi_error;
+@@ -238,14 +241,17 @@ afsql_dd_run_query(AFSqlDestDriver *self, const gchar *query, gboolean silent, d
+                     evt_tag_str("user", self->user),
+                     evt_tag_str("database", self->database),
+                     evt_tag_str("error", dbi_error),
+-                    evt_tag_str("query", query));
++                    evt_tag_str("query", query),
++                    evt_tag_str("escaped_query", escaped_query));
+         }
++      g_free(escaped_query);
+       return FALSE;
+     }
+   if (result)
+     *result = db_res;
+   else
+     dbi_result_free(db_res);
++  g_free(escaped_query);
+   return TRUE;
+ }
+ 
diff --git a/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-02.patch b/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-02.patch
new file mode 100644
index 0000000000..577db56bb8
--- /dev/null
+++ b/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-02.patch
@@ -0,0 +1,572 @@
+From 1aba7537f0c406090f98a649475acbf517440220 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?B=C3=A1lint=20Horv=C3=A1th?= <bal.horv.98@gmail.com>
+Date: Tue, 7 Apr 2026 16:58:51 +0200
+Subject: [PATCH] afsql: Added tests for SQL query
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Hofi <hofione@gmail.com>
+Signed-off-by: Bálint Horváth <bal.horv.98@gmail.com>
+Signed-off-by: Hofi <hofione@gmail.com>
+
+CVE: CVE-2026-39879
+Upstream-Status: Backport [https://github.com/syslog-ng/syslog-ng/commit/1aba7537f0c406090f98a649475acbf517440220]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ modules/afsql/CMakeLists.txt               |   2 +
+ modules/afsql/Makefile.am                  |   5 +-
+ modules/afsql/afsql.c                      |   2 +-
+ modules/afsql/tests/CMakeLists.txt         |   9 +
+ modules/afsql/tests/Makefile.am            |  30 +++
+ modules/afsql/tests/mock-dbi.c             | 129 ++++++++++++
+ modules/afsql/tests/mock-dbi.h             |  41 ++++
+ modules/afsql/tests/test_afsql.h           |  31 +++
+ modules/afsql/tests/test_afsql_run_query.c | 225 +++++++++++++++++++++
+ 9 files changed, 472 insertions(+), 2 deletions(-)
+ create mode 100644 modules/afsql/tests/CMakeLists.txt
+ create mode 100644 modules/afsql/tests/Makefile.am
+ create mode 100644 modules/afsql/tests/mock-dbi.c
+ create mode 100644 modules/afsql/tests/mock-dbi.h
+ create mode 100644 modules/afsql/tests/test_afsql.h
+ create mode 100644 modules/afsql/tests/test_afsql_run_query.c
+
+diff --git a/modules/afsql/CMakeLists.txt b/modules/afsql/CMakeLists.txt
+index 0d336f3a7..8921bbf33 100644
+--- a/modules/afsql/CMakeLists.txt
++++ b/modules/afsql/CMakeLists.txt
+@@ -28,3 +28,5 @@ add_module(
+   DEPENDS ${LIBDBI_LIBRARIES} OpenSSL::SSL
+   SOURCES ${AFSQL_SOURCES}
+ )
++
++add_test_subdirectory(tests)
+diff --git a/modules/afsql/Makefile.am b/modules/afsql/Makefile.am
+index afc81655d..dd3dd9b5a 100644
+--- a/modules/afsql/Makefile.am
++++ b/modules/afsql/Makefile.am
+@@ -33,6 +33,9 @@ BUILT_SOURCES				+=	\
+ 	modules/afsql/afsql-grammar.h
+ EXTRA_DIST				+= 	\
+ 	modules/afsql/afsql-grammar.ym	\
+-	modules/afsql/CMakeLists.txt
++	modules/afsql/CMakeLists.txt	\
++	modules/afsql/tests/CMakeLists.txt
+ 
+ .PHONY: modules/afsql/ mod-afsql mod-sql
++
++include modules/afsql/tests/Makefile.am
+diff --git a/modules/afsql/afsql.c b/modules/afsql/afsql.c
+index fb7ef3b32..2234acfe6 100644
+--- a/modules/afsql/afsql.c
++++ b/modules/afsql/afsql.c
+@@ -217,7 +217,7 @@ afsql_dd_set_create_statement_append(LogDriver *s, const gchar *create_statement
+  *
+  * NOTE: This function can only be called from the database thread.
+  **/
+-static gboolean
++gboolean
+ afsql_dd_run_query(AFSqlDestDriver *self, const gchar *query, gboolean silent, dbi_result *result)
+ {
+   dbi_result db_res;
+diff --git a/modules/afsql/tests/CMakeLists.txt b/modules/afsql/tests/CMakeLists.txt
+new file mode 100644
+index 000000000..12396f58f
+--- /dev/null
++++ b/modules/afsql/tests/CMakeLists.txt
+@@ -0,0 +1,9 @@
++if(ENABLE_SQL)
++  add_unit_test(
++    CRITERION LIBTEST
++    TARGET test_afsql_run_query
++    SOURCES test_afsql_run_query.c ../afsql.c mock-dbi.c
++    INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/.. ${LIBDBI_INCLUDE_DIRS}
++    DEPENDS OpenSSL::SSL
++  )
++endif()
+diff --git a/modules/afsql/tests/Makefile.am b/modules/afsql/tests/Makefile.am
+new file mode 100644
+index 000000000..96e0c1476
+--- /dev/null
++++ b/modules/afsql/tests/Makefile.am
+@@ -0,0 +1,30 @@
++if ENABLE_SQL
++
++modules_afsql_tests_TESTS = \
++	modules/afsql/tests/test_afsql_run_query
++
++check_PROGRAMS += ${modules_afsql_tests_TESTS}
++
++modules_afsql_tests_test_afsql_run_query_SOURCES = \
++	modules/afsql/tests/mock-dbi.h \
++	modules/afsql/tests/test_afsql.h \
++	modules/afsql/tests/test_afsql_run_query.c \
++	modules/afsql/afsql.c \
++	modules/afsql/tests/mock-dbi.c
++
++modules_afsql_tests_test_afsql_run_query_CFLAGS = \
++	$(TEST_CFLAGS) \
++	-I$(top_srcdir)/modules/afsql \
++	-I$(top_srcdir)/modules/afsql/tests \
++	$(LIBDBI_CFLAGS)
++
++modules_afsql_tests_test_afsql_run_query_LDADD = \
++	$(TEST_LDADD) \
++	$(OPENSSL_LIBS)
++
++modules_afsql_tests_test_afsql_run_query_LDFLAGS = \
++	$(LDFLAGS)
++
++endif
++
++EXTRA_DIST += modules/afsql/tests/CMakeLists.txt
+diff --git a/modules/afsql/tests/mock-dbi.c b/modules/afsql/tests/mock-dbi.c
+new file mode 100644
+index 000000000..af00dec07
+--- /dev/null
++++ b/modules/afsql/tests/mock-dbi.c
+@@ -0,0 +1,129 @@
++/*
++ * Copyright (c) 2026 One Identity LLC.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++ *
++ * As an additional exemption you are allowed to compile & link against the
++ * OpenSSL libraries as published by the OpenSSL project. See the file
++ * COPYING for details.
++ *
++ */
++
++#include "mock-dbi.h"
++#include <stdlib.h>
++
++dbi_result mock_dbi_query_result = (dbi_result) 0x1;
++gchar *mock_dbi_last_query = NULL;
++gboolean mock_dbi_result_freed = FALSE;
++const gchar *mock_dbi_error_message = "mock dbi error";
++
++void
++mock_dbi_reset(void)
++{
++  mock_dbi_query_result = (dbi_result) 0x1;
++  g_free(mock_dbi_last_query);
++  mock_dbi_last_query = NULL;
++  mock_dbi_result_freed = FALSE;
++  mock_dbi_error_message = "mock dbi error";
++}
++
++/* --- mocked functions (state-bearing) --- */
++
++dbi_result
++dbi_conn_query(dbi_conn conn, const char *statement)
++{
++  g_free(mock_dbi_last_query);
++  mock_dbi_last_query = g_strdup(statement);
++  return mock_dbi_query_result;
++}
++
++int
++dbi_conn_error(dbi_conn conn, const char **errmsg)
++{
++  if (errmsg)
++    *errmsg = mock_dbi_error_message;
++  return 0;
++}
++
++int
++dbi_result_free(dbi_result result)
++{
++  mock_dbi_result_freed = TRUE;
++  return 0;
++}
++
++/* --- no-op stubs for the rest of afsql.c --- */
++
++dbi_conn
++dbi_conn_new_r(const char *name, dbi_inst inst)
++{
++  return NULL;
++}
++
++int
++dbi_conn_connect(dbi_conn conn)
++{
++  return -1;
++}
++
++void
++dbi_conn_close(dbi_conn conn)
++{
++}
++
++int
++dbi_conn_ping(dbi_conn conn)
++{
++  return 0;
++}
++
++int
++dbi_conn_set_option(dbi_conn conn, const char *key, const char *value)
++{
++  return 0;
++}
++
++int
++dbi_conn_set_option_numeric(dbi_conn conn, const char *key, int value)
++{
++  return 0;
++}
++
++size_t
++dbi_conn_quote_string_copy(dbi_conn conn, const char *orig, char **dest)
++{
++  if (dest)
++    *dest = NULL;
++  return 0;
++}
++
++size_t
++dbi_conn_quote_binary_copy(dbi_conn conn, const unsigned char *orig, size_t length, unsigned char **dest)
++{
++  if (dest)
++    *dest = NULL;
++  return 0;
++}
++
++int
++dbi_initialize_r(const char *driverdir, dbi_inst *pInst)
++{
++  return 0;
++}
++
++unsigned int
++dbi_result_get_field_idx(dbi_result result, const char *fieldname)
++{
++  return 0;
++}
+diff --git a/modules/afsql/tests/mock-dbi.h b/modules/afsql/tests/mock-dbi.h
+new file mode 100644
+index 000000000..0f9ca800a
+--- /dev/null
++++ b/modules/afsql/tests/mock-dbi.h
+@@ -0,0 +1,41 @@
++/*
++ * Copyright (c) 2026 One Identity LLC.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++ *
++ * As an additional exemption you are allowed to compile & link against the
++ * OpenSSL libraries as published by the OpenSSL project. See the file
++ * COPYING for details.
++ *
++ */
++
++#ifndef MOCK_DBI_H_INCLUDED
++#define MOCK_DBI_H_INCLUDED
++
++#pragma GCC diagnostic ignored "-Wstrict-prototypes"
++
++#include <dbi.h>
++#include <glib.h>
++
++/*
++ * Mock state controlling dbi_conn_query behaviour.
++ */
++extern dbi_result mock_dbi_query_result;
++extern gchar *mock_dbi_last_query;
++extern gboolean mock_dbi_result_freed;
++extern const gchar *mock_dbi_error_message;
++
++void mock_dbi_reset(void);
++
++#endif /* MOCK_DBI_H_INCLUDED */
+diff --git a/modules/afsql/tests/test_afsql.h b/modules/afsql/tests/test_afsql.h
+new file mode 100644
+index 000000000..804e815e3
+--- /dev/null
++++ b/modules/afsql/tests/test_afsql.h
+@@ -0,0 +1,31 @@
++/*
++ * Copyright (c) 2026 One Identity LLC.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++ *
++ * As an additional exemption you are allowed to compile & link against the
++ * OpenSSL libraries as published by the OpenSSL project. See the file
++ * COPYING for details.
++ *
++ */
++
++#ifndef TEST_AFSQL_H_INCLUDED
++#define TEST_AFSQL_H_INCLUDED
++
++#include "afsql.h"
++
++gboolean afsql_dd_run_query(AFSqlDestDriver *self, const gchar *query, gboolean silent,
++                             dbi_result *result);
++
++#endif /* TEST_AFSQL_H_INCLUDED */
+diff --git a/modules/afsql/tests/test_afsql_run_query.c b/modules/afsql/tests/test_afsql_run_query.c
+new file mode 100644
+index 000000000..b26f631a7
+--- /dev/null
++++ b/modules/afsql/tests/test_afsql_run_query.c
+@@ -0,0 +1,225 @@
++/*
++ * Copyright (c) 2026 One Identity LLC.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++ *
++ * As an additional exemption you are allowed to compile & link against the
++ * OpenSSL libraries as published by the OpenSSL project. See the file
++ * COPYING for details.
++ *
++ */
++
++#include <criterion/criterion.h>
++#include <criterion/parameterized.h>
++
++#include "test_afsql.h"
++#include "mock-dbi.h"
++#include "apphook.h"
++
++/* ----------------------------- helpers ----------------------------- */
++
++static AFSqlDestDriver *
++_create_driver(void)
++{
++  AFSqlDestDriver *self = g_new0(AFSqlDestDriver, 1);
++  self->type     = g_strdup("mysql");
++  self->host     = g_strdup("localhost");
++  self->port     = g_strdup("3306");
++  self->user     = g_strdup("testuser");
++  self->database = g_strdup("testdb");
++  /* dbi_ctx is intentionally left NULL; mock-dbi ignores the conn handle */
++  return self;
++}
++
++static void
++_free_driver(AFSqlDestDriver *self)
++{
++  g_free(self->type);
++  g_free(self->host);
++  g_free(self->port);
++  g_free(self->user);
++  g_free(self->database);
++  g_free(self);
++}
++
++/* ----------------------------- fixtures ----------------------------- */
++
++static AFSqlDestDriver *driver;
++
++static void
++setup(void)
++{
++  app_startup();
++  mock_dbi_reset();
++  driver = _create_driver();
++}
++
++static void
++teardown(void)
++{
++  mock_dbi_reset();
++  _free_driver(driver);
++  app_shutdown();
++}
++
++TestSuite(afsql_dd_run_query, .init = setup, .fini = teardown);
++
++/* ----------------------------- tests -------------------------------- */
++
++Test(afsql_dd_run_query, successful_query_returns_true)
++{
++  mock_dbi_query_result = (dbi_result) 0x1;
++
++  gboolean ret = afsql_dd_run_query(driver, "SELECT 1", FALSE, NULL);
++
++  cr_assert(ret, "Expected TRUE on successful query");
++}
++
++Test(afsql_dd_run_query, failed_query_returns_false)
++{
++  mock_dbi_query_result = NULL;
++
++  gboolean ret = afsql_dd_run_query(driver, "SELECT 1", FALSE, NULL);
++
++  cr_assert_not(ret, "Expected FALSE when dbi_conn_query returns NULL");
++}
++
++Test(afsql_dd_run_query, result_pointer_is_populated_on_success)
++{
++  dbi_result sentinel = (dbi_result) 0xdeadbeef;
++  mock_dbi_query_result = sentinel;
++
++  dbi_result out = NULL;
++  afsql_dd_run_query(driver, "SELECT 1", FALSE, &out);
++
++  cr_assert_eq(out, sentinel,
++               "Expected *result to hold the dbi_result returned by dbi_conn_query");
++}
++
++Test(afsql_dd_run_query, result_is_freed_when_no_pointer_given)
++{
++  mock_dbi_query_result = (dbi_result) 0x1;
++  mock_dbi_result_freed = FALSE;
++
++  afsql_dd_run_query(driver, "SELECT 1", FALSE, NULL);
++
++  cr_assert(mock_dbi_result_freed,
++            "Expected dbi_result_free() to be called when result pointer is NULL");
++}
++
++Test(afsql_dd_run_query, result_is_not_freed_when_pointer_given)
++{
++  mock_dbi_query_result = (dbi_result) 0x1;
++  mock_dbi_result_freed = FALSE;
++
++  dbi_result out = NULL;
++  afsql_dd_run_query(driver, "SELECT 1", FALSE, &out);
++
++  cr_assert_not(mock_dbi_result_freed,
++                "Expected dbi_result_free() NOT to be called when result pointer is provided");
++}
++
++Test(afsql_dd_run_query, silent_mode_suppresses_error_on_failure)
++{
++  mock_dbi_query_result = NULL;
++
++  /* Should not crash or assert even though the query fails */
++  gboolean ret = afsql_dd_run_query(driver, "BAD QUERY", TRUE, NULL);
++
++  cr_assert_not(ret, "Expected FALSE on failure regardless of silent flag");
++}
++
++/*
++ * Parameterized regression tests for the SQL injection fix (commit e9dbd15bf).
++ *
++ * Each entry describes a raw query string and the exact string that must
++ * arrive at dbi_conn_query after convert_unsafe_utf8_to_escaped_text has
++ * processed it.  A NULL expected_query means the input is safe ASCII and
++ * must pass through byte-for-byte unchanged.
++ */
++typedef struct
++{
++  gchar description[64];
++  gchar raw_query[128];
++  gchar expected_query[128];
++  gboolean expected_query_is_null; /* TRUE → identical to raw_query */
++} QuerySanitizationParam;
++
++ParameterizedTestParameters(afsql_dd_run_query, query_sanitization)
++{
++  static QuerySanitizationParam params[] =
++  {
++    /* safe inputs — must be forwarded unchanged */
++    { "plain ascii select",                       "SELECT 1",                                       "", TRUE },
++    { "insert with safe string value",            "INSERT INTO logs (msg) VALUES ('hello world')",  "", TRUE },
++    { "query with numbers and underscores",       "SELECT id, log_level FROM events WHERE id = 42", "", TRUE },
++
++    /* control characters that must be escaped */
++    { "bell character \\x07",                     "SELECT '\x07'",                                  "SELECT '\\x07'",                              FALSE },
++    { "newline",                                  "INSERT INTO t (v) VALUES ('line1\nline2')",      "INSERT INTO t (v) VALUES ('line1\\nline2')",  FALSE },
++    { "carriage return",                          "INSERT INTO t (v) VALUES ('a\rb')",              "INSERT INTO t (v) VALUES ('a\\rb')",          FALSE },
++    { "tab",                                      "INSERT INTO t (v) VALUES ('col1\tcol2')",        "INSERT INTO t (v) VALUES ('col1\\tcol2')",    FALSE },
++    { "backspace",                                "INSERT INTO t (v) VALUES ('\b')",                "INSERT INTO t (v) VALUES ('\\b')",            FALSE },
++    { "form feed",                                "INSERT INTO t (v) VALUES ('\f')",                "INSERT INTO t (v) VALUES ('\\f')",            FALSE },
++    { "soh \\x01 unnamed control char",           "SELECT '\x01'",                                  "SELECT '\\x01'",                              FALSE },
++    { "unit separator \\x1f",                     "SELECT '\x1f'",                                  "SELECT '\\x1f'",                              FALSE },
++    { "multiple consecutive control chars",       "\x01\x02\x03",                                   "\\x01\\x02\\x03",                             FALSE },
++    { "backslash is doubled",                     "SELECT '\\'",                                    "SELECT '\\\\'",                               FALSE },
++
++    /* a literal backslash-n in the input (two chars: \ + n) must become \\n,
++     * not be re-interpreted as a newline escape */
++    { "pre-escaped \\n is not re-interpreted",    "SELECT '\\n'",                                   "SELECT '\\\\n'",                              FALSE },
++
++    /* invalid / overlong UTF-8 sequences */
++    { "invalid utf-8 byte \\xad is hex-escaped",  "SELECT '\xad'",                                  "SELECT '\\\\xad'",                            FALSE },
++    { "invalid utf-8 byte surrounded by valid",   "SELECT 'Á\xadÉ'",                                "SELECT 'Á\\\\xadÉ'",                          FALSE },
++    { "truncated 2-byte utf-8 start byte",        "SELECT '\xc3'",                                  "SELECT '\\\\xc3'",                            FALSE },
++    { "multiple consecutive invalid utf-8 bytes", "SELECT '\xad\xae'",                              "SELECT '\\\\xad\\\\xae'",                     FALSE },
++
++    /* SQL metacharacters: quotes and semicolons are NOT escaped
++     * (unsafe_flags=0 — the escaping targets binary/control safety, not SQL) */
++    { "single quote passes through",              "SELECT ''''",                                    "", TRUE },
++    { "double quote passes through",              "SELECT \"val\"",                                 "", TRUE },
++    { "semicolon passes through",                 "SELECT 1; DROP TABLE users",                     "", TRUE },
++
++    /* DEL (0x7f) is >= 32 and not a backslash, so it passes through */
++    { "del character 0x7f passes through",        "SELECT '\x7f'",                                  "", TRUE },
++
++    /* valid multibyte UTF-8 must not be mangled */
++    { "valid utf-8 multibyte passes through",     "SELECT 'árvíztűrőtükörfúrógép'",                 "", TRUE },
++    { "valid utf-8 followed by newline",          "SELECT 'árvíztűrőtükörfúrógép\n'",               "SELECT 'árvíztűrőtükörfúrógép\\n'",           FALSE },
++
++    /* empty query edge case */
++    { "empty query string",                       "",                                               "", TRUE },
++  };
++
++  return cr_make_param_array(QuerySanitizationParam, params,
++                             sizeof(params) / sizeof(params[0]));
++}
++
++ParameterizedTest(QuerySanitizationParam *p, afsql_dd_run_query, query_sanitization)
++{
++  mock_dbi_reset();
++  mock_dbi_query_result = (dbi_result) 0x1;
++
++  afsql_dd_run_query(driver, p->raw_query, FALSE, NULL);
++
++  cr_assert_not_null(mock_dbi_last_query,
++                     "[%s] Expected dbi_conn_query to have been called", p->description);
++
++  const gchar *expected = p->expected_query_is_null ? p->raw_query : p->expected_query;
++  cr_assert_str_eq(mock_dbi_last_query, expected,
++                   "[%s] Sanitised query mismatch.\n  got:      %s\n  expected: %s",
++                   p->description, mock_dbi_last_query, expected);
++}
diff --git a/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-03.patch b/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-03.patch
new file mode 100644
index 0000000000..c88ab04e73
--- /dev/null
+++ b/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-03.patch
@@ -0,0 +1,758 @@
+From 4b61a0b1dad69368bc3b93b607141708d0036830 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?B=C3=A1lint=20Horv=C3=A1th?= <bal.horv.98@gmail.com>
+Date: Tue, 7 Apr 2026 18:03:13 +0200
+Subject: [PATCH] afsql: Added tests against SQL injection
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Hofi <hofione@gmail.com>
+Signed-off-by: Bálint Horváth <bal.horv.98@gmail.com>
+Signed-off-by: Hofi <hofione@gmail.com>
+
+CVE: CVE-2026-39879
+Upstream-Status: Backport [https://github.com/syslog-ng/syslog-ng/commit/4b61a0b1dad69368bc3b93b607141708d0036830]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ modules/afsql/afsql.c                         |   2 +-
+ modules/afsql/tests/CMakeLists.txt            |   7 +
+ modules/afsql/tests/Makefile.am               |  25 +-
+ modules/afsql/tests/afsql_test_helpers.h      |  87 ++++
+ modules/afsql/tests/mock-dbi.c                |  29 +-
+ modules/afsql/tests/test_afsql.h              |   1 +
+ .../tests/test_afsql_build_insert_command.c   | 455 ++++++++++++++++++
+ modules/afsql/tests/test_afsql_run_query.c    |  29 +-
+ 8 files changed, 597 insertions(+), 38 deletions(-)
+ create mode 100644 modules/afsql/tests/afsql_test_helpers.h
+ create mode 100644 modules/afsql/tests/test_afsql_build_insert_command.c
+
+diff --git a/modules/afsql/afsql.c b/modules/afsql/afsql.c
+index 2234acfe6..04b35e607 100644
+--- a/modules/afsql/afsql.c
++++ b/modules/afsql/afsql.c
+@@ -890,7 +890,7 @@ afsql_dd_append_value_to_be_inserted(AFSqlDestDriver *self,
+   return TRUE;
+ }
+ 
+-static GString *
++GString *
+ afsql_dd_build_insert_command(AFSqlDestDriver *self, LogMessage *msg, GString *table)
+ {
+   GString *insert_command = g_string_sized_new(256);
+diff --git a/modules/afsql/tests/CMakeLists.txt b/modules/afsql/tests/CMakeLists.txt
+index 12396f58f..c4ee592c1 100644
+--- a/modules/afsql/tests/CMakeLists.txt
++++ b/modules/afsql/tests/CMakeLists.txt
+@@ -6,4 +6,11 @@ if(ENABLE_SQL)
+     INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/.. ${LIBDBI_INCLUDE_DIRS}
+     DEPENDS OpenSSL::SSL
+   )
++  add_unit_test(
++    CRITERION LIBTEST
++    TARGET test_afsql_build_insert_command
++    SOURCES test_afsql_build_insert_command.c ../afsql.c mock-dbi.c
++    INCLUDES ${CMAKE_CURRENT_SOURCE_DIR}/.. ${LIBDBI_INCLUDE_DIRS}
++    DEPENDS OpenSSL::SSL
++  )
+ endif()
+diff --git a/modules/afsql/tests/Makefile.am b/modules/afsql/tests/Makefile.am
+index 96e0c1476..c7fc64fc1 100644
+--- a/modules/afsql/tests/Makefile.am
++++ b/modules/afsql/tests/Makefile.am
+@@ -1,11 +1,13 @@
+ if ENABLE_SQL
+ 
+ modules_afsql_tests_TESTS = \
+-	modules/afsql/tests/test_afsql_run_query
++	modules/afsql/tests/test_afsql_run_query \
++	modules/afsql/tests/test_afsql_build_insert_command
+ 
+ check_PROGRAMS += ${modules_afsql_tests_TESTS}
+ 
+ modules_afsql_tests_test_afsql_run_query_SOURCES = \
++	modules/afsql/tests/afsql_test_helpers.h \
+ 	modules/afsql/tests/mock-dbi.h \
+ 	modules/afsql/tests/test_afsql.h \
+ 	modules/afsql/tests/test_afsql_run_query.c \
+@@ -25,6 +27,27 @@ modules_afsql_tests_test_afsql_run_query_LDADD = \
+ modules_afsql_tests_test_afsql_run_query_LDFLAGS = \
+ 	$(LDFLAGS)
+ 
++modules_afsql_tests_test_afsql_build_insert_command_SOURCES = \
++	modules/afsql/tests/afsql_test_helpers.h \
++	modules/afsql/tests/mock-dbi.h \
++	modules/afsql/tests/test_afsql.h \
++	modules/afsql/tests/test_afsql_build_insert_command.c \
++	modules/afsql/afsql.c \
++	modules/afsql/tests/mock-dbi.c
++
++modules_afsql_tests_test_afsql_build_insert_command_CFLAGS = \
++	$(TEST_CFLAGS) \
++	-I$(top_srcdir)/modules/afsql \
++	-I$(top_srcdir)/modules/afsql/tests \
++	$(LIBDBI_CFLAGS)
++
++modules_afsql_tests_test_afsql_build_insert_command_LDADD = \
++	$(TEST_LDADD) \
++	$(OPENSSL_LIBS)
++
++modules_afsql_tests_test_afsql_build_insert_command_LDFLAGS = \
++	$(LDFLAGS)
++
+ endif
+ 
+ EXTRA_DIST += modules/afsql/tests/CMakeLists.txt
+diff --git a/modules/afsql/tests/afsql_test_helpers.h b/modules/afsql/tests/afsql_test_helpers.h
+new file mode 100644
+index 000000000..9dabc763f
+--- /dev/null
++++ b/modules/afsql/tests/afsql_test_helpers.h
+@@ -0,0 +1,87 @@
++/*
++ * Copyright (c) 2026 One Identity LLC.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++ *
++ * As an additional exemption you are allowed to compile & link against the
++ * OpenSSL libraries as published by the OpenSSL project. See the file
++ * COPYING for details.
++ *
++ */
++
++#ifndef AFSQL_TEST_HELPERS_H_INCLUDED
++#define AFSQL_TEST_HELPERS_H_INCLUDED
++
++#include "test_afsql.h"
++#include "mock-dbi.h"
++#include "cfg.h"
++#include "logmsg/logmsg.h"
++#include "template/templates.h"
++
++static inline AFSqlDestDriver *
++_create_driver(void)
++{
++  AFSqlDestDriver *self = g_new0(AFSqlDestDriver, 1);
++  self->type            = g_strdup("mysql");
++  self->host            = g_strdup("localhost");
++  self->port            = g_strdup("3306");
++  self->user            = g_strdup("testuser");
++  self->database        = g_strdup("testdb");
++  self->quote_as_string = g_strdup("");
++  log_template_options_defaults(&self->template_options);
++  return self;
++}
++
++static inline void
++_free_driver(AFSqlDestDriver *self)
++{
++  for (gint i = 0; i < self->fields_len; i++)
++    {
++      g_free(self->fields[i].name);
++      g_free(self->fields[i].type);
++      log_template_unref(self->fields[i].value);
++    }
++  g_free(self->fields);
++  g_free(self->type);
++  g_free(self->host);
++  g_free(self->port);
++  g_free(self->user);
++  g_free(self->database);
++  g_free(self->quote_as_string);
++  g_free(self->null_value);
++  g_free(self);
++}
++
++static inline void
++_set_fields(AFSqlDestDriver *self, const gchar **col_names,
++            const gchar **templates, gint count)
++{
++  self->fields_len = count;
++  self->fields = g_new0(AFSqlField, count);
++  for (gint i = 0; i < count; i++)
++    {
++      self->fields[i].name  = g_strdup(col_names[i]);
++      self->fields[i].type  = g_strdup("text");
++      self->fields[i].value = log_template_new(configuration, NULL);
++      log_template_compile(self->fields[i].value, templates[i], NULL);
++    }
++}
++
++static inline GString *
++_make_table(const gchar *name)
++{
++  return g_string_new(name);
++}
++
++#endif /* AFSQL_TEST_HELPERS_H_INCLUDED */
+diff --git a/modules/afsql/tests/mock-dbi.c b/modules/afsql/tests/mock-dbi.c
+index af00dec07..7c1fca441 100644
+--- a/modules/afsql/tests/mock-dbi.c
++++ b/modules/afsql/tests/mock-dbi.c
+@@ -63,6 +63,27 @@ dbi_result_free(dbi_result result)
+   return 0;
+ }
+ 
++size_t
++dbi_conn_quote_string_copy(dbi_conn conn, const char *orig, char **dest)
++{
++  if (!orig || !dest)
++    return 0;
++
++  /* Minimal SQL quoting: wrap in single quotes, escape internal single quotes */
++  GString *quoted = g_string_new("'");
++  for (const char *s = orig; *s; s++)
++    {
++      if (*s == '\'')
++        g_string_append(quoted, "\\'");
++      else
++        g_string_append_c(quoted, *s);
++    }
++  g_string_append_c(quoted, '\'');
++  size_t quoted_len = quoted->len;
++  *dest = g_string_free(quoted, FALSE);
++  return quoted_len;
++}
++
+ /* --- no-op stubs for the rest of afsql.c --- */
+ 
+ dbi_conn
+@@ -100,14 +121,6 @@ dbi_conn_set_option_numeric(dbi_conn conn, const char *key, int value)
+   return 0;
+ }
+ 
+-size_t
+-dbi_conn_quote_string_copy(dbi_conn conn, const char *orig, char **dest)
+-{
+-  if (dest)
+-    *dest = NULL;
+-  return 0;
+-}
+-
+ size_t
+ dbi_conn_quote_binary_copy(dbi_conn conn, const unsigned char *orig, size_t length, unsigned char **dest)
+ {
+diff --git a/modules/afsql/tests/test_afsql.h b/modules/afsql/tests/test_afsql.h
+index 804e815e3..7fdbb838d 100644
+--- a/modules/afsql/tests/test_afsql.h
++++ b/modules/afsql/tests/test_afsql.h
+@@ -27,5 +27,6 @@
+ 
+ gboolean afsql_dd_run_query(AFSqlDestDriver *self, const gchar *query, gboolean silent,
+                              dbi_result *result);
++GString *afsql_dd_build_insert_command(AFSqlDestDriver *self, LogMessage *msg, GString *table);
+ 
+ #endif /* TEST_AFSQL_H_INCLUDED */
+diff --git a/modules/afsql/tests/test_afsql_build_insert_command.c b/modules/afsql/tests/test_afsql_build_insert_command.c
+new file mode 100644
+index 000000000..3fd1d3c00
+--- /dev/null
++++ b/modules/afsql/tests/test_afsql_build_insert_command.c
+@@ -0,0 +1,455 @@
++/*
++ * Copyright (c) 2026 One Identity LLC.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published
++ * by the Free Software Foundation, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
++ *
++ * As an additional exemption you are allowed to compile & link against the
++ * OpenSSL libraries as published by the OpenSSL project. See the file
++ * COPYING for details.
++ *
++ */
++
++#include <criterion/criterion.h>
++#include <criterion/parameterized.h>
++
++#include "afsql_test_helpers.h"
++#include "apphook.h"
++
++/* ----------------------------- fixtures ----------------------------- */
++
++static AFSqlDestDriver *driver;
++
++static void
++setup(void)
++{
++  app_startup();
++  configuration = cfg_new_snippet();
++  driver = NULL;
++}
++
++static void
++teardown(void)
++{
++  if (driver)
++    _free_driver(driver);
++  cfg_free(configuration);
++  app_shutdown();
++}
++
++/* ===================================================================
++ * Suite 1: structural correctness
++ * =================================================================== */
++
++TestSuite(afsql_dd_build_insert_command, .init = setup, .fini = teardown);
++
++Test(afsql_dd_build_insert_command, basic_insert_structure)
++{
++  driver = _create_driver();
++  const gchar *cols[]  = { "host", "message" };
++  const gchar *tmpls[] = { "myhost", "mymessage" };
++  _set_fields(driver, cols, tmpls, 2);
++
++  LogMessage *msg = log_msg_new_empty();
++  GString *table  = _make_table("logs");
++  GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
++
++  cr_assert_not_null(cmd);
++  cr_assert(g_str_has_prefix(cmd->str, "INSERT INTO logs ("),
++            "got: %s", cmd->str);
++  cr_assert(g_strstr_len(cmd->str, -1, ") VALUES (") != NULL,
++            "got: %s", cmd->str);
++  cr_assert(g_str_has_suffix(cmd->str, ")"),
++            "got: %s", cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++Test(afsql_dd_build_insert_command, column_names_appear_in_order)
++{
++  driver = _create_driver();
++  const gchar *cols[]  = { "col_a", "col_b", "col_c" };
++  const gchar *tmpls[] = { "va", "vb", "vc" };
++  _set_fields(driver, cols, tmpls, 3);
++
++  LogMessage *msg = log_msg_new_empty();
++  GString *table  = _make_table("t");
++  GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
++
++  const gchar *a = g_strstr_len(cmd->str, -1, "col_a");
++  const gchar *b = g_strstr_len(cmd->str, -1, "col_b");
++  const gchar *c = g_strstr_len(cmd->str, -1, "col_c");
++  cr_assert(a && b && c && a < b && b < c,
++            "Expected columns in order col_a, col_b, col_c, got: %s", cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++Test(afsql_dd_build_insert_command, default_flagged_field_is_skipped)
++{
++  driver = _create_driver();
++  const gchar *cols[]  = { "col_a", "col_b" };
++  const gchar *tmpls[] = { "va", "vb" };
++  _set_fields(driver, cols, tmpls, 2);
++  driver->fields[1].flags |= AFSQL_FF_DEFAULT;
++
++  LogMessage *msg = log_msg_new_empty();
++  GString *table  = _make_table("t");
++  GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
++
++  cr_assert(g_strstr_len(cmd->str, -1, "col_a") != NULL, "got: %s", cmd->str);
++  cr_assert(g_strstr_len(cmd->str, -1, "col_b") == NULL,
++            "Expected col_b absent (DEFAULT flag), got: %s", cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++Test(afsql_dd_build_insert_command, null_value_produces_NULL_keyword)
++{
++  driver = _create_driver();
++  driver->null_value   = g_strdup("-");
++  const gchar *cols[]  = { "msg" };
++  const gchar *tmpls[] = { "-" };
++  _set_fields(driver, cols, tmpls, 1);
++
++  LogMessage *msg = log_msg_new_empty();
++  GString *table  = _make_table("t");
++  GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
++
++  cr_assert(g_strstr_len(cmd->str, -1, "NULL") != NULL,
++            "Expected NULL keyword for null_value match, got: %s", cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++/*
++ * Typed values: LM_VT_NULL, LM_VT_INTEGER and LM_VT_BOOLEAN must emit
++ * unquoted SQL keywords/literals — never a quoted string.
++ */
++typedef struct
++{
++  gchar description[64];
++  gchar raw_value[32];
++  LogMessageValueType type;
++  gchar must_contain[16];      /* expected unquoted form   */
++  gchar must_not_contain[16];  /* must not be quoted       */
++} TypedValueParam;
++
++ParameterizedTestParameters(afsql_dd_build_insert_command, typed_value_is_not_quoted)
++{
++  static TypedValueParam params[] =
++  {
++    { "LM_VT_NULL emits bare NULL",          "",      LM_VT_NULL,    "NULL",  "'NULL'" },
++    { "LM_VT_INTEGER emits bare integer",    "42",    LM_VT_INTEGER, "42",    "'42'"   },
++    { "LM_VT_BOOLEAN true emits TRUE",       "true",  LM_VT_BOOLEAN, "TRUE",  "'TRUE'" },
++    { "LM_VT_BOOLEAN false emits FALSE",     "false", LM_VT_BOOLEAN, "FALSE", "'FALSE'"},
++  };
++  return cr_make_param_array(TypedValueParam, params,
++                             sizeof(params) / sizeof(params[0]));
++}
++
++ParameterizedTest(TypedValueParam *p, afsql_dd_build_insert_command, typed_value_is_not_quoted)
++{
++  driver = _create_driver();
++  const gchar *cols[]  = { "col" };
++  const gchar *tmpls[] = { "${VAL}" };
++  _set_fields(driver, cols, tmpls, 1);
++
++  LogMessage *msg = log_msg_new_empty();
++  NVHandle handle = log_msg_get_value_handle("VAL");
++  log_msg_set_value_with_type(msg, handle, p->raw_value, -1, p->type);
++  GString *table  = _make_table("t");
++  GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
++
++  cr_assert(g_strstr_len(cmd->str, -1, p->must_contain) != NULL,
++            "[%s] Expected '%s' in command, got: %s",
++            p->description, p->must_contain, cmd->str);
++  cr_assert(g_strstr_len(cmd->str, -1, p->must_not_contain) == NULL,
++            "[%s] Expected '%s' absent (value must not be quoted), got: %s",
++            p->description, p->must_not_contain, cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++/*
++ * Comma placement: the function looks ahead to skip DEFAULT fields when
++ * deciding whether to append ", ".  These tests verify no spurious leading,
++ * trailing, or doubled commas appear.
++ */
++typedef struct
++{
++  gchar       description[64];
++  gint         default_field_idx;  /* which of 3 fields gets AFSQL_FF_DEFAULT */
++  gchar       must_contain[32];
++  gchar       must_not_contain[32];
++} CommaPlacementParam;
++
++ParameterizedTestParameters(afsql_dd_build_insert_command, comma_placement)
++{
++  static CommaPlacementParam params[] =
++  {
++    { "no trailing comma when last field is DEFAULT",   2, "col_a, col_b", "col_b," },
++    { "no leading comma when first field is DEFAULT",   0, "col_b, col_c", ""       },
++    { "comma bridges over DEFAULT middle field",        1, "col_a, col_c", "col_b"  },
++  };
++  return cr_make_param_array(CommaPlacementParam, params,
++                             sizeof(params) / sizeof(params[0]));
++}
++
++ParameterizedTest(CommaPlacementParam *p, afsql_dd_build_insert_command, comma_placement)
++{
++  driver = _create_driver();
++  const gchar *cols[]  = { "col_a", "col_b", "col_c" };
++  const gchar *tmpls[] = { "va",    "vb",    "vc"    };
++  _set_fields(driver, cols, tmpls, 3);
++  driver->fields[p->default_field_idx].flags |= AFSQL_FF_DEFAULT;
++
++  LogMessage *msg = log_msg_new_empty();
++  GString *table  = _make_table("t");
++  GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
++
++  cr_assert(g_strstr_len(cmd->str, -1, p->must_contain) != NULL,
++            "[%s] Expected '%s' in command, got: %s",
++            p->description, p->must_contain, cmd->str);
++  if (p->must_not_contain[0])
++    cr_assert(g_strstr_len(cmd->str, -1, p->must_not_contain) == NULL,
++              "[%s] Expected '%s' absent from command, got: %s",
++              p->description, p->must_not_contain, cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++/* ===================================================================
++ * Suite 2: parameterized — table name and quote_as_string
++ * =================================================================== */
++
++TestSuite(table_name_in_command, .init = setup, .fini = teardown);
++
++typedef struct
++{
++  gchar description[64];
++  gchar table_name[64];
++  gchar quote_char[4];  /* quote_as_string value */
++  gchar expected_prefix[64];
++} TableNameParam;
++
++ParameterizedTestParameters(table_name_in_command, variants)
++{
++  static TableNameParam params[] =
++  {
++    { "no quoting",          "logs",   "",  "INSERT INTO logs ("   },
++    { "backtick quoting",    "logs",   "`", "INSERT INTO `logs` (" },
++    { "double-quote quoting", "logs",   "\"", "INSERT INTO \"logs\" ("},
++    { "table with underscore", "log_data", "", "INSERT INTO log_data ("},
++    { "table with dot (schema)", "db.logs", "", "INSERT INTO db.logs ("},
++  };
++  return cr_make_param_array(TableNameParam, params,
++                             sizeof(params) / sizeof(params[0]));
++}
++
++ParameterizedTest(TableNameParam *p, table_name_in_command, variants)
++{
++  driver = _create_driver();
++  g_free(driver->quote_as_string);
++  driver->quote_as_string  = g_strdup(p->quote_char);
++  const gchar *cols[]  = { "col" };
++  const gchar *tmpls[] = { "v" };
++  _set_fields(driver, cols, tmpls, 1);
++
++  LogMessage *msg = log_msg_new_empty();
++  GString *table  = _make_table(p->table_name);
++  GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
++
++  cr_assert(g_str_has_prefix(cmd->str, p->expected_prefix),
++            "[%s] Expected prefix '%s', got: %s",
++            p->description, p->expected_prefix, cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++/* ===================================================================
++ * Suite 3: parameterized — value rendering in VALUES clause
++ * =================================================================== */
++
++TestSuite(value_rendering, .init = setup, .fini = teardown);
++
++typedef struct
++{
++  gchar description[64];
++  gchar tmpl[32];        /* template string for the single field     */
++  gchar msg_key[32];     /* empty string means don't set a message key */
++  gchar msg_value[64];   /* message value                            */
++  gchar expected[64];    /* substring expected in the full command   */
++} ValueRenderingParam;
++
++ParameterizedTestParameters(value_rendering, variants)
++{
++  static ValueRenderingParam params[] =
++  {
++    { "literal value is quoted",        "hello",      "",     "",         "'hello'"      },
++    { "empty string is quoted",         "",           "",     "",         "''"           },
++    { "template expands from message",  "${HOST}",    "HOST", "myserver", "'myserver'"   },
++    { "spaces in value",                "hello world", "",     "",         "'hello world'"},
++  };
++  return cr_make_param_array(ValueRenderingParam, params,
++                             sizeof(params) / sizeof(params[0]));
++}
++
++ParameterizedTest(ValueRenderingParam *p, value_rendering, variants)
++{
++  driver = _create_driver();
++  const gchar *cols[]  = { "col" };
++  const gchar *tmpls[] = { p->tmpl };
++  _set_fields(driver, cols, tmpls, 1);
++
++  LogMessage *msg = log_msg_new_empty();
++  if (p->msg_key[0])
++    log_msg_set_value_by_name(msg, p->msg_key, p->msg_value, -1);
++
++  GString *table = _make_table("t");
++  GString *cmd   = afsql_dd_build_insert_command(driver, msg, table);
++
++  cr_assert(g_strstr_len(cmd->str, -1, p->expected) != NULL,
++            "[%s] Expected '%s' in command, got: %s",
++            p->description, p->expected, cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++/* ===================================================================
++ * Suite 4: SQL injection — values are safe, table name is not
++ *
++ * Values are rendered via dbi_conn_quote_string_copy, so injection
++ * payloads in message fields cannot break out of the quoted string.
++ *
++ * Table names are currently inserted bare (wrapped only with
++ * quote_as_string). A table name derived from a log message template
++ * can therefore inject arbitrary SQL — this suite documents that gap.
++ * =================================================================== */
++
++TestSuite(sql_injection, .init = setup, .fini = teardown);
++
++typedef struct
++{
++  gchar description[64];
++  gchar msg_value[64];           /* attacker-controlled field value  */
++  gchar must_not_contain[64];    /* raw payload must not appear bare */
++  gchar must_contain[64];        /* safely-quoted form must appear   */
++} ValueInjectionParam;
++
++ParameterizedTestParameters(sql_injection, value_is_safe)
++{
++  static ValueInjectionParam params[] =
++  {
++    { "single quote cannot break out",           "'; DROP TABLE users; --", "'; DROP TABLE users; --", "\\'; DROP TABLE users; --" },
++    { "multiple single quotes are all escaped",  "it's a ''test''",         "",                        "it\\'s a \\'\\'"           },
++    { "closing paren + VALUES pattern in value", "') VALUES ('evil",        "",                        "\\') VALUES (\\'evil"      },
++  };
++  return cr_make_param_array(ValueInjectionParam, params,
++                             sizeof(params) / sizeof(params[0]));
++}
++
++ParameterizedTest(ValueInjectionParam *p, sql_injection, value_is_safe)
++{
++  driver = _create_driver();
++  const gchar *cols[]  = { "msg" };
++  const gchar *tmpls[] = { "${MSG}" };
++  _set_fields(driver, cols, tmpls, 1);
++
++  LogMessage *msg = log_msg_new_empty();
++  log_msg_set_value_by_name(msg, "MSG", p->msg_value, -1);
++
++  GString *table = _make_table("t");
++  GString *cmd   = afsql_dd_build_insert_command(driver, msg, table);
++
++  if (p->must_not_contain[0])
++    {
++      gchar *raw = g_strdup_printf("'%s'", p->msg_value);
++      cr_assert(g_strstr_len(cmd->str, -1, raw) == NULL,
++                "[%s] Raw unescaped payload found in command: %s",
++                p->description, cmd->str);
++      g_free(raw);
++    }
++
++  cr_assert(g_strstr_len(cmd->str, -1, p->must_contain) != NULL,
++            "[%s] Expected escaped form '%s' in command, got: %s",
++            p->description, p->must_contain, cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++/*
++ * Table name injection: documents that a table name derived from a log
++ * message template is inserted bare into the SQL command.  This is the
++ * injection surface identified in the code review.
++ */
++typedef struct
++{
++  gchar description[64];
++  gchar table_payload[80];
++} TableInjectionParam;
++
++ParameterizedTestParameters(sql_injection, table_name_is_not_escaped)
++{
++  static TableInjectionParam params[] =
++  {
++    { "semicolon + DROP TABLE",         "logs; DROP TABLE users; --"       },
++    { "single quote breaks out",        "logs' WHERE 1=1 --"               },
++    { "UNION SELECT via table name",    "t UNION SELECT * FROM secrets --" },
++  };
++  return cr_make_param_array(TableInjectionParam, params,
++                             sizeof(params) / sizeof(params[0]));
++}
++
++ParameterizedTest(TableInjectionParam *p, sql_injection, table_name_is_not_escaped)
++{
++  driver = _create_driver();
++  const gchar *cols[]  = { "col" };
++  const gchar *tmpls[] = { "v" };
++  _set_fields(driver, cols, tmpls, 1);
++
++  LogMessage *msg = log_msg_new_empty();
++  GString *table  = _make_table(p->table_payload);
++  GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
++
++  /*
++   * Documents the current (unsafe) behaviour: the payload appears verbatim.
++   * Once table-name escaping is implemented these assertions should be
++   * inverted.
++   */
++  cr_assert(g_strstr_len(cmd->str, -1, p->table_payload) != NULL,
++            "[%s] Expected bare payload in command (documents unescaped table name), got: %s",
++            p->description, cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
+diff --git a/modules/afsql/tests/test_afsql_run_query.c b/modules/afsql/tests/test_afsql_run_query.c
+index b26f631a7..a94e48538 100644
+--- a/modules/afsql/tests/test_afsql_run_query.c
++++ b/modules/afsql/tests/test_afsql_run_query.c
+@@ -23,36 +23,9 @@
+ #include <criterion/criterion.h>
+ #include <criterion/parameterized.h>
+ 
+-#include "test_afsql.h"
+-#include "mock-dbi.h"
++#include "afsql_test_helpers.h"
+ #include "apphook.h"
+ 
+-/* ----------------------------- helpers ----------------------------- */
+-
+-static AFSqlDestDriver *
+-_create_driver(void)
+-{
+-  AFSqlDestDriver *self = g_new0(AFSqlDestDriver, 1);
+-  self->type     = g_strdup("mysql");
+-  self->host     = g_strdup("localhost");
+-  self->port     = g_strdup("3306");
+-  self->user     = g_strdup("testuser");
+-  self->database = g_strdup("testdb");
+-  /* dbi_ctx is intentionally left NULL; mock-dbi ignores the conn handle */
+-  return self;
+-}
+-
+-static void
+-_free_driver(AFSqlDestDriver *self)
+-{
+-  g_free(self->type);
+-  g_free(self->host);
+-  g_free(self->port);
+-  g_free(self->user);
+-  g_free(self->database);
+-  g_free(self);
+-}
+-
+ /* ----------------------------- fixtures ----------------------------- */
+ 
+ static AFSqlDestDriver *driver;
diff --git a/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-04.patch b/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-04.patch
new file mode 100644
index 0000000000..709bb1a564
--- /dev/null
+++ b/meta-oe/recipes-support/syslog-ng/files/CVE-2026-39879-04.patch
@@ -0,0 +1,176 @@
+From 4c0aebf7a1c0a8177bdf4fb67803e19dddc3dbf2 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?B=C3=A1lint=20Horv=C3=A1th?= <bal.horv.98@gmail.com>
+Date: Mon, 20 Apr 2026 16:37:04 +0200
+Subject: [PATCH] afsql: Sanitized table name to fix possible injection
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+The DONT_CREATE_TABLES early return in
+afsql_dd_ensure_table_is_syslogng_conform() bypasses
+_sanitize_sql_identifier(), leaving the table name unsanitized when
+the schema helper is skipped.
+
+Signed-off-by: Bálint Horváth <bal.horv.98@gmail.com>
+Signed-off-by: Hofi <hofione@gmail.com>
+
+CVE: CVE-2026-39879
+Upstream-Status: Backport [https://github.com/syslog-ng/syslog-ng/commit/4c0aebf7a1c0a8177bdf4fb67803e19dddc3dbf2]
+Signed-off-by: Peter Marko <peter.marko@siemens.com>
+---
+ modules/afsql/afsql.c                         |  3 +-
+ modules/afsql/tests/afsql_test_helpers.h      |  1 +
+ modules/afsql/tests/test_afsql.h              |  3 +-
+ .../tests/test_afsql_build_insert_command.c   | 71 +++++++++++++++----
+ 4 files changed, 62 insertions(+), 16 deletions(-)
+
+diff --git a/modules/afsql/afsql.c b/modules/afsql/afsql.c
+index 04b35e607..3f5032ab8 100644
+--- a/modules/afsql/afsql.c
++++ b/modules/afsql/afsql.c
+@@ -764,13 +764,14 @@ afsql_dd_disconnect(LogThreadedDestDriver *s)
+   self->dbi_ctx = NULL;
+ }
+ 
+-static GString *
++GString *
+ afsql_dd_ensure_accessible_database_table(AFSqlDestDriver *self, LogMessage *msg)
+ {
+   GString *table = g_string_sized_new(32);
+ 
+   LogTemplateEvalOptions options = {&self->template_options, LTZ_LOCAL, 0, NULL, LM_VT_STRING};
+   log_template_format(self->table, msg, &options, table);
++  _sanitize_sql_identifier(table->str);
+ 
+   if (!afsql_dd_ensure_table_is_syslogng_conform(self, table))
+     {
+diff --git a/modules/afsql/tests/afsql_test_helpers.h b/modules/afsql/tests/afsql_test_helpers.h
+index 9dabc763f..ba5f07763 100644
+--- a/modules/afsql/tests/afsql_test_helpers.h
++++ b/modules/afsql/tests/afsql_test_helpers.h
+@@ -60,6 +60,7 @@ _free_driver(AFSqlDestDriver *self)
+   g_free(self->database);
+   g_free(self->quote_as_string);
+   g_free(self->null_value);
++  log_template_unref(self->table);
+   g_free(self);
+ }
+ 
+diff --git a/modules/afsql/tests/test_afsql.h b/modules/afsql/tests/test_afsql.h
+index 7fdbb838d..3195c2156 100644
+--- a/modules/afsql/tests/test_afsql.h
++++ b/modules/afsql/tests/test_afsql.h
+@@ -26,7 +26,8 @@
+ #include "afsql.h"
+ 
+ gboolean afsql_dd_run_query(AFSqlDestDriver *self, const gchar *query, gboolean silent,
+-                             dbi_result *result);
++                            dbi_result *result);
+ GString *afsql_dd_build_insert_command(AFSqlDestDriver *self, LogMessage *msg, GString *table);
++GString *afsql_dd_ensure_accessible_database_table(AFSqlDestDriver *self, LogMessage *msg);
+ 
+ #endif /* TEST_AFSQL_H_INCLUDED */
+diff --git a/modules/afsql/tests/test_afsql_build_insert_command.c b/modules/afsql/tests/test_afsql_build_insert_command.c
+index 3fd1d3c00..130c6d54e 100644
+--- a/modules/afsql/tests/test_afsql_build_insert_command.c
++++ b/modules/afsql/tests/test_afsql_build_insert_command.c
+@@ -407,23 +407,24 @@ ParameterizedTest(ValueInjectionParam *p, sql_injection, value_is_safe)
+ }
+ 
+ /*
+- * Table name injection: documents that a table name derived from a log
+- * message template is inserted bare into the SQL command.  This is the
+- * injection surface identified in the code review.
++ * Table name injection: verifies that hostile characters in a table name
++ * derived from a log-message template are replaced with underscores before
++ * the name is used in any SQL statement.
+  */
+ typedef struct
+ {
+   gchar description[64];
+   gchar table_payload[80];
++  gchar sanitized_form[80];
+ } TableInjectionParam;
+ 
+ ParameterizedTestParameters(sql_injection, table_name_is_not_escaped)
+ {
+   static TableInjectionParam params[] =
+   {
+-    { "semicolon + DROP TABLE",         "logs; DROP TABLE users; --"       },
+-    { "single quote breaks out",        "logs' WHERE 1=1 --"               },
+-    { "UNION SELECT via table name",    "t UNION SELECT * FROM secrets --" },
++    { "semicolon + DROP TABLE",      "logs; DROP TABLE users; --",       "logs__DROP_TABLE_users____"        },
++    { "single quote breaks out",     "logs' WHERE 1=1 --",               "logs__WHERE_1_1___"                },
++    { "UNION SELECT via table name", "t UNION SELECT * FROM secrets --", "t_UNION_SELECT___FROM_secrets___"  },
+   };
+   return cr_make_param_array(TableInjectionParam, params,
+                              sizeof(params) / sizeof(params[0]));
+@@ -437,18 +438,60 @@ ParameterizedTest(TableInjectionParam *p, sql_injection, table_name_is_not_escap
+   _set_fields(driver, cols, tmpls, 1);
+ 
+   LogMessage *msg = log_msg_new_empty();
+-  GString *table  = _make_table(p->table_payload);
++  GString *table  = _make_table(p->sanitized_form);
+   GString *cmd    = afsql_dd_build_insert_command(driver, msg, table);
+ 
+-  /*
+-   * Documents the current (unsafe) behaviour: the payload appears verbatim.
+-   * Once table-name escaping is implemented these assertions should be
+-   * inverted.
+-   */
+-  cr_assert(g_strstr_len(cmd->str, -1, p->table_payload) != NULL,
+-            "[%s] Expected bare payload in command (documents unescaped table name), got: %s",
++  /* Raw hostile payload must NOT appear verbatim in the SQL command. */
++  cr_assert(g_strstr_len(cmd->str, -1, p->table_payload) == NULL,
++            "[%s] Hostile payload found verbatim in command: %s",
+             p->description, cmd->str);
+ 
++  /* Sanitized (underscore-replaced) form MUST appear. */
++  cr_assert(g_strstr_len(cmd->str, -1, p->sanitized_form) != NULL,
++            "[%s] Expected sanitized form '%s' in command, got: %s",
++            p->description, p->sanitized_form, cmd->str);
++
++  g_string_free(cmd, TRUE);
++  g_string_free(table, TRUE);
++  log_msg_unref(msg);
++}
++
++/*
++ * When AFSQL_DDF_DONT_CREATE_TABLES is set, afsql_dd_ensure_table_is_syslogng_conform
++ * returns TRUE immediately, skipping its own _sanitize_sql_identifier call.  The only
++ * sanitization is then the line added by the GHSA-qwf9-6222-m24m fix inside
++ * afsql_dd_ensure_accessible_database_table, immediately after log_template_format.
++ */
++Test(sql_injection, dont_create_tables_flag_sanitizes_table_name)
++{
++  driver = _create_driver();
++  driver->super.flags |= AFSQL_DDF_DONT_CREATE_TABLES;
++
++  driver->table = log_template_new(configuration, NULL);
++  log_template_compile_literal_string(driver->table, "logs;evil'table (name)");
++
++  const gchar *cols[]  = { "col" };
++  const gchar *tmpls[] = { "v" };
++  _set_fields(driver, cols, tmpls, 1);
++
++  LogMessage *msg = log_msg_new_empty();
++
++  GString *table = afsql_dd_ensure_accessible_database_table(driver, msg);
++  cr_assert_not_null(table, "ensure_accessible_database_table returned NULL unexpectedly");
++
++  cr_assert(g_strstr_len(table->str, -1, ";") == NULL,
++            "Semicolon survived sanitization: %s", table->str);
++  cr_assert(g_strstr_len(table->str, -1, "'") == NULL,
++            "Single quote survived sanitization: %s", table->str);
++  cr_assert(g_strstr_len(table->str, -1, "(") == NULL,
++            "Opening paren survived sanitization: %s", table->str);
++  cr_assert(g_strstr_len(table->str, -1, " ") == NULL,
++            "Space survived sanitization: %s", table->str);
++
++  GString *cmd = afsql_dd_build_insert_command(driver, msg, table);
++  cr_assert(g_strstr_len(cmd->str, -1, "logs_evil_table__name_") != NULL,
++            "Sanitized table name not found in INSERT command: %s", cmd->str);
++
+   g_string_free(cmd, TRUE);
+   g_string_free(table, TRUE);
+   log_msg_unref(msg);
diff --git a/meta-oe/recipes-support/syslog-ng/syslog-ng_4.11.0.bb b/meta-oe/recipes-support/syslog-ng/syslog-ng_4.11.0.bb
index 1846758681..f48ac50ecd 100644
--- a/meta-oe/recipes-support/syslog-ng/syslog-ng_4.11.0.bb
+++ b/meta-oe/recipes-support/syslog-ng/syslog-ng_4.11.0.bb
@@ -23,6 +23,10 @@ SRC_URI = "https://github.com/balabit/syslog-ng/releases/download/${BP}/${BP}.ta
            file://syslog-ng-tmp.conf \
            file://syslog-ng.service-the-syslog-ng-service.patch \
            file://0001-Fix-buildpaths-warning.patch \
+           file://CVE-2026-39879-01.patch \
+           file://CVE-2026-39879-02.patch \
+           file://CVE-2026-39879-03.patch \
+           file://CVE-2026-39879-04.patch \
 "
 SRC_URI:append:powerpc64le = " file://0001-plugin.c-workaround-powerpc64le-segfaults-error.patch"
 
