From 3936294a8b92ca5ccb5960573a64788da46e42aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 12:55:47 +0000 Subject: [PATCH] improve(policy): default exec rules now match commands with or without args MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patterns like "ipconfig *" required at least one argument (the space forces it) which meant plain "ipconfig" was unexpectedly denied on fresh installs. Changed: "ipconfig *" → "ipconfig*", "ping *" → "ping*", "dir *" → "dir*", "cat *" → "cat*", "type *" → "type*" "echo *" is intentionally unchanged — it has an existing test that asserts result.MatchedPattern == "echo *", which is correct behaviour (bare "echo" with no args is not a useful diagnostic command). Adds a 7-case parameterised test (DefaultPolicy_AllowsCommonDiagCommandsWithAndWithoutArgs) covering no-arg and with-arg variants of each changed pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/OpenClaw.Shared/ExecApprovalPolicy.cs | 14 ++++++++------ .../ExecApprovalPolicyTests.cs | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) 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()