Git Product home page Git Product logo

purplepanda's Introduction

PurplePanda

This tool fetches resources from different cloud/saas applications focusing on permissions in order to identify privilege escalation paths and dangerous permissions in the cloud/saas configurations. Note that PurplePanda searches both privileges escalation paths within a platform and across platforms.

The name comes from the animal Red Panda. This panda eats peas, just like Purple Panda, which can ingest API keys/tokens found by these PEASS. The color was changed to purple because this tool is meant mainly for Purple Teams (because it can be highly useful for both Blue and Red Teams).

How to use

Each folder inside /intel defines one platform that can be enumerated and contains a README.md file explaining how to use that specific module.

Download Neo4jDesktop and create a database. Then export the env variables PURPLEPANDA_NEO4J_URL and PURPLEPANDA_PWD with the URL to the neo4j database and the password.

If you want shodan to be used with public IPs discovered during the enumeration export a env variable called SHODAN_KEY with a valid api key of shodan.

Then just install and launch the program indicating the platforms you want to enumerate comma separated like.

Local install

git clone https://github.com/carlospolop/PurplePanda
cd PurplePanda
python3 -m venv .
source bin/activate
python3 -m pip install -r requirements.txt
export PURPLEPANDA_NEO4J_URL="bolt://neo4j@localhost:7687"
export PURPLEPANDA_PWD="neo4j_pwd_4_purplepanda"
python3 main.py -h # Get help
python3 main.py -e -p google,github,k8s --github-only-org --k8s-get-secret-values --gcp-get-secret-values # Enumerate google, github and k8s

Docker

# Consider adding the API keys in the Dockerfile
docker rm -f purplepanda
docker build --tag=purplepanda .
# Execute -h
## CHange -h for the params you want to run purplepanda with
docker run -t \
    -e PURPLEPANDA_NEO4J_URL="bolt://[email protected]:7687" \
    -e PURPLEPANDA_PWD="s3cr3t" \
    -e GOOGLE_DISCOVERY=... \
    -e GITHUB_DISCOVERY=... \
    -e K8S_DISCOVERY=... \
    -e CONCOURSE_DISCOVERY=... \
    -e CIRCLECI_DISCOVERY=... \
    purplepanda python3 main.py -h

## -t is needed to see the output properly
## If you are using Neo4Desktop to connec to the DB use the domain host.docker.internal
## You might need to use the option '-v' to mount files with configurations

PurplePanda has 2 analysis modes:

  • -e (enumerate): This is the main one, it will try to gather data and analyze it.
  • -a (analyze): This will perform a quick analysis of the provided credentials.

Video tutorial

Check how to use and inspect the data gathered by PurplePanda:

Tutorial

For Blue/Purple Teams

Use credentials for each platform with at least admin read access to all the resources of the platform. This will help you to see exactly the privesc paths that can be abused within your configurations in each platform and across

For Red Teams

PurplePanda is also designed to be used by Red Teams. In general, cloud/saas platforms won't give everyone access to read the configuration of the platform, that's why PurplePanda supports the use of several keys for the same platform, in order to try to enumerate everything with all the keys you compromised and have the most accurate view of the configuration of the platform.

Supported platforms

How to use the data

Use the -d parameter indicating a directory. Then, PurplePanda will write in this directory several interesting analysis in csv format of the information obtained from all the platforms. The recommendation is to find interesting and unexpected things in those files and then move to analyze those interesting cases with the graphs.

Each folder inside /intel defines one platform that can be enumerated and contains a README.md file explaining how to use that specific module. Moreover, each folder also contains a HOW_TO_USE.md file and a QUERIES.md file.

In the HOW_TO_USE.md file you can find the best queries to perform an investigation on how to escalate privileges (for Purple, Blue, and Red Teams).

In the QUERIES.md file you will find all proposed queries to investigate the data easier.

How to visualize the data in graphs

Follow the instructions indicated in VISUALIZE_GRAPHS.md

How to Contribute

In the root folder and in each folder inside intel/ you will find a TODO.md file. You can find in those files how you can help. Just send a PR with the addition.

PRs with fixes are also welcome :)

Moreover, if you have other ideas that aren't in those TODO files feel free to send a PR.

By Carlos PolopTM

purplepanda's People

Contributors

carlospolop avatar lemontreeran avatar njmulsqb avatar paralax avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

