Completed
Last Updated: 28 Jul 2026 05:26 by ADMIN
Raul
Created on: 29 Jan 2025 13:07
Category: Filter
Type: Feature Request
2
Add the ability to convert old versions of FilterExpressions to work with the latest security improvements

Please create a method that will convert the old expressions to new format and make them re-usable again.

7 comments
ADMIN
Rumen
Posted on: 28 Jul 2026 05:26

Hi Raul,

Thank you for raising this topic. We explored the possibility of providing an in-product way to automatically convert existing saved filter states. However, this is not something we can safely implement within the RadFilter source. Such a solution would require reintroducing BinaryFormatter deserialization, which is the exact insecure code path we intentionally removed. Including it again would reintroduce that security risk for all users of the control.

As highlighted earlier in this thread, Paul and Attila (thank you both) have already identified a safe and practical approach: a small extension method you add to your own project that uses the old deserialization logic just long enough to convert a saved state, then re-saves it in the new secure format. You can see the full snippet and usage steps earlier in this thread (LoadLegacySettings / ConvertLegacySettings). Since the risky code lives only in your own project and only runs against your own trusted data, it's a safe, one-time migration step, once all your saved filters are converted, you can remove it entirely.

 

Regards,
Rumen
Progress Telerik

Stay tuned by visiting our public roadmap and feedback portal pages! Or perhaps, if you are new to our Telerik family, check out our getting started resources
Michael
Posted on: 27 Jul 2026 15:24

Attila,

Thank you for the response!  I realized that the reports saved in my app are XML based, not JSON.  (This app hasn't had a Telerik upgrade since 2019!)  So, I had to create my own conversion tool.  I am making progress.

ADMIN
Attila Antal
Posted on: 24 Jul 2026 07:52

Hi Michael,

Here is a more convenient way of converting the old filter states to the new format.

Update the RadFilterExtensions as follows

public static class RadFilterExtensions
{
    public static void LoadLegacySettings(this Telerik.Web.UI.RadFilter filter, string state)
    {
        using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
        {
            var bytes = System.Convert.FromBase64String(state);
            memoryStream.Write(bytes, 0, bytes.Length);
            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);

            var savedState = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Deserialize(memoryStream);
            filter.RootGroup.Expressions.Clear();
            ((System.Web.UI.IStateManager)filter.RootGroup).LoadViewState(savedState);
            filter.RecreateControl();
        }
    }

    // Converts a single legacy (BinaryFormatter ViewState) filter state string to the current format.
    // Use this during a one-time migration - the filter control is used as a temporary vehicle
    // to deserialize the old format and re-serialize it in the new format.
    public static string ConvertLegacySettings(this Telerik.Web.UI.RadFilter filter, string legacyState)
    {
        filter.LoadLegacySettings(legacyState);
        return filter.SaveSettings();
    }

    // Converts a collection of legacy filter states keyed by any ID type (e.g. int database PK).
    // Returns a dictionary mapping each key to its converted state string, ready to be saved back.
    public static Dictionary<TKey, string> ConvertMultipleLegacySettings<TKey>(this Telerik.Web.UI.RadFilter filter, IDictionary<TKey, string> legacyStates)
    {
        var results = new Dictionary<TKey, string>(legacyStates.Count);
        foreach (var kvp in legacyStates)
        {
            results[kvp.Key] = filter.ConvertLegacySettings(kvp.Value);
        }
        return results;
    }
}

Usage

  1. Load the old filter states from the database into a Dictionary
  2. Call the ConvertMultipleLegacySettings extension method and pass the filter states dictionary to it
  3. Loop thgrough the converted states, and store them in the database
protected void btnConvertFilter_Click(object sender, EventArgs e)
{
    // Example: load legacy states from the database, keyed by their record ID.
    // Replace this with your actual data access logic.
    var legacyStatesFromDb = new Dictionary<int, string>
    {
        // { recordId, legacyFilterState },
        // { 1, "AAEAAAD..." },
        // { 2, "AAEAAAD..." },
    };

    // Convert all states from the old BinaryFormatter ViewState format to the new format.
    Dictionary<int, string> convertedStates = RadFilter1.ConvertMultipleLegacySettings(legacyStatesFromDb);

    // Persist the converted states back to the database.
    foreach (var kvp in convertedStates)
    {
        // Example: UpdateFilterStateInDb(kvp.Key, kvp.Value);
    }
}

The provided code is intended as a migration example to help convert existing filter states from the old format to the new one. It is not designed to be a general-purpose implementation, and we do not plan to provide additional implementations beyond this example.

Pleasse let us know if you have trouble converting the filter states.

Regards,
Attila Antal
Progress Telerik

Stay tuned by visiting our public roadmap and feedback portal pages! Or perhaps, if you are new to our Telerik family, check out our getting started resources
Michael
Posted on: 23 Jul 2026 22:22

Any update on this effort to use saved reports created before the upgrade?

I haven't updated our version in quite some time and this is a new problem.

Here are the exception details:

Exception Message:
exception processing inspection report: Invalid JSON primitive: .

