mirror of
https://github.com/microsoft/mcp.git
synced 2026-11-04 12:01:31 +00:00
Allow Azure Resource Graph Kusto endpoints in Kusto cluster URI validation (#4029)
* Allow Azure Resource Graph Kusto endpoints in Kusto cluster URI validation Add .arg.core.windows.net to KustoClient's domain suffix allow-list so the Kusto tools can query Azure Resource Graph Kusto endpoints. The Kusto SDKs' well-known endpoints already trust this suffix, and KustoClient already keeps the endpoint path and requests the Kusto scope these endpoints use. Fixes #4028 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: df5f4d96-6fea-431c-bc49-01af1461746b * Make the KustoClient request test endpoint-agnostic Rename the test and use neutral database and query arguments so it doesn't suggest that Azure Resource Graph endpoints get special handling. Any cluster URI that passes the allow-list works; this one includes a path to show it's preserved. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: df5f4d96-6fea-431c-bc49-01af1461746b --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: df5f4d96-6fea-431c-bc49-01af1461746b
This commit is contained in:
1 parent
c3e9ec1093
commit
150473f1fc
3 files changed
+34
No files matched your search
@@ -0,0 +1,3 @@
|
||||
changes:
|
||||
- section: "Bugs Fixed"
|
||||
description: "Fixed Kusto tools rejecting Azure Resource Graph Kusto endpoints (`*.arg.core.windows.net`), which the Kusto SDKs already trust."
|
||||
@@ -26,6 +26,7 @@ public sealed class KustoClient(
|
||||
".kustomfa.windows.net",
|
||||
".kusto.data.microsoft.com",
|
||||
".kusto.fabric.microsoft.com",
|
||||
".arg.core.windows.net", // Azure Resource Graph Kusto endpoints: https://github.com/microsoft/mcp/issues/4028
|
||||
".adx.loganalytics.azure.com",
|
||||
".adx.applicationinsights.azure.com",
|
||||
".adx.monitor.azure.com",
|
||||
|
||||
@@ -47,6 +47,27 @@ public sealed class KustoClientTests
|
||||
Assert.NotNull(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteQueryCommandAsync_KeepsPathAndUsesKustoScope()
|
||||
{
|
||||
// Arrange - any cluster URI that passes the allow-list works here; this one includes a path to show it's preserved
|
||||
const string clusterUri = "https://mycluster.arg.core.windows.net/providers/Microsoft.ResourceGraph/connector/kusto/sample";
|
||||
var handler = new MockHttpMessageHandler();
|
||||
using var httpClient = new HttpClient(handler);
|
||||
_azureService.GetClient(Arg.Any<string>()).Returns(httpClient);
|
||||
var kustoClient = new KustoClient(clusterUri, _tokenCredential, "azmcp", _azureService);
|
||||
|
||||
// Act
|
||||
await kustoClient.ExecuteQueryCommandAsync("testdb", "test query", TestContext.Current.CancellationToken);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(new Uri($"{clusterUri}/v1/rest/query"), handler.LastRequestUri);
|
||||
await _tokenCredential.Received(1).GetTokenAsync(
|
||||
Arg.Is<TokenRequestContext>(context =>
|
||||
context.Scopes.Length == 1 && context.Scopes[0] == "https://kusto.kusto.windows.net/.default"),
|
||||
Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
#region SSRF Protection Tests
|
||||
|
||||
[Theory]
|
||||
@@ -57,6 +78,8 @@ public sealed class KustoClientTests
|
||||
[InlineData("https://test.kusto.windows.net.example.com/path")]
|
||||
[InlineData("https://169.254.169.254/metadata/instance")] // Azure IMDS
|
||||
[InlineData("https://management.azure.com/subscriptions")]
|
||||
[InlineData("https://arg.core.windows.net")] // Resource Graph suffix without an endpoint name
|
||||
[InlineData("https://mycluster.arg.core.windows.net.example.com")]
|
||||
public void Constructor_RejectsInvalidClusterUris(string invalidClusterUri)
|
||||
{
|
||||
// Act & Assert
|
||||
@@ -109,6 +132,9 @@ public sealed class KustoClientTests
|
||||
[InlineData("https://mycluster.adx.applicationinsights.azure.com")]
|
||||
// Germany
|
||||
[InlineData("https://mycluster.kusto.sovcloud-api.de")]
|
||||
// Azure Resource Graph
|
||||
[InlineData("https://mycluster.arg.core.windows.net")]
|
||||
[InlineData("https://mycluster.arg.core.windows.net/providers/Microsoft.ResourceGraph/connector/kusto/sample")]
|
||||
public void Constructor_AcceptsValidKustoClusterUris(string validClusterUri)
|
||||
{
|
||||
// Act - should not throw
|
||||
@@ -198,8 +224,12 @@ public sealed class KustoClientTests
|
||||
|
||||
private sealed class MockHttpMessageHandler : HttpMessageHandler
|
||||
{
|
||||
public Uri? LastRequestUri { get; private set; }
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
LastRequestUri = request.RequestUri;
|
||||
|
||||
// The caller (HttpClient.SendAsync) takes ownership and is responsible for disposal HttpResponseMessage.
|
||||
var response = new HttpResponseMessage(HttpStatusCode.OK)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user