purplepanda's Issues

Feature: nmap scanner to scan all the found IPs

PurplePanda should get all the public IPs found, and call nmap using some python lib and create the following relations with the output of nmap (check the relations purplepanda is already creating with shodan).

KeyError: 'displayName' error on GCP disc_sa.disc_sas

For some reason the display name doesn't come in some cases an throw the following error:

Google disc_sa._disc_sas ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0% • 2 • 0:00:06 ⠹
Traceback (most recent call last):
File "/opt/PurplePanda/main.py", line 215, in
main()
File "/opt/PurplePanda/main.py", line 188, in main
PurplePanda().start_discovery(functions)
File "/opt/PurplePanda/core/utils/purplepanda.py", line 178, in start_discovery
t.result()
File "/usr/lib/python3.9/concurrent/futures/_base.py", line 439, in result
return self.__get_result()
File "/usr/lib/python3.9/concurrent/futures/_base.py", line 391, in __get_result
raise self._exception
File "/usr/lib/python3.9/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/opt/PurplePanda/intel/google/purplepanda_google.py", line 70, in discover
DiscoverSaas(
File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
self._call_f(f)
File "/opt/PurplePanda/core/utils/discover_saas.py", line 39, in _call_f
func()
File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
self._call_f(f)
File "/opt/PurplePanda/core/utils/discover_saas.py", line 39, in _call_f
func()
File "/opt/PurplePanda/core/utils/purplepanda.py", line 52, in discover
self._disc()
File "/opt/PurplePanda/intel/google/discovery/disc_sa.py", line 24, in _disc
self._disc_loop(projects, self._disc_sas, name.split(".")[-1]+"._disc_sas")
File "/opt/PurplePanda/core/utils/purplepanda.py", line 64, in _disc_loop
func(item, **kwargs)
File "/opt/PurplePanda/intel/google/discovery/disc_sa.py", line 43, in _disc_sas
displayName = sa["displayName"],
KeyError: 'displayName'

gcp_disc_client.py creates GcpOrganization from "domain:" members without "name" property, leading to crashes

In gcp_disc_client.py, when gathering IAM policies in get_iam_policy, the following code creates a GcpOrganization without the name property:

                elif member.startswith("domain:"):
                    o_domain = member.replace("domain:", "")
                    m_obj: GcpOrganization = GcpOrganization(
                        domain = o_domain
                    ).save()
                    m_objs = [m_obj]

This makes PurplePanda (for GCP) crash when attempting to get the Org Policies for this GcpOrganization. Trace of the crash because the name property is missing:

Google disc_org_policies.orgs ━━━━━━━━━━━━━━━━━━━━╺━━━━━━━━━━━━━━━━━━━ 50.0% • 2 • 0:00:04 ⠋
Traceback (most recent call last):
  File "/opt/PurplePanda/main.py", line 216, in <module>
    main()
  File "/opt/PurplePanda/main.py", line 188, in main
    PurplePanda().start_discovery(functions)
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 197, in start_discovery
    t.result()
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/purplepanda_google.py", line 80, in discover
    ).do_discovery()
      ^^^^^^^^^^^^^^
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
    self._call_f(f)
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f
    func()
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
    self._call_f(f)
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f
    func()
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 56, in discover
    self._disc()
  File "/opt/PurplePanda/intel/google/discovery/disc_org_policies.py", line 28, in _disc
    self._disc_loop(orgs, self._disc_org_policies, __name__.split(".")[-1]+".orgs")
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 68, in _disc_loop
    func(item, **kwargs) # Here the keyworded arguments are being passed to the function
    ^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/discovery/disc_org_policies.py", line 40, in _disc_org_policies
    prep_http = self.service.organizations().listOrgPolicies(resource=org_obj.name)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/lib/python3.11/site-packages/googleapiclient/discovery.py", line 1114, in method
    raise TypeError('Missing required parameter "%s"' % name)
TypeError: Missing required parameter "resource"

The main issue hence seems to be that disc_org_policies.py expects the name property to be populated, but because the GcpOrganization was instantiated without the name property, it is not present:

    def _disc_org_policies(self, org_obj:GcpOrganization):
        """Discover each policy of the organization"""

        prep_http = self.service.organizations().listOrgPolicies(resource=org_obj.name)   # here the name is required but not present
        policies: List[str] = self.execute_http_req(prep_http, "policies")
        for policy in policies:
            self._save_policy(policy, org_obj)

