mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 13:01:56 +00:00
feat(microsoft365): add new check admincenter_settings_password_never_expire (#6023)
Co-authored-by: MrCloudSec <hello@mistercloudsec.com>
This commit is contained in:
committed by
Pablo Lara
parent
335980c8d8
commit
43927a62f3
@@ -50,9 +50,7 @@ from prowler.providers.microsoft365.exceptions.exceptions import (
|
||||
Microsoft365TenantIdAndClientSecretNotBelongingToClientIdError,
|
||||
)
|
||||
from prowler.providers.microsoft365.lib.mutelist.mutelist import Microsoft365Mutelist
|
||||
from prowler.providers.microsoft365.lib.regions.microsoft365_regions import (
|
||||
get_regions_config,
|
||||
)
|
||||
from prowler.providers.microsoft365.lib.regions.regions import get_regions_config
|
||||
from prowler.providers.microsoft365.models import (
|
||||
Microsoft365IdentityInfo,
|
||||
Microsoft365RegionConfig,
|
||||
|
||||
@@ -22,11 +22,13 @@ class AdminCenter(Microsoft365Service):
|
||||
gather(
|
||||
self._get_directory_roles(),
|
||||
self._get_groups(),
|
||||
self._get_domains(),
|
||||
)
|
||||
)
|
||||
|
||||
self.directory_roles = attributes[0]
|
||||
self.groups = attributes[1]
|
||||
self.domains = attributes[2]
|
||||
|
||||
async def _get_users(self):
|
||||
logger.info("Microsoft365 - Getting users...")
|
||||
@@ -130,6 +132,28 @@ class AdminCenter(Microsoft365Service):
|
||||
)
|
||||
return groups
|
||||
|
||||
async def _get_domains(self):
|
||||
logger.info("Microsoft365 - Getting domains...")
|
||||
domains = {}
|
||||
try:
|
||||
domains_list = await self.client.domains.get()
|
||||
domains.update({})
|
||||
for domain in domains_list.value:
|
||||
domains.update(
|
||||
{
|
||||
domain.id: Domain(
|
||||
id=domain.id,
|
||||
password_validity_period=domain.password_validity_period_in_days,
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as error:
|
||||
logger.error(
|
||||
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
|
||||
)
|
||||
return domains
|
||||
|
||||
|
||||
class User(BaseModel):
|
||||
id: str
|
||||
@@ -149,3 +173,8 @@ class Group(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
visibility: str
|
||||
|
||||
|
||||
class Domain(BaseModel):
|
||||
id: str
|
||||
password_validity_period: int
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"Provider": "microsoft",
|
||||
"CheckID": "admincenter_settings_password_never_expire",
|
||||
"CheckTitle": "Ensure the 'Password expiration policy' is set to 'Set passwords to never expire (recommended)'",
|
||||
"CheckType": [],
|
||||
"ServiceName": "admincenter",
|
||||
"SubServiceName": "",
|
||||
"ResourceIdTemplate": "",
|
||||
"Severity": "medium",
|
||||
"ResourceType": "Microsoft365Domain",
|
||||
"Description": "This control ensures that the password expiration policy is set to 'Set passwords to never expire (recommended)'. This aligns with modern recommendations to enhance security by avoiding arbitrary password changes and focusing on supplementary controls like MFA.",
|
||||
"Risk": "Arbitrary password expiration policies can lead to weaker passwords due to frequent changes. Users may adopt insecure habits such as using simple, memorable passwords.",
|
||||
"RelatedUrl": "https://www.cisecurity.org/insights/white-papers/cis-password-policy-guide",
|
||||
"Remediation": {
|
||||
"Code": {
|
||||
"CLI": "Set-MsolUser -UserPrincipalName <user> -PasswordNeverExpires $true",
|
||||
"NativeIaC": "",
|
||||
"Other": "",
|
||||
"Terraform": ""
|
||||
},
|
||||
"Recommendation": {
|
||||
"Text": "Enable the 'Never Expire Passwords' option in Microsoft 365 Admin Center.",
|
||||
"Url": "https://learn.microsoft.com/en-us/microsoft-365/admin/misc/password-policy-recommendations?view=o365-worldwide"
|
||||
}
|
||||
},
|
||||
"Categories": [],
|
||||
"DependsOn": [],
|
||||
"RelatedTo": [],
|
||||
"Notes": ""
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
from prowler.lib.check.models import Check, Check_Report_Microsoft365
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_client import (
|
||||
admincenter_client,
|
||||
)
|
||||
|
||||
|
||||
class admincenter_settings_password_never_expire(Check):
|
||||
def execute(self) -> Check_Report_Microsoft365:
|
||||
findings = []
|
||||
for domain in admincenter_client.domains.values():
|
||||
report = Check_Report_Microsoft365(self.metadata(), resource=domain)
|
||||
report.resource_id = domain.id
|
||||
report.resource_name = domain.id
|
||||
report.status = "FAIL"
|
||||
report.status_extended = (
|
||||
f"Domain {domain.id} does not have a Password never expires policy."
|
||||
)
|
||||
|
||||
if domain.password_validity_period == 2147483647:
|
||||
report.status = "PASS"
|
||||
report.status_extended = (
|
||||
f"Domain {domain.id} Password policy is set to never expire."
|
||||
)
|
||||
|
||||
findings.append(report)
|
||||
|
||||
return findings
|
||||
+1
-3
@@ -9,9 +9,7 @@ class admincenter_users_between_two_and_four_global_admins(Check):
|
||||
findings = []
|
||||
|
||||
directory_roles = admincenter_client.directory_roles
|
||||
report = Check_Report_Microsoft365(
|
||||
metadata=self.metadata(), resource=admincenter_client.directory_roles
|
||||
)
|
||||
report = Check_Report_Microsoft365(metadata=self.metadata(), resource={})
|
||||
report.status = "FAIL"
|
||||
report.resource_name = "Global Administrator"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from azure.identity import AzureAuthorityHosts
|
||||
|
||||
from prowler.providers.microsoft365.lib.regions.microsoft365_regions import (
|
||||
from prowler.providers.microsoft365.lib.regions.regions import (
|
||||
MICROSOFT365_CHINA_CLOUD,
|
||||
MICROSOFT365_GENERIC_CLOUD,
|
||||
MICROSOFT365_US_GOV_CLOUD,
|
||||
|
||||
@@ -29,7 +29,6 @@ def set_mocked_microsoft365_provider(
|
||||
audit_config: dict = None,
|
||||
azure_region_config: Microsoft365RegionConfig = Microsoft365RegionConfig(),
|
||||
) -> Microsoft365Provider:
|
||||
|
||||
provider = MagicMock()
|
||||
provider.type = "microsoft365"
|
||||
provider.session.credentials = credentials
|
||||
|
||||
@@ -212,7 +212,6 @@ class TestMicrosoft365Provider:
|
||||
"prowler.providers.microsoft365.microsoft365_provider.GraphServiceClient"
|
||||
) as mock_graph_client,
|
||||
):
|
||||
|
||||
# Mock the return value of DefaultAzureCredential
|
||||
mock_credentials = MagicMock()
|
||||
mock_credentials.get_token.return_value = AccessToken(
|
||||
@@ -271,7 +270,6 @@ class TestMicrosoft365Provider:
|
||||
with patch(
|
||||
"prowler.providers.microsoft365.microsoft365_provider.Microsoft365Provider.setup_session"
|
||||
) as mock_setup_session:
|
||||
|
||||
mock_setup_session.side_effect = Microsoft365HTTPResponseError(
|
||||
file="test_file", original_exception="Simulated HttpResponseError"
|
||||
)
|
||||
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
from unittest import mock
|
||||
from uuid import uuid4
|
||||
|
||||
from tests.providers.microsoft365.microsoft365_fixtures import (
|
||||
DOMAIN,
|
||||
set_mocked_microsoft365_provider,
|
||||
)
|
||||
|
||||
|
||||
class Test_admincenter_settings_password_never_expire:
|
||||
def test_admincenter_no_domains(self):
|
||||
admincenter_client = mock.MagicMock
|
||||
admincenter_client.audited_tenant = "audited_tenant"
|
||||
admincenter_client.audited_domain = DOMAIN
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.admincenter.admincenter_settings_password_never_expire.admincenter_settings_password_never_expire.admincenter_client",
|
||||
new=admincenter_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_settings_password_never_expire.admincenter_settings_password_never_expire import (
|
||||
admincenter_settings_password_never_expire,
|
||||
)
|
||||
|
||||
admincenter_client.domains = {}
|
||||
|
||||
check = admincenter_settings_password_never_expire()
|
||||
result = check.execute()
|
||||
assert len(result) == 0
|
||||
|
||||
def test_admincenter_domain_password_expire(self):
|
||||
admincenter_client = mock.MagicMock
|
||||
admincenter_client.audited_tenant = "audited_tenant"
|
||||
admincenter_client.audited_domain = DOMAIN
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.admincenter.admincenter_settings_password_never_expire.admincenter_settings_password_never_expire.admincenter_client",
|
||||
new=admincenter_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_service import (
|
||||
Domain,
|
||||
)
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_settings_password_never_expire.admincenter_settings_password_never_expire import (
|
||||
admincenter_settings_password_never_expire,
|
||||
)
|
||||
|
||||
id_domain = str(uuid4())
|
||||
|
||||
admincenter_client.domains = {
|
||||
id_domain: Domain(id=id_domain, password_validity_period=5),
|
||||
}
|
||||
|
||||
check = admincenter_settings_password_never_expire()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "FAIL"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Domain {id_domain} does not have a Password never expires policy."
|
||||
)
|
||||
assert result[0].resource_name == id_domain
|
||||
assert result[0].resource_id == id_domain
|
||||
|
||||
def test_admincenter_password_not_expire(self):
|
||||
admincenter_client = mock.MagicMock
|
||||
admincenter_client.audited_tenant = "audited_tenant"
|
||||
admincenter_client.audited_domain = DOMAIN
|
||||
|
||||
with (
|
||||
mock.patch(
|
||||
"prowler.providers.common.provider.Provider.get_global_provider",
|
||||
return_value=set_mocked_microsoft365_provider(),
|
||||
),
|
||||
mock.patch(
|
||||
"prowler.providers.microsoft365.services.admincenter.admincenter_settings_password_never_expire.admincenter_settings_password_never_expire.admincenter_client",
|
||||
new=admincenter_client,
|
||||
),
|
||||
):
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_service import (
|
||||
Domain,
|
||||
)
|
||||
from prowler.providers.microsoft365.services.admincenter.admincenter_settings_password_never_expire.admincenter_settings_password_never_expire import (
|
||||
admincenter_settings_password_never_expire,
|
||||
)
|
||||
|
||||
id_domain = str(uuid4())
|
||||
|
||||
admincenter_client.domains = {
|
||||
id_domain: Domain(id=id_domain, password_validity_period=2147483647),
|
||||
}
|
||||
|
||||
check = admincenter_settings_password_never_expire()
|
||||
result = check.execute()
|
||||
assert len(result) == 1
|
||||
assert result[0].status == "PASS"
|
||||
assert (
|
||||
result[0].status_extended
|
||||
== f"Domain {id_domain} Password policy is set to never expire."
|
||||
)
|
||||
assert result[0].resource_name == id_domain
|
||||
assert result[0].resource_id == id_domain
|
||||
Reference in New Issue
Block a user