diff --git a/src/OpenClaw.Shared/ExecApprovalPolicy.cs b/src/OpenClaw.Shared/ExecApprovalPolicy.cs index 6f08273..b5e96e1 100644 --- a/src/OpenClaw.Shared/ExecApprovalPolicy.cs +++ b/src/OpenClaw.Shared/ExecApprovalPolicy.cs @@ -269,17 +269,19 @@ public class ExecApprovalPolicy { return new List { - // Allow common read-only / diagnostic commands + // Allow common read-only / diagnostic commands. + // Patterns use stem* (no space before wildcard) so that the command + // also matches when invoked without arguments (e.g. "ipconfig" alone). new() { Pattern = "echo *", Action = ExecApprovalAction.Allow, Description = "Echo commands" }, new() { Pattern = "Get-*", Action = ExecApprovalAction.Allow, Shells = new[] { "powershell", "pwsh" }, Description = "PowerShell Get- cmdlets (read-only)" }, - new() { Pattern = "dir *", Action = ExecApprovalAction.Allow, Description = "Directory listing" }, + new() { Pattern = "dir*", Action = ExecApprovalAction.Allow, Description = "Directory listing" }, new() { Pattern = "hostname", Action = ExecApprovalAction.Allow, Description = "Hostname query" }, new() { Pattern = "whoami", Action = ExecApprovalAction.Allow, Description = "Current user" }, new() { Pattern = "systeminfo", Action = ExecApprovalAction.Allow, Description = "System info" }, - new() { Pattern = "ipconfig *", Action = ExecApprovalAction.Allow, Description = "Network config" }, - new() { Pattern = "ping *", Action = ExecApprovalAction.Allow, Description = "Ping" }, - new() { Pattern = "type *", Action = ExecApprovalAction.Allow, Shells = new[] { "cmd" }, Description = "Read file (cmd)" }, - new() { Pattern = "cat *", Action = ExecApprovalAction.Allow, Description = "Read file" }, + new() { Pattern = "ipconfig*", Action = ExecApprovalAction.Allow, Description = "Network config" }, + new() { Pattern = "ping*", Action = ExecApprovalAction.Allow, Description = "Ping" }, + new() { Pattern = "type*", Action = ExecApprovalAction.Allow, Shells = new[] { "cmd" }, Description = "Read file (cmd)" }, + new() { Pattern = "cat*", Action = ExecApprovalAction.Allow, Description = "Read file" }, // Deny dangerous patterns explicitly new() { Pattern = "Remove-Item *", Action = ExecApprovalAction.Deny, Description = "Block file deletion" }, diff --git a/tests/OpenClaw.Shared.Tests/ExecApprovalPolicyTests.cs b/tests/OpenClaw.Shared.Tests/ExecApprovalPolicyTests.cs index 85613f4..abc8370 100644 --- a/tests/OpenClaw.Shared.Tests/ExecApprovalPolicyTests.cs +++ b/tests/OpenClaw.Shared.Tests/ExecApprovalPolicyTests.cs @@ -90,6 +90,24 @@ public class ExecApprovalPolicyTests : IDisposable var result = policy.Evaluate("whoami"); Assert.True(result.Allowed); } + + [Theory] + [InlineData("ipconfig")] + [InlineData("ipconfig /all")] + [InlineData("ping 8.8.8.8")] + [InlineData("dir")] + [InlineData("dir C:\\")] + [InlineData("cat README.md")] + [InlineData("type README.md")] + public void DefaultPolicy_AllowsCommonDiagCommandsWithAndWithoutArgs(string command) + { + // Patterns like "ipconfig*" (no space before wildcard) must match both + // the no-arg invocation ("ipconfig") and the with-args form ("ipconfig /all"). + var policy = CreatePolicy(); + var shell = command.StartsWith("type") ? "cmd" : null; + var result = shell != null ? policy.Evaluate(command, shell) : policy.Evaluate(command); + Assert.True(result.Allowed, $"Expected '{command}' to be allowed by default policy"); + } [Fact] public void CustomRules_FirstMatchWins()