I have a dirty hack to fix the issue for me.

In the case of our environment, the "domain:" member looks as follows: <ORG-ID>-<RANDOM_STRING>.apps.googleusercontent.com.

Hence I added the following patch to gcp_disc_client.py:

--- a/intel/google/discovery/gcp_disc_client.py
+++ b/intel/google/discovery/gcp_disc_client.py
@@ -270,10 +270,17 @@ class GcpDiscClient(PurplePanda):
                 
                 elif member.startswith("domain:"):
                     o_domain = member.replace("domain:", "")
+                    if "apps.googleusercontent.com" in o_domain:
+                        o_name = "organizations/" + o_domain.split("-")[0]
+                    else:
+                        o_name = "organizations/" + o_domain
                     m_obj: GcpOrganization = GcpOrganization(
-                        domain = o_domain
+                        domain = o_domain,
+                        name = o_name
                     ).save()
                     m_objs = [m_obj]
+                    print(f"IAM policy for {resource_name}:")
+                    print(f"Role: {role_name}, member: {member}")
                 
                 elif member == "allUsers":
                     m_obj: GcpUserAccount = GcpUserAccount(

This quick and dirty patch only constructs the name property correctly for cases where the domain follows the above format ( <ORG-ID>-<RANDOM_STRING>.apps.googleusercontent.com). As I have no patch that would work for other domain formats too I have no PR request at this time but wanted to report the bug nonetheless.

TypeError: 'GcpProject' object is not subscriptable

When running your amazing tool on a few service accounts that I found, I get the following error:
[12:56:47] INFO INFO:core.utils.purplepanda:Enumerating purplepanda.py:187 google... [12:56:48] disc_orgs took 0s purplepanda.py:75 [12:56:49] disc_folders took 0s purplepanda.py:75 [12:56:50] disc_projects took 0s purplepanda.py:75 [12:56:50] WARNING WARNING:core.db.customogm:Objects of class customogm.py:73 <class 'intel.google.models.gcp_organization.GcpOrg anization'> where searched for but nothing was found WARNING WARNING:core.db.customogm:Objects of class customogm.py:73 <class 'intel.google.models.gcp_folder.GcpFolder'> where searched for but nothing was found disc_org_policies.orgs took 0s purplepanda.py:75 disc_org_policies.folders took 0s purplepanda.py:75 [12:56:51] disc_org_policies.projects took 0s purplepanda.py:75 [12:56:51] WARNING WARNING:core.db.customogm:Objects of class customogm.py:73 <class 'intel.google.models.gcp_organization.GcpOrg anization'> where searched for but nothing was found [12:56:52] disc_custom_roles_permissions took 0s purplepanda.py:75 disc_sa._disc_sas took 0s purplepanda.py:75 [12:56:52] WARNING WARNING:core.db.customogm:Objects of class customogm.py:73 <class 'intel.google.models.gcp_service_account.Gcp ServiceAccount'> where searched for but nothing was found disc_sa._disc_special_sas took 0s purplepanda.py:75 WARNING WARNING:core.db.customogm:Objects of class customogm.py:73 <class 'intel.google.models.gcp_workspace.GcpWorksp ace'> where searched for but nothing was found [12:56:53] disc_storage took 0s purplepanda.py:75 disc_compute_subnetworks took 0s purplepanda.py:75 [12:56:54] disc_clusters took 0s purplepanda.py:75 [12:56:55] disc_secrets took 0s purplepanda.py:75 disc_composer took 0s purplepanda.py:75 disc_pubsub took 0s purplepanda.py:75 disc_compute_instances took 0s purplepanda.py:75 disc_sourcerepo took 0s purplepanda.py:75 disc_cloud_run took 0s purplepanda.py:75 disc_cloud_functions took 0s purplepanda.py:75 disc_cloudbuild._disc_builds took 0s purplepanda.py:75 disc_dns took 0s purplepanda.py:75 disc_sql took 0s purplepanda.py:75 disc_pubsub.subscriptions took 0s purplepanda.py:75 [12:56:56] disc_compute_networks took 0s purplepanda.py:75 disc_pubsub.schemas took 0s purplepanda.py:75 [12:56:57] disc_cloudbuild._disc_triggers took 1s purplepanda.py:75 [12:57:46] disc_kms took 51s purplepanda.py:75 Google disc_bigquery ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0% • 1 • 0:00:52 ⠼ Traceback (most recent call last): File "/Users/testuser/PurplePanda/main.py", line 216, in <module> main() File "/Users/testuser/PurplePanda/main.py", line 188, in main PurplePanda().start_discovery(functions) File "/Users/testuser/PurplePanda/core/utils/purplepanda.py", line 197, in start_discovery t.result() File "/Users/testuser/opt/anaconda3/lib/python3.9/concurrent/futures/_base.py", line 439, in result return self.__get_result() File "/Users/testuser/opt/anaconda3/lib/python3.9/concurrent/futures/_base.py", line 391, in __get_result raise self._exception File "/Users/testuser/opt/anaconda3/lib/python3.9/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/Users/testuser/PurplePanda/intel/google/purplepanda_google.py", line 76, in discover DiscoverSaas( File "/Users/testuser/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery self._call_f(f) File "/Users/testuser/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f func() File "/Users/testuser/PurplePanda/core/utils/discover_saas.py", line 27, in do_discovery t.result() File "/Users/testuser/opt/anaconda3/lib/python3.9/concurrent/futures/_base.py", line 439, in result return self.__get_result() File "/Users/testuser/opt/anaconda3/lib/python3.9/concurrent/futures/_base.py", line 391, in __get_result raise self._exception File "/Users/testuser/opt/anaconda3/lib/python3.9/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/Users/testuser/PurplePanda/core/utils/discover_saas.py", line 39, in _do_parallel self._call_f(f) File "/Users/testuser/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f func() File "/Users/testuser/PurplePanda/core/utils/purplepanda.py", line 56, in discover self._disc() File "/Users/testuser/PurplePanda/intel/google/discovery/disc_bigquery.py", line 20, in _disc self._disc_loop(projects, self._disc_datasets, __name__.split(".")[-1]) File "/Users/testuser/PurplePanda/core/utils/purplepanda.py", line 68, in _disc_loop func(item, **kwargs) # Here the keyworded arguments are being passed to the function File "/Users/testuser/PurplePanda/intel/google/discovery/disc_bigquery.py", line 26, in _disc_datasets project_id: str = project["id"] TypeError: 'GcpProject' object is not subscriptable

Maybe because the service accounts have too few permissions?

Never ending disc_projects

Hello,

I tried to run PurplePanda to enumerate GCP as Viewer but disc_projects never ends.

$ export GOOGLE_DISCOVERY='Z29vZ2xlOgotIGZpbGVfcGF0aDogIiIK' $ python3 main.py -e -p google --gcp-get-secret-values/opt/PurplePanda/lib/python3.10/site-packages/google/auth/_default.py:83: UserWarning: Your application has authenticated using end user credentials from Google Cloud SDK without a quota project. You might receive
a "quota exceeded" or "API not enabled" error. We recommend you rerun gcloud auth application-default login and make sure a quota project is added. Or you can use service accounts instead. For more information
about service accounts, see https://cloud.google.com/docs/authentication/
warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
[10:12:14] INFO INFO:core.utils.purplepanda:Enumerating google... purplepanda.py:187
/opt/PurplePanda/lib/python3.10/site-packages/google/auth/_default.py:83: UserWarning: Your application has authenticated using end user credentials from Google Cloud SDK without a quota project. You might receive
a "quota exceeded" or "API not enabled" error. We recommend you rerun gcloud auth application-default login and make sure a quota project is added. Or you can use service accounts instead. For more information
about service accounts, see https://cloud.google.com/docs/authentication/
warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)
[10:12:21] disc_orgs took 1s purplepanda.py:75
[10:12:22] disc_folders took 0s purplepanda.py:75
Google disc_projects ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0% • 1 • 0:10:27 ⠏

disc_projects.py

##while running the gcp scan script i'm getting the following error:
neo4j is installed and able to connect to purplepanda, exports were done with no issues. PLEASE ADVISE##
Google disc_projects ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0% • 1 • 0:01:38 ⠙
Traceback (most recent call last):
File "/opt/Cloud/GCP/PurplePanda/google-cloud-sdk/bin/../../main.py", line 215, in
main()
File "/opt/Cloud/GCP/PurplePanda/google-cloud-sdk/bin/../../main.py", line 188, in main
PurplePanda().start_discovery(functions)
File "/opt/Cloud/GCP/PurplePanda/core/utils/purplepanda.py", line 178, in start_discovery
t.result()
File "/usr/lib/python3.10/concurrent/futures/_base.py", line 439, in result
return self.__get_result()
File "/usr/lib/python3.10/concurrent/futures/_base.py", line 391, in __get_result
raise self._exception
File "/usr/lib/python3.10/concurrent/futures/thread.py", line 58, in run
result = self.fn(*self.args, **self.kwargs)
File "/opt/Cloud/GCP/PurplePanda/intel/google/purplepanda_google.py", line 74, in discover
).do_discovery()
File "/opt/Cloud/GCP/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
self._call_f(f)
File "/opt/Cloud/GCP/PurplePanda/core/utils/discover_saas.py", line 39, in _call_f
func()
File "/opt/Cloud/GCP/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
self._call_f(f)
File "/opt/Cloud/GCP/PurplePanda/core/utils/discover_saas.py", line 39, in _call_f
func()
File "/opt/Cloud/GCP/PurplePanda/core/utils/purplepanda.py", line 52, in discover
self._disc()
File "/opt/Cloud/GCP/PurplePanda/intel/google/discovery/disc_projects.py", line 28, in _disc
self._disc_loop(projects, self._disc_networks, name.split(".")[-1])
File "/opt/Cloud/GCP/PurplePanda/core/utils/purplepanda.py", line 64, in _disc_loop
func(item, **kwargs)
File "/opt/Cloud/GCP/PurplePanda/intel/google/discovery/disc_projects.py", line 44, in _disc_networks
parent: dict = p["parent"]
KeyError: 'parent'

