namespace AIStudio.Tools.PluginSystem; /// /// Checks if a plugin is forbidden. /// public static class ForbiddenPlugins { private const string ID_PATTERN = "ID = \""; private static readonly int ID_PATTERN_LEN = ID_PATTERN.Length; /// /// Checks if the given code represents a forbidden plugin. /// /// The code to check. /// The result of the check. public static PluginCheckResult Check(ReadOnlySpan code) { var endIndex = 0; var foundAnyId = false; var id = ReadOnlySpan.Empty; while (true) { // Create a slice of the code starting at the end index. // This way we can search for all IDs in the code: code = code[endIndex..]; // Read the next ID as a string: if (!TryGetId(code, out id, out endIndex)) { // When no ID was found at all, we block this plugin. // When another ID was found previously, we allow this plugin. if(foundAnyId) return new PluginCheckResult(false, null); return new PluginCheckResult(true, "No ID was found."); } // Try to parse the ID as a GUID: if (!Guid.TryParse(id, out var parsedGuid)) { // Again, when no ID was found at all, we block this plugin. if(foundAnyId) return new PluginCheckResult(false, null); return new PluginCheckResult(true, "The ID is not a valid GUID."); } // Check if the GUID is forbidden: if (FORBIDDEN_PLUGINS.TryGetValue(parsedGuid, out var reason)) return new PluginCheckResult(true, reason); foundAnyId = true; } } private static bool TryGetId(ReadOnlySpan code, out ReadOnlySpan id, out int endIndex) { // // Please note: the code variable is a slice of the original code. // That means the indices are relative to the slice, not the original code. // id = ReadOnlySpan.Empty; endIndex = 0; // Find the next ID: var idStartIndex = code.IndexOf(ID_PATTERN); if (idStartIndex < 0) return false; // Find the start index of the value (Guid): var valueStartIndex = idStartIndex + ID_PATTERN_LEN; // Find the end index of the value. In order to do that, // we create a slice of the code starting at the value // start index. That means that the end index is relative // to the inner slice, not the original code nor the outer slice. var valueEndIndex = code[valueStartIndex..].IndexOf('"'); if (valueEndIndex < 0) return false; // From the perspective of the start index is the end index // the length of the value: endIndex = valueStartIndex + valueEndIndex; id = code.Slice(valueStartIndex, valueEndIndex); return true; } /// /// The forbidden plugins. /// /// /// A dictionary that maps the GUID of a plugin to the reason why it is forbidden. /// // ReSharper disable once CollectionNeverUpdated.Local private static readonly Dictionary FORBIDDEN_PLUGINS = [ ]; }