Repository navigation
Validate IDs before they are passed to az commands - #524
Krishnadheeraj (DheerajPannala) merged 4 commits into
Conversation
Reject non-GUID agent and service principal IDs from config and Graph responses before interpolating them into az command lines, and refuse cmd.exe-wrapped command lines with unquoted shell metacharacters. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ca54af2-675b-403e-ae16-72b6ba110c95
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ca54af2-675b-403e-ae16-72b6ba110c95
Dependency Review✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.Scanned FilesNone |
There was a problem hiding this comment.
🟡 Changes recommended
A critical validation bypass lets cmd.exe percent expansion expose command separators in accepted untrusted arguments, enabling command injection.
1 open issue
What changed in this PR
This PR hardens the Agent365 CLI’s Azure CLI calls by validating agent and service principal IDs and checking command lines before Windows shell execution.
Changes:
- Add GUID validation during configuration loading, cleanup, Entra queries, and consent operations.
- Reject unquoted shell metacharacters, unbalanced quotes, and line breaks in Windows command wrappers.
- Add regression tests, update test fixtures to use GUIDs, and document the change.
| File | Description |
|---|---|
src/Tests/Microsoft.Agents.A365.DevTools.Cli.Tests/Services/Agent365ConfigServiceTests.cs |
Tests rejection of non-GUID agent IDs. |
src/Tests/Microsoft.Agents.A365.DevTools.Cli.Tests/Services/AdminConsentHelperTests.cs |
Uses realistic GUID fixtures without changing assertions. |
src/Tests/Microsoft.Agents.A365.DevTools.Cli.Tests/Helpers/CommandArgumentGuardTests.cs |
Tests GUID normalization and command-line checks. |
src/Tests/Microsoft.Agents.A365.DevTools.Cli.Tests/Commands/CleanupCommandTests.cs |
Tests invalid user-ID rejection and updates fixtures. |
src/Microsoft.Agents.A365.DevTools.Cli/Services/Helpers/AdminConsentHelper.cs |
Validates app and service principal IDs for consent lookups. |
src/Microsoft.Agents.A365.DevTools.Cli/Services/ConfigService.cs |
Validates agent user and blueprint IDs. |
src/Microsoft.Agents.A365.DevTools.Cli/Services/CommandExecutor.cs |
Applies checks to both Windows execution paths. |
src/Microsoft.Agents.A365.DevTools.Cli/Helpers/CommandArgumentGuard.cs |
Adds shared GUID and shell-command guards. |
src/Microsoft.Agents.A365.DevTools.Cli/Commands/SetupSubcommands/AzRestConsentRunner.cs |
Rejects invalid resource service principal IDs. |
src/Microsoft.Agents.A365.DevTools.Cli/Commands/QueryEntraCommand.cs |
Validates IDs before Entra lookup calls. |
src/Microsoft.Agents.A365.DevTools.Cli/Commands/CleanupCommand.cs |
Validates and normalizes IDs before deletion calls. |
CHANGELOG.md |
Records ID validation under Unreleased fixes. |
Review effort: Balanced
Rick Brighenti (rbrighenti)
left a comment
There was a problem hiding this comment.
Reviewed the ID validation and command-executor changes. No blocking findings.
…sion cmd.exe expands %VAR% before it parses quotes, so expansion can shift quote boundaries after validation. Reject '%' outright, run cmd.exe with /v:off so '!' is never expanded, stop URL-encoding the one az rest filter that produced '%', and validate the grant id used in the consent PATCH URL. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ca54af2-675b-403e-ae16-72b6ba110c95
- Add consent runner tests for non-GUID resource SP ids, unsafe grant ids, and base64url grant ids. - Run the Windows batch-file tests via a relative script path so profile paths with spaces and NoDefaultCurrentDirectoryInExePath do not matter. - Balance the quotes in the percent-expansion payload so only the '%' check can refuse it. - Add a windows-latest CI job for the cmd.exe wrapper tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7ca54af2-675b-403e-ae16-72b6ba110c95
There was a problem hiding this comment.
🔵 Needs a closer look
Human assessment is needed because new identifier and consent-type validation paths lack regression coverage, leaving rejection behavior and downstream-call safeguards unverified.
No open issues 🎉
4 resolved since last review
Review effort: Balanced


Summary
Agent and service principal IDs are now validated as GUIDs before the CLI passes them to Azure CLI commands, and the command executor refuses
cmd.exe-wrapped command lines that contain unquoted shell metacharacters.Why
On Windows,
azis run throughcmd.exe /c, so every value placed on that command line has to be a plain token. Some IDs that reachazwere read froma365.generated.config.jsonor from Microsoft Graph responses without checking their format.Changes
agenticUserIdandagentBlueprintIdmust be GUIDs when present (agenticAppIdwas already checked).cleanup/cleanup instance:agenticAppIdandagenticUserIdare checked again right beforeaz ad app delete/az ad user delete.query-entra: the agent identity ID, the service principal object ID, and each grant's resource ID must be GUIDs before they are used in anazcall. A non-GUID config value exits with code 1. A non-GUID resource ID skips the display-name lookup.CommandExecutor: when a command is wrapped incmd.exe /c, the command line is rejected if it contains& | < > ^outside double quotes, unbalanced quotes, or line breaks. Quoted values such as--url "...&$select=id"are unchanged.Behavior change
None for configs written by the CLI. A hand-edited config with a non-GUID agent ID now fails to load with a validation error instead of being passed to
az.Testing
CommandArgumentGuardTests: rejected command lines, plus the realazcommand shapes the CLI builds (quoted&in URLs, URL-encoded filters, JMESPath--query,--body @"file") to guard against false positives.agenticUserId/agentBlueprintId, and acleanup instancetest confirming a non-GUIDagenticUserIdexits 1 without callingaz.AdminConsentHelperTestsand theCleanupCommandTestsfixture now use GUIDs for IDs that are GUIDs in real Graph responses and configs; assertions are unchanged.