-
Notifications
You must be signed in to change notification settings - Fork 347
Disable external process commands in HTTP + On-Behalf-Of mode #1522
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR implements a security enhancement to disable external process commands (specifically azqr) when the MCP server is running in HTTP mode with On-Behalf-Of authentication. This prevents security risks where spawned processes would execute under the server's host identity rather than the user's context.
Changes:
- Added
IsHttpOnBehalfOfMode()helper method toServiceStartOptionsto detect HTTP + OBO mode - Implemented conditional registration logic in
ExtensionSetupto excludeAzqrCommandin HTTP + OBO scenarios - Updated changelog to document the security enhancement
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| core/Azure.Mcp.Core/src/Areas/Server/Options/ServiceStartOptions.cs | Adds helper method to detect HTTP + On-Behalf-Of mode |
| tools/Azure.Mcp.Tools.Extension/src/ExtensionSetup.cs | Implements conditional registration to exclude azqr command in HTTP + OBO mode |
| servers/Azure.Mcp.Server/changelog-entries/1768876346795.yaml | Documents the security feature addition |
| public bool IsHttpOnBehalfOfMode() | ||
| { | ||
| return Transport == TransportTypes.Http | ||
| && OutgoingAuthStrategy == OutgoingAuthStrategy.UseOnBehalfOf; | ||
| } |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new method IsHttpOnBehalfOfMode() and the conditional registration logic in ExtensionSetup lack test coverage. Consider adding unit tests to verify:
IsHttpOnBehalfOfMode()returnstruewhen Transport is Http and OutgoingAuthStrategy is UseOnBehalfOfIsHttpOnBehalfOfMode()returnsfalsefor other combinations (stdio transport, different auth strategies, etc.)ShouldExposeExternalProcessCommands()correctly excludes azqr command registration when in HTTP + OBO modeShouldExposeExternalProcessCommands()allows azqr command registration when ServiceStartOptions is null or not in HTTP + OBO mode
These tests would help ensure the security fix works as intended and prevent regressions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like some good things to practice with having GitHub Copilot CLI or MCP server tools within VS Code follow.
| /// Determines whether the server is running in HTTP mode with On-Behalf-Of authentication. | ||
| /// </summary> | ||
| /// <returns>True if running in HTTP transport with On-Behalf-Of authentication; false otherwise.</returns> | ||
| public bool IsHttpOnBehalfOfMode() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Subjective style: this can be a property which looks a subjectively cleaner with c# syntax.
[JsonIgnore]
public bool IsHttpOnBehalfOfMode => Transport == TransportTypes.Http && OutgoingAuthStrategy == OutgoingAuthStrategy.UseOnBehalfOf;
At the end of the day, both are creating parameterless methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks - yes, that’s actually what Copilot suggested. I was thinking it could be a method since we’re "computing" a boolean (coming from a Java mindset), but let’s go with a property given that’s more idiomatic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| public bool IsHttpOnBehalfOfMode() | ||
| { | ||
| return Transport == TransportTypes.Http | ||
| && OutgoingAuthStrategy == OutgoingAuthStrategy.UseOnBehalfOf; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like some good things to practice with having GitHub Copilot CLI or MCP server tools within VS Code follow.
| var extension = new CommandGroup(Name, "Extension commands for additional Azure tooling functionality. Includes running Azure Quick Review (azqr) commands directly from the MCP server to get service recommendations, generating Azure CLI commands from user intent, and getting installation instructions for Azure CLI, Azure Developer CLI and Azure Core Function Tools CLI.", Title); | ||
|
|
||
| // Azure CLI and Azure Developer CLI tools are hidden | ||
| // extension.AddCommand("az", new AzCommand(loggerFactory.CreateLogger<AzCommand>())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend removing this comment OR putting it into the new if block body related to external process commands. If it returns, I suspect we'd want it guarded by the same logic.
| { | ||
| ServiceStartOptions? startOptions = serviceProvider.GetService<ServiceStartOptions>(); | ||
|
|
||
| if (startOptions is null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want some modern C# null variable checking syntactic sugar:
if (serviceProvider.GetService<ServiceStartOptions>() is ServiceStartOptions startOptions)
{
return !startOptions.IsHttpOnBehalfOfMode();
}
return true/whateverDefault;
Totally optional. Current code is fine. Just teaching.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks, will use type pattern matching here
|
|
||
| public CommandGroup RegisterCommands(IServiceProvider serviceProvider) | ||
| { | ||
| var extension = new CommandGroup(Name, "Extension commands for additional Azure tooling functionality. Includes running Azure Quick Review (azqr) commands directly from the MCP server to get service recommendations, generating Azure CLI commands from user intent, and getting installation instructions for Azure CLI, Azure Developer CLI and Azure Core Function Tools CLI.", Title); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will this message need to be updated to exclude azqr if we don't add it below?
|
|
||
| if (startOptions is null) | ||
| { | ||
| // First container (CLI routing) - startOptions not available, allow all commands |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First container
Now, I know what this means, but I doubt anyone else would. Please reference the Program.cs files in the servers folder where the multiple containers are detailed. Ugly for now, but referencing that design complexity in a cleaner way is out of scope for this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point - yes, the multi-container setup is pretty confusing :). Let me link to the entry point / program.cs where that is happening.

What does this PR do?
External process commands (like azqr) use
IExternalProcessServiceto spawn local processes. In HTTP + OBO mode, this is a security risk: processes run under the server's host identity (not the OBO user's context), and malicious requests could exhaust server resources.IsHttpOnBehalfOfMode()method toServiceStartOptionsAzqrCommandregistration inExtensionSetupFixes #1521
[Any additional context, screenshots, or information that helps reviewers]GitHub issue number?
[Link to the GitHub issue this PR addresses]Pre-merge Checklist
servers/Azure.Mcp.Server/CHANGELOG.mdand/orservers/Fabric.Mcp.Server/CHANGELOG.mdfor product changes (features, bug fixes, UI/UX, updated dependencies)servers/Azure.Mcp.Server/README.mdand/orservers/Fabric.Mcp.Server/README.mddocumentationeng/scripts/Process-PackageReadMe.ps1. See Package README/servers/Azure.Mcp.Server/docs/azmcp-commands.mdand/or/docs/fabric-commands.md.\eng\scripts\Update-AzCommandsMetadata.ps1to update tool metadata in azmcp-commands.md (required for CI)ToolDescriptionEvaluatorand obtained a score of0.4or more and a top 3 ranking for all related test promptsconsolidated-tools.json/servers/Azure.Mcp.Server/docs/e2eTestPrompts.mdcrypto mining, spam, data exfiltration, etc.)/azp run mcp - pullrequest - liveto run Live Test Pipeline