Skip to content

Kusto: expand cluster hostname allow-list to match public SDK endpoint list#2431

Open
gholliday wants to merge 1 commit intomicrosoft:mainfrom
gholliday:fix/kusto-ade-proxy-hostnames
Open

Kusto: expand cluster hostname allow-list to match public SDK endpoint list#2431
gholliday wants to merge 1 commit intomicrosoft:mainfrom
gholliday:fix/kusto-ade-proxy-hostnames

Conversation

@gholliday
Copy link
Copy Markdown
Member

@gholliday gholliday commented Apr 17, 2026

What does this PR do?

Expands the Kusto SSRF hostname allow-list in KustoClient to match the authoritative public SDK endpoint list at wellKnownKustoEndpoints.json (Azure Kusto Python SDK, MIT; mirrored in azure-kusto-go).

Without these entries, connections to Azure Data Explorer clusters in several documented endpoints were rejected with ArgumentException from ValidateAndNormalizeClusterUri before any request was sent, even though the cluster hostnames are publicly documented and the SDKs themselves trust them.

Hostnames / suffixes added (all public-documented):

  • Sovereign clouds
    • EagleX / USSec: .kusto.core.eaglex.ic.gov, .kustomfa.core.eaglex.ic.gov, .arg.core.eaglex.ic.gov, plus exact hosts adx.applicationinsights.azure.eaglex.ic.gov, adx.loganalytics.azure.eaglex.ic.gov, adx.monitor.azure.eaglex.ic.gov
    • SCloud / USNat: .kusto.core.microsoft.scloud, .kustomfa.core.microsoft.scloud, .arg.core.microsoft.scloud, plus the corresponding ADE proxy exact hosts
    • France: .kusto.francecentral.cloudapi.de, .kustomfa.francecentral.cloudapi.de
    • Singapore: .kusto.singaporecloud.azure.cn, .kustomfa.singaporecloud.azure.cn
  • Public cloud endpoints
    • *.kusto.aria.microsoft.com (Azure Resource Graph)
    • *.playfab.com (PlayFab Insights)
    • api.securityplatform.microsoft.com (Security Platform)
    • *.int.kustodev.windows.net, plus adx.int.applicationinsights.io, adx.int.loganalytics.io, adx.int.monitor.azure.com (INT ring)

Not changed: GetKustoScope continues to fall back to the public-cloud Kusto scope for any non-China / non-USGov hosts. The new sovereign hostnames will therefore still need separate scope-mapping work to be fully usable end-to-end — that's intentionally out of scope here (would need air-gapped test infra); this PR just stops the allow-list from being the first and earliest failure.

Regression tests