GCP: Method _disc_sas in disc_sa.py crashes when processing keys missing keyAlgorithm dict key (e.g. keys with 'keyOrigin'='USER_PROVIDED')

In the method _disc_sas in the file disc_sa.py, GcpApiKey objects are constructed:

    def _disc_sas(self, p_obj:GcpProject):
        """Discover all the service accounts of a project"""

        project_name: str = p_obj.name
        http_prep = self.service.projects().serviceAccounts()#.list(name=project_name)
        accounts: List[str] = self.execute_http_req(http_prep, "accounts", disable_warn=True, list_kwargs={"name": project_name})
        for sa in accounts:
            sa_name = sa["name"]

            sa_obj = GcpServiceAccount(
                name = sa_name,
                uniqueId = sa["uniqueId"],
                email = sa["email"],
                displayName = sa.get("displayName", ""),
                description = sa.get("description", "")
            ).save()
            sa_obj.projects.update(p_obj)
            sa_obj.save()

            http_prep = self.service.projects().serviceAccounts().keys()#.list(name=sa_name)
            keys: List[str] = self.execute_http_req(http_prep, "keys", list_kwargs={"name": sa_name})
            for key in keys:
                if key["keyOrigin"] != "USER_PROVIDED":
                    api_key_obj: GcpApiKey = GcpApiKey(
                        name=key["name"],
                        validAfterTime=key["validAfterTime"],
                        validBeforeTime=key["validBeforeTime"],
                        keyAlgorithm=key["keyAlgorithm"],
                        keyOrigin=key["keyOrigin"],
                        keyType=key["keyType"],
                    )
                    api_key_obj.service_accounts.update(sa_obj)
                    api_key_obj.save()

            self.get_iam_policy(sa_obj, self.service.projects().serviceAccounts(), sa_obj.name)

