Files
prowler/tests/providers/m365/lib/arguments/m365_arguments_test.py
2025-10-09 12:24:24 +02:00

598 lines
22 KiB
Python

import argparse
from unittest.mock import MagicMock
from prowler.providers.m365.lib.arguments import arguments
class TestM365Arguments:
def setup_method(self):
"""Setup mock ArgumentParser for testing"""
self.mock_parser = MagicMock()
self.mock_subparsers = MagicMock()
self.mock_m365_parser = MagicMock()
self.mock_auth_group = MagicMock()
self.mock_auth_modes_group = MagicMock()
self.mock_regions_group = MagicMock()
# Setup the mock chain
self.mock_parser.add_subparsers.return_value = self.mock_subparsers
self.mock_subparsers.add_parser.return_value = self.mock_m365_parser
self.mock_m365_parser.add_argument_group.side_effect = [
self.mock_auth_group,
self.mock_regions_group,
]
self.mock_auth_group.add_mutually_exclusive_group.return_value = (
self.mock_auth_modes_group
)
def test_init_parser_creates_subparser(self):
"""Test that init_parser creates the M365 subparser correctly"""
# Create a mock object that has the necessary attributes
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
# Call init_parser
arguments.init_parser(mock_m365_args)
# Verify subparser was created
self.mock_subparsers.add_parser.assert_called_once_with(
"m365",
parents=[mock_m365_args.common_providers_parser],
help="M365 Provider",
)
def test_init_parser_creates_argument_groups(self):
"""Test that init_parser creates the correct argument groups"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Verify argument groups were created
assert self.mock_m365_parser.add_argument_group.call_count == 2
calls = self.mock_m365_parser.add_argument_group.call_args_list
assert calls[0][0][0] == "Authentication Modes"
assert calls[1][0][0] == "Regions"
def test_init_parser_creates_mutually_exclusive_auth_group(self):
"""Test that init_parser creates mutually exclusive authentication group"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Verify mutually exclusive group was created for authentication modes
self.mock_auth_group.add_mutually_exclusive_group.assert_called_once()
def test_init_parser_adds_authentication_arguments(self):
"""Test that init_parser adds all authentication arguments"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Verify authentication arguments were added to the mutually exclusive group
assert self.mock_auth_modes_group.add_argument.call_count == 5
# Check that all authentication arguments are present
calls = self.mock_auth_modes_group.add_argument.call_args_list
auth_args = [call[0][0] for call in calls]
assert "--az-cli-auth" in auth_args
assert "--env-auth" in auth_args
assert "--sp-env-auth" in auth_args
assert "--browser-auth" in auth_args
assert "--certificate-auth" in auth_args
def test_init_parser_adds_non_exclusive_arguments(self):
"""Test that init_parser adds non-exclusive arguments directly to parser"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Verify non-exclusive arguments were added to main parser
assert self.mock_m365_parser.add_argument.call_count == 3
# Check that non-exclusive arguments are present
calls = self.mock_m365_parser.add_argument.call_args_list
non_exclusive_args = [call[0][0] for call in calls]
assert "--tenant-id" in non_exclusive_args
assert "--init-modules" in non_exclusive_args
assert "--certificate-path" in non_exclusive_args
def test_init_parser_adds_region_arguments(self):
"""Test that init_parser adds region arguments"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Verify region arguments were added to regions group
assert self.mock_regions_group.add_argument.call_count == 1
# Check that region argument is present
calls = self.mock_regions_group.add_argument.call_args_list
region_args = [call[0][0] for call in calls]
assert "--region" in region_args
def test_az_cli_auth_argument_configuration(self):
"""Test that az-cli-auth argument is configured correctly"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Find the az-cli-auth argument call
calls = self.mock_auth_modes_group.add_argument.call_args_list
az_cli_call = None
for call in calls:
if call[0][0] == "--az-cli-auth":
az_cli_call = call
break
assert az_cli_call is not None
# Check argument configuration
kwargs = az_cli_call[1]
assert kwargs["action"] == "store_true"
assert "Azure CLI authentication" in kwargs["help"]
def test_sp_env_auth_argument_configuration(self):
"""Test that sp-env-auth argument is configured correctly"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Find the sp-env-auth argument call
calls = self.mock_auth_modes_group.add_argument.call_args_list
sp_env_call = None
for call in calls:
if call[0][0] == "--sp-env-auth":
sp_env_call = call
break
assert sp_env_call is not None
# Check argument configuration
kwargs = sp_env_call[1]
assert kwargs["action"] == "store_true"
assert "Service Principal environment variables" in kwargs["help"]
def test_browser_auth_argument_configuration(self):
"""Test that browser-auth argument is configured correctly"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Find the browser-auth argument call
calls = self.mock_auth_modes_group.add_argument.call_args_list
browser_auth_call = None
for call in calls:
if call[0][0] == "--browser-auth":
browser_auth_call = call
break
assert browser_auth_call is not None
# Check argument configuration
kwargs = browser_auth_call[1]
assert kwargs["action"] == "store_true"
assert "Azure interactive browser authentication" in kwargs["help"]
def test_certificate_auth_argument_configuration(self):
"""Test that certificate-auth argument is configured correctly"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Find the certificate-auth argument call
calls = self.mock_auth_modes_group.add_argument.call_args_list
cert_auth_call = None
for call in calls:
if call[0][0] == "--certificate-auth":
cert_auth_call = call
break
assert cert_auth_call is not None
# Check argument configuration
kwargs = cert_auth_call[1]
assert kwargs["action"] == "store_true"
assert "Certificate authentication" in kwargs["help"]
def test_tenant_id_argument_configuration(self):
"""Test that tenant-id argument is configured correctly"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Find the tenant-id argument call
calls = self.mock_m365_parser.add_argument.call_args_list
tenant_id_call = None
for call in calls:
if call[0][0] == "--tenant-id":
tenant_id_call = call
break
assert tenant_id_call is not None
# Check argument configuration
kwargs = tenant_id_call[1]
assert kwargs["nargs"] == "?"
assert kwargs["default"] is None
assert "Microsoft 365 Tenant ID" in kwargs["help"]
assert "--browser-auth" in kwargs["help"]
def test_init_modules_argument_configuration(self):
"""Test that init-modules argument is configured correctly"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Find the init-modules argument call
calls = self.mock_m365_parser.add_argument.call_args_list
init_modules_call = None
for call in calls:
if call[0][0] == "--init-modules":
init_modules_call = call
break
assert init_modules_call is not None
# Check argument configuration
kwargs = init_modules_call[1]
assert kwargs["action"] == "store_true"
assert "Initialize Microsoft 365 PowerShell modules" in kwargs["help"]
def test_region_argument_configuration(self):
"""Test that region argument is configured correctly"""
mock_m365_args = MagicMock()
mock_m365_args.subparsers = self.mock_subparsers
mock_m365_args.common_providers_parser = MagicMock()
arguments.init_parser(mock_m365_args)
# Find the region argument call
calls = self.mock_regions_group.add_argument.call_args_list
region_call = None
for call in calls:
if call[0][0] == "--region":
region_call = call
break
assert region_call is not None
# Check argument configuration
kwargs = region_call[1]
assert kwargs["nargs"] == "?"
assert kwargs["default"] == "M365Global"
assert kwargs["choices"] == [
"M365Global",
"M365GlobalChina",
"M365USGovernment",
]
assert "Microsoft 365 region" in kwargs["help"]
assert "M365Global" in kwargs["help"]
class TestM365ArgumentsIntegration:
def test_real_argument_parsing_az_cli_auth(self):
"""Test parsing arguments with Azure CLI authentication"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
# Create a mock object that mimics the structure used by the init_parser function
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with Azure CLI auth
args = parser.parse_args(["m365", "--az-cli-auth"])
assert args.az_cli_auth is True
assert not hasattr(args, "env_auth")
assert args.sp_env_auth is False
assert args.browser_auth is False
assert args.certificate_auth is False
def test_real_argument_parsing_sp_env_auth(self):
"""Test parsing arguments with service principal environment authentication"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with service principal environment auth
args = parser.parse_args(["m365", "--sp-env-auth"])
assert args.az_cli_auth is False
assert not hasattr(args, "env_auth")
assert args.sp_env_auth is True
assert args.browser_auth is False
assert args.certificate_auth is False
def test_real_argument_parsing_browser_auth_with_tenant_id(self):
"""Test parsing arguments with browser authentication and tenant ID"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with browser auth and tenant ID
args = parser.parse_args(
[
"m365",
"--browser-auth",
"--tenant-id",
"12345678-1234-5678-abcd-123456789012",
]
)
assert args.az_cli_auth is False
assert not hasattr(args, "env_auth")
assert args.sp_env_auth is False
assert args.browser_auth is True
assert args.certificate_auth is False
assert args.tenant_id == "12345678-1234-5678-abcd-123456789012"
def test_real_argument_parsing_certificate_auth(self):
"""Test parsing arguments with certificate authentication"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with certificate auth
args = parser.parse_args(["m365", "--certificate-auth"])
assert args.az_cli_auth is False
assert not hasattr(args, "env_auth")
assert args.sp_env_auth is False
assert args.browser_auth is False
assert args.certificate_auth is True
def test_real_argument_parsing_with_init_modules(self):
"""Test parsing arguments with init modules flag"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with init modules
args = parser.parse_args(["m365", "--az-cli-auth", "--init-modules"])
assert args.az_cli_auth is True
assert args.init_modules is True
def test_real_argument_parsing_with_different_regions(self):
"""Test parsing arguments with different region options"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Test M365Global (default)
args = parser.parse_args(["m365", "--az-cli-auth"])
assert args.region == "M365Global"
# Test M365GlobalChina
args = parser.parse_args(
["m365", "--az-cli-auth", "--region", "M365GlobalChina"]
)
assert args.region == "M365GlobalChina"
# Test M365USGovernment
args = parser.parse_args(
["m365", "--az-cli-auth", "--region", "M365USGovernment"]
)
assert args.region == "M365USGovernment"
def test_real_argument_parsing_no_authentication_defaults(self):
"""Test parsing arguments without any authentication flags (should have defaults)"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments without explicit auth (defaults should apply)
args = parser.parse_args(["m365"])
assert args.az_cli_auth is False
assert not hasattr(args, "env_auth")
assert args.sp_env_auth is False
assert args.browser_auth is False
assert args.certificate_auth is False
assert args.tenant_id is None
assert args.init_modules is False
assert args.region == "M365Global"
def test_real_argument_parsing_complete_configuration(self):
"""Test parsing arguments with all non-exclusive options"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with complete configuration
args = parser.parse_args(
[
"m365",
"--browser-auth",
"--tenant-id",
"12345678-1234-5678-abcd-123456789012",
"--init-modules",
"--region",
"M365USGovernment",
]
)
assert args.browser_auth is True
assert args.tenant_id == "12345678-1234-5678-abcd-123456789012"
assert args.init_modules is True
assert args.region == "M365USGovernment"
def test_mutually_exclusive_authentication_enforcement(self):
"""Test that authentication methods are mutually exclusive"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# This should raise SystemExit due to mutually exclusive group
try:
parser.parse_args(["m365", "--az-cli-auth", "--sp-env-auth"])
assert False, "Expected SystemExit due to mutually exclusive arguments"
except SystemExit:
# This is expected
pass
def test_tenant_id_without_arguments(self):
"""Test that tenant-id can be specified without an argument (optional value)"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with tenant-id but no value (should be None due to nargs="?")
args = parser.parse_args(["m365", "--az-cli-auth", "--tenant-id"])
assert args.tenant_id is None
def test_certificate_path_argument_configuration(self):
"""Test that certificate_path argument is properly configured"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with certificate-path
args = parser.parse_args(
["m365", "--certificate-auth", "--certificate-path", "/path/to/cert.pem"]
)
assert args.certificate_auth is True
assert args.certificate_path == "/path/to/cert.pem"
def test_certificate_path_without_value(self):
"""Test certificate_path argument without value (should be None due to nargs='?')"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse arguments with certificate-path but no value
args = parser.parse_args(["m365", "--certificate-auth", "--certificate-path"])
assert args.certificate_auth is True
assert args.certificate_path is None
def test_certificate_auth_with_certificate_path_integration(self):
"""Test certificate authentication with certificate path integration"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
common_parser = argparse.ArgumentParser(add_help=False)
mock_m365_args = MagicMock()
mock_m365_args.subparsers = subparsers
mock_m365_args.common_providers_parser = common_parser
arguments.init_parser(mock_m365_args)
# Parse complete certificate authentication arguments
args = parser.parse_args(
[
"m365",
"--certificate-auth",
"--certificate-path",
"/home/user/cert.pem",
"--tenant-id",
"12345678-1234-1234-1234-123456789012",
]
)
assert args.certificate_auth is True
assert args.certificate_path == "/home/user/cert.pem"
assert args.tenant_id == "12345678-1234-1234-1234-123456789012"
assert args.az_cli_auth is False
assert not hasattr(args, "env_auth")
assert args.sp_env_auth is False
assert args.browser_auth is False