cartography 0.102.0rc1__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 +327 -0
- 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 -44
- 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 +97 -0
- cartography/models/aws/ec2/route_tables.py +128 -0
- cartography/models/aws/ec2/routes.py +85 -0
- 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.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/METADATA +3 -14
- cartography-0.103.0rc1.dist-info/RECORD +396 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
- cartography-0.102.0rc1.dist-info/RECORD +0 -377
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
cartography/intel/okta/roles.py
CHANGED
|
@@ -26,7 +26,7 @@ def _get_user_roles(api_client: ApiClient, user_id: str, okta_org_id: str) -> st
|
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
28
|
# https://developer.okta.com/docs/reference/api/roles/#list-roles
|
|
29
|
-
response = api_client.get_path(f
|
|
29
|
+
response = api_client.get_path(f"/{user_id}/roles")
|
|
30
30
|
check_rate_limit(response)
|
|
31
31
|
return response.text
|
|
32
32
|
|
|
@@ -42,7 +42,7 @@ def _get_group_roles(api_client: ApiClient, group_id: str, okta_org_id: str) ->
|
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
44
|
# https://developer.okta.com/docs/reference/api/roles/#list-roles-assigned-to-group
|
|
45
|
-
response = api_client.get_path(f
|
|
45
|
+
response = api_client.get_path(f"/{group_id}/roles")
|
|
46
46
|
check_rate_limit(response)
|
|
47
47
|
return response.text
|
|
48
48
|
|
|
@@ -94,7 +94,12 @@ def transform_group_roles_data(data: str, okta_org_id: str) -> List[Dict]:
|
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
@timeit
|
|
97
|
-
def _load_user_role(
|
|
97
|
+
def _load_user_role(
|
|
98
|
+
neo4j_session: neo4j.Session,
|
|
99
|
+
user_id: str,
|
|
100
|
+
roles_data: List[Dict],
|
|
101
|
+
okta_update_tag: int,
|
|
102
|
+
) -> None:
|
|
98
103
|
ingest = """
|
|
99
104
|
MATCH (user:OktaUser{id: $USER_ID})<-[:RESOURCE]-(org:OktaOrganization)
|
|
100
105
|
WITH user,org
|
|
@@ -122,7 +127,9 @@ def _load_user_role(neo4j_session: neo4j.Session, user_id: str, roles_data: List
|
|
|
122
127
|
|
|
123
128
|
@timeit
|
|
124
129
|
def _load_group_role(
|
|
125
|
-
neo4j_session: neo4j.Session,
|
|
130
|
+
neo4j_session: neo4j.Session,
|
|
131
|
+
group_id: str,
|
|
132
|
+
roles_data: List[Dict],
|
|
126
133
|
okta_update_tag: int,
|
|
127
134
|
) -> None:
|
|
128
135
|
ingest = """
|
|
@@ -152,7 +159,10 @@ def _load_group_role(
|
|
|
152
159
|
|
|
153
160
|
@timeit
|
|
154
161
|
def sync_roles(
|
|
155
|
-
neo4j_session: str,
|
|
162
|
+
neo4j_session: str,
|
|
163
|
+
okta_org_id: str,
|
|
164
|
+
okta_update_tag: int,
|
|
165
|
+
okta_api_key: str,
|
|
156
166
|
sync_state: OktaSyncState,
|
|
157
167
|
) -> None:
|
|
158
168
|
"""
|
cartography/intel/okta/users.py
CHANGED
|
@@ -12,7 +12,6 @@ from cartography.intel.okta.sync_state import OktaSyncState
|
|
|
12
12
|
from cartography.intel.okta.utils import check_rate_limit
|
|
13
13
|
from cartography.util import timeit
|
|
14
14
|
|
|
15
|
-
|
|
16
15
|
logger = logging.getLogger(__name__)
|
|
17
16
|
|
|
18
17
|
|
|
@@ -56,7 +55,9 @@ def _get_okta_users(user_client: UsersClient) -> List[Dict]:
|
|
|
56
55
|
|
|
57
56
|
|
|
58
57
|
@timeit
|
|
59
|
-
def transform_okta_user_list(
|
|
58
|
+
def transform_okta_user_list(
|
|
59
|
+
okta_user_list: List[User],
|
|
60
|
+
) -> Tuple[List[Dict], List[str]]:
|
|
60
61
|
users: List[Dict] = []
|
|
61
62
|
user_ids: List[str] = []
|
|
62
63
|
|
|
@@ -91,7 +92,9 @@ def transform_okta_user(okta_user: User) -> Dict:
|
|
|
91
92
|
user_props["activated"] = None
|
|
92
93
|
|
|
93
94
|
if okta_user.statusChanged:
|
|
94
|
-
user_props["status_changed"] = okta_user.statusChanged.strftime(
|
|
95
|
+
user_props["status_changed"] = okta_user.statusChanged.strftime(
|
|
96
|
+
"%m/%d/%Y, %H:%M:%S",
|
|
97
|
+
)
|
|
95
98
|
else:
|
|
96
99
|
user_props["status_changed"] = None
|
|
97
100
|
|
|
@@ -101,12 +104,16 @@ def transform_okta_user(okta_user: User) -> Dict:
|
|
|
101
104
|
user_props["last_login"] = None
|
|
102
105
|
|
|
103
106
|
if okta_user.lastUpdated:
|
|
104
|
-
user_props["okta_last_updated"] = okta_user.lastUpdated.strftime(
|
|
107
|
+
user_props["okta_last_updated"] = okta_user.lastUpdated.strftime(
|
|
108
|
+
"%m/%d/%Y, %H:%M:%S",
|
|
109
|
+
)
|
|
105
110
|
else:
|
|
106
111
|
user_props["okta_last_updated"] = None
|
|
107
112
|
|
|
108
113
|
if okta_user.passwordChanged:
|
|
109
|
-
user_props["password_changed"] = okta_user.passwordChanged.strftime(
|
|
114
|
+
user_props["password_changed"] = okta_user.passwordChanged.strftime(
|
|
115
|
+
"%m/%d/%Y, %H:%M:%S",
|
|
116
|
+
)
|
|
110
117
|
else:
|
|
111
118
|
user_props["password_changed"] = None
|
|
112
119
|
|
|
@@ -120,7 +127,9 @@ def transform_okta_user(okta_user: User) -> Dict:
|
|
|
120
127
|
|
|
121
128
|
@timeit
|
|
122
129
|
def _load_okta_users(
|
|
123
|
-
neo4j_session: neo4j.Session,
|
|
130
|
+
neo4j_session: neo4j.Session,
|
|
131
|
+
okta_org_id: str,
|
|
132
|
+
user_list: List[Dict],
|
|
124
133
|
okta_update_tag: int,
|
|
125
134
|
) -> None:
|
|
126
135
|
"""
|
|
@@ -175,8 +184,11 @@ def _load_okta_users(
|
|
|
175
184
|
|
|
176
185
|
@timeit
|
|
177
186
|
def sync_okta_users(
|
|
178
|
-
neo4j_session: neo4j.Session,
|
|
179
|
-
|
|
187
|
+
neo4j_session: neo4j.Session,
|
|
188
|
+
okta_org_id: str,
|
|
189
|
+
okta_update_tag: int,
|
|
190
|
+
okta_api_key: str,
|
|
191
|
+
sync_state: OktaSyncState,
|
|
180
192
|
) -> None:
|
|
181
193
|
"""
|
|
182
194
|
Sync okta users
|
cartography/intel/okta/utils.py
CHANGED
|
@@ -43,9 +43,9 @@ def check_rate_limit(response: Response) -> None:
|
|
|
43
43
|
"""
|
|
44
44
|
rate_limit_threshold = 0.1
|
|
45
45
|
|
|
46
|
-
remaining = response.headers.get(
|
|
47
|
-
limit = response.headers.get(
|
|
48
|
-
reset_time = response.headers.get(
|
|
46
|
+
remaining = response.headers.get("x-rate-limit-remaining")
|
|
47
|
+
limit = response.headers.get("x-rate-limit-limit")
|
|
48
|
+
reset_time = response.headers.get("x-rate-limit-reset")
|
|
49
49
|
|
|
50
50
|
if remaining and limit and reset_time:
|
|
51
51
|
if (int(remaining) / int(limit)) < rate_limit_threshold:
|
|
@@ -58,5 +58,7 @@ def check_rate_limit(response: Response) -> None:
|
|
|
58
58
|
f"Okta API limit exceeded. Sleep time of {sleep_time_seconds} would exceed one minute. Crashing "
|
|
59
59
|
f"Okta sync to avoid blocking.",
|
|
60
60
|
)
|
|
61
|
-
logger.warning(
|
|
61
|
+
logger.warning(
|
|
62
|
+
f"Okta rate limit threshold reached. Waiting {sleep_time_seconds} seconds.",
|
|
63
|
+
)
|
|
62
64
|
time.sleep(sleep_time_seconds)
|
|
@@ -4,9 +4,7 @@ import neo4j
|
|
|
4
4
|
from pdpyras import APISession
|
|
5
5
|
|
|
6
6
|
from cartography.config import Config
|
|
7
|
-
from cartography.intel.pagerduty.escalation_policies import
|
|
8
|
-
sync_escalation_policies,
|
|
9
|
-
)
|
|
7
|
+
from cartography.intel.pagerduty.escalation_policies import sync_escalation_policies
|
|
10
8
|
from cartography.intel.pagerduty.schedules import sync_schedules
|
|
11
9
|
from cartography.intel.pagerduty.services import sync_services
|
|
12
10
|
from cartography.intel.pagerduty.teams import sync_teams
|
|
@@ -23,7 +21,8 @@ stat_handler = get_stats_client(__name__)
|
|
|
23
21
|
|
|
24
22
|
@timeit
|
|
25
23
|
def start_pagerduty_ingestion(
|
|
26
|
-
neo4j_session: neo4j.Session,
|
|
24
|
+
neo4j_session: neo4j.Session,
|
|
25
|
+
config: Config,
|
|
27
26
|
) -> None:
|
|
28
27
|
"""
|
|
29
28
|
Perform ingestion of pagerduty data.
|
|
@@ -35,7 +34,9 @@ def start_pagerduty_ingestion(
|
|
|
35
34
|
"UPDATE_TAG": config.update_tag,
|
|
36
35
|
}
|
|
37
36
|
if not config.pagerduty_api_key:
|
|
38
|
-
logger.info(
|
|
37
|
+
logger.info(
|
|
38
|
+
"PagerDuty import is not configured - skipping this module. See docs to configure.",
|
|
39
|
+
)
|
|
39
40
|
return
|
|
40
41
|
session = APISession(config.pagerduty_api_key)
|
|
41
42
|
if config.pagerduty_request_timeout is not None:
|
|
@@ -54,8 +55,8 @@ def start_pagerduty_ingestion(
|
|
|
54
55
|
|
|
55
56
|
merge_module_sync_metadata(
|
|
56
57
|
neo4j_session,
|
|
57
|
-
group_type=
|
|
58
|
-
group_id=
|
|
58
|
+
group_type="pagerduty",
|
|
59
|
+
group_id="module",
|
|
59
60
|
synced_type="pagerduty",
|
|
60
61
|
update_tag=config.update_tag,
|
|
61
62
|
stat_handler=stat_handler,
|
|
@@ -31,7 +31,9 @@ def get_escalation_policies(pd_session: APISession) -> List[Dict[str, Any]]:
|
|
|
31
31
|
|
|
32
32
|
|
|
33
33
|
def load_escalation_policy_data(
|
|
34
|
-
neo4j_session: neo4j.Session,
|
|
34
|
+
neo4j_session: neo4j.Session,
|
|
35
|
+
data: List[Dict],
|
|
36
|
+
update_tag: int,
|
|
35
37
|
) -> None:
|
|
36
38
|
"""
|
|
37
39
|
Transform and load escalation_policy information
|
|
@@ -82,7 +84,9 @@ def load_escalation_policy_data(
|
|
|
82
84
|
|
|
83
85
|
|
|
84
86
|
def _attach_rules(
|
|
85
|
-
neo4j_session: neo4j.Session,
|
|
87
|
+
neo4j_session: neo4j.Session,
|
|
88
|
+
data: List[Dict],
|
|
89
|
+
update_tag: int,
|
|
86
90
|
) -> None:
|
|
87
91
|
"""
|
|
88
92
|
Add escalation policy rules, and attach them to targets.
|
|
@@ -122,7 +126,9 @@ def _attach_rules(
|
|
|
122
126
|
|
|
123
127
|
|
|
124
128
|
def _attach_user_targets(
|
|
125
|
-
neo4j_session: neo4j.Session,
|
|
129
|
+
neo4j_session: neo4j.Session,
|
|
130
|
+
data: List[Dict],
|
|
131
|
+
update_tag: int,
|
|
126
132
|
) -> None:
|
|
127
133
|
"""
|
|
128
134
|
Add relationship between escalation policy and services.
|
|
@@ -142,7 +148,9 @@ def _attach_user_targets(
|
|
|
142
148
|
|
|
143
149
|
|
|
144
150
|
def _attach_schedule_targets(
|
|
145
|
-
neo4j_session: neo4j.Session,
|
|
151
|
+
neo4j_session: neo4j.Session,
|
|
152
|
+
data: List[Dict],
|
|
153
|
+
update_tag: int,
|
|
146
154
|
) -> None:
|
|
147
155
|
"""
|
|
148
156
|
Add relationship between escalation policy and services.
|
|
@@ -162,7 +170,9 @@ def _attach_schedule_targets(
|
|
|
162
170
|
|
|
163
171
|
|
|
164
172
|
def _attach_services(
|
|
165
|
-
neo4j_session: neo4j.Session,
|
|
173
|
+
neo4j_session: neo4j.Session,
|
|
174
|
+
data: List[Dict],
|
|
175
|
+
update_tag: int,
|
|
166
176
|
) -> None:
|
|
167
177
|
"""
|
|
168
178
|
Add relationship between escalation policy and services.
|
|
@@ -182,7 +192,9 @@ def _attach_services(
|
|
|
182
192
|
|
|
183
193
|
|
|
184
194
|
def _attach_teams(
|
|
185
|
-
neo4j_session: neo4j.Session,
|
|
195
|
+
neo4j_session: neo4j.Session,
|
|
196
|
+
data: List[Dict],
|
|
197
|
+
update_tag: int,
|
|
186
198
|
) -> None:
|
|
187
199
|
"""
|
|
188
200
|
Add relationship between escalation policy and teams.
|
|
@@ -32,7 +32,9 @@ def get_schedules(pd_session: APISession) -> List[Dict[str, Any]]:
|
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def load_schedule_data(
|
|
35
|
-
neo4j_session: neo4j.Session,
|
|
35
|
+
neo4j_session: neo4j.Session,
|
|
36
|
+
data: List[Dict],
|
|
37
|
+
update_tag: int,
|
|
36
38
|
) -> None:
|
|
37
39
|
"""
|
|
38
40
|
Transform and load schedule information
|
|
@@ -72,7 +74,9 @@ def load_schedule_data(
|
|
|
72
74
|
|
|
73
75
|
|
|
74
76
|
def _attach_users(
|
|
75
|
-
neo4j_session: neo4j.Session,
|
|
77
|
+
neo4j_session: neo4j.Session,
|
|
78
|
+
data: List[Dict],
|
|
79
|
+
update_tag: int,
|
|
76
80
|
) -> None:
|
|
77
81
|
"""
|
|
78
82
|
Add relationship between schedule and users.
|
|
@@ -91,7 +95,9 @@ def _attach_users(
|
|
|
91
95
|
|
|
92
96
|
|
|
93
97
|
def _attach_layers(
|
|
94
|
-
neo4j_session: neo4j.Session,
|
|
98
|
+
neo4j_session: neo4j.Session,
|
|
99
|
+
data: List[Dict],
|
|
100
|
+
update_tag: int,
|
|
95
101
|
) -> None:
|
|
96
102
|
"""
|
|
97
103
|
Create layers for a schedule and attach them together
|
|
@@ -133,7 +139,9 @@ def _attach_layers(
|
|
|
133
139
|
|
|
134
140
|
|
|
135
141
|
def _attach_layer_users(
|
|
136
|
-
neo4j_session: neo4j.Session,
|
|
142
|
+
neo4j_session: neo4j.Session,
|
|
143
|
+
data: List[Dict],
|
|
144
|
+
update_tag: int,
|
|
137
145
|
) -> None:
|
|
138
146
|
"""
|
|
139
147
|
Add relationship between schedule layers and users.
|
|
@@ -34,7 +34,8 @@ def get_services(pd_session: APISession) -> List[Dict[str, Any]]:
|
|
|
34
34
|
|
|
35
35
|
@timeit
|
|
36
36
|
def get_integrations(
|
|
37
|
-
pd_session: APISession,
|
|
37
|
+
pd_session: APISession,
|
|
38
|
+
services: List[Dict[str, Any]],
|
|
38
39
|
) -> List[Dict[str, Any]]:
|
|
39
40
|
"""
|
|
40
41
|
Get integrations from services.
|
|
@@ -51,7 +52,9 @@ def get_integrations(
|
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
def load_service_data(
|
|
54
|
-
neo4j_session: neo4j.Session,
|
|
55
|
+
neo4j_session: neo4j.Session,
|
|
56
|
+
data: List[Dict],
|
|
57
|
+
update_tag: int,
|
|
55
58
|
) -> None:
|
|
56
59
|
"""
|
|
57
60
|
Transform and load service information
|
|
@@ -104,7 +107,9 @@ def load_service_data(
|
|
|
104
107
|
|
|
105
108
|
|
|
106
109
|
def _attach_teams(
|
|
107
|
-
neo4j_session: neo4j.Session,
|
|
110
|
+
neo4j_session: neo4j.Session,
|
|
111
|
+
data: List[Dict],
|
|
112
|
+
update_tag: int,
|
|
108
113
|
) -> None:
|
|
109
114
|
"""
|
|
110
115
|
Add relationship between teams and services.
|
|
@@ -123,7 +128,9 @@ def _attach_teams(
|
|
|
123
128
|
|
|
124
129
|
|
|
125
130
|
def load_integration_data(
|
|
126
|
-
neo4j_session: neo4j.Session,
|
|
131
|
+
neo4j_session: neo4j.Session,
|
|
132
|
+
data: List[Dict],
|
|
133
|
+
update_tag: int,
|
|
127
134
|
) -> None:
|
|
128
135
|
"""
|
|
129
136
|
Transform and load integration information
|
|
@@ -33,7 +33,8 @@ def get_teams(pd_session: APISession) -> List[Dict[str, Any]]:
|
|
|
33
33
|
|
|
34
34
|
@timeit
|
|
35
35
|
def get_team_members(
|
|
36
|
-
pd_session: APISession,
|
|
36
|
+
pd_session: APISession,
|
|
37
|
+
teams: List[Dict[str, Any]],
|
|
37
38
|
) -> List[Dict[str, str]]:
|
|
38
39
|
relations: List[Dict[str, str]] = []
|
|
39
40
|
for team in teams:
|
|
@@ -46,7 +47,9 @@ def get_team_members(
|
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
def load_team_data(
|
|
49
|
-
neo4j_session: neo4j.Session,
|
|
50
|
+
neo4j_session: neo4j.Session,
|
|
51
|
+
data: List[Dict],
|
|
52
|
+
update_tag: int,
|
|
50
53
|
) -> None:
|
|
51
54
|
"""
|
|
52
55
|
Transform and load teamuser information
|
|
@@ -73,7 +76,9 @@ def load_team_data(
|
|
|
73
76
|
|
|
74
77
|
|
|
75
78
|
def load_team_relations(
|
|
76
|
-
neo4j_session: neo4j.Session,
|
|
79
|
+
neo4j_session: neo4j.Session,
|
|
80
|
+
data: List[Dict],
|
|
81
|
+
update_tag: int,
|
|
77
82
|
) -> None:
|
|
78
83
|
"""
|
|
79
84
|
Attach users to their teams
|
|
@@ -30,7 +30,9 @@ def get_users(pd_session: APISession) -> List[Dict[str, Any]]:
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def load_user_data(
|
|
33
|
-
neo4j_session: neo4j.Session,
|
|
33
|
+
neo4j_session: neo4j.Session,
|
|
34
|
+
data: List[Dict],
|
|
35
|
+
update_tag: int,
|
|
34
36
|
) -> None:
|
|
35
37
|
"""
|
|
36
38
|
Transform and load user information
|
|
@@ -30,7 +30,9 @@ def get_vendors(pd_session: APISession) -> List[Dict[str, Any]]:
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def load_vendor_data(
|
|
33
|
-
neo4j_session: neo4j.Session,
|
|
33
|
+
neo4j_session: neo4j.Session,
|
|
34
|
+
data: List[Dict],
|
|
35
|
+
update_tag: int,
|
|
34
36
|
) -> None:
|
|
35
37
|
"""
|
|
36
38
|
Transform and load vendor information
|
|
@@ -8,23 +8,41 @@ from cartography.intel.semgrep.deployment import sync_deployment
|
|
|
8
8
|
from cartography.intel.semgrep.findings import sync_findings
|
|
9
9
|
from cartography.util import timeit
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
logger = logging.getLogger(__name__)
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
@timeit
|
|
16
15
|
def start_semgrep_ingestion(
|
|
17
|
-
neo4j_session: neo4j.Session,
|
|
16
|
+
neo4j_session: neo4j.Session,
|
|
17
|
+
config: Config,
|
|
18
18
|
) -> None:
|
|
19
19
|
common_job_parameters = {
|
|
20
20
|
"UPDATE_TAG": config.update_tag,
|
|
21
21
|
}
|
|
22
22
|
if not config.semgrep_app_token:
|
|
23
|
-
logger.info(
|
|
23
|
+
logger.info(
|
|
24
|
+
"Semgrep import is not configured - skipping this module. See docs to configure.",
|
|
25
|
+
)
|
|
24
26
|
return
|
|
25
27
|
|
|
26
28
|
# sync_deployment must be called first since it populates common_job_parameters
|
|
27
29
|
# with the deployment ID and slug, which are required by the other sync functions
|
|
28
|
-
sync_deployment(
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
sync_deployment(
|
|
31
|
+
neo4j_session,
|
|
32
|
+
config.semgrep_app_token,
|
|
33
|
+
config.update_tag,
|
|
34
|
+
common_job_parameters,
|
|
35
|
+
)
|
|
36
|
+
sync_dependencies(
|
|
37
|
+
neo4j_session,
|
|
38
|
+
config.semgrep_app_token,
|
|
39
|
+
config.semgrep_dependency_ecosystems,
|
|
40
|
+
config.update_tag,
|
|
41
|
+
common_job_parameters,
|
|
42
|
+
) # noqa: E501
|
|
43
|
+
sync_findings(
|
|
44
|
+
neo4j_session,
|
|
45
|
+
config.semgrep_app_token,
|
|
46
|
+
config.update_tag,
|
|
47
|
+
common_job_parameters,
|
|
48
|
+
)
|
|
@@ -26,30 +26,34 @@ _MAX_RETRIES = 3
|
|
|
26
26
|
# The keys in this dictionary must be in Semgrep's list of supported ecosystems, defined here:
|
|
27
27
|
# https://semgrep.dev/api/v1/docs/#tag/SupplyChainService/operation/semgrep_app.products.sca.handlers.dependency.list_dependencies_conexxion
|
|
28
28
|
ECOSYSTEM_TO_SCHEMA: Dict = {
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
"gomod": SemgrepGoLibrarySchema,
|
|
30
|
+
"npm": SemgrepNpmLibrarySchema,
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
def parse_and_validate_semgrep_ecosystems(ecosystems: str) -> List[str]:
|
|
35
35
|
validated_ecosystems: List[str] = []
|
|
36
|
-
for ecosystem in ecosystems.split(
|
|
36
|
+
for ecosystem in ecosystems.split(","):
|
|
37
37
|
ecosystem = ecosystem.strip().lower()
|
|
38
38
|
|
|
39
39
|
if ecosystem in ECOSYSTEM_TO_SCHEMA:
|
|
40
40
|
validated_ecosystems.append(ecosystem)
|
|
41
41
|
else:
|
|
42
|
-
valid_ecosystems: str =
|
|
42
|
+
valid_ecosystems: str = ",".join(ECOSYSTEM_TO_SCHEMA.keys())
|
|
43
43
|
raise ValueError(
|
|
44
44
|
f'Error parsing `semgrep-dependency-ecosystems`. You specified "{ecosystems}". '
|
|
45
45
|
f'Please check that your input is formatted as comma-separated values, e.g. "gomod,npm". '
|
|
46
|
-
f
|
|
46
|
+
f"Full list of supported ecosystems: {valid_ecosystems}.",
|
|
47
47
|
)
|
|
48
48
|
return validated_ecosystems
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
@timeit
|
|
52
|
-
def get_dependencies(
|
|
52
|
+
def get_dependencies(
|
|
53
|
+
semgrep_app_token: str,
|
|
54
|
+
deployment_id: str,
|
|
55
|
+
ecosystem: str,
|
|
56
|
+
) -> List[Dict[str, Any]]:
|
|
53
57
|
"""
|
|
54
58
|
Gets all dependencies for the given ecosystem within the given Semgrep deployment ID.
|
|
55
59
|
param: semgrep_app_token: The Semgrep App token to use for authentication.
|
|
@@ -73,14 +77,23 @@ def get_dependencies(semgrep_app_token: str, deployment_id: str, ecosystem: str)
|
|
|
73
77
|
},
|
|
74
78
|
}
|
|
75
79
|
|
|
76
|
-
logger.info(
|
|
80
|
+
logger.info(
|
|
81
|
+
f"Retrieving Semgrep {ecosystem} dependencies for deployment '{deployment_id}'.",
|
|
82
|
+
)
|
|
77
83
|
while has_more:
|
|
78
84
|
try:
|
|
79
|
-
response = requests.post(
|
|
85
|
+
response = requests.post(
|
|
86
|
+
deps_url,
|
|
87
|
+
json=request_data,
|
|
88
|
+
headers=headers,
|
|
89
|
+
timeout=_TIMEOUT,
|
|
90
|
+
)
|
|
80
91
|
response.raise_for_status()
|
|
81
92
|
data = response.json()
|
|
82
93
|
except (ReadTimeout, HTTPError):
|
|
83
|
-
logger.warning(
|
|
94
|
+
logger.warning(
|
|
95
|
+
f"Failed to retrieve Semgrep {ecosystem} dependencies for page {page}. Retrying...",
|
|
96
|
+
)
|
|
84
97
|
retries += 1
|
|
85
98
|
if retries >= _MAX_RETRIES:
|
|
86
99
|
raise
|
|
@@ -93,7 +106,9 @@ def get_dependencies(semgrep_app_token: str, deployment_id: str, ecosystem: str)
|
|
|
93
106
|
page += 1
|
|
94
107
|
request_data["cursor"] = data.get("cursor")
|
|
95
108
|
|
|
96
|
-
logger.info(
|
|
109
|
+
logger.info(
|
|
110
|
+
f"Retrieved {len(all_deps)} Semgrep {ecosystem} dependencies in {page} pages.",
|
|
111
|
+
)
|
|
97
112
|
return all_deps
|
|
98
113
|
|
|
99
114
|
|
|
@@ -142,19 +157,20 @@ def transform_dependencies(raw_deps: List[Dict[str, Any]]) -> List[Dict[str, Any
|
|
|
142
157
|
# If Semgrep eventually supports version specifiers, update this line accordingly.
|
|
143
158
|
specifier = f"=={version}"
|
|
144
159
|
|
|
145
|
-
deps.append(
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
160
|
+
deps.append(
|
|
161
|
+
{
|
|
162
|
+
# existing dependency properties:
|
|
163
|
+
"id": id,
|
|
164
|
+
"name": name,
|
|
165
|
+
"specifier": specifier,
|
|
166
|
+
"version": version,
|
|
167
|
+
"repo_url": repo_url,
|
|
168
|
+
# Semgrep-specific properties:
|
|
169
|
+
"ecosystem": raw_dep["ecosystem"],
|
|
170
|
+
"transitivity": raw_dep["transitivity"].lower(),
|
|
171
|
+
"url": raw_dep["definedAt"]["url"],
|
|
172
|
+
},
|
|
173
|
+
)
|
|
158
174
|
|
|
159
175
|
return deps
|
|
160
176
|
|
|
@@ -167,7 +183,9 @@ def load_dependencies(
|
|
|
167
183
|
deployment_id: str,
|
|
168
184
|
update_tag: int,
|
|
169
185
|
) -> None:
|
|
170
|
-
logger.info(
|
|
186
|
+
logger.info(
|
|
187
|
+
f"Loading {len(dependencies)} {dependency_schema().label} objects into the graph.",
|
|
188
|
+
)
|
|
171
189
|
load(
|
|
172
190
|
neo4j_session,
|
|
173
191
|
dependency_schema(),
|
|
@@ -183,8 +201,12 @@ def cleanup(
|
|
|
183
201
|
dependency_schema: Callable,
|
|
184
202
|
common_job_parameters: Dict[str, Any],
|
|
185
203
|
) -> None:
|
|
186
|
-
logger.info(
|
|
187
|
-
|
|
204
|
+
logger.info(
|
|
205
|
+
f"Running Semgrep Dependencies cleanup job for {dependency_schema().label}.",
|
|
206
|
+
)
|
|
207
|
+
GraphJob.from_node_schema(dependency_schema(), common_job_parameters).run(
|
|
208
|
+
neo4j_session,
|
|
209
|
+
)
|
|
188
210
|
|
|
189
211
|
|
|
190
212
|
@timeit
|
|
@@ -225,9 +247,9 @@ def sync_dependencies(
|
|
|
225
247
|
|
|
226
248
|
merge_module_sync_metadata(
|
|
227
249
|
neo4j_session=neo4j_session,
|
|
228
|
-
group_type=
|
|
250
|
+
group_type="Semgrep",
|
|
229
251
|
group_id=deployment_id,
|
|
230
|
-
synced_type=
|
|
252
|
+
synced_type="SemgrepDependency",
|
|
231
253
|
update_tag=update_tag,
|
|
232
254
|
stat_handler=stat_handler,
|
|
233
255
|
)
|
|
@@ -40,7 +40,9 @@ def get_deployment(semgrep_app_token: str) -> Dict[str, Any]:
|
|
|
40
40
|
|
|
41
41
|
@timeit
|
|
42
42
|
def load_semgrep_deployment(
|
|
43
|
-
neo4j_session: neo4j.Session,
|
|
43
|
+
neo4j_session: neo4j.Session,
|
|
44
|
+
deployment: Dict[str, Any],
|
|
45
|
+
update_tag: int,
|
|
44
46
|
) -> None:
|
|
45
47
|
logger.info(f"Loading SemgrepDeployment {deployment} into the graph.")
|
|
46
48
|
load(
|