cartography 0.102.0rc1__py3-none-any.whl → 0.103.0__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 +376 -249
- cartography/client/core/tx.py +39 -18
- cartography/config.py +28 -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/cloudwatch.py +93 -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/efs.py +93 -0
- 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 +57 -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/cloudflare/__init__.py +74 -0
- cartography/intel/cloudflare/accounts.py +57 -0
- cartography/intel/cloudflare/dnsrecords.py +64 -0
- cartography/intel/cloudflare/members.py +75 -0
- cartography/intel/cloudflare/roles.py +65 -0
- cartography/intel/cloudflare/zones.py +64 -0
- 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/openai/__init__.py +86 -0
- cartography/intel/openai/adminapikeys.py +90 -0
- cartography/intel/openai/apikeys.py +96 -0
- cartography/intel/openai/projects.py +94 -0
- cartography/intel/openai/serviceaccounts.py +82 -0
- cartography/intel/openai/users.py +78 -0
- cartography/intel/openai/util.py +29 -0
- 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/intel/tailscale/__init__.py +77 -0
- cartography/intel/tailscale/acls.py +146 -0
- cartography/intel/tailscale/devices.py +127 -0
- cartography/intel/tailscale/postureintegrations.py +81 -0
- cartography/intel/tailscale/tailnets.py +76 -0
- cartography/intel/tailscale/users.py +80 -0
- cartography/intel/tailscale/utils.py +132 -0
- 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/cloudwatch/__init__.py +0 -0
- cartography/models/aws/cloudwatch/loggroup.py +52 -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/efs/__init__.py +0 -0
- cartography/models/aws/efs/mount_target.py +52 -0
- 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/cloudflare/__init__.py +0 -0
- cartography/models/cloudflare/account.py +25 -0
- cartography/models/cloudflare/dnsrecord.py +55 -0
- cartography/models/cloudflare/member.py +82 -0
- cartography/models/cloudflare/role.py +44 -0
- cartography/models/cloudflare/zone.py +59 -0
- 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/openai/__init__.py +0 -0
- cartography/models/openai/adminapikey.py +90 -0
- cartography/models/openai/apikey.py +84 -0
- cartography/models/openai/organization.py +17 -0
- cartography/models/openai/project.py +70 -0
- cartography/models/openai/serviceaccount.py +50 -0
- cartography/models/openai/user.py +49 -0
- 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/models/tailscale/__init__.py +0 -0
- cartography/models/tailscale/device.py +95 -0
- cartography/models/tailscale/group.py +86 -0
- cartography/models/tailscale/postureintegration.py +58 -0
- cartography/models/tailscale/tag.py +102 -0
- cartography/models/tailscale/tailnet.py +29 -0
- cartography/models/tailscale/user.py +52 -0
- cartography/stats.py +3 -3
- cartography/sync.py +113 -31
- cartography/util.py +84 -62
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/METADATA +8 -15
- cartography-0.103.0.dist-info/RECORD +442 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/WHEEL +1 -1
- cartography-0.102.0rc1.dist-info/RECORD +0 -377
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/entry_points.txt +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0.dist-info}/top_level.txt +0 -0
|
@@ -13,11 +13,11 @@ from cartography.util import timeit
|
|
|
13
13
|
@timeit
|
|
14
14
|
@aws_handle_regions
|
|
15
15
|
def get_iam_instance_profiles(boto3_session: boto3.Session) -> list[dict[str, Any]]:
|
|
16
|
-
client = boto3_session.client(
|
|
17
|
-
paginator = client.get_paginator(
|
|
16
|
+
client = boto3_session.client("iam", config=get_botocore_config())
|
|
17
|
+
paginator = client.get_paginator("list_instance_profiles")
|
|
18
18
|
instance_profiles = []
|
|
19
19
|
for page in paginator.paginate():
|
|
20
|
-
instance_profiles.extend(page[
|
|
20
|
+
instance_profiles.extend(page["InstanceProfiles"])
|
|
21
21
|
return instance_profiles
|
|
22
22
|
|
|
23
23
|
|
|
@@ -25,12 +25,12 @@ def transform_instance_profiles(data: list[dict[str, Any]]) -> list[dict[str, An
|
|
|
25
25
|
transformed = []
|
|
26
26
|
for profile in data:
|
|
27
27
|
transformed_profile = {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
"Arn": profile["Arn"],
|
|
29
|
+
"CreateDate": profile["CreateDate"],
|
|
30
|
+
"InstanceProfileId": profile["InstanceProfileId"],
|
|
31
|
+
"InstanceProfileName": profile["InstanceProfileName"],
|
|
32
|
+
"Path": profile["Path"],
|
|
33
|
+
"Roles": [role["Arn"] for role in profile.get("Roles", [])],
|
|
34
34
|
}
|
|
35
35
|
transformed.append(transformed_profile)
|
|
36
36
|
return transformed
|
|
@@ -38,11 +38,11 @@ def transform_instance_profiles(data: list[dict[str, Any]]) -> list[dict[str, An
|
|
|
38
38
|
|
|
39
39
|
@timeit
|
|
40
40
|
def load_iam_instance_profiles(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
41
|
+
neo4j_session: neo4j.Session,
|
|
42
|
+
data: list[dict[str, Any]],
|
|
43
|
+
current_aws_account_id: str,
|
|
44
|
+
update_tag: int,
|
|
45
|
+
common_job_parameters: dict[str, Any],
|
|
46
46
|
) -> None:
|
|
47
47
|
load(
|
|
48
48
|
neo4j_session,
|
|
@@ -55,19 +55,19 @@ def load_iam_instance_profiles(
|
|
|
55
55
|
|
|
56
56
|
@timeit
|
|
57
57
|
def sync_iam_instance_profiles(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
boto3_session: boto3.Session,
|
|
59
|
+
neo4j_session: neo4j.Session,
|
|
60
|
+
current_aws_account_id: str,
|
|
61
|
+
update_tag: int,
|
|
62
|
+
regions: list[str],
|
|
63
|
+
common_job_parameters: dict[str, Any],
|
|
64
64
|
) -> None:
|
|
65
65
|
profiles = get_iam_instance_profiles(boto3_session)
|
|
66
66
|
profiles = transform_instance_profiles(profiles)
|
|
67
67
|
load_iam_instance_profiles(
|
|
68
68
|
neo4j_session,
|
|
69
69
|
profiles,
|
|
70
|
-
common_job_parameters[
|
|
71
|
-
common_job_parameters[
|
|
70
|
+
common_job_parameters["AWS_ID"],
|
|
71
|
+
common_job_parameters["UPDATE_TAG"],
|
|
72
72
|
common_job_parameters,
|
|
73
73
|
)
|
|
@@ -8,27 +8,35 @@ import neo4j
|
|
|
8
8
|
|
|
9
9
|
from cartography.client.core.tx import load
|
|
10
10
|
from cartography.graph.job import GraphJob
|
|
11
|
-
from cartography.models.aws.identitycenter.awsidentitycenter import
|
|
12
|
-
|
|
11
|
+
from cartography.models.aws.identitycenter.awsidentitycenter import (
|
|
12
|
+
AWSIdentityCenterInstanceSchema,
|
|
13
|
+
)
|
|
14
|
+
from cartography.models.aws.identitycenter.awspermissionset import (
|
|
15
|
+
AWSPermissionSetSchema,
|
|
16
|
+
)
|
|
13
17
|
from cartography.models.aws.identitycenter.awsssouser import AWSSSOUserSchema
|
|
14
18
|
from cartography.util import aws_handle_regions
|
|
15
19
|
from cartography.util import run_cleanup_job
|
|
16
20
|
from cartography.util import timeit
|
|
21
|
+
|
|
17
22
|
logger = logging.getLogger(__name__)
|
|
18
23
|
|
|
19
24
|
|
|
20
25
|
@timeit
|
|
21
26
|
@aws_handle_regions
|
|
22
|
-
def get_identity_center_instances(
|
|
27
|
+
def get_identity_center_instances(
|
|
28
|
+
boto3_session: boto3.session.Session,
|
|
29
|
+
region: str,
|
|
30
|
+
) -> List[Dict]:
|
|
23
31
|
"""
|
|
24
32
|
Get all AWS IAM Identity Center instances in the current region
|
|
25
33
|
"""
|
|
26
|
-
client = boto3_session.client(
|
|
34
|
+
client = boto3_session.client("sso-admin", region_name=region)
|
|
27
35
|
instances = []
|
|
28
36
|
|
|
29
|
-
paginator = client.get_paginator(
|
|
37
|
+
paginator = client.get_paginator("list_instances")
|
|
30
38
|
for page in paginator.paginate():
|
|
31
|
-
instances.extend(page.get(
|
|
39
|
+
instances.extend(page.get("Instances", []))
|
|
32
40
|
|
|
33
41
|
return instances
|
|
34
42
|
|
|
@@ -44,7 +52,9 @@ def load_identity_center_instances(
|
|
|
44
52
|
"""
|
|
45
53
|
Load Identity Center instances into the graph
|
|
46
54
|
"""
|
|
47
|
-
logger.info(
|
|
55
|
+
logger.info(
|
|
56
|
+
f"Loading {len(instance_data)} Identity Center instances for region {region}",
|
|
57
|
+
)
|
|
48
58
|
load(
|
|
49
59
|
neo4j_session,
|
|
50
60
|
AWSIdentityCenterInstanceSchema(),
|
|
@@ -57,24 +67,28 @@ def load_identity_center_instances(
|
|
|
57
67
|
|
|
58
68
|
@timeit
|
|
59
69
|
@aws_handle_regions
|
|
60
|
-
def get_permission_sets(
|
|
70
|
+
def get_permission_sets(
|
|
71
|
+
boto3_session: boto3.session.Session,
|
|
72
|
+
instance_arn: str,
|
|
73
|
+
region: str,
|
|
74
|
+
) -> List[Dict]:
|
|
61
75
|
"""
|
|
62
76
|
Get all permission sets for a given Identity Center instance
|
|
63
77
|
"""
|
|
64
|
-
client = boto3_session.client(
|
|
78
|
+
client = boto3_session.client("sso-admin", region_name=region)
|
|
65
79
|
permission_sets = []
|
|
66
80
|
|
|
67
|
-
paginator = client.get_paginator(
|
|
81
|
+
paginator = client.get_paginator("list_permission_sets")
|
|
68
82
|
for page in paginator.paginate(InstanceArn=instance_arn):
|
|
69
83
|
# Get detailed info for each permission set
|
|
70
|
-
for arn in page.get(
|
|
84
|
+
for arn in page.get("PermissionSets", []):
|
|
71
85
|
details = client.describe_permission_set(
|
|
72
86
|
InstanceArn=instance_arn,
|
|
73
87
|
PermissionSetArn=arn,
|
|
74
88
|
)
|
|
75
|
-
permission_set = details.get(
|
|
89
|
+
permission_set = details.get("PermissionSet", {})
|
|
76
90
|
if permission_set:
|
|
77
|
-
permission_set[
|
|
91
|
+
permission_set["RoleHint"] = (
|
|
78
92
|
f":role/aws-reserved/sso.amazonaws.com/AWSReservedSSO_{permission_set.get('Name')}"
|
|
79
93
|
)
|
|
80
94
|
permission_sets.append(permission_set)
|
|
@@ -94,7 +108,9 @@ def load_permission_sets(
|
|
|
94
108
|
"""
|
|
95
109
|
Load Identity Center permission sets into the graph
|
|
96
110
|
"""
|
|
97
|
-
logger.info(
|
|
111
|
+
logger.info(
|
|
112
|
+
f"Loading {len(permission_sets)} permission sets for instance {instance_arn} in region {region}",
|
|
113
|
+
)
|
|
98
114
|
|
|
99
115
|
load(
|
|
100
116
|
neo4j_session,
|
|
@@ -117,15 +133,15 @@ def get_sso_users(
|
|
|
117
133
|
"""
|
|
118
134
|
Get all SSO users for a given Identity Store
|
|
119
135
|
"""
|
|
120
|
-
client = boto3_session.client(
|
|
136
|
+
client = boto3_session.client("identitystore", region_name=region)
|
|
121
137
|
users = []
|
|
122
138
|
|
|
123
|
-
paginator = client.get_paginator(
|
|
139
|
+
paginator = client.get_paginator("list_users")
|
|
124
140
|
for page in paginator.paginate(IdentityStoreId=identity_store_id):
|
|
125
|
-
user_page = page.get(
|
|
141
|
+
user_page = page.get("Users", [])
|
|
126
142
|
for user in user_page:
|
|
127
|
-
if user.get(
|
|
128
|
-
user[
|
|
143
|
+
if user.get("ExternalIds", None):
|
|
144
|
+
user["ExternalId"] = user.get("ExternalIds")[0].get("Id")
|
|
129
145
|
users.append(user)
|
|
130
146
|
|
|
131
147
|
return users
|
|
@@ -143,7 +159,9 @@ def load_sso_users(
|
|
|
143
159
|
"""
|
|
144
160
|
Load SSO users into the graph
|
|
145
161
|
"""
|
|
146
|
-
logger.info(
|
|
162
|
+
logger.info(
|
|
163
|
+
f"Loading {len(users)} SSO users for identity store {identity_store_id} in region {region}",
|
|
164
|
+
)
|
|
147
165
|
|
|
148
166
|
load(
|
|
149
167
|
neo4j_session,
|
|
@@ -169,19 +187,25 @@ def get_role_assignments(
|
|
|
169
187
|
"""
|
|
170
188
|
|
|
171
189
|
logger.info(f"Getting role assignments for {len(users)} users")
|
|
172
|
-
client = boto3_session.client(
|
|
190
|
+
client = boto3_session.client("sso-admin", region_name=region)
|
|
173
191
|
role_assignments = []
|
|
174
192
|
|
|
175
193
|
for user in users:
|
|
176
|
-
user_id = user[
|
|
177
|
-
paginator = client.get_paginator(
|
|
178
|
-
for page in paginator.paginate(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
194
|
+
user_id = user["UserId"]
|
|
195
|
+
paginator = client.get_paginator("list_account_assignments_for_principal")
|
|
196
|
+
for page in paginator.paginate(
|
|
197
|
+
InstanceArn=instance_arn,
|
|
198
|
+
PrincipalId=user_id,
|
|
199
|
+
PrincipalType="USER",
|
|
200
|
+
):
|
|
201
|
+
for assignment in page.get("AccountAssignments", []):
|
|
202
|
+
role_assignments.append(
|
|
203
|
+
{
|
|
204
|
+
"UserId": user_id,
|
|
205
|
+
"PermissionSetArn": assignment.get("PermissionSetArn"),
|
|
206
|
+
"AccountId": assignment.get("AccountId"),
|
|
207
|
+
},
|
|
208
|
+
)
|
|
185
209
|
|
|
186
210
|
return role_assignments
|
|
187
211
|
|
|
@@ -214,12 +238,22 @@ def load_role_assignments(
|
|
|
214
238
|
|
|
215
239
|
|
|
216
240
|
@timeit
|
|
217
|
-
def cleanup(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
241
|
+
def cleanup(
|
|
242
|
+
neo4j_session: neo4j.Session,
|
|
243
|
+
common_job_parameters: Dict[str, Any],
|
|
244
|
+
) -> None:
|
|
245
|
+
GraphJob.from_node_schema(
|
|
246
|
+
AWSIdentityCenterInstanceSchema(),
|
|
247
|
+
common_job_parameters,
|
|
248
|
+
).run(neo4j_session)
|
|
249
|
+
GraphJob.from_node_schema(AWSPermissionSetSchema(), common_job_parameters).run(
|
|
250
|
+
neo4j_session,
|
|
251
|
+
)
|
|
252
|
+
GraphJob.from_node_schema(AWSSSOUserSchema(), common_job_parameters).run(
|
|
253
|
+
neo4j_session,
|
|
254
|
+
)
|
|
221
255
|
run_cleanup_job(
|
|
222
|
-
|
|
256
|
+
"aws_import_identity_center_cleanup.json",
|
|
223
257
|
neo4j_session,
|
|
224
258
|
common_job_parameters,
|
|
225
259
|
)
|
|
@@ -251,8 +285,8 @@ def sync_identity_center_instances(
|
|
|
251
285
|
|
|
252
286
|
# For each instance, get and load its permission sets and SSO users
|
|
253
287
|
for instance in instances:
|
|
254
|
-
instance_arn = instance[
|
|
255
|
-
identity_store_id = instance[
|
|
288
|
+
instance_arn = instance["InstanceArn"]
|
|
289
|
+
identity_store_id = instance["IdentityStoreId"]
|
|
256
290
|
|
|
257
291
|
permission_sets = get_permission_sets(boto3_session, instance_arn, region)
|
|
258
292
|
|
|
@@ -15,15 +15,33 @@ from cartography.util import aws_handle_regions
|
|
|
15
15
|
from cartography.util import aws_paginate
|
|
16
16
|
from cartography.util import timeit
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
logger = logging.getLogger(__name__)
|
|
20
19
|
|
|
21
20
|
# As of 7/22/24, Inspector is only available in the below regions. We will need to update this hardcoded list here over
|
|
22
21
|
# time. :\ https://docs.aws.amazon.com/general/latest/gr/inspector2.html
|
|
23
22
|
AWS_INSPECTOR_REGIONS = {
|
|
24
|
-
"us-east-2",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
23
|
+
"us-east-2",
|
|
24
|
+
"us-east-1",
|
|
25
|
+
"us-west-1",
|
|
26
|
+
"us-west-2",
|
|
27
|
+
"af-south-1",
|
|
28
|
+
"ap-east-1",
|
|
29
|
+
"ap-southeast-3",
|
|
30
|
+
"ap-south-1",
|
|
31
|
+
"ap-northeast-3",
|
|
32
|
+
"ap-northeast-2",
|
|
33
|
+
"ap-southeast-1",
|
|
34
|
+
"ap-southeast-2",
|
|
35
|
+
"ap-northeast-1",
|
|
36
|
+
"ca-central-1",
|
|
37
|
+
"eu-central-1",
|
|
38
|
+
"eu-west-1",
|
|
39
|
+
"eu-west-2",
|
|
40
|
+
"eu-south-1",
|
|
41
|
+
"eu-west-3",
|
|
42
|
+
"eu-north-1",
|
|
43
|
+
"eu-central-2",
|
|
44
|
+
"me-south-1",
|
|
27
45
|
"sa-east-1",
|
|
28
46
|
}
|
|
29
47
|
|
|
@@ -31,9 +49,9 @@ AWS_INSPECTOR_REGIONS = {
|
|
|
31
49
|
@timeit
|
|
32
50
|
@aws_handle_regions
|
|
33
51
|
def get_inspector_findings(
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
52
|
+
session: boto3.session.Session,
|
|
53
|
+
region: str,
|
|
54
|
+
current_aws_account_id: str,
|
|
37
55
|
) -> List[Dict[str, Any]]:
|
|
38
56
|
"""
|
|
39
57
|
We must list_findings by filtering the request, otherwise the request could tiemout.
|
|
@@ -42,28 +60,31 @@ def get_inspector_findings(
|
|
|
42
60
|
list_members will get us all the accounts that
|
|
43
61
|
have delegated access to the account specified by current_aws_account_id.
|
|
44
62
|
"""
|
|
45
|
-
client = session.client(
|
|
63
|
+
client = session.client("inspector2", region_name=region)
|
|
46
64
|
|
|
47
|
-
members = aws_paginate(client,
|
|
65
|
+
members = aws_paginate(client, "list_members", "members")
|
|
48
66
|
# the current host account may not be considered a "member", but we still fetch its findings
|
|
49
|
-
accounts = [current_aws_account_id] + [m[
|
|
67
|
+
accounts = [current_aws_account_id] + [m["accountId"] for m in members]
|
|
50
68
|
|
|
51
69
|
findings = []
|
|
52
70
|
for account in accounts:
|
|
53
|
-
logger.info(f
|
|
71
|
+
logger.info(f"Getting findings for member account {account} in region {region}")
|
|
54
72
|
findings.extend(
|
|
55
73
|
aws_paginate(
|
|
56
|
-
client,
|
|
57
|
-
|
|
74
|
+
client,
|
|
75
|
+
"list_findings",
|
|
76
|
+
"findings",
|
|
77
|
+
filterCriteria={
|
|
78
|
+
"awsAccountId": [
|
|
58
79
|
{
|
|
59
|
-
|
|
60
|
-
|
|
80
|
+
"comparison": "EQUALS",
|
|
81
|
+
"value": account,
|
|
61
82
|
},
|
|
62
83
|
],
|
|
63
|
-
|
|
84
|
+
"findingStatus": [
|
|
64
85
|
{
|
|
65
|
-
|
|
66
|
-
|
|
86
|
+
"comparison": "NOT_EQUALS",
|
|
87
|
+
"value": "CLOSED",
|
|
67
88
|
},
|
|
68
89
|
],
|
|
69
90
|
},
|
|
@@ -72,48 +93,70 @@ def get_inspector_findings(
|
|
|
72
93
|
return findings
|
|
73
94
|
|
|
74
95
|
|
|
75
|
-
def transform_inspector_findings(
|
|
96
|
+
def transform_inspector_findings(
|
|
97
|
+
results: List[Dict[str, Any]],
|
|
98
|
+
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
|
|
76
99
|
findings_list: List[Dict] = []
|
|
77
100
|
packages: Dict[str, Any] = {}
|
|
78
101
|
|
|
79
102
|
for f in results:
|
|
80
103
|
finding: Dict = {}
|
|
81
104
|
|
|
82
|
-
finding[
|
|
83
|
-
finding[
|
|
84
|
-
finding[
|
|
85
|
-
finding[
|
|
86
|
-
finding[
|
|
87
|
-
finding[
|
|
88
|
-
finding[
|
|
89
|
-
finding[
|
|
90
|
-
finding[
|
|
91
|
-
finding[
|
|
92
|
-
if f.get(
|
|
93
|
-
finding[
|
|
94
|
-
if f[
|
|
95
|
-
finding[
|
|
96
|
-
if f[
|
|
97
|
-
finding[
|
|
98
|
-
if f[
|
|
99
|
-
finding[
|
|
100
|
-
if f.get(
|
|
101
|
-
finding[
|
|
102
|
-
finding[
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
finding[
|
|
109
|
-
|
|
110
|
-
finding[
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
finding[
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
finding[
|
|
105
|
+
finding["id"] = f["findingArn"]
|
|
106
|
+
finding["arn"] = f["findingArn"]
|
|
107
|
+
finding["severity"] = f["severity"]
|
|
108
|
+
finding["name"] = f["title"]
|
|
109
|
+
finding["firstobservedat"] = f["firstObservedAt"]
|
|
110
|
+
finding["updatedat"] = f["updatedAt"]
|
|
111
|
+
finding["awsaccount"] = f["awsAccountId"]
|
|
112
|
+
finding["description"] = f["description"]
|
|
113
|
+
finding["type"] = f["type"]
|
|
114
|
+
finding["status"] = f["status"]
|
|
115
|
+
if f.get("inspectorScoreDetails"):
|
|
116
|
+
finding["cvssscore"] = f["inspectorScoreDetails"]["adjustedCvss"]["score"]
|
|
117
|
+
if f["resources"][0]["type"] == "AWS_EC2_INSTANCE":
|
|
118
|
+
finding["instanceid"] = f["resources"][0]["id"]
|
|
119
|
+
if f["resources"][0]["type"] == "AWS_ECR_CONTAINER_IMAGE":
|
|
120
|
+
finding["ecrimageid"] = f["resources"][0]["id"].split("/")[2]
|
|
121
|
+
if f["resources"][0]["type"] == "AWS_ECR_REPOSITORY":
|
|
122
|
+
finding["ecrrepositoryid"] = f["resources"][0]["id"]
|
|
123
|
+
if f.get("networkReachabilityDetails"):
|
|
124
|
+
finding["protocol"] = f["networkReachabilityDetails"]["protocol"]
|
|
125
|
+
finding["portrangebegin"] = f["networkReachabilityDetails"][
|
|
126
|
+
"openPortRange"
|
|
127
|
+
]["begin"]
|
|
128
|
+
finding["portrangeend"] = f["networkReachabilityDetails"]["openPortRange"][
|
|
129
|
+
"end"
|
|
130
|
+
]
|
|
131
|
+
finding["portrange"] = _port_range_string(f["networkReachabilityDetails"])
|
|
132
|
+
if f.get("packageVulnerabilityDetails"):
|
|
133
|
+
finding["vulnerabilityid"] = f["packageVulnerabilityDetails"][
|
|
134
|
+
"vulnerabilityId"
|
|
135
|
+
]
|
|
136
|
+
finding["referenceurls"] = f["packageVulnerabilityDetails"].get(
|
|
137
|
+
"referenceUrls",
|
|
138
|
+
)
|
|
139
|
+
finding["relatedvulnerabilities"] = f["packageVulnerabilityDetails"].get(
|
|
140
|
+
"relatedVulnerabilities",
|
|
141
|
+
)
|
|
142
|
+
finding["source"] = f["packageVulnerabilityDetails"].get("source")
|
|
143
|
+
finding["sourceurl"] = f["packageVulnerabilityDetails"].get("sourceUrl")
|
|
144
|
+
finding["vendorcreatedat"] = f["packageVulnerabilityDetails"].get(
|
|
145
|
+
"vendorCreatedAt",
|
|
146
|
+
)
|
|
147
|
+
finding["vendorseverity"] = f["packageVulnerabilityDetails"].get(
|
|
148
|
+
"vendorSeverity",
|
|
149
|
+
)
|
|
150
|
+
finding["vendorupdatedat"] = f["packageVulnerabilityDetails"].get(
|
|
151
|
+
"vendorUpdatedAt",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
new_packages = _process_packages(
|
|
155
|
+
f["packageVulnerabilityDetails"],
|
|
156
|
+
f["awsAccountId"],
|
|
157
|
+
f["findingArn"],
|
|
158
|
+
)
|
|
159
|
+
finding["vulnerablepackageids"] = list(new_packages.keys())
|
|
117
160
|
packages = {**packages, **new_packages}
|
|
118
161
|
|
|
119
162
|
findings_list.append(finding)
|
|
@@ -129,47 +172,51 @@ def transform_inspector_packages(packages: Dict[str, Any]) -> List[Dict[str, Any
|
|
|
129
172
|
return packages_list
|
|
130
173
|
|
|
131
174
|
|
|
132
|
-
def _process_packages(
|
|
175
|
+
def _process_packages(
|
|
176
|
+
package_details: Dict[str, Any],
|
|
177
|
+
aws_account_id: str,
|
|
178
|
+
finding_arn: str,
|
|
179
|
+
) -> Dict[str, Any]:
|
|
133
180
|
packages: Dict[str, Any] = {}
|
|
134
|
-
for package in package_details[
|
|
181
|
+
for package in package_details["vulnerablePackages"]:
|
|
135
182
|
new_package = {}
|
|
136
|
-
new_package[
|
|
183
|
+
new_package["id"] = (
|
|
137
184
|
f"{package.get('name', '')}|"
|
|
138
185
|
f"{package.get('arch', '')}|"
|
|
139
186
|
f"{package.get('version', '')}|"
|
|
140
187
|
f"{package.get('release', '')}|"
|
|
141
188
|
f"{package.get('epoch', '')}"
|
|
142
189
|
)
|
|
143
|
-
new_package[
|
|
144
|
-
new_package[
|
|
145
|
-
new_package[
|
|
146
|
-
new_package[
|
|
147
|
-
new_package[
|
|
148
|
-
new_package[
|
|
149
|
-
new_package[
|
|
150
|
-
new_package[
|
|
151
|
-
new_package[
|
|
152
|
-
new_package[
|
|
153
|
-
new_package[
|
|
154
|
-
|
|
155
|
-
packages[new_package[
|
|
190
|
+
new_package["name"] = package.get("name")
|
|
191
|
+
new_package["arch"] = package.get("arch")
|
|
192
|
+
new_package["version"] = package.get("version")
|
|
193
|
+
new_package["release"] = package.get("release")
|
|
194
|
+
new_package["epoch"] = package.get("epoch")
|
|
195
|
+
new_package["manager"] = package.get("packageManager")
|
|
196
|
+
new_package["filepath"] = package.get("filePath")
|
|
197
|
+
new_package["fixedinversion"] = package.get("fixedInVersion")
|
|
198
|
+
new_package["sourcelayerhash"] = package.get("sourceLayerHash")
|
|
199
|
+
new_package["awsaccount"] = aws_account_id
|
|
200
|
+
new_package["findingarn"] = finding_arn
|
|
201
|
+
|
|
202
|
+
packages[new_package["id"]] = new_package
|
|
156
203
|
|
|
157
204
|
return packages
|
|
158
205
|
|
|
159
206
|
|
|
160
207
|
def _port_range_string(details: Dict[str, Any]) -> str:
|
|
161
|
-
begin = details[
|
|
162
|
-
end = details[
|
|
208
|
+
begin = details["openPortRange"]["begin"]
|
|
209
|
+
end = details["openPortRange"]["end"]
|
|
163
210
|
return f"{begin}-{end}"
|
|
164
211
|
|
|
165
212
|
|
|
166
213
|
@timeit
|
|
167
214
|
def load_inspector_findings(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
215
|
+
neo4j_session: neo4j.Session,
|
|
216
|
+
findings: List[Dict[str, Any]],
|
|
217
|
+
region: str,
|
|
218
|
+
aws_update_tag: int,
|
|
219
|
+
current_aws_account_id: str,
|
|
173
220
|
) -> None:
|
|
174
221
|
load(
|
|
175
222
|
neo4j_session,
|
|
@@ -183,11 +230,11 @@ def load_inspector_findings(
|
|
|
183
230
|
|
|
184
231
|
@timeit
|
|
185
232
|
def load_inspector_packages(
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
233
|
+
neo4j_session: neo4j.Session,
|
|
234
|
+
packages: List[Dict[str, Any]],
|
|
235
|
+
region: str,
|
|
236
|
+
aws_update_tag: int,
|
|
237
|
+
current_aws_account_id: str,
|
|
191
238
|
) -> None:
|
|
192
239
|
load(
|
|
193
240
|
neo4j_session,
|
|
@@ -200,10 +247,17 @@ def load_inspector_packages(
|
|
|
200
247
|
|
|
201
248
|
|
|
202
249
|
@timeit
|
|
203
|
-
def cleanup(
|
|
250
|
+
def cleanup(
|
|
251
|
+
neo4j_session: neo4j.Session,
|
|
252
|
+
common_job_parameters: Dict[str, Any],
|
|
253
|
+
) -> None:
|
|
204
254
|
logger.info("Running AWS Inspector cleanup")
|
|
205
|
-
GraphJob.from_node_schema(AWSInspectorFindingSchema(), common_job_parameters).run(
|
|
206
|
-
|
|
255
|
+
GraphJob.from_node_schema(AWSInspectorFindingSchema(), common_job_parameters).run(
|
|
256
|
+
neo4j_session,
|
|
257
|
+
)
|
|
258
|
+
GraphJob.from_node_schema(AWSInspectorPackageSchema(), common_job_parameters).run(
|
|
259
|
+
neo4j_session,
|
|
260
|
+
)
|
|
207
261
|
|
|
208
262
|
|
|
209
263
|
@timeit
|
|
@@ -215,14 +269,30 @@ def sync(
|
|
|
215
269
|
update_tag: int,
|
|
216
270
|
common_job_parameters: Dict[str, Any],
|
|
217
271
|
) -> None:
|
|
218
|
-
inspector_regions = [
|
|
272
|
+
inspector_regions = [
|
|
273
|
+
region for region in regions if region in AWS_INSPECTOR_REGIONS
|
|
274
|
+
]
|
|
219
275
|
|
|
220
276
|
for region in inspector_regions:
|
|
221
|
-
logger.info(
|
|
277
|
+
logger.info(
|
|
278
|
+
f"Syncing AWS Inspector findings for account {current_aws_account_id} and region {region}",
|
|
279
|
+
)
|
|
222
280
|
findings = get_inspector_findings(boto3_session, region, current_aws_account_id)
|
|
223
281
|
finding_data, package_data = transform_inspector_findings(findings)
|
|
224
282
|
logger.info(f"Loading {len(finding_data)} findings")
|
|
225
|
-
load_inspector_findings(
|
|
283
|
+
load_inspector_findings(
|
|
284
|
+
neo4j_session,
|
|
285
|
+
finding_data,
|
|
286
|
+
region,
|
|
287
|
+
update_tag,
|
|
288
|
+
current_aws_account_id,
|
|
289
|
+
)
|
|
226
290
|
logger.info(f"Loading {len(package_data)} packages")
|
|
227
|
-
load_inspector_packages(
|
|
291
|
+
load_inspector_packages(
|
|
292
|
+
neo4j_session,
|
|
293
|
+
package_data,
|
|
294
|
+
region,
|
|
295
|
+
update_tag,
|
|
296
|
+
current_aws_account_id,
|
|
297
|
+
)
|
|
228
298
|
cleanup(neo4j_session, common_job_parameters)
|