You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
{vm-repair} Fixes a command-injection vulnerability - #10443
This PR hardens nested Azure CLI execution in vm-repair without rejecting valid percent-encoded URIs.
On Windows, _call_az_command() now invokes the current Python interpreter with -m azure.cli and shell=False, instead of launching through cmd.exe and az.cmd. This prevents shell command execution and environment-variable expansion while preserving literal % and ! characters.
Existing command builders remain in place. Double-quote/control-character restrictions, secret masking, error reporting, and asynchronous behavior are preserved. Linux/macOS retain their existing launch path.
This checklist is used to make sure that common guidelines for a pull request are followed.
Related command
General Guidelines
Have you run azdev style <YOUR_EXT> locally? (pip install azdev required)
Have you run python scripts/ci/test_index.py -q locally? (pip install azdev required)
There is a pipeline to automatically build, upload and publish extension wheels.
Once your pull request is merged into main branch, a new pull request will be created to update src/index.json automatically.
You only need to update the version information in file setup.py and historical information in file HISTORY.rst in your PR but do not modify src/index.json.
On a Windows host, a password such as CorrectHorse7! passes validate_vm_password, but this new guard rejects it when _create_repair_vm() validates the VM template. By then, managed-disk create() has already copied the OS disk into the source resource group (custom.py:402–428). Failure cleanup deletes only the repair resource group (custom.py:591–594), leaving the copied disk behind and accruing charges. Keep this sink-level guard, but validate the credentials or preflight the VM-create command before creating resources. Add a regression test asserting that rejected credentials cause no resource mutations.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
The current head addresses the earlier Copilot reviewer findings: Windows validation rejects quotes, expansion syntax and control characters in the original execution tokens; errors omit rejected values; unmanaged restore preserves the complete URI assignment as one argument; and restore/NIC-revert validation precedes detach/IP swap.
Upstream CI has completed with no failed checks. The author-added src/vm-repair/azext_vm_repair/tests/latest/test_command_injection.py exercises the Windows sink with mocked process creation, masked-credential rejection, both restore branches, embedded-quote argument injection, and NIC rejection/happy paths. HISTORY.rst documents the Windows %/! compatibility restrictions and matches version 2.4.6. No unresolved changed-line finding was confirmed. Real Windows launcher behavior and live Azure disk/NIC transitions were not independently verified by the Agent.
Test validation
Agent live test: Not run; this is a review-only PR. Check author-provided test evidence and upstream CI separately.
Regression coverage: Not applicable to the changed files.
Risk assessment — 77/100 · Critical · High confidence
The Critical rating is driven by security-sensitive behavior, public CLI behavior, dependency or supply chain and 1 additional signal(s).
Change scope: 5 changed files, 551 changed lines (+490 / -61), including 3 production files.
Affected components:vm-repair
Risk drivers: security-sensitive behavior (+28); public CLI behavior (+18); dependency or supply chain (+18); failure-handling behavior (+12)
Regression evidence: Changed regression tests are included, reducing risk.
Confidence: High because changed-line patches were available for every production file.
Required review: Owning-squad review is required for vm-repair before merge.
[P1] Preserve interpreter isolation when bypassing the Windows launcher
Please add -I before -m azure.cli. On a standard Python installation, this child inherits import lookup from the working directory and PYTHONPATH, so a local azure/cli package can run instead of the installed CLI with the operator's permissions. I reproduced both routes through the production helper using a harmless print-only package; the same interpreter with -I loaded the genuine CLI successfully. The standard Windows launcher already uses -IBm.
Update the argv expectations and replace the PYTHONPATH-dependent fake-CLI receiver with coverage that verifies untrusted local packages cannot replace Azure CLI. Preserve the existing literal-argument and error-handling tests. This confirms the existing open isolation thread; it does not need a duplicate inline discussion.
Existing review comments: disposition
Topic
Current-head assessment
Shell expansion through percent signs and exclamation marks
Shell-free argument-list execution addresses the earlier cmd.exe expansion path; real-process literal-value tests pass on Windows.
Sensitive rejected tokens appearing in validation errors
The sink validates original execution tokens and omits their values from rejection errors; focused tests pass.
Managed/unmanaged restore validation after detachment
Preflight checks now precede detach; end-to-end mocked restore rejection tests pass. One older thread remains unresolved in GitHub, but the reported ordering problem is addressed in code.
The complete URI assignment is quoted as one token; rejection and single-token regression tests pass.
NIC revert validation after the IP swap
Revert-command validation now precedes the swap; rejection and happy-path tests pass.
Compatibility of percent-encoded URIs
Percent signs and exclamation marks are accepted literally; the percent-encoded restore test passes. The changelog describes the current shell-free behavior.
Missing interpreter isolation
Still open and independently reproduced; blocks approval.
The older engineering-agent report describes a prior head and is not evidence that this revision is ready to approve.
Validation
Validation used an isolated archive of the exact reviewed commit. The maintainer's dirty checkout was not changed. An existing development virtual environment supplied dependencies; no packages were installed.
Check
Result
Focused PR tests
PASS: 92 tests across the two changed regression-test modules, including real Windows subprocess tests.
All safe extension unit tests
PASS: 146 tests, 2 subtests; 3 warnings noted below. This total includes the focused tests, not an additional 146 distinct tests.
Independent launcher-isolation checks
FAIL: 2 regression assertions demonstrate working-directory and PYTHONPATH substitution; PASS: 1 isolated-interpreter control. These are reviewer-added checks, not failures in the author's existing suite.
Extension wheel build
PASS: version 2.4.6 wheel built locally. Setuptools emitted deprecation notices.
GitHub check runs
23 succeeded, 1 skipped. No failed or pending check runs were returned.
Legacy combined commit status
API returned pending with zero status contexts; this is distinct from the completed check-run results.
Local azdev style/linter
Not rerun; corresponding current-head CI checks succeeded.
License, credential scan, initialization files, extension index
Corresponding current-head CI checks succeeded. No independent local credential scan was claimed.
Breaking-change check
Current-head CI succeeded.
Live Azure integration tests
Not run. The live-only scenario module was explicitly excluded. No resource provisioning or disk/NIC changes were performed.
Test commands
From the isolated extension directory, with that directory on PYTHONPATH:
The test runner in VS Code did not discover the isolated files, so pytest was invoked directly using the existing repository virtual environment.
Warnings and limitations
Three existing returned-warning tests emitted an unraisable-exception warning because command_helper.__del__ accessed a missing is_verbose attribute. The relevant files are not changed by this PR; this is not counted as a new PR finding.
Full resource lifecycle behavior was not exercised. Restore and NIC ordering evidence comes from mocked unit tests.
Actual source-VM repair scripts and their catalog are unchanged. No cross-repository contract change was found.
Version 2.4.6 and its changelog entry are present. The extension index is unchanged.
No other high-confidence changed-line defect was confirmed. This is a scoped PR review, not a guarantee that the extension is free of vulnerabilities.
The reason will be displayed to describe this comment to others. Learn more.
[P2] Preserve the running CLI when its installation is in the user site
The -I change fixes the import-isolation issue, but it also disables user site-packages. I confirmed a compatibility regression with the actual helper under a controlled user-site-only visibility setup: the real CLI's normal version command succeeds, and the previously reviewed helper succeeds, while this revision's nested command fails with No module named azure.cli.
Please keep isolation, but bootstrap the trusted running CLI installation and its dependencies for the child. Add a user-site-only installation regression alongside the clean/working-directory/PYTHONPATH isolation cases. Do not simply remove -I. This confirms the existing open compatibility thread rather than introducing a separate duplicate issue.
Validation
All local checks used an isolated snapshot of the exact latest head and an existing development environment. The maintainer's dirty checkout was not modified. No packages were installed.
Current-head CI succeeded; not independently rerun locally.
Live Azure scenarios
Not run. The live-only scenario module was explicitly excluded.
Test coverage and limitations
The complete safe suite was run with the live-only scenario module excluded.
Current-head tests cover literal shell metacharacters, percent-encoded URIs, expansion-like values, secret masking, rejection before tokenization, restore preflight, NIC revert ordering, asynchronous invocation, and CLI error reporting.
Restore and NIC state-transition evidence is mocked; no live VM, disk, or NIC changes were performed.
The same three unraisable destructor warnings seen in the previous review remain in unchanged warning-return tests. They are not new PR findings.
The current head retains version 2.4.6 and updates its changelog for isolation and pre-tokenization validation.
No script-library catalog or run-id changes are involved. No new cross-repository dependency was found.
No other high-confidence delta defect was confirmed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 PR Validation — ️✔️ All clear
This PR hardens nested Azure CLI execution in vm-repair without rejecting valid percent-encoded URIs.
On Windows,
_call_az_command()now invokes the current Python interpreter with-m azure.cliandshell=False, instead of launching throughcmd.exeandaz.cmd. This prevents shell command execution and environment-variable expansion while preserving literal%and!characters.Existing command builders remain in place. Double-quote/control-character restrictions, secret masking, error reporting, and asynchronous behavior are preserved. Linux/macOS retain their existing launch path.
This checklist is used to make sure that common guidelines for a pull request are followed.
Related command
General Guidelines
azdev style <YOUR_EXT>locally? (pip install azdevrequired)python scripts/ci/test_index.py -qlocally? (pip install azdevrequired)For new extensions:
About Extension Publish
There is a pipeline to automatically build, upload and publish extension wheels.
Once your pull request is merged into main branch, a new pull request will be created to update
src/index.jsonautomatically.You only need to update the version information in file setup.py and historical information in file HISTORY.rst in your PR but do not modify
src/index.json.