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
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
import neo4j
|
|
4
|
+
from cloudflare import Cloudflare
|
|
5
|
+
|
|
6
|
+
import cartography.intel.cloudflare.accounts
|
|
7
|
+
import cartography.intel.cloudflare.dnsrecords
|
|
8
|
+
import cartography.intel.cloudflare.members
|
|
9
|
+
import cartography.intel.cloudflare.roles
|
|
10
|
+
import cartography.intel.cloudflare.zones
|
|
11
|
+
from cartography.config import Config
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@timeit
|
|
18
|
+
def start_cloudflare_ingestion(neo4j_session: neo4j.Session, config: Config) -> None:
|
|
19
|
+
"""
|
|
20
|
+
If this module is configured, perform ingestion of Cloudflare data. Otherwise warn and exit
|
|
21
|
+
:param neo4j_session: Neo4J session for database interface
|
|
22
|
+
:param config: A cartography.config object
|
|
23
|
+
:return: None
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
if not config.cloudflare_token:
|
|
27
|
+
logger.info(
|
|
28
|
+
"Cloudflare import is not configured - skipping this module. "
|
|
29
|
+
"See docs to configure.",
|
|
30
|
+
)
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
# Create client
|
|
34
|
+
client = Cloudflare(api_token=config.cloudflare_token)
|
|
35
|
+
|
|
36
|
+
common_job_parameters = {
|
|
37
|
+
"UPDATE_TAG": config.update_tag,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for account in cartography.intel.cloudflare.accounts.sync(
|
|
41
|
+
neo4j_session,
|
|
42
|
+
client,
|
|
43
|
+
common_job_parameters,
|
|
44
|
+
):
|
|
45
|
+
account_job_parameters = common_job_parameters.copy()
|
|
46
|
+
account_job_parameters["account_id"] = account["id"]
|
|
47
|
+
cartography.intel.cloudflare.roles.sync(
|
|
48
|
+
neo4j_session,
|
|
49
|
+
client,
|
|
50
|
+
account_job_parameters,
|
|
51
|
+
account_id=account["id"],
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
cartography.intel.cloudflare.members.sync(
|
|
55
|
+
neo4j_session,
|
|
56
|
+
client,
|
|
57
|
+
account_job_parameters,
|
|
58
|
+
account_id=account["id"],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
for zone in cartography.intel.cloudflare.zones.sync(
|
|
62
|
+
neo4j_session,
|
|
63
|
+
client,
|
|
64
|
+
account_job_parameters,
|
|
65
|
+
account_id=account["id"],
|
|
66
|
+
):
|
|
67
|
+
zone_job_parameters = account_job_parameters.copy()
|
|
68
|
+
zone_job_parameters["zone_id"] = zone["id"]
|
|
69
|
+
cartography.intel.cloudflare.dnsrecords.sync(
|
|
70
|
+
neo4j_session,
|
|
71
|
+
client,
|
|
72
|
+
zone_job_parameters,
|
|
73
|
+
zone_id=zone["id"],
|
|
74
|
+
)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.account import CloudflareAccountSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@timeit
|
|
18
|
+
def sync(
|
|
19
|
+
neo4j_session: neo4j.Session,
|
|
20
|
+
client: Cloudflare,
|
|
21
|
+
common_job_parameters: Dict[str, Any],
|
|
22
|
+
) -> List[Dict]:
|
|
23
|
+
accounts = get(client)
|
|
24
|
+
load_accounts(
|
|
25
|
+
neo4j_session,
|
|
26
|
+
accounts,
|
|
27
|
+
common_job_parameters["UPDATE_TAG"],
|
|
28
|
+
)
|
|
29
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
30
|
+
return accounts
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@timeit
|
|
34
|
+
def get(client: Cloudflare) -> List[Dict[str, Any]]:
|
|
35
|
+
return [account.to_dict() for account in client.accounts.list()]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def load_accounts(
|
|
39
|
+
neo4j_session: neo4j.Session,
|
|
40
|
+
data: List[Dict[str, Any]],
|
|
41
|
+
update_tag: int,
|
|
42
|
+
) -> None:
|
|
43
|
+
logger.info("Loading %d Cloudflare accounts into Neo4j.", len(data))
|
|
44
|
+
load(
|
|
45
|
+
neo4j_session,
|
|
46
|
+
CloudflareAccountSchema(),
|
|
47
|
+
data,
|
|
48
|
+
lastupdated=update_tag,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def cleanup(
|
|
53
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
54
|
+
) -> None:
|
|
55
|
+
GraphJob.from_node_schema(CloudflareAccountSchema(), common_job_parameters).run(
|
|
56
|
+
neo4j_session
|
|
57
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.dnsrecord import CloudflareDNSRecordSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
16
|
+
_TIMEOUT = (60, 60)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@timeit
|
|
20
|
+
def sync(
|
|
21
|
+
neo4j_session: neo4j.Session,
|
|
22
|
+
client: Cloudflare,
|
|
23
|
+
common_job_parameters: Dict[str, Any],
|
|
24
|
+
zone_id: str,
|
|
25
|
+
) -> None:
|
|
26
|
+
dnsrecords = get(client, zone_id)
|
|
27
|
+
load_dnsrecords(
|
|
28
|
+
neo4j_session,
|
|
29
|
+
dnsrecords,
|
|
30
|
+
zone_id,
|
|
31
|
+
common_job_parameters["UPDATE_TAG"],
|
|
32
|
+
)
|
|
33
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@timeit
|
|
37
|
+
def get(client: Cloudflare, zone_id: str) -> List[Dict[str, Any]]:
|
|
38
|
+
return [
|
|
39
|
+
dnsrecord.to_dict() for dnsrecord in client.dns.records.list(zone_id=zone_id)
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_dnsrecords(
|
|
44
|
+
neo4j_session: neo4j.Session,
|
|
45
|
+
data: List[Dict[str, Any]],
|
|
46
|
+
zone_id: str,
|
|
47
|
+
update_tag: int,
|
|
48
|
+
) -> None:
|
|
49
|
+
logger.info("Loading %d Cloudflare DNSRecords into Neo4j.", len(data))
|
|
50
|
+
load(
|
|
51
|
+
neo4j_session,
|
|
52
|
+
CloudflareDNSRecordSchema(),
|
|
53
|
+
data,
|
|
54
|
+
lastupdated=update_tag,
|
|
55
|
+
zone_id=zone_id,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def cleanup(
|
|
60
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
61
|
+
) -> None:
|
|
62
|
+
GraphJob.from_node_schema(CloudflareDNSRecordSchema(), common_job_parameters).run(
|
|
63
|
+
neo4j_session
|
|
64
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.member import CloudflareMemberSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
# Connect and read timeouts of 60 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
16
|
+
_TIMEOUT = (60, 60)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@timeit
|
|
20
|
+
def sync(
|
|
21
|
+
neo4j_session: neo4j.Session,
|
|
22
|
+
client: Cloudflare,
|
|
23
|
+
common_job_parameters: Dict[str, Any],
|
|
24
|
+
account_id: str,
|
|
25
|
+
) -> None:
|
|
26
|
+
members = get(client, account_id)
|
|
27
|
+
transformed_members = transform_members(members)
|
|
28
|
+
load_members(
|
|
29
|
+
neo4j_session,
|
|
30
|
+
transformed_members,
|
|
31
|
+
account_id,
|
|
32
|
+
common_job_parameters["UPDATE_TAG"],
|
|
33
|
+
)
|
|
34
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@timeit
|
|
38
|
+
def get(client: Cloudflare, account_id: str) -> List[Dict[str, Any]]:
|
|
39
|
+
return [
|
|
40
|
+
member.to_dict()
|
|
41
|
+
for member in client.accounts.members.list(account_id=account_id)
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def load_members(
|
|
46
|
+
neo4j_session: neo4j.Session,
|
|
47
|
+
data: List[Dict[str, Any]],
|
|
48
|
+
account_id: str,
|
|
49
|
+
update_tag: int,
|
|
50
|
+
) -> None:
|
|
51
|
+
logger.info("Loading %d Cloudflare members into Neo4j.", len(data))
|
|
52
|
+
load(
|
|
53
|
+
neo4j_session,
|
|
54
|
+
CloudflareMemberSchema(),
|
|
55
|
+
data,
|
|
56
|
+
lastupdated=update_tag,
|
|
57
|
+
account_id=account_id,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def transform_members(data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
62
|
+
result: List[Dict[str, Any]] = []
|
|
63
|
+
for member in data:
|
|
64
|
+
member["roles_ids"] = [role["id"] for role in member.get("roles", [])]
|
|
65
|
+
member["policies_ids"] = [policy["id"] for policy in member.get("policies", [])]
|
|
66
|
+
result.append(member)
|
|
67
|
+
return result
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def cleanup(
|
|
71
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
72
|
+
) -> None:
|
|
73
|
+
GraphJob.from_node_schema(CloudflareMemberSchema(), common_job_parameters).run(
|
|
74
|
+
neo4j_session
|
|
75
|
+
)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.role import CloudflareRoleSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@timeit
|
|
18
|
+
def sync(
|
|
19
|
+
neo4j_session: neo4j.Session,
|
|
20
|
+
client: Cloudflare,
|
|
21
|
+
common_job_parameters: Dict[str, Any],
|
|
22
|
+
account_id: str,
|
|
23
|
+
) -> None:
|
|
24
|
+
roles = get(client, account_id)
|
|
25
|
+
load_roles(
|
|
26
|
+
neo4j_session,
|
|
27
|
+
roles,
|
|
28
|
+
account_id,
|
|
29
|
+
common_job_parameters["UPDATE_TAG"],
|
|
30
|
+
)
|
|
31
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@timeit
|
|
35
|
+
def get(
|
|
36
|
+
client: Cloudflare,
|
|
37
|
+
account_id: str,
|
|
38
|
+
) -> List[Dict[str, Any]]:
|
|
39
|
+
return [
|
|
40
|
+
role.to_dict() for role in client.accounts.roles.list(account_id=account_id)
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def load_roles(
|
|
45
|
+
neo4j_session: neo4j.Session,
|
|
46
|
+
data: List[Dict[str, Any]],
|
|
47
|
+
account_id: str,
|
|
48
|
+
update_tag: int,
|
|
49
|
+
) -> None:
|
|
50
|
+
logger.info("Loading %d Cloudflare roles into Neo4j.", len(data))
|
|
51
|
+
load(
|
|
52
|
+
neo4j_session,
|
|
53
|
+
CloudflareRoleSchema(),
|
|
54
|
+
data,
|
|
55
|
+
lastupdated=update_tag,
|
|
56
|
+
account_id=account_id,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def cleanup(
|
|
61
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
62
|
+
) -> None:
|
|
63
|
+
GraphJob.from_node_schema(CloudflareRoleSchema(), common_job_parameters).run(
|
|
64
|
+
neo4j_session
|
|
65
|
+
)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Any
|
|
3
|
+
from typing import Dict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import neo4j
|
|
7
|
+
from cloudflare import Cloudflare
|
|
8
|
+
|
|
9
|
+
from cartography.client.core.tx import load
|
|
10
|
+
from cartography.graph.job import GraphJob
|
|
11
|
+
from cartography.models.cloudflare.zone import CloudflareZoneSchema
|
|
12
|
+
from cartography.util import timeit
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@timeit
|
|
18
|
+
def sync(
|
|
19
|
+
neo4j_session: neo4j.Session,
|
|
20
|
+
client: Cloudflare,
|
|
21
|
+
common_job_parameters: Dict[str, Any],
|
|
22
|
+
account_id: str,
|
|
23
|
+
) -> List[Dict]:
|
|
24
|
+
zones = get(client, account_id)
|
|
25
|
+
load_zones(
|
|
26
|
+
neo4j_session,
|
|
27
|
+
zones,
|
|
28
|
+
account_id,
|
|
29
|
+
common_job_parameters["UPDATE_TAG"],
|
|
30
|
+
)
|
|
31
|
+
cleanup(neo4j_session, common_job_parameters)
|
|
32
|
+
return zones
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@timeit
|
|
36
|
+
def get(
|
|
37
|
+
client: Cloudflare,
|
|
38
|
+
account_id: str,
|
|
39
|
+
) -> List[Dict[str, Any]]:
|
|
40
|
+
return [zone.to_dict() for zone in client.zones.list(account=account_id)]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_zones(
|
|
44
|
+
neo4j_session: neo4j.Session,
|
|
45
|
+
data: List[Dict[str, Any]],
|
|
46
|
+
account_id: str,
|
|
47
|
+
update_tag: int,
|
|
48
|
+
) -> None:
|
|
49
|
+
logger.info("Loading %d Cloudflare zones into Neo4j.", len(data))
|
|
50
|
+
load(
|
|
51
|
+
neo4j_session,
|
|
52
|
+
CloudflareZoneSchema(),
|
|
53
|
+
data,
|
|
54
|
+
lastupdated=update_tag,
|
|
55
|
+
account_id=account_id,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def cleanup(
|
|
60
|
+
neo4j_session: neo4j.Session, common_job_parameters: Dict[str, Any]
|
|
61
|
+
) -> None:
|
|
62
|
+
GraphJob.from_node_schema(CloudflareZoneSchema(), common_job_parameters).run(
|
|
63
|
+
neo4j_session
|
|
64
|
+
)
|
|
@@ -5,15 +5,16 @@ import neo4j
|
|
|
5
5
|
|
|
6
6
|
from cartography.config import Config
|
|
7
7
|
from cartography.util import load_resource_binary
|
|
8
|
+
|
|
8
9
|
logger = logging.getLogger(__name__)
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def get_index_statements() -> List[str]:
|
|
12
13
|
statements = []
|
|
13
|
-
with load_resource_binary(
|
|
14
|
+
with load_resource_binary("cartography.data", "indexes.cypher") as f:
|
|
14
15
|
for line in f.readlines():
|
|
15
16
|
statements.append(
|
|
16
|
-
line.decode(
|
|
17
|
+
line.decode("UTF-8").rstrip("\r\n"),
|
|
17
18
|
)
|
|
18
19
|
return statements
|
|
19
20
|
|
|
@@ -20,7 +20,8 @@ stat_handler = get_stats_client(__name__)
|
|
|
20
20
|
|
|
21
21
|
@timeit
|
|
22
22
|
def start_crowdstrike_ingestion(
|
|
23
|
-
neo4j_session: neo4j.Session,
|
|
23
|
+
neo4j_session: neo4j.Session,
|
|
24
|
+
config: Config,
|
|
24
25
|
) -> None:
|
|
25
26
|
"""
|
|
26
27
|
Perform ingestion of crowdstrike data.
|
|
@@ -31,10 +32,7 @@ def start_crowdstrike_ingestion(
|
|
|
31
32
|
common_job_parameters = {
|
|
32
33
|
"UPDATE_TAG": config.update_tag,
|
|
33
34
|
}
|
|
34
|
-
if
|
|
35
|
-
not config.crowdstrike_client_id or
|
|
36
|
-
not config.crowdstrike_client_secret
|
|
37
|
-
):
|
|
35
|
+
if not config.crowdstrike_client_id or not config.crowdstrike_client_secret:
|
|
38
36
|
logger.error("crowdstrike config not found")
|
|
39
37
|
return
|
|
40
38
|
|
|
@@ -60,18 +58,22 @@ def start_crowdstrike_ingestion(
|
|
|
60
58
|
group_id = config.crowdstrike_api_url
|
|
61
59
|
merge_module_sync_metadata(
|
|
62
60
|
neo4j_session,
|
|
63
|
-
group_type=
|
|
61
|
+
group_type="crowdstrike",
|
|
64
62
|
group_id=group_id,
|
|
65
|
-
synced_type=
|
|
63
|
+
synced_type="crowdstrike",
|
|
66
64
|
update_tag=config.update_tag,
|
|
67
65
|
stat_handler=stat_handler,
|
|
68
66
|
)
|
|
69
67
|
|
|
70
68
|
|
|
71
69
|
@timeit
|
|
72
|
-
def cleanup(
|
|
70
|
+
def cleanup(
|
|
71
|
+
neo4j_session: neo4j.Session, common_job_parameters: dict[str, Any]
|
|
72
|
+
) -> None:
|
|
73
73
|
logger.info("Running Crowdstrike cleanup")
|
|
74
|
-
GraphJob.from_node_schema(CrowdstrikeHostSchema(), common_job_parameters).run(
|
|
74
|
+
GraphJob.from_node_schema(CrowdstrikeHostSchema(), common_job_parameters).run(
|
|
75
|
+
neo4j_session
|
|
76
|
+
)
|
|
75
77
|
|
|
76
78
|
# Cleanup other crowdstrike assets not handled by the data model
|
|
77
79
|
run_cleanup_job(
|
|
@@ -44,7 +44,11 @@ def load_host_data(
|
|
|
44
44
|
)
|
|
45
45
|
|
|
46
46
|
|
|
47
|
-
def get_host_ids(
|
|
47
|
+
def get_host_ids(
|
|
48
|
+
client: Hosts,
|
|
49
|
+
crowdstrikeapi_filter: str = "",
|
|
50
|
+
crowdstrikeapi_limit: int = 5000,
|
|
51
|
+
) -> List[List[str]]:
|
|
48
52
|
ids = []
|
|
49
53
|
parameters = {"filter": crowdstrikeapi_filter, "limit": crowdstrikeapi_limit}
|
|
50
54
|
response = client.QueryDevicesByFilter(parameters=parameters)
|
|
@@ -25,7 +25,9 @@ def sync_vulnerabilities(
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
def load_vulnerability_data(
|
|
28
|
-
neo4j_session: neo4j.Session,
|
|
28
|
+
neo4j_session: neo4j.Session,
|
|
29
|
+
data: List[Dict],
|
|
30
|
+
update_tag: int,
|
|
29
31
|
) -> None:
|
|
30
32
|
"""
|
|
31
33
|
Transform and load scan information
|
|
@@ -111,7 +113,9 @@ def _load_cves(neo4j_session: neo4j.Session, data: List[Dict], update_tag: int)
|
|
|
111
113
|
)
|
|
112
114
|
|
|
113
115
|
|
|
114
|
-
def get_spotlight_vulnerability_ids(
|
|
116
|
+
def get_spotlight_vulnerability_ids(
|
|
117
|
+
client: Spotlight_Vulnerabilities,
|
|
118
|
+
) -> List[List[str]]:
|
|
115
119
|
ids = []
|
|
116
120
|
parameters = {"filter": 'status:!"closed"', "limit": 400}
|
|
117
121
|
response = client.queryVulnerabilities(parameters=parameters)
|
|
@@ -135,7 +139,8 @@ def get_spotlight_vulnerability_ids(client: Spotlight_Vulnerabilities) -> List[L
|
|
|
135
139
|
|
|
136
140
|
|
|
137
141
|
def get_spotlight_vulnerabilities(
|
|
138
|
-
client: Spotlight_Vulnerabilities,
|
|
142
|
+
client: Spotlight_Vulnerabilities,
|
|
143
|
+
ids: List[str],
|
|
139
144
|
) -> List[Dict]:
|
|
140
145
|
response = client.getVulnerabilities(ids=",".join(ids))
|
|
141
146
|
body = response.get("body", {})
|
|
@@ -38,21 +38,33 @@ def _sync_year_archives(
|
|
|
38
38
|
) -> None:
|
|
39
39
|
existing_years = feed.get_cve_sync_metadata(neo4j_session)
|
|
40
40
|
current_year = datetime.now().year
|
|
41
|
-
logger.info(
|
|
41
|
+
logger.info(
|
|
42
|
+
f"Syncing CVE data for year archives. Existing years: {existing_years}. Current year: {current_year}",
|
|
43
|
+
)
|
|
42
44
|
for year in range(1999, current_year + 1):
|
|
43
45
|
if year in existing_years:
|
|
44
46
|
continue
|
|
45
47
|
logger.info(f"Syncing CVE data for year {year}")
|
|
46
|
-
cves = feed.get_published_cves_per_year(
|
|
48
|
+
cves = feed.get_published_cves_per_year(
|
|
49
|
+
http_session,
|
|
50
|
+
config.nist_cve_url,
|
|
51
|
+
str(year),
|
|
52
|
+
cve_api_key,
|
|
53
|
+
)
|
|
47
54
|
feed_metadata = feed.transform_cve_feed(cves)
|
|
48
55
|
feed.load_cve_feed(neo4j_session, [feed_metadata], config.update_tag)
|
|
49
56
|
published_cves = feed.transform_cves(cves)
|
|
50
|
-
feed.load_cves(
|
|
57
|
+
feed.load_cves(
|
|
58
|
+
neo4j_session,
|
|
59
|
+
published_cves,
|
|
60
|
+
feed_metadata["FEED_ID"],
|
|
61
|
+
config.update_tag,
|
|
62
|
+
)
|
|
51
63
|
merge_module_sync_metadata(
|
|
52
64
|
neo4j_session,
|
|
53
|
-
group_type=
|
|
65
|
+
group_type="CVE",
|
|
54
66
|
group_id=year,
|
|
55
|
-
synced_type=
|
|
67
|
+
synced_type="year",
|
|
56
68
|
update_tag=config.update_tag,
|
|
57
69
|
stat_handler=stat_handler,
|
|
58
70
|
)
|
|
@@ -66,16 +78,26 @@ def _sync_modified_data(
|
|
|
66
78
|
) -> None:
|
|
67
79
|
logger.info("Syncing CVE data for modified data")
|
|
68
80
|
last_modified_date = feed.get_last_modified_cve_date(neo4j_session)
|
|
69
|
-
cves = feed.get_modified_cves(
|
|
81
|
+
cves = feed.get_modified_cves(
|
|
82
|
+
http_session,
|
|
83
|
+
config.nist_cve_url,
|
|
84
|
+
last_modified_date,
|
|
85
|
+
cve_api_key,
|
|
86
|
+
)
|
|
70
87
|
feed_metadata = feed.transform_cve_feed(cves)
|
|
71
88
|
feed.load_cve_feed(neo4j_session, [feed_metadata], config.update_tag)
|
|
72
89
|
modified_cves = feed.transform_cves(cves)
|
|
73
|
-
feed.load_cves(
|
|
90
|
+
feed.load_cves(
|
|
91
|
+
neo4j_session,
|
|
92
|
+
modified_cves,
|
|
93
|
+
feed_metadata["FEED_ID"],
|
|
94
|
+
config.update_tag,
|
|
95
|
+
)
|
|
74
96
|
merge_module_sync_metadata(
|
|
75
97
|
neo4j_session,
|
|
76
|
-
group_type=
|
|
77
|
-
group_id=feed_metadata[
|
|
78
|
-
synced_type=
|
|
98
|
+
group_type="CVE",
|
|
99
|
+
group_id=feed_metadata["timestamp"][:4],
|
|
100
|
+
synced_type="modified",
|
|
79
101
|
update_tag=config.update_tag,
|
|
80
102
|
stat_handler=stat_handler,
|
|
81
103
|
)
|
|
@@ -83,7 +105,8 @@ def _sync_modified_data(
|
|
|
83
105
|
|
|
84
106
|
@timeit
|
|
85
107
|
def start_cve_ingestion(
|
|
86
|
-
neo4j_session: neo4j.Session,
|
|
108
|
+
neo4j_session: neo4j.Session,
|
|
109
|
+
config: Config,
|
|
87
110
|
) -> None:
|
|
88
111
|
"""
|
|
89
112
|
Perform ingestion of CVE data from NIST APIs.
|
|
@@ -95,6 +118,16 @@ def start_cve_ingestion(
|
|
|
95
118
|
return
|
|
96
119
|
cve_api_key: str | None = config.cve_api_key if config.cve_api_key else None
|
|
97
120
|
with _retryable_session() as http_session:
|
|
98
|
-
_sync_year_archives(
|
|
99
|
-
|
|
121
|
+
_sync_year_archives(
|
|
122
|
+
http_session,
|
|
123
|
+
neo4j_session=neo4j_session,
|
|
124
|
+
config=config,
|
|
125
|
+
cve_api_key=cve_api_key,
|
|
126
|
+
)
|
|
127
|
+
_sync_modified_data(
|
|
128
|
+
http_session,
|
|
129
|
+
neo4j_session=neo4j_session,
|
|
130
|
+
config=config,
|
|
131
|
+
cve_api_key=cve_api_key,
|
|
132
|
+
)
|
|
100
133
|
# CVEs are never deleted, so we don't need to run a cleanup job
|