cartography 0.102.0rc2__py3-none-any.whl → 0.103.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cartography might be problematic. Click here for more details.
- cartography/__main__.py +1 -2
- cartography/_version.py +2 -2
- cartography/cli.py +302 -253
- cartography/client/core/tx.py +39 -18
- cartography/config.py +4 -0
- cartography/driftdetect/__main__.py +1 -2
- cartography/driftdetect/add_shortcut.py +10 -2
- cartography/driftdetect/cli.py +71 -75
- cartography/driftdetect/detect_deviations.py +7 -3
- cartography/driftdetect/get_states.py +20 -8
- cartography/driftdetect/model.py +5 -5
- cartography/driftdetect/serializers.py +8 -6
- cartography/driftdetect/storage.py +2 -2
- cartography/graph/cleanupbuilder.py +35 -15
- cartography/graph/job.py +46 -17
- cartography/graph/querybuilder.py +165 -80
- cartography/graph/statement.py +35 -26
- cartography/intel/analysis.py +4 -1
- cartography/intel/aws/__init__.py +114 -55
- cartography/intel/aws/apigateway.py +134 -63
- cartography/intel/aws/cloudtrail.py +127 -0
- cartography/intel/aws/config.py +56 -20
- cartography/intel/aws/dynamodb.py +108 -40
- cartography/intel/aws/ec2/__init__.py +2 -2
- cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
- cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
- cartography/intel/aws/ec2/images.py +49 -20
- cartography/intel/aws/ec2/instances.py +234 -136
- cartography/intel/aws/ec2/internet_gateways.py +40 -11
- cartography/intel/aws/ec2/key_pairs.py +44 -20
- cartography/intel/aws/ec2/launch_templates.py +101 -59
- cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
- cartography/intel/aws/ec2/load_balancers.py +82 -42
- cartography/intel/aws/ec2/network_acls.py +89 -65
- cartography/intel/aws/ec2/network_interfaces.py +146 -87
- cartography/intel/aws/ec2/reserved_instances.py +45 -16
- cartography/intel/aws/ec2/route_tables.py +138 -98
- cartography/intel/aws/ec2/security_groups.py +71 -21
- cartography/intel/aws/ec2/snapshots.py +61 -22
- cartography/intel/aws/ec2/subnets.py +54 -18
- cartography/intel/aws/ec2/tgw.py +100 -34
- cartography/intel/aws/ec2/util.py +1 -1
- cartography/intel/aws/ec2/volumes.py +69 -41
- cartography/intel/aws/ec2/vpc.py +37 -12
- cartography/intel/aws/ec2/vpc_peerings.py +83 -24
- cartography/intel/aws/ecr.py +88 -32
- cartography/intel/aws/ecs.py +83 -47
- cartography/intel/aws/eks.py +55 -29
- cartography/intel/aws/elasticache.py +42 -18
- cartography/intel/aws/elasticsearch.py +57 -20
- cartography/intel/aws/emr.py +61 -23
- cartography/intel/aws/iam.py +401 -145
- cartography/intel/aws/iam_instance_profiles.py +22 -22
- cartography/intel/aws/identitycenter.py +71 -37
- cartography/intel/aws/inspector.py +159 -89
- cartography/intel/aws/kms.py +92 -38
- cartography/intel/aws/lambda_function.py +103 -34
- cartography/intel/aws/organizations.py +30 -10
- cartography/intel/aws/permission_relationships.py +133 -51
- cartography/intel/aws/rds.py +249 -85
- cartography/intel/aws/redshift.py +107 -46
- cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
- cartography/intel/aws/resources.py +53 -46
- cartography/intel/aws/route53.py +108 -61
- cartography/intel/aws/s3.py +168 -83
- cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
- cartography/intel/aws/secretsmanager.py +24 -12
- cartography/intel/aws/securityhub.py +20 -9
- cartography/intel/aws/sns.py +166 -0
- cartography/intel/aws/sqs.py +60 -28
- cartography/intel/aws/ssm.py +70 -30
- cartography/intel/aws/util/arns.py +7 -7
- cartography/intel/aws/util/common.py +31 -4
- cartography/intel/azure/__init__.py +78 -19
- cartography/intel/azure/compute.py +101 -27
- cartography/intel/azure/cosmosdb.py +496 -170
- cartography/intel/azure/sql.py +296 -105
- cartography/intel/azure/storage.py +322 -113
- cartography/intel/azure/subscription.py +39 -23
- cartography/intel/azure/tenant.py +13 -4
- cartography/intel/azure/util/credentials.py +95 -55
- cartography/intel/bigfix/__init__.py +2 -2
- cartography/intel/bigfix/computers.py +93 -65
- cartography/intel/create_indexes.py +3 -2
- cartography/intel/crowdstrike/__init__.py +11 -9
- cartography/intel/crowdstrike/endpoints.py +5 -1
- cartography/intel/crowdstrike/spotlight.py +8 -3
- cartography/intel/cve/__init__.py +46 -13
- cartography/intel/cve/feed.py +48 -12
- cartography/intel/digitalocean/__init__.py +22 -13
- cartography/intel/digitalocean/compute.py +75 -108
- cartography/intel/digitalocean/management.py +44 -80
- cartography/intel/digitalocean/platform.py +48 -43
- cartography/intel/dns.py +36 -10
- cartography/intel/duo/__init__.py +21 -16
- cartography/intel/duo/api_host.py +14 -9
- cartography/intel/duo/endpoints.py +50 -45
- cartography/intel/duo/groups.py +18 -14
- cartography/intel/duo/phones.py +37 -34
- cartography/intel/duo/tokens.py +26 -23
- cartography/intel/duo/users.py +54 -50
- cartography/intel/duo/web_authn_credentials.py +30 -25
- cartography/intel/entra/__init__.py +25 -7
- cartography/intel/entra/ou.py +112 -0
- cartography/intel/entra/users.py +69 -63
- cartography/intel/gcp/__init__.py +185 -49
- cartography/intel/gcp/compute.py +418 -231
- cartography/intel/gcp/crm.py +96 -43
- cartography/intel/gcp/dns.py +60 -19
- cartography/intel/gcp/gke.py +72 -38
- cartography/intel/gcp/iam.py +61 -41
- cartography/intel/gcp/storage.py +84 -55
- cartography/intel/github/__init__.py +13 -11
- cartography/intel/github/repos.py +270 -137
- cartography/intel/github/teams.py +170 -88
- cartography/intel/github/users.py +70 -39
- cartography/intel/github/util.py +36 -34
- cartography/intel/gsuite/__init__.py +47 -26
- cartography/intel/gsuite/api.py +73 -30
- cartography/intel/jamf/__init__.py +19 -1
- cartography/intel/jamf/computers.py +30 -7
- cartography/intel/jamf/util.py +7 -2
- cartography/intel/kandji/__init__.py +6 -3
- cartography/intel/kandji/devices.py +14 -8
- cartography/intel/kubernetes/namespaces.py +7 -4
- cartography/intel/kubernetes/pods.py +7 -4
- cartography/intel/kubernetes/services.py +8 -4
- cartography/intel/lastpass/__init__.py +2 -2
- cartography/intel/lastpass/users.py +23 -12
- cartography/intel/oci/__init__.py +44 -11
- cartography/intel/oci/iam.py +134 -38
- cartography/intel/oci/organizations.py +13 -6
- cartography/intel/oci/utils.py +43 -20
- cartography/intel/okta/__init__.py +66 -15
- cartography/intel/okta/applications.py +42 -20
- cartography/intel/okta/awssaml.py +93 -33
- cartography/intel/okta/factors.py +16 -4
- cartography/intel/okta/groups.py +56 -29
- cartography/intel/okta/organization.py +5 -1
- cartography/intel/okta/origins.py +6 -2
- cartography/intel/okta/roles.py +15 -5
- cartography/intel/okta/users.py +20 -8
- cartography/intel/okta/utils.py +6 -4
- cartography/intel/pagerduty/__init__.py +8 -7
- cartography/intel/pagerduty/escalation_policies.py +18 -6
- cartography/intel/pagerduty/schedules.py +12 -4
- cartography/intel/pagerduty/services.py +11 -4
- cartography/intel/pagerduty/teams.py +8 -3
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/semgrep/__init__.py +24 -6
- cartography/intel/semgrep/dependencies.py +50 -28
- cartography/intel/semgrep/deployment.py +3 -1
- cartography/intel/semgrep/findings.py +42 -18
- cartography/intel/snipeit/__init__.py +17 -3
- cartography/intel/snipeit/asset.py +12 -6
- cartography/intel/snipeit/user.py +8 -5
- cartography/intel/snipeit/util.py +9 -4
- cartography/models/aws/apigateway.py +21 -17
- cartography/models/aws/apigatewaycertificate.py +28 -22
- cartography/models/aws/apigatewayresource.py +28 -20
- cartography/models/aws/apigatewaystage.py +33 -25
- cartography/models/aws/cloudtrail/__init__.py +0 -0
- cartography/models/aws/cloudtrail/trail.py +61 -0
- cartography/models/aws/dynamodb/gsi.py +30 -22
- cartography/models/aws/dynamodb/tables.py +25 -17
- cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
- cartography/models/aws/ec2/images.py +36 -34
- cartography/models/aws/ec2/instances.py +51 -45
- cartography/models/aws/ec2/keypair.py +21 -16
- cartography/models/aws/ec2/keypair_instance.py +28 -21
- cartography/models/aws/ec2/launch_configurations.py +30 -26
- cartography/models/aws/ec2/launch_template_versions.py +48 -38
- cartography/models/aws/ec2/launch_templates.py +21 -17
- cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
- cartography/models/aws/ec2/load_balancers.py +47 -37
- cartography/models/aws/ec2/network_acl_rules.py +38 -30
- cartography/models/aws/ec2/network_acls.py +38 -29
- cartography/models/aws/ec2/networkinterface_instance.py +52 -39
- cartography/models/aws/ec2/networkinterfaces.py +53 -37
- cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
- cartography/models/aws/ec2/reservations.py +18 -14
- cartography/models/aws/ec2/route_table_associations.py +44 -34
- cartography/models/aws/ec2/route_tables.py +50 -43
- cartography/models/aws/ec2/routes.py +45 -37
- cartography/models/aws/ec2/securitygroup_instance.py +29 -20
- cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
- cartography/models/aws/ec2/subnet_instance.py +24 -19
- cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
- cartography/models/aws/ec2/volumes.py +47 -40
- cartography/models/aws/eks/clusters.py +23 -21
- cartography/models/aws/emr.py +32 -30
- cartography/models/aws/iam/instanceprofile.py +33 -24
- cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
- cartography/models/aws/identitycenter/awspermissionset.py +37 -29
- cartography/models/aws/identitycenter/awsssouser.py +23 -21
- cartography/models/aws/inspector/findings.py +77 -65
- cartography/models/aws/inspector/packages.py +35 -29
- cartography/models/aws/s3/__init__.py +0 -0
- cartography/models/aws/s3/account_public_access_block.py +51 -0
- cartography/models/aws/sns/__init__.py +0 -0
- cartography/models/aws/sns/topic.py +50 -0
- cartography/models/aws/ssm/instance_information.py +51 -39
- cartography/models/aws/ssm/instance_patch.py +32 -26
- cartography/models/bigfix/bigfix_computer.py +42 -38
- cartography/models/bigfix/bigfix_root.py +3 -3
- cartography/models/core/common.py +12 -10
- cartography/models/core/nodes.py +5 -2
- cartography/models/core/relationships.py +14 -6
- cartography/models/crowdstrike/hosts.py +37 -35
- cartography/models/cve/cve.py +34 -32
- cartography/models/cve/cve_feed.py +6 -6
- cartography/models/digitalocean/__init__.py +0 -0
- cartography/models/digitalocean/account.py +21 -0
- cartography/models/digitalocean/droplet.py +56 -0
- cartography/models/digitalocean/project.py +48 -0
- cartography/models/duo/api_host.py +3 -3
- cartography/models/duo/endpoint.py +43 -41
- cartography/models/duo/group.py +14 -14
- cartography/models/duo/phone.py +27 -27
- cartography/models/duo/token.py +16 -16
- cartography/models/duo/user.py +46 -44
- cartography/models/duo/web_authn_credential.py +27 -19
- cartography/models/entra/ou.py +48 -0
- cartography/models/entra/tenant.py +24 -18
- cartography/models/entra/user.py +64 -48
- cartography/models/gcp/iam.py +23 -23
- cartography/models/github/orgs.py +5 -4
- cartography/models/github/teams.py +37 -31
- cartography/models/github/users.py +34 -23
- cartography/models/kandji/device.py +22 -16
- cartography/models/kandji/tenant.py +6 -4
- cartography/models/lastpass/tenant.py +3 -3
- cartography/models/lastpass/user.py +32 -28
- cartography/models/semgrep/dependencies.py +36 -24
- cartography/models/semgrep/deployment.py +5 -5
- cartography/models/semgrep/findings.py +58 -42
- cartography/models/semgrep/locations.py +27 -21
- cartography/models/snipeit/asset.py +30 -21
- cartography/models/snipeit/tenant.py +6 -4
- cartography/models/snipeit/user.py +19 -12
- cartography/stats.py +3 -3
- cartography/sync.py +107 -31
- cartography/util.py +84 -62
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/METADATA +3 -14
- cartography-0.103.0rc1.dist-info/RECORD +396 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
- cartography-0.102.0rc2.dist-info/RECORD +0 -381
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.102.0rc2.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
cartography/intel/gcp/iam.py
CHANGED
|
@@ -20,7 +20,9 @@ DESCRIBE_SLEEP = 1
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
@timeit
|
|
23
|
-
def get_gcp_service_accounts(
|
|
23
|
+
def get_gcp_service_accounts(
|
|
24
|
+
iam_client: Resource, project_id: str
|
|
25
|
+
) -> List[Dict[str, Any]]:
|
|
24
26
|
"""
|
|
25
27
|
Retrieve a list of GCP service accounts for a given project.
|
|
26
28
|
|
|
@@ -30,19 +32,29 @@ def get_gcp_service_accounts(iam_client: Resource, project_id: str) -> List[Dict
|
|
|
30
32
|
"""
|
|
31
33
|
service_accounts: List[Dict[str, Any]] = []
|
|
32
34
|
try:
|
|
33
|
-
request =
|
|
34
|
-
|
|
35
|
+
request = (
|
|
36
|
+
iam_client.projects()
|
|
37
|
+
.serviceAccounts()
|
|
38
|
+
.list(
|
|
39
|
+
name=f"projects/{project_id}",
|
|
40
|
+
)
|
|
35
41
|
)
|
|
36
42
|
while request is not None:
|
|
37
43
|
response = request.execute()
|
|
38
|
-
if
|
|
39
|
-
service_accounts.extend(response[
|
|
40
|
-
request =
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
if "accounts" in response:
|
|
45
|
+
service_accounts.extend(response["accounts"])
|
|
46
|
+
request = (
|
|
47
|
+
iam_client.projects()
|
|
48
|
+
.serviceAccounts()
|
|
49
|
+
.list_next(
|
|
50
|
+
previous_request=request,
|
|
51
|
+
previous_response=response,
|
|
52
|
+
)
|
|
43
53
|
)
|
|
44
54
|
except Exception as e:
|
|
45
|
-
logger.warning(
|
|
55
|
+
logger.warning(
|
|
56
|
+
f"Error retrieving service accounts for project {project_id}: {e}"
|
|
57
|
+
)
|
|
46
58
|
return service_accounts
|
|
47
59
|
|
|
48
60
|
|
|
@@ -59,17 +71,17 @@ def get_gcp_roles(iam_client: Resource, project_id: str) -> List[Dict]:
|
|
|
59
71
|
roles = []
|
|
60
72
|
|
|
61
73
|
# Get custom roles
|
|
62
|
-
custom_req = iam_client.projects().roles().list(parent=f
|
|
74
|
+
custom_req = iam_client.projects().roles().list(parent=f"projects/{project_id}")
|
|
63
75
|
while custom_req is not None:
|
|
64
76
|
resp = custom_req.execute()
|
|
65
|
-
roles.extend(resp.get(
|
|
77
|
+
roles.extend(resp.get("roles", []))
|
|
66
78
|
custom_req = iam_client.projects().roles().list_next(custom_req, resp)
|
|
67
79
|
|
|
68
80
|
# Get predefined roles
|
|
69
|
-
predefined_req = iam_client.roles().list(view=
|
|
81
|
+
predefined_req = iam_client.roles().list(view="FULL")
|
|
70
82
|
while predefined_req is not None:
|
|
71
83
|
resp = predefined_req.execute()
|
|
72
|
-
roles.extend(resp.get(
|
|
84
|
+
roles.extend(resp.get("roles", []))
|
|
73
85
|
predefined_req = iam_client.roles().list_next(predefined_req, resp)
|
|
74
86
|
|
|
75
87
|
return roles
|
|
@@ -93,17 +105,19 @@ def load_gcp_service_accounts(
|
|
|
93
105
|
:param project_id: The GCP Project ID associated with the service accounts.
|
|
94
106
|
:param gcp_update_tag: The timestamp of the current sync run.
|
|
95
107
|
"""
|
|
96
|
-
logger.debug(
|
|
108
|
+
logger.debug(
|
|
109
|
+
f"Loading {len(service_accounts)} service accounts for project {project_id}"
|
|
110
|
+
)
|
|
97
111
|
transformed_service_accounts = []
|
|
98
112
|
for sa in service_accounts:
|
|
99
113
|
transformed_sa = {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
114
|
+
"id": sa["uniqueId"],
|
|
115
|
+
"email": sa.get("email"),
|
|
116
|
+
"displayName": sa.get("displayName"),
|
|
117
|
+
"oauth2ClientId": sa.get("oauth2ClientId"),
|
|
118
|
+
"uniqueId": sa.get("uniqueId"),
|
|
119
|
+
"disabled": sa.get("disabled", False),
|
|
120
|
+
"projectId": project_id,
|
|
107
121
|
}
|
|
108
122
|
transformed_service_accounts.append(transformed_sa)
|
|
109
123
|
|
|
@@ -113,7 +127,7 @@ def load_gcp_service_accounts(
|
|
|
113
127
|
transformed_service_accounts,
|
|
114
128
|
lastupdated=gcp_update_tag,
|
|
115
129
|
projectId=project_id,
|
|
116
|
-
additional_labels=[
|
|
130
|
+
additional_labels=["GCPPrincipal"],
|
|
117
131
|
)
|
|
118
132
|
|
|
119
133
|
|
|
@@ -135,25 +149,25 @@ def load_gcp_roles(
|
|
|
135
149
|
logger.debug(f"Loading {len(roles)} roles for project {project_id}")
|
|
136
150
|
transformed_roles = []
|
|
137
151
|
for role in roles:
|
|
138
|
-
role_name = role[
|
|
139
|
-
if role_name.startswith(
|
|
140
|
-
if role_name in [
|
|
141
|
-
role_type =
|
|
152
|
+
role_name = role["name"]
|
|
153
|
+
if role_name.startswith("roles/"):
|
|
154
|
+
if role_name in ["roles/owner", "roles/editor", "roles/viewer"]:
|
|
155
|
+
role_type = "BASIC"
|
|
142
156
|
else:
|
|
143
|
-
role_type =
|
|
157
|
+
role_type = "PREDEFINED"
|
|
144
158
|
else:
|
|
145
|
-
role_type =
|
|
159
|
+
role_type = "CUSTOM"
|
|
146
160
|
|
|
147
161
|
transformed_role = {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
162
|
+
"id": role_name,
|
|
163
|
+
"name": role_name,
|
|
164
|
+
"title": role.get("title"),
|
|
165
|
+
"description": role.get("description"),
|
|
166
|
+
"deleted": role.get("deleted", False),
|
|
167
|
+
"etag": role.get("etag"),
|
|
168
|
+
"includedPermissions": role.get("includedPermissions", []),
|
|
169
|
+
"roleType": role_type,
|
|
170
|
+
"projectId": project_id,
|
|
157
171
|
}
|
|
158
172
|
transformed_roles.append(transformed_role)
|
|
159
173
|
|
|
@@ -167,7 +181,9 @@ def load_gcp_roles(
|
|
|
167
181
|
|
|
168
182
|
|
|
169
183
|
@timeit
|
|
170
|
-
def cleanup(
|
|
184
|
+
def cleanup(
|
|
185
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
186
|
+
) -> None:
|
|
171
187
|
"""
|
|
172
188
|
Run cleanup jobs for GCP IAM data in Neo4j.
|
|
173
189
|
|
|
@@ -177,7 +193,7 @@ def cleanup(neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any])
|
|
|
177
193
|
logger.debug("Running GCP IAM cleanup job")
|
|
178
194
|
job_params = {
|
|
179
195
|
**common_job_parameters,
|
|
180
|
-
|
|
196
|
+
"projectId": common_job_parameters.get("PROJECT_ID"),
|
|
181
197
|
}
|
|
182
198
|
|
|
183
199
|
cleanup_jobs = [
|
|
@@ -210,8 +226,12 @@ def sync(
|
|
|
210
226
|
|
|
211
227
|
# Get and load service accounts
|
|
212
228
|
service_accounts = get_gcp_service_accounts(iam_client, project_id)
|
|
213
|
-
logger.info(
|
|
214
|
-
|
|
229
|
+
logger.info(
|
|
230
|
+
f"Found {len(service_accounts)} service accounts in project {project_id}"
|
|
231
|
+
)
|
|
232
|
+
load_gcp_service_accounts(
|
|
233
|
+
neo4j_session, service_accounts, project_id, gcp_update_tag
|
|
234
|
+
)
|
|
215
235
|
|
|
216
236
|
# Get and load roles
|
|
217
237
|
roles = get_gcp_roles(iam_client, project_id)
|
cartography/intel/gcp/storage.py
CHANGED
|
@@ -33,7 +33,7 @@ def get_gcp_buckets(storage: Resource, project_id: str) -> Dict:
|
|
|
33
33
|
return res
|
|
34
34
|
except HttpError as e:
|
|
35
35
|
reason = compute._get_error_reason(e)
|
|
36
|
-
if reason ==
|
|
36
|
+
if reason == "invalid":
|
|
37
37
|
logger.warning(
|
|
38
38
|
(
|
|
39
39
|
"The project %s is invalid - returned a 400 invalid error."
|
|
@@ -43,12 +43,15 @@ def get_gcp_buckets(storage: Resource, project_id: str) -> Dict:
|
|
|
43
43
|
e,
|
|
44
44
|
)
|
|
45
45
|
return {}
|
|
46
|
-
elif reason ==
|
|
46
|
+
elif reason == "forbidden":
|
|
47
47
|
logger.warning(
|
|
48
48
|
(
|
|
49
49
|
"You do not have storage.bucket.list access to the project %s. "
|
|
50
50
|
"Full details: %s"
|
|
51
|
-
),
|
|
51
|
+
),
|
|
52
|
+
project_id,
|
|
53
|
+
e,
|
|
54
|
+
)
|
|
52
55
|
return {}
|
|
53
56
|
else:
|
|
54
57
|
raise
|
|
@@ -56,7 +59,7 @@ def get_gcp_buckets(storage: Resource, project_id: str) -> Dict:
|
|
|
56
59
|
|
|
57
60
|
@timeit
|
|
58
61
|
def transform_gcp_buckets(bucket_res: Dict) -> List[Dict]:
|
|
59
|
-
|
|
62
|
+
"""
|
|
60
63
|
Transform the GCP Storage Bucket response object for Neo4j ingestion
|
|
61
64
|
|
|
62
65
|
:type bucket_res: The GCP storage resource object (https://cloud.google.com/storage/docs/json_api/v1/buckets)
|
|
@@ -64,40 +67,52 @@ def transform_gcp_buckets(bucket_res: Dict) -> List[Dict]:
|
|
|
64
67
|
|
|
65
68
|
:rtype: list
|
|
66
69
|
:return: List of buckets ready for ingestion to Neo4j
|
|
67
|
-
|
|
70
|
+
"""
|
|
68
71
|
|
|
69
72
|
bucket_list = []
|
|
70
|
-
for b in bucket_res.get(
|
|
73
|
+
for b in bucket_res.get("items", []):
|
|
71
74
|
bucket = {}
|
|
72
|
-
bucket[
|
|
73
|
-
bucket[
|
|
74
|
-
b.get(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
bucket[
|
|
79
|
-
bucket[
|
|
80
|
-
bucket[
|
|
81
|
-
bucket[
|
|
82
|
-
bucket[
|
|
83
|
-
bucket[
|
|
84
|
-
bucket[
|
|
85
|
-
bucket[
|
|
86
|
-
bucket[
|
|
87
|
-
bucket[
|
|
88
|
-
bucket[
|
|
89
|
-
bucket[
|
|
90
|
-
bucket[
|
|
91
|
-
bucket[
|
|
92
|
-
bucket[
|
|
93
|
-
bucket[
|
|
75
|
+
bucket["etag"] = b.get("etag")
|
|
76
|
+
bucket["iam_config_bucket_policy_only"] = (
|
|
77
|
+
b.get("iamConfiguration", {})
|
|
78
|
+
.get("bucketPolicyOnly", {})
|
|
79
|
+
.get("enabled", None)
|
|
80
|
+
)
|
|
81
|
+
bucket["id"] = b["id"]
|
|
82
|
+
bucket["labels"] = [(key, val) for (key, val) in b.get("labels", {}).items()]
|
|
83
|
+
bucket["owner_entity"] = b.get("owner", {}).get("entity")
|
|
84
|
+
bucket["owner_entity_id"] = b.get("owner", {}).get("entityId")
|
|
85
|
+
bucket["kind"] = b.get("kind")
|
|
86
|
+
bucket["location"] = b.get("location")
|
|
87
|
+
bucket["location_type"] = b.get("locationType")
|
|
88
|
+
bucket["meta_generation"] = b.get("metageneration", None)
|
|
89
|
+
bucket["project_number"] = b["projectNumber"]
|
|
90
|
+
bucket["self_link"] = b.get("selfLink")
|
|
91
|
+
bucket["storage_class"] = b.get("storageClass")
|
|
92
|
+
bucket["time_created"] = b.get("timeCreated")
|
|
93
|
+
bucket["updated"] = b.get("updated")
|
|
94
|
+
bucket["versioning_enabled"] = b.get("versioning", {}).get("enabled", None)
|
|
95
|
+
bucket["default_event_based_hold"] = b.get("defaultEventBasedHold", None)
|
|
96
|
+
bucket["retention_period"] = b.get("retentionPolicy", {}).get(
|
|
97
|
+
"retentionPeriod",
|
|
98
|
+
None,
|
|
99
|
+
)
|
|
100
|
+
bucket["default_kms_key_name"] = b.get("encryption", {}).get(
|
|
101
|
+
"defaultKmsKeyName",
|
|
102
|
+
)
|
|
103
|
+
bucket["log_bucket"] = b.get("logging", {}).get("logBucket")
|
|
104
|
+
bucket["requester_pays"] = b.get("billing", {}).get("requesterPays", None)
|
|
94
105
|
bucket_list.append(bucket)
|
|
95
106
|
return bucket_list
|
|
96
107
|
|
|
97
108
|
|
|
98
109
|
@timeit
|
|
99
|
-
def load_gcp_buckets(
|
|
100
|
-
|
|
110
|
+
def load_gcp_buckets(
|
|
111
|
+
neo4j_session: neo4j.Session,
|
|
112
|
+
buckets: List[Dict],
|
|
113
|
+
gcp_update_tag: int,
|
|
114
|
+
) -> None:
|
|
115
|
+
"""
|
|
101
116
|
Ingest GCP Storage Buckets to Neo4j
|
|
102
117
|
|
|
103
118
|
:type neo4j_session: Neo4j session object
|
|
@@ -111,7 +126,7 @@ def load_gcp_buckets(neo4j_session: neo4j.Session, buckets: List[Dict], gcp_upda
|
|
|
111
126
|
|
|
112
127
|
:rtype: NoneType
|
|
113
128
|
:return: Nothing
|
|
114
|
-
|
|
129
|
+
"""
|
|
115
130
|
|
|
116
131
|
query = """
|
|
117
132
|
MERGE(p:GCPProject{projectnumber:$ProjectNumber})
|
|
@@ -146,30 +161,34 @@ def load_gcp_buckets(neo4j_session: neo4j.Session, buckets: List[Dict], gcp_upda
|
|
|
146
161
|
for bucket in buckets:
|
|
147
162
|
neo4j_session.run(
|
|
148
163
|
query,
|
|
149
|
-
ProjectNumber=bucket[
|
|
150
|
-
BucketId=bucket[
|
|
151
|
-
SelfLink=bucket[
|
|
152
|
-
Kind=bucket[
|
|
153
|
-
Location=bucket[
|
|
154
|
-
LocationType=bucket[
|
|
155
|
-
MetaGeneration=bucket[
|
|
156
|
-
StorageClass=bucket[
|
|
157
|
-
TimeCreated=bucket[
|
|
158
|
-
RetentionPeriod=bucket[
|
|
159
|
-
IamConfigBucketPolicyOnly=bucket[
|
|
160
|
-
OwnerEntity=bucket[
|
|
161
|
-
OwnerEntityId=bucket[
|
|
162
|
-
VersioningEnabled=bucket[
|
|
163
|
-
LogBucket=bucket[
|
|
164
|
-
RequesterPays=bucket[
|
|
165
|
-
DefaultKmsKeyName=bucket[
|
|
164
|
+
ProjectNumber=bucket["project_number"],
|
|
165
|
+
BucketId=bucket["id"],
|
|
166
|
+
SelfLink=bucket["self_link"],
|
|
167
|
+
Kind=bucket["kind"],
|
|
168
|
+
Location=bucket["location"],
|
|
169
|
+
LocationType=bucket["location_type"],
|
|
170
|
+
MetaGeneration=bucket["meta_generation"],
|
|
171
|
+
StorageClass=bucket["storage_class"],
|
|
172
|
+
TimeCreated=bucket["time_created"],
|
|
173
|
+
RetentionPeriod=bucket["retention_period"],
|
|
174
|
+
IamConfigBucketPolicyOnly=bucket["iam_config_bucket_policy_only"],
|
|
175
|
+
OwnerEntity=bucket["owner_entity"],
|
|
176
|
+
OwnerEntityId=bucket["owner_entity_id"],
|
|
177
|
+
VersioningEnabled=bucket["versioning_enabled"],
|
|
178
|
+
LogBucket=bucket["log_bucket"],
|
|
179
|
+
RequesterPays=bucket["requester_pays"],
|
|
180
|
+
DefaultKmsKeyName=bucket["default_kms_key_name"],
|
|
166
181
|
gcp_update_tag=gcp_update_tag,
|
|
167
182
|
)
|
|
168
183
|
_attach_gcp_bucket_labels(neo4j_session, bucket, gcp_update_tag)
|
|
169
184
|
|
|
170
185
|
|
|
171
186
|
@timeit
|
|
172
|
-
def _attach_gcp_bucket_labels(
|
|
187
|
+
def _attach_gcp_bucket_labels(
|
|
188
|
+
neo4j_session: neo4j.Session,
|
|
189
|
+
bucket: Resource,
|
|
190
|
+
gcp_update_tag: int,
|
|
191
|
+
) -> None:
|
|
173
192
|
"""
|
|
174
193
|
Attach GCP bucket labels to the bucket.
|
|
175
194
|
:param neo4j_session: The neo4j session
|
|
@@ -189,19 +208,22 @@ def _attach_gcp_bucket_labels(neo4j_session: neo4j.Session, bucket: Resource, gc
|
|
|
189
208
|
ON CREATE SET r.firstseen = timestamp()
|
|
190
209
|
SET r.lastupdated = $gcp_update_tag
|
|
191
210
|
"""
|
|
192
|
-
for
|
|
211
|
+
for key, val in bucket.get("labels", []):
|
|
193
212
|
neo4j_session.run(
|
|
194
213
|
query,
|
|
195
214
|
BucketLabelId=f"GCPBucket_{key}",
|
|
196
215
|
Key=key,
|
|
197
216
|
Value=val,
|
|
198
|
-
BucketId=bucket[
|
|
217
|
+
BucketId=bucket["id"],
|
|
199
218
|
gcp_update_tag=gcp_update_tag,
|
|
200
219
|
)
|
|
201
220
|
|
|
202
221
|
|
|
203
222
|
@timeit
|
|
204
|
-
def cleanup_gcp_buckets(
|
|
223
|
+
def cleanup_gcp_buckets(
|
|
224
|
+
neo4j_session: neo4j.Session,
|
|
225
|
+
common_job_parameters: Dict,
|
|
226
|
+
) -> None:
|
|
205
227
|
"""
|
|
206
228
|
Delete out-of-date GCP Storage Bucket nodes and relationships
|
|
207
229
|
|
|
@@ -214,12 +236,19 @@ def cleanup_gcp_buckets(neo4j_session: neo4j.Session, common_job_parameters: Dic
|
|
|
214
236
|
:rtype: NoneType
|
|
215
237
|
:return: Nothing
|
|
216
238
|
"""
|
|
217
|
-
run_cleanup_job(
|
|
239
|
+
run_cleanup_job(
|
|
240
|
+
"gcp_storage_bucket_cleanup.json",
|
|
241
|
+
neo4j_session,
|
|
242
|
+
common_job_parameters,
|
|
243
|
+
)
|
|
218
244
|
|
|
219
245
|
|
|
220
246
|
@timeit
|
|
221
247
|
def sync_gcp_buckets(
|
|
222
|
-
neo4j_session: neo4j.Session,
|
|
248
|
+
neo4j_session: neo4j.Session,
|
|
249
|
+
storage: Resource,
|
|
250
|
+
project_id: str,
|
|
251
|
+
gcp_update_tag: int,
|
|
223
252
|
common_job_parameters: Dict,
|
|
224
253
|
) -> None:
|
|
225
254
|
"""
|
|
@@ -247,5 +276,5 @@ def sync_gcp_buckets(
|
|
|
247
276
|
storage_res = get_gcp_buckets(storage, project_id)
|
|
248
277
|
bucket_list = transform_gcp_buckets(storage_res)
|
|
249
278
|
load_gcp_buckets(neo4j_session, bucket_list, gcp_update_tag)
|
|
250
|
-
# TODO scope the cleanup to the current project - https://github.com/
|
|
279
|
+
# TODO scope the cleanup to the current project - https://github.com/cartography-cncf/cartography/issues/381
|
|
251
280
|
cleanup_gcp_buckets(neo4j_session, common_job_parameters)
|
|
@@ -23,7 +23,9 @@ def start_github_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
|
|
|
23
23
|
:return: None
|
|
24
24
|
"""
|
|
25
25
|
if not config.github_config:
|
|
26
|
-
logger.info(
|
|
26
|
+
logger.info(
|
|
27
|
+
"GitHub import is not configured - skipping this module. See docs to configure.",
|
|
28
|
+
)
|
|
27
29
|
return
|
|
28
30
|
|
|
29
31
|
auth_tokens = json.loads(base64.b64decode(config.github_config).decode())
|
|
@@ -31,28 +33,28 @@ def start_github_ingestion(neo4j_session: neo4j.Session, config: Config) -> None
|
|
|
31
33
|
"UPDATE_TAG": config.update_tag,
|
|
32
34
|
}
|
|
33
35
|
# run sync for the provided github tokens
|
|
34
|
-
for auth_data in auth_tokens[
|
|
36
|
+
for auth_data in auth_tokens["organization"]:
|
|
35
37
|
try:
|
|
36
38
|
cartography.intel.github.users.sync(
|
|
37
39
|
neo4j_session,
|
|
38
40
|
common_job_parameters,
|
|
39
|
-
auth_data[
|
|
40
|
-
auth_data[
|
|
41
|
-
auth_data[
|
|
41
|
+
auth_data["token"],
|
|
42
|
+
auth_data["url"],
|
|
43
|
+
auth_data["name"],
|
|
42
44
|
)
|
|
43
45
|
cartography.intel.github.repos.sync(
|
|
44
46
|
neo4j_session,
|
|
45
47
|
common_job_parameters,
|
|
46
|
-
auth_data[
|
|
47
|
-
auth_data[
|
|
48
|
-
auth_data[
|
|
48
|
+
auth_data["token"],
|
|
49
|
+
auth_data["url"],
|
|
50
|
+
auth_data["name"],
|
|
49
51
|
)
|
|
50
52
|
cartography.intel.github.teams.sync_github_teams(
|
|
51
53
|
neo4j_session,
|
|
52
54
|
common_job_parameters,
|
|
53
|
-
auth_data[
|
|
54
|
-
auth_data[
|
|
55
|
-
auth_data[
|
|
55
|
+
auth_data["token"],
|
|
56
|
+
auth_data["url"],
|
|
57
|
+
auth_data["name"],
|
|
56
58
|
)
|
|
57
59
|
except exceptions.RequestException as e:
|
|
58
60
|
logger.error("Could not complete request to the GitHub API: %s", e)
|