GetProjectOwners

Parse a GCP service account email for the project name, then lookup project owners and add them to a list of potential service owners for ranking.

python · Cortex Attack Surface Management

Source

import demistomock as demisto  # noqa: F401
from CommonServerPython import *  # noqa: F401

"""Script for identifying and recommending the most likely owners of a discovered service
from those surfaced by Cortex ASM Enrichment.
"""


from typing import Any
import traceback
import re


def is_gcp_iam_account(service_account: str) -> bool:
    """
    Determine whether service account is user-managed

    Docs: https://cloud.google.com/iam/docs/service-account-types
    """
    return bool(re.search(r"^.+@[^\.]+(\.iam\.gserviceaccount\.com)$", service_account))


def extract_project_name(service_account: str) -> str:
    """
    Extract project name from GCP IAM service account
    """
    match = re.search(r"(?<=@)[^\.]+(?=\.iam\.gserviceaccount\.com)", service_account)
    if match:
        return match.group()
    else:
        raise ValueError(f"Could not extract project name from service account {service_account}")


def get_iam_policy(project_name: str) -> list[dict[str, Any]]:
    """
    Retrieve IAM policy for project
    """
    try:
        return demisto.executeCommand("gcp-iam-project-iam-policy-get", args={"project_name": f"projects/{project_name}"})
    except Exception as ex:
        raise RuntimeError(f"Error retrieving IAM policy for GCP project {project_name}") from ex


def get_project_owners(results: list[dict[str, Any]]) -> list[str]:
    """
    Return list of principals with the "owner" role from results of get_iam_policy
    """
    try:
        policy = results[0]["Contents"]["bindings"]
        owners: list[str] = []
        for group in policy:
            if group["role"] == "roles/owner":
                owners.extend(member.replace("user:", "") for member in group["members"])
        return owners
    except Exception as ex:
        raise ValueError("Error getting project owners from IAM policy") from ex


def main():
    try:
        unranked = demisto.args().get("owners", [])
        external_service = demisto.args().get("external_service", "")
        found_owners = False

        # only handle GCP for now
        if "Google" in external_service:
            for owner in unranked:
                if is_gcp_iam_account(owner["email"]):
                    owners = get_project_owners(get_iam_policy(extract_project_name(owner["email"])))
                    if owners:
                        found_owners = True
                        current_owners = demisto.incident().get("CustomFields").get("asmserviceownerunrankedraw")
                        if not isinstance(current_owners, list):
                            # cast to list because if there's only one element, it will be returned as a dict
                            current_owners = [current_owners]
                        for email in owners:
                            current_owners.append(
                                {
                                    "name": "n/a",
                                    "email": email,
                                    # The "Chain: " prefix is used for internal model processing and will be removed
                                    # from final service owner source; see RankServiceOwners.py::justify
                                    "source": "Chain: GCP project owner of service account",
                                    "timestamp": datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
                                }
                            )
                        demisto.executeCommand("setAlert", {"asmserviceownerunrankedraw": current_owners})
        if found_owners:
            return_results(
                CommandResults(readable_output="Project owners of service accounts written to asmserviceownerunrankedraw")
            )
        else:
            return_results(CommandResults(readable_output="No additional project owners found"))

    except (ValueError, RuntimeError) as ex:
        demisto.info(f"Error retrieving project owners: {str(ex)}")
        return_results(CommandResults(readable_output="No additional project owners found"))

    except Exception as ex:
        demisto.error(traceback.format_exc())  # print the traceback
        return_error(f"Failed to execute GetProjectOwners. Error: {str(ex)}")


if __name__ in ("__main__", "__builtin__", "builtins"):
    main()

README

Parse a GCP service account email for the project name, then lookup project owners and add them to a list of potential service owners for ranking.

Script Data


Name Description
Script Type python3
Cortex XSOAR Version 6.8.0

Inputs


Argument Name Description
owners List of potential service owners
external_service String indicating which cloud provider the service is

Outputs


There are no outputs for this script.