fix(security): add Uri.EscapeDataString to all URL path parameters

20 instances across 5 service files were missing URL encoding on
dynamic path segments (node, iface, zone, vnet). Fixes D003 regression.

- NodeService: 7 methods
- NetworkService: 7 methods (5 node-network + 2 SDN remove)
- CloudInitService: 3 methods
- TaskService: 2 methods (GetTask, GetTaskLog)
- TemplateService: 1 method (CreateTemplate)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Clint Branham
2026-03-23 17:40:36 -05:00
parent 625ea0fa28
commit 053fd4c06a
11 changed files with 20 additions and 1437 deletions
@@ -50,7 +50,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{node}/qemu/{vmid}/config")
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/config")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
if (data == null) return new PveCloudInitConfig();
@@ -95,7 +95,7 @@ namespace PSProxmoxVE.Core.Services
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{node}/qemu/{vmid}/config", formData)
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/config", formData)
.GetAwaiter().GetResult();
}
finally
@@ -121,7 +121,7 @@ namespace PSProxmoxVE.Core.Services
try
{
// Request a Cloud-Init dump (user-data section) — this causes PVE to rebuild the image
var dumpResponse = client.GetAsync($"nodes/{node}/qemu/{vmid}/cloudinit/dump?type=user")
var dumpResponse = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/cloudinit/dump?type=user")
.GetAwaiter().GetResult();
var data = JObject.Parse(dumpResponse)["data"];
return data?.ToString() ?? string.Empty;
@@ -44,7 +44,7 @@ namespace PSProxmoxVE.Core.Services
if (session == null) throw new ArgumentNullException(nameof(session));
if (string.IsNullOrWhiteSpace(node)) throw new ArgumentNullException(nameof(node));
var resource = $"nodes/{node}/network";
var resource = $"nodes/{Uri.EscapeDataString(node)}/network";
if (!string.IsNullOrEmpty(type))
resource += $"?type={Uri.EscapeDataString(type!)}";
@@ -83,7 +83,7 @@ namespace PSProxmoxVE.Core.Services
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
var response = client.PostAsync($"nodes/{node}/network", formData)
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/network", formData)
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNetwork>() ?? new PveNetwork();
@@ -119,7 +119,7 @@ namespace PSProxmoxVE.Core.Services
var formData = config.ToDictionary(
kvp => kvp.Key,
kvp => kvp.Value?.ToString() ?? string.Empty);
client.PutAsync($"nodes/{node}/network/{iface}", formData)
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/network/{Uri.EscapeDataString(iface)}", formData)
.GetAwaiter().GetResult();
}
finally
@@ -144,7 +144,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"nodes/{node}/network/{iface}").GetAwaiter().GetResult();
client.DeleteAsync($"nodes/{Uri.EscapeDataString(node)}/network/{Uri.EscapeDataString(iface)}").GetAwaiter().GetResult();
}
finally
{
@@ -165,7 +165,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PutAsync($"nodes/{node}/network")
var response = client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/network")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}
@@ -263,7 +263,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/sdn/zones/{zone}").GetAwaiter().GetResult();
client.DeleteAsync($"cluster/sdn/zones/{Uri.EscapeDataString(zone)}").GetAwaiter().GetResult();
}
finally
{
@@ -313,7 +313,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.DeleteAsync($"cluster/sdn/vnets/{vnet}").GetAwaiter().GetResult();
client.DeleteAsync($"cluster/sdn/vnets/{Uri.EscapeDataString(vnet)}").GetAwaiter().GetResult();
}
finally
{
+7 -7
View File
@@ -63,7 +63,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{node}/status").GetAwaiter().GetResult();
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/status").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveNodeStatus>() ?? new PveNodeStatus();
}
@@ -86,7 +86,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{node}/config").GetAwaiter().GetResult();
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/config").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data as JObject ?? new JObject();
}
@@ -111,7 +111,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"nodes/{node}/config", config).GetAwaiter().GetResult();
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/config", config).GetAwaiter().GetResult();
}
finally
{
@@ -132,7 +132,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.GetAsync($"nodes/{node}/dns").GetAwaiter().GetResult();
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/dns").GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data as JObject ?? new JObject();
}
@@ -157,7 +157,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
client.PutAsync($"nodes/{node}/dns", config).GetAwaiter().GetResult();
client.PutAsync($"nodes/{Uri.EscapeDataString(node)}/dns", config).GetAwaiter().GetResult();
}
finally
{
@@ -180,7 +180,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{node}/startall", formData).GetAwaiter().GetResult();
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/startall", formData).GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
@@ -204,7 +204,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{node}/stopall", formData).GetAwaiter().GetResult();
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/stopall", formData).GetAwaiter().GetResult();
return ParseTask(response, node);
}
finally
+2 -2
View File
@@ -43,7 +43,7 @@ namespace PSProxmoxVE.Core.Services
try
{
var encodedUpid = Uri.EscapeDataString(upid);
var response = client.GetAsync($"nodes/{node}/tasks/{encodedUpid}/status")
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/tasks/{encodedUpid}/status")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
var task = data?.ToObject<PveTask>() ?? new PveTask { Upid = upid };
@@ -69,7 +69,7 @@ namespace PSProxmoxVE.Core.Services
try
{
var encodedUpid = Uri.EscapeDataString(upid);
var response = client.GetAsync($"nodes/{node}/tasks/{encodedUpid}/log")
var response = client.GetAsync($"nodes/{Uri.EscapeDataString(node)}/tasks/{encodedUpid}/log")
.GetAwaiter().GetResult();
var data = JObject.Parse(response)["data"];
return data?.ToObject<PveTaskLog[]>() ?? Array.Empty<PveTaskLog>();
@@ -61,7 +61,7 @@ namespace PSProxmoxVE.Core.Services
IPveHttpClient client = _injectedClient ?? new PveHttpClient(session);
try
{
var response = client.PostAsync($"nodes/{node}/qemu/{vmid}/template")
var response = client.PostAsync($"nodes/{Uri.EscapeDataString(node)}/qemu/{vmid}/template")
.GetAwaiter().GetResult();
return ParseTask(response, node);
}