|
17 | 17 | (get_file_json, truncate_text, shell_safe_json_parse, b64_to_hex, hash_string, random_string, |
18 | 18 | open_page_in_browser, can_launch_browser, handle_exception, ConfiguredDefaultSetter, send_raw_request, |
19 | 19 | should_disable_connection_verify, parse_proxy_resource_id, get_az_user_agent, get_az_rest_user_agent, |
20 | | - _get_parent_proc_name, is_wsl, run_cmd, run_az_cmd) |
| 20 | + _get_parent_proc_name, is_wsl, run_cmd, run_az_cmd, roughly_parse_command, roughly_parse_command_with_casing) |
21 | 21 | from azure.cli.core.mock import DummyCli |
22 | 22 |
|
23 | 23 |
|
@@ -612,6 +612,83 @@ def _get_mock_HttpOperationError(response_text): |
612 | 612 |
|
613 | 613 | return mock_http_error |
614 | 614 |
|
| 615 | + def test_roughly_parse_command(self): |
| 616 | + """Test roughly_parse_command function that extracts command parts and converts to lowercase""" |
| 617 | + # Basic command parsing |
| 618 | + self.assertEqual(roughly_parse_command(['az', 'vm', 'create']), 'az vm create') |
| 619 | + self.assertEqual(roughly_parse_command(['account', 'show']), 'account show') |
| 620 | + self.assertEqual(roughly_parse_command(['network', 'vnet', 'list']), 'network vnet list') |
| 621 | + |
| 622 | + # Test case conversion - should convert to lowercase |
| 623 | + self.assertEqual(roughly_parse_command(['az', 'VM', 'CREATE']), 'az vm create') |
| 624 | + self.assertEqual(roughly_parse_command(['Account', 'Show']), 'account show') |
| 625 | + |
| 626 | + # Test with flags - should stop at first flag and not include flag values |
| 627 | + self.assertEqual(roughly_parse_command(['az', 'vm', 'create', '--name', 'secretVM']), 'az vm create') |
| 628 | + self.assertEqual(roughly_parse_command(['az', 'storage', 'account', 'create', '--name', 'mystorageaccount']), 'az storage account create') |
| 629 | + self.assertEqual(roughly_parse_command(['az', 'keyvault', 'create', '--resource-group', 'myRG', '--name', 'myVault']), 'az keyvault create') |
| 630 | + |
| 631 | + # Test with short flags |
| 632 | + self.assertEqual(roughly_parse_command(['az', 'vm', 'list', '-g', 'myResourceGroup']), 'az vm list') |
| 633 | + self.assertEqual(roughly_parse_command(['az', 'group', 'create', '-n', 'myGroup', '-l', 'eastus']), 'az group create') |
| 634 | + |
| 635 | + # Edge cases |
| 636 | + self.assertEqual(roughly_parse_command([]), '') |
| 637 | + self.assertEqual(roughly_parse_command(['az']), 'az') |
| 638 | + self.assertEqual(roughly_parse_command(['--help']), '') # Starts with flag |
| 639 | + self.assertEqual(roughly_parse_command(['-h']), '') # Starts with short flag |
| 640 | + |
| 641 | + def test_roughly_parse_command_with_casing(self): |
| 642 | + """Test roughly_parse_command_with_casing function that preserves original casing""" |
| 643 | + # Basic command parsing with case preservation |
| 644 | + self.assertEqual(roughly_parse_command_with_casing(['az', 'vm', 'create']), 'az vm create') |
| 645 | + self.assertEqual(roughly_parse_command_with_casing(['account', 'show']), 'account show') |
| 646 | + self.assertEqual(roughly_parse_command_with_casing(['network', 'vnet', 'list']), 'network vnet list') |
| 647 | + |
| 648 | + # Test case preservation - should keep original casing |
| 649 | + self.assertEqual(roughly_parse_command_with_casing(['az', 'VM', 'CREATE']), 'az VM CREATE') |
| 650 | + self.assertEqual(roughly_parse_command_with_casing(['Account', 'Show']), 'Account Show') |
| 651 | + self.assertEqual(roughly_parse_command_with_casing(['Az', 'Network', 'Vnet', 'List']), 'Az Network Vnet List') |
| 652 | + |
| 653 | + # Test with flags - should stop at first flag and not include sensitive flag values |
| 654 | + self.assertEqual(roughly_parse_command_with_casing(['az', 'vm', 'create', '--name', 'secretVM']), 'az vm create') |
| 655 | + self.assertEqual(roughly_parse_command_with_casing(['az', 'VM', 'Create', '--name', 'superSecretVM']), 'az VM Create') |
| 656 | + self.assertEqual(roughly_parse_command_with_casing(['az', 'storage', 'account', 'create', '--name', 'mystorageaccount']), 'az storage account create') |
| 657 | + self.assertEqual(roughly_parse_command_with_casing(['az', 'keyvault', 'create', '--resource-group', 'myRG', '--name', 'myVault']), 'az keyvault create') |
| 658 | + |
| 659 | + # Test with short flags |
| 660 | + self.assertEqual(roughly_parse_command_with_casing(['az', 'VM', 'list', '-g', 'myResourceGroup']), 'az VM list') |
| 661 | + self.assertEqual(roughly_parse_command_with_casing(['az', 'Group', 'create', '-n', 'myGroup', '-l', 'eastus']), 'az Group create') |
| 662 | + |
| 663 | + # Test mixed case scenarios that might reveal user typing patterns |
| 664 | + self.assertEqual(roughly_parse_command_with_casing(['Az', 'Vm', 'Create']), 'Az Vm Create') |
| 665 | + self.assertEqual(roughly_parse_command_with_casing(['AZ', 'STORAGE', 'BLOB', 'LIST']), 'AZ STORAGE BLOB LIST') |
| 666 | + |
| 667 | + # Edge cases |
| 668 | + self.assertEqual(roughly_parse_command_with_casing([]), '') |
| 669 | + self.assertEqual(roughly_parse_command_with_casing(['az']), 'az') |
| 670 | + self.assertEqual(roughly_parse_command_with_casing(['Az']), 'Az') |
| 671 | + self.assertEqual(roughly_parse_command_with_casing(['--help']), '') # Starts with flag |
| 672 | + self.assertEqual(roughly_parse_command_with_casing(['-h']), '') # Starts with short flag |
| 673 | + |
| 674 | + # Security test - ensure no sensitive information leaks after flags |
| 675 | + test_cases_with_secrets = [ |
| 676 | + (['az', 'vm', 'create', '--admin-password', 'SuperSecret123!'], 'az vm create'), |
| 677 | + (['az', 'sql', 'server', 'create', '--admin-user', 'admin', '--admin-password', 'VerySecret!'], 'az sql server create'), |
| 678 | + (['az', 'storage', 'account', 'create', '--name', 'storageacct', '--access-tier', 'Hot'], 'az storage account create'), |
| 679 | + (['Az', 'KeyVault', 'Secret', 'Set', '--vault-name', 'myVault', '--name', 'secretName', '--value', 'topSecret'], 'Az KeyVault Secret Set') |
| 680 | + ] |
| 681 | + |
| 682 | + for args, expected in test_cases_with_secrets: |
| 683 | + with self.subTest(args=args): |
| 684 | + result = roughly_parse_command_with_casing(args) |
| 685 | + self.assertEqual(result, expected) |
| 686 | + # Ensure no sensitive values made it through |
| 687 | + self.assertNotIn('SuperSecret123!', result) |
| 688 | + self.assertNotIn('VerySecret!', result) |
| 689 | + self.assertNotIn('topSecret', result) |
| 690 | + self.assertNotIn('storageacct', result) # Even non-secret values after flags should not appear |
| 691 | + |
615 | 692 |
|
616 | 693 | if __name__ == '__main__': |
617 | 694 | unittest.main() |
0 commit comments