Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(organization): add new check organization_members_mfa_required #6304

Open
wants to merge 2 commits into
base: PRWLR-5749-create-organization-service
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"Provider": "github",
"CheckID": "organization_members_mfa_required",
"CheckTitle": "Check if organization members are required to have MFA enabled.",
"CheckType": [],
"ServiceName": "organization",
"SubServiceName": "",
"ResourceIdTemplate": "",
"Severity": "critical",
"ResourceType": "Other",
"Description": "Ensure that all organization members are required to have multi-factor authentication (MFA) enabled. Enforcing MFA for all organization members helps protect the organization's resources and data from unauthorized access and security breaches.",
"Risk": "Without Multi-Factor Authentication (MFA), user accounts are vulnerable to unauthorized access if their passwords are compromised. This can lead to unauthorized actions such as data theft, malicious code commits, and repository manipulation, potentially compromising the organization's source code and intellectual property.",
"RelatedUrl": "https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-two-factor-authentication-for-your-organization/requiring-two-factor-authentication-in-your-organization",
"Remediation": {
"Code": {
"CLI": "",
"NativeIaC": "",
"Other": "https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-two-factor-authentication-for-your-organization",
"Terraform": ""
},
"Recommendation": {
"Text": "Mandate the use of MFA for all organization members. This significantly enhances account security by adding an additional layer of protection beyond a username and password. MFA ensures that even if a password is compromised, unauthorized access to user accounts and repositories is prevented, safeguarding sensitive data and critical assets.",
"Url": "https://docs.github.com/en/organizations/keeping-your-organization-secure/managing-two-factor-authentication-for-your-organization/preparing-to-require-two-factor-authentication-in-your-organization"
}
},
"Categories": [],
"DependsOn": [],
"RelatedTo": [],
"Notes": ""
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from typing import List

from prowler.lib.check.models import Check, Check_Report_Github
from prowler.providers.github.services.organization.organization_client import (
organization_client,
)


class organization_members_mfa_required(Check):
"""Check if organization members are required to have two-factor authentication enabled.

This class verifies whether each organization requires its members to have two-factor authentication enabled.
"""

def execute(self) -> List[Check_Report_Github]:
"""Execute the Github Organization Members MFA Required check.

Iterates over all organizations and checks if members are required to have two-factor authentication enabled.

Returns:
List[Check_Report_Github]: A list of reports for each repository
"""
findings = []
for org in organization_client.organizations.values():
report = Check_Report_Github(self.metadata())
report.resource_id = org.id
report.resource_name = org.name
report.status = "FAIL"
report.status_extended = f"Organization {org.name} does not require members to have two-factor authentication enabled."

if org.mfa_required:
report.status = "PASS"
report.status_extended = f"Organization {org.name} does require members to have two-factor authentication enabled."

findings.append(report)

return findings
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,17 @@ def _list_organizations(self):
try:
for client in self.clients:
for org in client.get_user().get_orgs():
try:
require_mfa = org.two_factor_requirement_enabled is not None
except Exception as error:
require_mfa = False
logger.error(
f"{error.__class__.__name__}[{error.__traceback__.tb_lineno}]: {error}"
)
organizations[org.id] = Org(
id=org.id,
name=org.login,
mfa_required=require_mfa,
)
except Exception as error:
logger.error(
Expand All @@ -31,3 +39,4 @@ class Org(BaseModel):

id: int
name: str
mfa_required: bool = False
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
from unittest import mock

from prowler.providers.github.services.organization.organization_service import Org
from tests.providers.github.github_fixtures import set_mocked_github_provider


class Test_organization_members_mfa_required:
def test_no_organizations(self):
organization_client = mock.MagicMock
organization_client.organizations = {}

with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_github_provider(),
), mock.patch(
"prowler.providers.github.services.organization.organization_members_mfa_required.organization_members_mfa_required.organization_client",
new=organization_client,
):
from prowler.providers.github.services.organization.organization_members_mfa_required.organization_members_mfa_required import (
organization_members_mfa_required,
)

check = organization_members_mfa_required()
result = check.execute()
assert len(result) == 0

def test_organization_mfa_disabled(self):
organization_client = mock.MagicMock
org_name = "test-organization"
organization_client.organizations = {
1: Org(
id=1,
name=org_name,
mfa_required=False,
),
}

with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_github_provider(),
), mock.patch(
"prowler.providers.github.services.organization.organization_members_mfa_required.organization_members_mfa_required.organization_client",
new=organization_client,
):
from prowler.providers.github.services.organization.organization_members_mfa_required.organization_members_mfa_required import (
organization_members_mfa_required,
)

check = organization_members_mfa_required()
result = check.execute()
assert len(result) == 1
assert result[0].resource_id == 1
assert result[0].resource_name == "test-organization"
assert result[0].status == "FAIL"
assert (
result[0].status_extended
== f"Organization {org_name} does not require members to have two-factor authentication enabled."
)

def test_one_organization_securitymd(self):
organization_client = mock.MagicMock
org_name = "test-organization"
organization_client.organizations = {
1: Org(
id=1,
name=org_name,
mfa_required=True,
),
}

with mock.patch(
"prowler.providers.common.provider.Provider.get_global_provider",
return_value=set_mocked_github_provider(),
), mock.patch(
"prowler.providers.github.services.organization.organization_members_mfa_required.organization_members_mfa_required.organization_client",
new=organization_client,
):
from prowler.providers.github.services.organization.organization_members_mfa_required.organization_members_mfa_required import (
organization_members_mfa_required,
)

check = organization_members_mfa_required()
result = check.execute()
assert len(result) == 1
assert result[0].resource_id == 1
assert result[0].resource_name == "test-organization"
assert result[0].status == "PASS"
assert (
result[0].status_extended
== f"Organization {org_name} does require members to have two-factor authentication enabled."
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def mock_list_organizations(_):
1: Org(
id=1,
name="test-organization",
mfa_required=True,
),
}

Expand All @@ -33,3 +34,4 @@ def test_list_organizations(self):
repository_service = Organization(set_mocked_github_provider())
assert len(repository_service.organizations) == 1
assert repository_service.organizations[1].name == "test-organization"
assert repository_service.organizations[1].mfa_required
Loading