Beyond the expanded [InlineData] coverage for Constructor_AcceptsValidKustoClusterUris and Constructor_AcceptsValidKustoExactHostnames, two new [Fact] tests use a CapturingHandler : HttpMessageHandler to record the outgoing request.RequestUri and assert that ADE-proxy-style cluster URLs (e.g. https://ade.applicationinsights.io/subscriptions/<id>/resourcegroups/<rg>/providers/Microsoft.OperationalInsights/workspaces/<ws>) have their full resource path preserved in the outgoing POST /v1/rest/query URL — for both ExecuteCommandAsync and ExecuteControlCommandAsync.

GitHub issue number?

No existing issue. I searched microsoft/mcp for InvalidClusterHostName, ADE proxy, sovereign cloud, and kusto hostname terms — the only marginally-related report (#527) was closed not planned / needs-author-feedback and is about a different failure mode (az command fallback). Happy to file one if maintainers prefer.

Pre-merge Checklist

  • Read contribution guidelines
  • PR title clearly describes the change
  • Commit history is clean with descriptive messages (single commit)
  • Added comprehensive tests for new/modified functionality (208/208 pass via ./eng/scripts/Test-Code.ps1 -Paths Kusto; 25 new InlineData cases + 2 new regression [Fact] tests)
  • Created a changelog entry — servers/Azure.Mcp.Server/changelog-entries/gholliday-kusto-ade-proxy-hostnames.yaml (Bugs Fixed section); validated via ./eng/scripts/Compile-Changelog.ps1 -DryRun

The MCP tool changes section of the checklist does not apply — this PR does not add, rename, or modify any tool; it only expands an internal SSRF allow-list inside an existing service class.

…t list

The SSRF allow-list in KustoClient was missing several hostnames/suffixes that are documented in the authoritative public Kusto endpoint list at https://github.com/Azure/azure-kusto-python/blob/master/azure-kusto-data/azure/kusto/data/wellKnownKustoEndpoints.json (mirrored in azure-kusto-go).

Without these entries, connections to Azure Data Explorer clusters in the following documented endpoints were rejected with ArgumentException before any request was sent:

- Sovereign clouds: EagleX/USSec (.kusto.core.eaglex.ic.gov, adx.applicationinsights.azure.eaglex.ic.gov, etc.), SCloud/USNat (.kusto.core.microsoft.scloud, etc.), France (.kusto.francecentral.cloudapi.de), Singapore (.kusto.singaporecloud.azure.cn)

- Public-cloud endpoints: *.kusto.aria.microsoft.com (Azure Resource Graph), *.playfab.com (PlayFab Insights), api.securityplatform.microsoft.com (Security Platform), *.int.kustodev.windows.net (INT)

Also adds regression tests that capture the outgoing HTTP request URI via an HttpMessageHandler to verify that the full resource path (e.g. subscriptions/<id>/resourceGroups/<rg>/providers/.../workspaces/<ws>) is preserved end-to-end for ADE proxy URLs in both ExecuteCommandAsync and ExecuteControlCommandAsync.

Does not alter GetKustoScope; new sovereign cloud endpoints continue to fall back to the public-cloud Kusto scope (out of scope for this change).
@github-actions
Copy link
Copy Markdown

Thank you for your contribution @gholliday! We will review the pull request and get back to you soon.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Expands the Kusto cluster endpoint allow-list (used for SSRF protection/validation) to include additional well-known endpoints (including several sovereign clouds and proxy hostnames), and adds unit tests to validate acceptance plus ADE proxy path preservation in outgoing request URIs.

Changes:

  • Extended the allowed Kusto domain suffixes and exact hostnames to cover additional endpoints (sovereign clouds + other published endpoints).
  • Updated unit tests to cover the expanded allow-list and ensure ADE proxy URLs retain their full resource path when building request URIs.
  • Added a server changelog entry describing the allow-list expansion.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
tools/Azure.Mcp.Tools.Kusto/src/Services/KustoClient.cs Expands the hard-coded allow-list of valid Kusto endpoint hostnames/suffixes and updates inline documentation about the source lists.
tools/Azure.Mcp.Tools.Kusto/tests/Azure.Mcp.Tools.Kusto.UnitTests/KustoClientTests.cs Adds test vectors for the new allow-list entries and adds ADE proxy request-URI preservation assertions via a capturing HTTP handler.
servers/Azure.Mcp.Server/changelog-entries/gholliday-kusto-ade-proxy-hostnames.yaml Adds release notes entry for the allow-list expansion.
Comment on lines +15 to +18
// Valid Kusto cluster domain suffixes from official Kusto endpoints configuration.
// Source (authoritative, MIT-licensed): AllowedKustoSuffixes in
// https://github.com/Azure/azure-kusto-python/blob/master/azure-kusto-data/azure/kusto/data/wellKnownKustoEndpoints.json
// Mirror: https://github.com/Azure/azure-kusto-go/blob/master/azkustodata/trusted_endpoints/well_known_kusto_endpoints.json
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description references changes to ClusterUtilities.NormalizeClusterUrl (and new ClusterUtilitiesTests.cs / KustoHttpServiceTests.cs with LastRequestUri capture), but those types/files don’t exist anywhere in this repo based on search. The only ADE proxy path-preservation coverage added here is in KustoClientTests. Please either update the PR description to match the actual implementation, or include the missing commits/files if they were intended to be part of this PR.

Copilot uses AI. Check for mistakes.
changes:
- section: "Bugs Fixed"
description: |
Expanded the Kusto cluster hostname allow-list to match the authoritative public SDK endpoint list. Previously, connections to Azure Data Explorer clusters in several documented sovereign clouds (EagleX/USSec, SCloud/USNat, France, Singapore), along with Azure Resource Graph (`*.kusto.aria.microsoft.com`), PlayFab Insights (`*.playfab.com`), and Security Platform (`api.securityplatform.microsoft.com`) endpoints, were rejected with an `ArgumentException` before the request was sent. The allow-list now matches the [`wellKnownKustoEndpoints.json`](https://github.com/Azure/azure-kusto-python/blob/master/azure-kusto-data/azure/kusto/data/wellKnownKustoEndpoints.json) file from the public Azure Kusto Python SDK (MIT).
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The changelog entry claims Azure Resource Graph endpoints are *.kusto.aria.microsoft.com, but the code’s allow-list does not permit arbitrary subdomains of kusto.aria.microsoft.com (only exact kusto.aria.microsoft.com / eu.kusto.aria.microsoft.com are allowed). Also, the newly-added ARG-related suffix in this PR is *.arg.core.*, which isn’t mentioned here. Please update the description to reflect the actual allowed hosts/suffixes introduced by this change so the release notes are accurate.

Suggested change
Expanded the Kusto cluster hostname allow-list to match the authoritative public SDK endpoint list. Previously, connections to Azure Data Explorer clusters in several documented sovereign clouds (EagleX/USSec, SCloud/USNat, France, Singapore), along with Azure Resource Graph (`*.kusto.aria.microsoft.com`), PlayFab Insights (`*.playfab.com`), and Security Platform (`api.securityplatform.microsoft.com`) endpoints, were rejected with an `ArgumentException` before the request was sent. The allow-list now matches the [`wellKnownKustoEndpoints.json`](https://github.com/Azure/azure-kusto-python/blob/master/azure-kusto-data/azure/kusto/data/wellKnownKustoEndpoints.json) file from the public Azure Kusto Python SDK (MIT).
Expanded the Kusto cluster hostname allow-list to match the authoritative public SDK endpoint list. Previously, connections to Azure Data Explorer clusters in several documented sovereign clouds (EagleX/USSec, SCloud/USNat, France, Singapore), along with Azure Resource Graph (`kusto.aria.microsoft.com`, `eu.kusto.aria.microsoft.com`, and `*.arg.core.*`), PlayFab Insights (`*.playfab.com`), and Security Platform (`api.securityplatform.microsoft.com`) endpoints, were rejected with an `ArgumentException` before the request was sent. The allow-list now matches the [`wellKnownKustoEndpoints.json`](https://github.com/Azure/azure-kusto-python/blob/master/azure-kusto-data/azure/kusto/data/wellKnownKustoEndpoints.json) file from the public Azure Kusto Python SDK (MIT).
Copilot uses AI. Check for mistakes.
#endregion

[Fact]
public async Task ExecuteCommandAsync_WithAdeProxyUrl_PreservesFullResourcePathInOutgoingUri()
Copy link

Copilot AI Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name says ExecuteCommandAsync_... but the test is actually exercising ExecuteQueryCommandAsync. Renaming the test to match the public method under test will make failures easier to interpret and keep the suite consistent.

Suggested change
public async Task ExecuteCommandAsync_WithAdeProxyUrl_PreservesFullResourcePathInOutgoingUri()
public async Task ExecuteQueryCommandAsync_WithAdeProxyUrl_PreservesFullResourcePathInOutgoingUri()
Copilot uses AI. Check for mistakes.
changes:
- section: "Bugs Fixed"
description: |
Expanded the Kusto cluster hostname allow-list to match the authoritative public SDK endpoint list. Previously, connections to Azure Data Explorer clusters in several documented sovereign clouds (EagleX/USSec, SCloud/USNat, France, Singapore), along with Azure Resource Graph (`*.kusto.aria.microsoft.com`), PlayFab Insights (`*.playfab.com`), and Security Platform (`api.securityplatform.microsoft.com`) endpoints, were rejected with an `ArgumentException` before the request was sent. The allow-list now matches the [`wellKnownKustoEndpoints.json`](https://github.com/Azure/azure-kusto-python/blob/master/azure-kusto-data/azure/kusto/data/wellKnownKustoEndpoints.json) file from the public Azure Kusto Python SDK (MIT).
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This changelog reads as a complete fix, but GetKustoScope() in KustoClient.cs still only maps .chinacloudapi.cn / .azure.cn to China and .usgovcloudapi.net / .azure.us to US Gov. Everything else falls back to the public-cloud Kusto scope. So clusters in EagleX (.ic.gov), SCloud (.scloud), and sovcloud-{fr,de,sg} (plus .azure.{fr,de,sg}) will now pass this allow-list but still fail at token acquisition with a mismatched audience.

You call this out in the PR body as intentionally out of scope, but the changelog loses that nuance. Worth adding a sentence so release-notes readers don't assume those clouds work end-to-end after this change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment