From 43e0c9301527b5046e0390a643376ce3bf64313f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?SAL=C4=B0H=20=C3=96ZKARA?= Date: Mon, 15 Jun 2026 23:23:26 +0300 Subject: [PATCH] Improve string-indexer detection and update schema Enhance AbpDynamicSortingGuard.IsConstantStringIndexer to more robustly recognize true string-keyed indexers: verify the call is instance-bound with a single constant string argument, ensure the resolved method is a special-name instance getter with a single string parameter, and confirm it matches an actual indexer property via reflection. This prevents arbitrary methods named "get_Item" from being treated as safe indexer accesses. Also update the dashboard descriptor schema description to clarify visualizations are a flat list of name-identified items (wording change only). --- .../Services/AbpDynamicSortingGuard.cs | 35 +++++++++++++++++-- .../dashboard-descriptor.schema.json | 2 +- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs index 9546890786..25f7f72439 100644 --- a/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs +++ b/framework/src/Volo.Abp.Ddd.Application/Volo/Abp/Application/Services/AbpDynamicSortingGuard.cs @@ -2,6 +2,7 @@ using System; using System.Linq; using System.Linq.Dynamic.Core; using System.Linq.Expressions; +using System.Reflection; using System.Runtime.CompilerServices; using Volo.Abp.Validation; @@ -114,9 +115,37 @@ internal static class AbpDynamicSortingGuard private static bool IsConstantStringIndexer(MethodCallExpression node) { - return node.Method.Name == "get_Item" - && node.Arguments.Count == 1 - && node.Arguments[0] is ConstantExpression { Value: string }; + // Must be an instance call with a single constant string argument. + if (node.Object == null + || node.Arguments.Count != 1 + || node.Arguments[0] is not ConstantExpression { Value: string }) + { + return false; + } + + // And the resolved method must be the get accessor of a real string-keyed + // indexer (a special-name property getter), not an arbitrary method that + // merely shares the compiler-generated "get_Item" name. This keeps the + // guard's "no method calls" rule intact for everything except indexers. + var method = node.Method; + var parameters = method.GetParameters(); + if (!method.IsSpecialName + || method.IsStatic + || parameters.Length != 1 + || parameters[0].ParameterType != typeof(string)) + { + return false; + } + + return method.DeclaringType? + .GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) + .Any(property => + { + var indexParameters = property.GetIndexParameters(); + return indexParameters.Length == 1 + && indexParameters[0].ParameterType == typeof(string) + && property.GetGetMethod(nonPublic: true) == method; + }) == true; } } } diff --git a/schemas/low-code/definitions/dashboard-descriptor.schema.json b/schemas/low-code/definitions/dashboard-descriptor.schema.json index 0d4a55d038..c0c77d7be9 100644 --- a/schemas/low-code/definitions/dashboard-descriptor.schema.json +++ b/schemas/low-code/definitions/dashboard-descriptor.schema.json @@ -19,7 +19,7 @@ }, "visualizations": { "type": "array", - "description": "Flat, name-keyed list of dashboard visualizations. Each visualization positions itself on the grid via row, order, and width.", + "description": "Flat list of name-identified dashboard visualizations. Each visualization positions itself on the grid via row, order, and width.", "items": { "$ref": "dashboard-visualization-descriptor.schema.json" },