Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Assets/Tests/Editor/McpEditorWindowRefreshPolicyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,6 @@ private static ToolSettingsSectionData CreateToolSettingsData(
{
return new ToolSettingsSectionData(
showToolSettings,
allowThirdPartyTools: false,
DynamicCodeSecurityLevel.Restricted,
System.Array.Empty<ToolToggleItem>(),
System.Array.Empty<ToolToggleItem>(),
Expand Down
13 changes: 0 additions & 13 deletions Assets/Tests/Editor/ToolSettingsSectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ public void Update_HeaderOnlyRefreshAfterLoad_PreservesLoadedRows()
ToolSettingsSectionData headerOnlyData = CreateData(
compileEnabled: false,
includeGetLogs: false,
allowThirdPartyTools: false,
includeThirdPartyTool: true,
hasToolListData: false);

Expand Down Expand Up @@ -177,14 +176,6 @@ private static VisualElement CreateRootElement()
{
name = "cli-reference-link"
};
Toggle allowThirdPartyToggle = new Toggle
{
name = "allow-third-party-toggle"
};
Label allowThirdPartyLabel = new Label
{
name = "allow-third-party-label"
};
Button securityLevelRestrictedButton = new Button
{
name = "security-level-restricted-button"
Expand All @@ -202,8 +193,6 @@ private static VisualElement CreateRootElement()
name = "tool-settings-info-container"
};

foldout.Add(allowThirdPartyToggle);
foldout.Add(allowThirdPartyLabel);
foldout.Add(securityLevelRestrictedButton);
foldout.Add(securityLevelFullAccessButton);
foldout.Add(securityLevelDescription);
Expand All @@ -218,7 +207,6 @@ private static ToolSettingsSectionData CreateData(
bool compileEnabled,
bool includeGetLogs,
bool showToolSettings = true,
bool allowThirdPartyTools = true,
bool includeThirdPartyTool = false,
bool hasToolListData = true,
DynamicCodeSecurityLevel dynamicCodeSecurityLevel = DynamicCodeSecurityLevel.Restricted)
Expand Down Expand Up @@ -256,7 +244,6 @@ private static ToolSettingsSectionData CreateData(

return new ToolSettingsSectionData(
showToolSettings: showToolSettings,
allowThirdPartyTools: allowThirdPartyTools,
dynamicCodeSecurityLevel: dynamicCodeSecurityLevel,
builtInTools: builtInTools,
thirdPartyTools: thirdPartyTools,
Expand Down
44 changes: 32 additions & 12 deletions Assets/Tests/Editor/ULoopSettingsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,17 +108,17 @@ public void GetSettings_WhenNewFileAbsentAndLegacyExists_ShouldMigrateRemainingF

ULoopSettingsData result = ULoopSettings.GetSettings();

Assert.IsTrue(result.allowThirdPartyTools);
Assert.AreEqual((int)DynamicCodeSecurityLevel.Restricted, result.dynamicCodeSecurityLevel);
Assert.IsTrue(File.Exists(SettingsFilePath), $"{SettingsFilePath} should be created by migration");
string updatedJson = File.ReadAllText(SettingsFilePath);
StringAssert.DoesNotContain("\"allowThirdPartyTools\"", updatedJson);
}

[Test]
public void GetSettings_WhenNewFileExists_ShouldIgnoreLegacy()
{
ULoopSettingsData newSettings = new ULoopSettingsData
{
allowThirdPartyTools = true,
dynamicCodeSecurityLevel = (int)DynamicCodeSecurityLevel.FullAccess
};
File.WriteAllText(SettingsFilePath, JsonUtility.ToJson(newSettings, true));
Expand All @@ -133,7 +133,6 @@ public void GetSettings_WhenNewFileExists_ShouldIgnoreLegacy()

ULoopSettingsData result = ULoopSettings.GetSettings();

Assert.IsTrue(result.allowThirdPartyTools);
Assert.AreEqual((int)DynamicCodeSecurityLevel.FullAccess, result.dynamicCodeSecurityLevel);
}

Expand All @@ -142,15 +141,13 @@ public void SetAndGet_RoundTrip_ShouldPreserveRemainingValues()
{
ULoopSettingsData written = new ULoopSettingsData
{
allowThirdPartyTools = true,
dynamicCodeSecurityLevel = (int)DynamicCodeSecurityLevel.Restricted
};
ULoopSettings.SaveSettings(written);
ULoopSettings.InvalidateCache();

ULoopSettingsData readBack = ULoopSettings.GetSettings();

Assert.AreEqual(written.allowThirdPartyTools, readBack.allowThirdPartyTools);
Assert.AreEqual(written.dynamicCodeSecurityLevel, readBack.dynamicCodeSecurityLevel);
}

Expand Down Expand Up @@ -189,7 +186,6 @@ public void GetSettings_WhenBothFilesAbsent_ShouldReturnDefaults()

ULoopSettingsData result = ULoopSettings.GetSettings();

Assert.IsFalse(result.allowThirdPartyTools);
Assert.AreEqual((int)DynamicCodeSecurityLevel.Restricted, result.dynamicCodeSecurityLevel);
Assert.IsFalse(File.Exists(LegacySettingsFilePath),
"Legacy file should not be created when both files are absent");
Expand All @@ -200,7 +196,7 @@ public void GetSettings_WhenPrimaryMissingAndBackupExists_ShouldRecoverFromBacku
{
DeleteIfExists(SettingsFilePath);

ULoopSettingsData backupData = new ULoopSettingsData
SettingsFileFixture backupData = new SettingsFileFixture
{
allowThirdPartyTools = false,
dynamicCodeSecurityLevel = (int)DynamicCodeSecurityLevel.FullAccess
Expand All @@ -211,9 +207,10 @@ public void GetSettings_WhenPrimaryMissingAndBackupExists_ShouldRecoverFromBacku
ULoopSettingsData result = ULoopSettings.GetSettings();

Assert.IsTrue(File.Exists(SettingsFilePath), $"{SettingsFilePath} should be recovered from .bak");
Assert.IsFalse(result.allowThirdPartyTools);
Assert.AreEqual((int)DynamicCodeSecurityLevel.FullAccess, result.dynamicCodeSecurityLevel);
Assert.IsFalse(File.Exists(SettingsBackupPath), ".bak should be consumed by recovery");
string updatedJson = File.ReadAllText(SettingsFilePath);
StringAssert.DoesNotContain("\"allowThirdPartyTools\"", updatedJson);
}

[Test]
Expand All @@ -222,7 +219,7 @@ public void GetSettings_WhenOldSecurityJsonExists_ShouldRenameToPermissions()
DeleteIfExists(SettingsFilePath);
DeleteIfExists(LegacySettingsFilePath);

ULoopSettingsData oldData = new ULoopSettingsData
SettingsFileFixture oldData = new SettingsFileFixture
{
allowThirdPartyTools = false,
dynamicCodeSecurityLevel = (int)DynamicCodeSecurityLevel.Restricted
Expand All @@ -234,8 +231,30 @@ public void GetSettings_WhenOldSecurityJsonExists_ShouldRenameToPermissions()

Assert.IsTrue(File.Exists(SettingsFilePath), "New settings file should exist after rename");
Assert.IsFalse(File.Exists(OldSettingsFilePath), "Old settings file should be removed after rename");
Assert.IsFalse(result.allowThirdPartyTools);
Assert.AreEqual((int)DynamicCodeSecurityLevel.Restricted, result.dynamicCodeSecurityLevel);
string updatedJson = File.ReadAllText(SettingsFilePath);
StringAssert.DoesNotContain("\"allowThirdPartyTools\"", updatedJson);
}

[Test]
public void GetSettings_WhenOldSecurityJsonExistsAndLegacyDisablesRunTests_ShouldPreferLegacyToolToggle()
{
DeleteIfExists(SettingsFilePath);
DeleteIfExists(ToolSettingsFilePath);

ULoopSettingsData oldData = new ULoopSettingsData
{
dynamicCodeSecurityLevel = (int)DynamicCodeSecurityLevel.Restricted
};
File.WriteAllText(OldSettingsFilePath, JsonUtility.ToJson(oldData, true));
File.WriteAllText(LegacySettingsFilePath, "{ \"enableTestsExecution\": false, \"showDeveloperTools\": true }");
InvalidateBothCaches();

ULoopSettingsData result = ULoopSettings.GetSettings();

Assert.AreEqual((int)DynamicCodeSecurityLevel.Restricted, result.dynamicCodeSecurityLevel);
Assert.IsFalse(ToolSettings.IsToolEnabled(McpConstants.TOOL_NAME_RUN_TESTS));
Assert.IsFalse(File.Exists(OldSettingsFilePath), "Old settings file should be removed after legacy migration");
}

[Test]
Expand All @@ -253,8 +272,9 @@ public void GetSettings_WhenJsonContainsRemovedFields_ShouldIgnoreThem()

ULoopSettingsData result = ULoopSettings.GetSettings();

Assert.IsTrue(result.allowThirdPartyTools);
Assert.AreEqual((int)DynamicCodeSecurityLevel.FullAccess, result.dynamicCodeSecurityLevel);
string updatedJson = File.ReadAllText(SettingsFilePath);
StringAssert.DoesNotContain("\"allowThirdPartyTools\"", updatedJson);
}

[Test]
Expand Down Expand Up @@ -344,7 +364,7 @@ public void SaveSettings_WhenJsonContainsRemovedFields_ShouldRewriteWithoutThem(

StringAssert.DoesNotContain("\"enableTestsExecution\"", updatedJson);
StringAssert.DoesNotContain("\"allowMenuItemExecution\"", updatedJson);
StringAssert.Contains("\"allowThirdPartyTools\"", updatedJson);
StringAssert.DoesNotContain("\"allowThirdPartyTools\"", updatedJson);
StringAssert.Contains("\"dynamicCodeSecurityLevel\"", updatedJson);
}

Expand Down
23 changes: 7 additions & 16 deletions Packages/src/Editor/Config/ULoopSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ namespace io.github.hatayama.uLoopMCP
/// </summary>
public static class ULoopSettings
{
private const string LEGACY_ALLOW_THIRD_PARTY_TOOLS_FIELD = "allowThirdPartyTools";

private static string SettingsFilePath =>
Path.Combine(McpConstants.ULOOP_DIR, McpConstants.ULOOP_SETTINGS_FILE_NAME);

Expand Down Expand Up @@ -63,18 +65,6 @@ public static void UpdateSettings(Func<ULoopSettingsData, ULoopSettingsData> tra
SaveSettings(updated);
}

public static bool GetAllowThirdPartyTools()
{
return GetSettings().allowThirdPartyTools;
}

public static void SetAllowThirdPartyTools(bool value)
{
ULoopSettingsData settings = GetSettings();
ULoopSettingsData updated = settings with { allowThirdPartyTools = value };
SaveSettings(updated);
}

public static DynamicCodeSecurityLevel GetDynamicCodeSecurityLevel()
{
ULoopSettingsData settings = GetSettings();
Expand Down Expand Up @@ -159,7 +149,8 @@ private static void LoadSettings()
_cachedSettings = JsonUtility.FromJson<ULoopSettingsData>(json);
bool migratedToolToggles = ApplyLegacyToolToggleMigrations(json);
bool normalizedDynamicCode = NormalizeLegacyDisabledDynamicCode();
if (migratedToolToggles || normalizedDynamicCode)
bool removedThirdPartyToolsField = json.Contains($"\"{LEGACY_ALLOW_THIRD_PARTY_TOOLS_FIELD}\"");
if (migratedToolToggles || normalizedDynamicCode || removedThirdPartyToolsField)
{
SaveSettings(_cachedSettings);
}
Expand All @@ -178,7 +169,9 @@ private static bool LegacyFileHasSecurityFields()
}

string json = File.ReadAllText(LegacySettingsFilePath);
return json.Contains($"\"{nameof(LegacySecuritySettingsProbe.allowThirdPartyTools)}\"")
return json.Contains($"\"{LEGACY_ALLOW_THIRD_PARTY_TOOLS_FIELD}\"")
|| json.Contains($"\"{nameof(LegacySecuritySettingsProbe.enableTestsExecution)}\"")
|| json.Contains($"\"{nameof(LegacySecuritySettingsProbe.allowMenuItemExecution)}\"")
|| json.Contains($"\"{nameof(LegacySecuritySettingsProbe.dynamicCodeSecurityLevel)}\"");
}

Expand All @@ -199,7 +192,6 @@ private class LegacySecuritySettingsProbe
{
public bool enableTestsExecution = true;
public bool allowMenuItemExecution = true;
public bool allowThirdPartyTools = false;
public int dynamicCodeSecurityLevel = (int)DynamicCodeSecurityLevel.Restricted;
}

Expand Down Expand Up @@ -227,7 +219,6 @@ private static void MigrateFromLegacySettings()

_cachedSettings = new ULoopSettingsData
{
allowThirdPartyTools = probe.allowThirdPartyTools,
dynamicCodeSecurityLevel = probe.dynamicCodeSecurityLevel
};

Expand Down
1 change: 0 additions & 1 deletion Packages/src/Editor/Config/ULoopSettingsData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ namespace io.github.hatayama.uLoopMCP
[Serializable]
public record ULoopSettingsData
{
public bool allowThirdPartyTools = false;
public int dynamicCodeSecurityLevel = (int)DynamicCodeSecurityLevel.Restricted;
}
}
67 changes: 2 additions & 65 deletions Packages/src/Editor/Security/McpSecurityChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ private static bool IsToolAllowedByAttribute(ToolAttributeInfo toolInfo)
switch (toolInfo.RequiredSecuritySetting)
{
case SecuritySettings.None:
return IsThirdPartyToolAllowed(toolInfo.ToolName);
return true;
default:
return false; // Unknown setting - block by default
}
Expand Down Expand Up @@ -148,18 +148,7 @@ public static string GetBlockReason(string toolName)
return $"Tool '{toolName}' is not allowed by security policy.";
}

switch (toolInfo.Value.RequiredSecuritySetting)
{
case SecuritySettings.None:
if (IsThirdPartyTool(toolName))
{
return "Third party tools execution is disabled. Enable 'Allow Third Party Tools' in uLoopMCP Security Settings.";
}
return $"Tool '{toolName}' is not allowed by security policy.";

default:
return $"Tool '{toolName}' is not allowed by security policy.";
}
return $"Tool '{toolName}' is not allowed by security policy.";
}

/// <summary>
Expand All @@ -175,58 +164,6 @@ public static ToolSecurityInfo GetToolSecurityInfo(string toolName)
return new ToolSecurityInfo(toolName, isAllowed, reason);
}

