improve(policy): default exec rules now match commands with or without args

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>
This commit is contained in:
github-actions[bot] 2026-05-01 12:55:47 +00:00 committed by GitHub
parent 29510a16eb
commit 3936294a8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 26 additions and 6 deletions

View File

@ -269,17 +269,19 @@ public class ExecApprovalPolicy
{
return new List<ExecApprovalRule>
{
// 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" },

View File

@ -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()