Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: [email protected]
Control: affects -1 + src:vitrage
User: [email protected]
Usertags: pu

Hi,

[ Reason ]
I'd like to address CVE-2026-28370.

[ Impact ]
CVE-2026-28370: Remote code execution through Vitrage query parser

[ Tests ]
Vitrage build process includes unit tests from upstream.

[ Risks ]
Not much, the patch is rather small, and upstream CI has functional tests.

[ Checklist ]
  [x] *all* changes are documented in the d/changelog
  [x] I reviewed all changes and I approve them
  [x] attach debdiff against the package in (old)stable
  [x] the issue is verified as fixed in unstable

[ Changes ]
Just upstream patch.

Please allow me to upload Vitrage 9.0.0-3+deb12u1 to Bullseye p-u.

Cheers,

Thomas Goirand (zigo)
diff -Nru vitrage-9.0.0/debian/changelog vitrage-9.0.0/debian/changelog
--- vitrage-9.0.0/debian/changelog      2022-09-24 17:10:08.000000000 +0200
+++ vitrage-9.0.0/debian/changelog      2026-06-08 22:00:22.000000000 +0200
@@ -1,3 +1,11 @@
+vitrage (9.0.0-3+deb12u1) bookworm; urgency=medium
+
+  * CVE-2026-28370 / OSSA-2026-003: Remote code execution through Vitrage query
+    parser. Applied upstream patch: Replace eval with function matching.
+    (Closes: #1139452)
+
+ -- Thomas Goirand <[email protected]>  Mon, 08 Jun 2026 22:00:22 +0200
+
 vitrage (9.0.0-3) unstable; urgency=medium
 
   * Uploading to unstable.
diff -Nru 
vitrage-9.0.0/debian/patches/CVE-2026-28370_OSSA-2026-003_Replace_eval_with_function_matching.patch
 
vitrage-9.0.0/debian/patches/CVE-2026-28370_OSSA-2026-003_Replace_eval_with_function_matching.patch
--- 
vitrage-9.0.0/debian/patches/CVE-2026-28370_OSSA-2026-003_Replace_eval_with_function_matching.patch
 1970-01-01 01:00:00.000000000 +0100
+++ 
vitrage-9.0.0/debian/patches/CVE-2026-28370_OSSA-2026-003_Replace_eval_with_function_matching.patch
 2026-06-08 22:00:22.000000000 +0200
@@ -0,0 +1,148 @@
+Author: Dmitriy Rabotyagov <[email protected]>
+Date: Tue, 30 Sep 2025 20:46:39 +0200
+Description: Replace eval with function matching
+ Using eval in code is not safe and may lead to security
+ risks, especially given that query itself is supplied by a user.
+ .
+ This refactors the code making is presumably safe
+ and prone to code injections, which are possible with eval.
+ .
+Story: 2011539
+Task: 52866
+Bug-Debian: https://bugs.debian.org/1139452
+Change-Id: If629023052aa2c067c419bba10837f77bcc3e59c
+Signed-off-by: Dmitriy Rabotyagov <[email protected]>
+Origin: upstream, https://review.opendev.org/c/openstack/vitrage/+/962671
+Last-Update: 2026-06-09
+
+diff --git a/releasenotes/notes/grap_query_eval_fixup-9232ce40ad85993e.yaml 
b/releasenotes/notes/grap_query_eval_fixup-9232ce40ad85993e.yaml
+new file mode 100644
+index 0000000..9fa203a
+--- /dev/null
++++ b/releasenotes/notes/grap_query_eval_fixup-9232ce40ad85993e.yaml
+@@ -0,0 +1,5 @@
++---
++security:
++  - |
++    A security issue in the entity graph querying mechanism has been fixed.
++    This change hardens the query parser against malicious input.
+diff --git a/vitrage/graph/query.py b/vitrage/graph/query.py
+index c206a4c..c11cb46 100644
+--- a/vitrage/graph/query.py
++++ b/vitrage/graph/query.py
+@@ -12,6 +12,7 @@
+ # License for the specific language governing permissions and limitations
+ # under the License.
+ 
++import operator
+ from oslo_log import log as logging
+ 
+ from vitrage.common.exception import VitrageError
+@@ -21,13 +22,21 @@
+ operators = [
+     '<',
+     '<=',
+-    # '=',
+     '==',
+     '!=',
+     '>=',
+     '>',
+ ]
+ 
++ops = {
++    '<': operator.lt,
++    '<=': operator.le,
++    '==': operator.eq,
++    '!=': operator.ne,
++    '>=': operator.ge,
++    '>': operator.gt,
++}
++
+ logical_operations = [
+     'and',
+     'or'
+@@ -64,10 +73,7 @@
+     :return: a predicate "match(item)"
+     """
+     try:
+-        expression = _create_query_expression(query=query_dict)
+-        LOG.debug('create_predicate::%s', expression)
+-        expression = 'lambda item: ' + expression
+-        return eval(expression)
++        return _create_query_function(query=query_dict)
+     except Exception as e:
+         LOG.error('invalid query format %s. Exception: %s',
+                   query_dict, e)
+@@ -75,46 +81,42 @@
+                            query_dict, e)
+ 
+ 
+-def _create_query_expression(query, parent_operator=None):
+-    expressions = []
++def _create_query_function(query, parent_operator=None):
+ 
+     # First element or element under logical operation
+     if not parent_operator and isinstance(query, dict):
+         (key, value) = query.copy().popitem()
+-        return _create_query_expression(value, key)
++        return _create_query_function(value, key)
+ 
+     # Continue recursion on logical (and/or) operation
+     elif parent_operator in logical_operations and isinstance(query, list):
+-        for val in query:
+-            expressions.append(_create_query_expression(val))
+-        return _join_logical_operator(parent_operator, expressions)
++        predicates = [_create_query_function(val) for val in query]
++
++        if not predicates:
++            return lambda item: False
++
++        if parent_operator == 'and':
++            return lambda item: all(p(item) for p in predicates)
++        elif parent_operator == 'or':
++            return lambda item: any(p(item) for p in predicates)
+ 
+     # Recursion evaluate leaf (stop condition)
+     elif parent_operator in operators:
+-        for key, val in query.items():
+-            expressions.append('item.get(' + _evaluable_str(key) + ')' +
+-                               parent_operator + ' ' + _evaluable_str(val))
+-        return _join_logical_operator('and', expressions)
++        predicates = []
++        op_func = ops[parent_operator]
++        for field, value in query.items():
++            predicates.append(
++                lambda item, f=field, v=value: op_func(item.get(f), v)
++            )
++
++        # Multiple conditions under a comparison operator are implicitly 'and'
++        if len(predicates) > 1:
++            return lambda item: all(p(item) for p in predicates)
++        elif predicates:
++            return predicates[0]
++        else:
++            return lambda item: False
++
+     else:
+         raise VitrageError('invalid partial query format',
+                            parent_operator, query)
+-
+-
+-def _evaluable_str(value):
+-    """wrap string/unicode with back tick"""
+-    if isinstance(value, str):
+-        return '\'' + value + '\''
+-    else:
+-        return str(value)
+-
+-
+-def _join_logical_operator(op, expressions):
+-    """Create an expressions string
+-
+-    Example input:
+-        op='AND'
+-        expressions=['a == b', 'c < d']
+-    Example output: (a == b AND c < d)
+-    """
+-    separator = ' ' + op + ' '
+-    return '(' + separator.join(expressions) + ')'
diff -Nru vitrage-9.0.0/debian/patches/series 
vitrage-9.0.0/debian/patches/series
--- vitrage-9.0.0/debian/patches/series 2022-09-24 17:10:08.000000000 +0200
+++ vitrage-9.0.0/debian/patches/series 2026-06-08 22:00:22.000000000 +0200
@@ -2,3 +2,4 @@
 fix-oslo-config-generator.patch
 reproducible-build.patch
 Fix-compat-with-oslo.db-12.1.0.patch
+CVE-2026-28370_OSSA-2026-003_Replace_eval_with_function_matching.patch

Reply via email to