/// <summary>
/// Checks if third party tools execution is allowed
/// </summary>
/// <returns>True if third party tools execution is allowed</returns>
private static bool IsThirdPartyToolsAllowed()
{
return ULoopSettings.GetAllowThirdPartyTools();
}

/// <summary>
/// Checks if a specific tool is allowed (considers both explicit setting and third party status)
/// </summary>
/// <param name="toolName">The name of the tool to check</param>
/// <returns>True if tool is allowed</returns>
private static bool IsThirdPartyToolAllowed(string toolName)
{
// If it's not a third party tool (i.e., official tool), allow it
if (!IsThirdPartyTool(toolName))
{
return true;
}

// If it's a third party tool, check the setting
return IsThirdPartyToolsAllowed();
}

/// <summary>
/// Checks if a tool is a third party tool based on its assembly
/// </summary>
/// <param name="toolName">The name of the tool to check</param>
/// <returns>True if tool is from a third party assembly</returns>
private static bool IsThirdPartyTool(string toolName)
{
// Get tool type from registry
var registry = CustomToolManager.GetRegistry();
if (registry == null)
{
return true; // Default to third party if registry unavailable
}

var toolType = registry.GetToolType(toolName);
if (toolType == null)
{
return true; // Default to third party if tool type not found
}

// Check assembly name
string assemblyName = toolType.Assembly.GetName().Name;

// Official uLoopMCP assembly
return assemblyName != "uLoopMCP.Editor";
}
}

/// <summary>
Expand Down
8 changes: 0 additions & 8 deletions Packages/src/Editor/UI/McpEditorModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,14 +209,6 @@ public void UpdateToolEnabled(string toolName, bool enabled)
ToolSettings.SetToolEnabled(toolName, enabled);
}

/// <summary>
/// Update AllowThirdPartyTools setting with persistence
/// </summary>
public void UpdateAllowThirdPartyTools(bool allow)
{
ULoopSettings.SetAllowThirdPartyTools(allow);
}

public void UpdateShowConfiguration(bool show)
{
UpdateUIState(ui => new UIState(
Expand Down
Loading
Loading