This method crashes when trying to instantiate a GcpApiKey when that key has no keyAlgorithm attribute. Example trace:

Traceback (most recent call last):
  File "/opt/PurplePanda/main.py", line 216, in <module>
    main()
  File "/opt/PurplePanda/main.py", line 188, in main
    PurplePanda().start_discovery(functions)
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 197, in start_discovery
    t.result()
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/purplepanda_google.py", line 80, in discover
    ).do_discovery()
      ^^^^^^^^^^^^^^
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
    self._call_f(f)
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f
    func()
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
    self._call_f(f)
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f
    func()
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 56, in discover
    self._disc()
  File "/opt/PurplePanda/intel/google/discovery/disc_sa.py", line 24, in _disc
    self._disc_loop(projects, self._disc_sas, __name__.split(".")[-1]+"._disc_sas")
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 68, in _disc_loop
    func(item, **kwargs) # Here the keyworded arguments are being passed to the function
    ^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/discovery/disc_sa.py", line 57, in _disc_sas
    keyAlgorithm=key["keyAlgorithm"],
                 ~~~^^^^^^^^^^^^^^^^
KeyError: 'keyAlgorithm'

This seems to happen with some keys that do not have a keyAlgorithm set. For example, the following key which has 'keyOrigin': 'USER_PROVIDED'. In this case, there is no keyAlgorithm key/value pair in the dict:
{'name': 'projects/<CENSORED>/serviceAccounts/<CENSORED>@<CENSORED>.iam.gserviceaccount.com/keys/<CENSORED>', 'validAfterTime': '2023-12-11T13:40:19Z', 'validBeforeTime': '2024-12-10T13:40:19Z', 'keyOrigin': 'USER_PROVIDED', 'keyType': 'USER_MANAGED'}

