diff --git a/docs/review/findings.json b/docs/review/findings.json index d783826..df95a91 100644 --- a/docs/review/findings.json +++ b/docs/review/findings.json @@ -1,15 +1,15 @@ { "_schema_version": "1.0", "_description": "PSProxmoxVE stable findings ledger. IDs are permanent (F001, F002...). Resolved findings are never deleted — they are marked resolved with evidence. If a finding reappears, it is marked regressed and retains its original ID.", - "last_updated": "2026-03-26", + "last_updated": "2026-05-22", "last_scan_date": "2026-03-26", "counters": { - "next_id": 86, + "next_id": 92, "total_open": 7, - "total_resolved": 77, + "total_resolved": 83, "total_regressed": 0, "open": 7, - "resolved": 77, + "resolved": 83, "regressed": 0, "wont_fix": 1 }, diff --git a/src/PSProxmoxVE.Core/Services/VmService.cs b/src/PSProxmoxVE.Core/Services/VmService.cs index 8e1d5e6..5b7764a 100644 --- a/src/PSProxmoxVE.Core/Services/VmService.cs +++ b/src/PSProxmoxVE.Core/Services/VmService.cs @@ -610,7 +610,11 @@ namespace PSProxmoxVE.Core.Services if (args != null) { foreach (var arg in args) + { + if (arg == null) + throw new ArgumentException("Args elements must not be null.", nameof(args)); data.Add(new KeyValuePair("command", arg)); + } } var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/agent/exec", data) diff --git a/tests/PSProxmoxVE.Core.Tests/Services/VmServiceTests.cs b/tests/PSProxmoxVE.Core.Tests/Services/VmServiceTests.cs index 74826e5..136b064 100644 --- a/tests/PSProxmoxVE.Core.Tests/Services/VmServiceTests.cs +++ b/tests/PSProxmoxVE.Core.Tests/Services/VmServiceTests.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using Moq; @@ -77,5 +78,36 @@ namespace PSProxmoxVE.Core.Tests.Services Assert.Equal("command", only.Key); Assert.Equal("whoami", only.Value); } + + [Fact] + public void ExecuteGuestCommand_EmptyArgs_SendsSingleCommandEntry() + { + List>? captured = null; + var mockClient = new Mock(); + mockClient + .Setup(c => c.PostAsync(It.IsAny(), It.IsAny>>())) + .Callback>>((_, data) => captured = data.ToList()) + .ReturnsAsync("{\"data\":{\"pid\":9}}"); + + var service = new VmService(mockClient.Object); + service.ExecuteGuestCommand(CreateSession(), TestNode, TestVmId, "whoami", new string[0]); + + Assert.NotNull(captured); + var only = Assert.Single(captured!); + Assert.Equal("command", only.Key); + Assert.Equal("whoami", only.Value); + } + + [Fact] + public void ExecuteGuestCommand_NullArgElement_ThrowsArgumentException() + { + var mockClient = new Mock(); + var service = new VmService(mockClient.Object); + + var ex = Assert.Throws(() => + service.ExecuteGuestCommand(CreateSession(), TestNode, TestVmId, + "cmd.exe", new[] { "/c", null!, "echo" })); + Assert.Equal("args", ex.ParamName); + } } }