Stack Trace:
System.ArgumentException: Invalid JSON primitive: .     at System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializePrimitiveObject()     at System.Web.Script.Serialization.JavaScriptObjectDeserializer.DeserializeInternal(Int32 depth)     at System.Web.Script.Serialization.JavaScriptObjectDeserializer.BasicDeserialize(String input, Int32 depthLimit, JavaScriptSerializer serializer)     at System.Web.Script.Serialization.JavaScriptSerializer.Deserialize(JavaScriptSerializer serializer, String input, Type type, Int32 depthLimit)     at System.Web.Script.Serialization.JavaScriptSerializer.Deserialize[T](String input)     at Telerik.Web.UI.RadFilterExpressionSerializer.Deserialize(String state)     at Telerik.Web.UI.RadFilterStatePersister.ApplySettings(Control control)     at Telerik.Web.UI.RadPersistenceManager.LoadState()     at PARIS.Reports.InspectionReports.RadComboRpt_SelectedIndexChanged(Object sender, RadComboBoxSelectedIndexChangedEventArgs e) in [Method in my app that pulls up a saved report]
ADMIN
Attila Antal
Posted on: 11 Mar 2025 13:20

That is a good idea, Paul. Thank you!

We've been thinking from different perspectives, and in all cases we would like to avoid returning any of the vulnerable classes/methods (LosFormatter, BinaryFormatter) into our source code and other ways of doings this will be overwhelming and unnecessary.

I think the extension method you shared would be a very good workaround until all Filter Expressions are converted to the new format, then everyone can rid their apps from those flag raising lines of code.

@Paul, as a token of gratitude for sharing this idea, I have rewarded you with Telerik points.

Workaround

Here is an example of integrating Paul's Idea for Extension Method.

Create a new Static Class in your app that will contain the extension method. The method uses the original code we removed from the source code with the Security Updates:

public static class RadFilterExtensions
{
    public static void LoadLegacySettings(this Telerik.Web.UI.RadFilter filter, string state)
    {
        using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
        {
            var bytes = System.Convert.FromBase64String(state);
            memoryStream.Write(bytes, 0, bytes.Length);
            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);

            var savedState = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter().Deserialize(memoryStream);
            filter.RootGroup.Expressions.Clear();
            ((System.Web.UI.IStateManager)filter.RootGroup).LoadViewState(savedState);
            filter.RecreateControl();
        }
    }
}

 

When loading the settings, you can use a Try/Catch block that will first try restoring the state with the new Secure method and if it fails, falls back to the Legacy function. Important: As stated by Paul, this code should only be used until all filters are converted the new base64 format.

protected void btnLoadSettings_Click(object sender, EventArgs e)
{
    string savedState = "......";

    try
    {
        // Try the new secure function, will work if it's the correct base64
        RadFilter1.LoadSettings(savedState);
    }
    catch (Exception)
    {
        // Fall back to Legacy Converter
        RadFilter1.LoadLegacySettings(savedState);
    }
}

Regards,
Attila Antal
Progress Telerik

Enjoyed our products? Share your experience on G2 and receive a $25 Amazon gift card for a limited time!

Paul
Posted on: 10 Mar 2025 16:46

As a workaround you could extend the radFilter class or create an extension method to load the old base64 string. I intend to use this method to update all old base64 filters I have saved. As this was changed during a security improvement, I wouldn't use this method other than for updating any saved base64 values over to the new format.  

 

public void LoadLegacySettings(this Telerik.Web.UI.RadFilter radFilter, string state)
{
    using (System.IO.MemoryStream memoryStream = new System.IO.MemoryStream())
    {
        byte[] numArray = Convert.FromBase64String(state);
        memoryStream.Write(numArray, 0, System.Convert.ToInt32(numArray.Length));
        memoryStream.Seek((long)0, SeekOrigin.Begin);
        var formatter = new Binary.BinaryFormatter() { AssemblyFormat = FormatterAssemblyStyle.Simple };
        object obj = formatter.Deserialize(memoryStream);
        radFilter.RootGroup.Expressions.Clear();
        (IStateManager)radFilter.RootGroup.LoadViewState(obj);
        radFilter.RecreateControl();
    }
}
ADMIN
Attila Antal
Posted on: 29 Jan 2025 13:35

Hi Raul,

Thank you for the feature request.

We recently became aware of an issue stemming from changes we implemented as part of an important security improvement. Specifically, we removed the now-unsafe BinaryFormatter with a more secure alternative. While this update was necessary to ensure a higher level of security, it appears to have caused unintended side effects that may affect the SaveSettings() and LoadSettings() functions of the RadFilter.

The filters that were saved previously were stored in a different format than the updated functions do, thus the expressions stored in previous version are no longer compatible.

We're currently researching for an approach that will allow converting the previous expressions to the new format and make it work with the latest security changes.

Regards,
Attila Antal
Progress Telerik

Stay tuned by visiting our public roadmap and feedback portal pages! Or perhaps, if you are new to our Telerik family, check out our getting started resources