A potential fix could be:

  1. Check if the keyAlgorithm key exists in the key dict
  2. If it exists, use its value when instantiating the GcpApiKey object (keyAlgorithm=key["keyAlgorithm"])
  3. If the key does not exist, use the empty string "" when instantiating the GcpApiKey object (keyAlgorithm="")

Crash during init of GcpWorkloadIdentityPool

Hi Carlos,

I ran PurplePanda on our GCP environment. During the disc_projects step, when PurplePanda tries to instantiate a GcpWorkloadIdentityPool object, it crashes with an IndexError (out of range) on the following line:
name_part = name.split("/projects/")[1]

Full trace:

[16:02:41] disc_folders took 586s                                                                                                                        purplepanda.py:75
[16:14:42] ERROR    ERROR:intel.google.discovery.disc_projects:The role projects/<CENSORED>/roles/custom.deploy from                    gcp_disc_client.py:369
                    projects/<CENSORED> wasn't found                                                                                                          
Google disc_projects ━━━━╸━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 12.2% • 814 • 0:22:16 ⠧
Traceback (most recent call last):
  File "/opt/PurplePanda/main.py", line 216, in <module>
    main()
  File "/opt/PurplePanda/main.py", line 188, in main
    PurplePanda().start_discovery(functions)
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 197, in start_discovery
    t.result()
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 449, in result
    return self.__get_result()
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/concurrent/futures/_base.py", line 401, in __get_result
    raise self._exception
  File "/usr/lib/python3.11/concurrent/futures/thread.py", line 58, in run
    result = self.fn(*self.args, **self.kwargs)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/purplepanda_google.py", line 80, in discover
    ).do_discovery()
      ^^^^^^^^^^^^^^
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
    self._call_f(f)
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f
    func()
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 17, in do_discovery
    self._call_f(f)
  File "/opt/PurplePanda/core/utils/discover_saas.py", line 44, in _call_f
    func()
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 56, in discover
    self._disc()
  File "/opt/PurplePanda/intel/google/discovery/disc_projects.py", line 29, in _disc
    self._disc_loop(projects, self._disc_projects, __name__.split(".")[-1])
  File "/opt/PurplePanda/core/utils/purplepanda.py", line 68, in _disc_loop
    func(item, **kwargs) # Here the keyworded arguments are being passed to the function
    ^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/discovery/disc_projects.py", line 73, in _disc_projects
    self.get_iam_policy(p_obj, self.service.projects(), p_obj.name.split("/")[1])
  File "/opt/PurplePanda/intel/google/discovery/gcp_disc_client.py", line 312, in get_iam_policy
    m_obj: GcpWorkloadIdentityPool = GcpWorkloadIdentityPool(name=name).save()
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/PurplePanda/intel/google/models/gcp_workload_identity_pool.py", line 19, in __init__
    name_part = name.split("/projects/")[1]
                ~~~~~~~~~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range

I added a few print statements to see which name triggers this error. The name looks as follows:
//iam.googleapis.com/locations/global/workforcePools/<CENSORED>-workforce-pool/subject/<CENSORED>@<CENSORED>.<CENSORED>

It seems that the constructor of GcpWorkloadIdentityPool cannot deal with a name that is formatted this way. It seems to expect one that has the string /projects/ in it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.