cartography 0.96.1__tar.gz → 0.97.0__tar.gz
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-0.96.1 → cartography-0.97.0}/PKG-INFO +1 -1
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/identitycenter.py +1 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/cve/__init__.py +51 -19
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/cve/feed.py +19 -37
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/github/repos.py +80 -62
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/github/teams.py +115 -17
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/github/users.py +6 -1
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/github/util.py +2 -1
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/github/orgs.py +1 -1
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/github/teams.py +17 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/github/users.py +15 -6
- {cartography-0.96.1 → cartography-0.97.0}/cartography.egg-info/PKG-INFO +1 -1
- {cartography-0.96.1 → cartography-0.97.0}/setup.py +1 -1
- {cartography-0.96.1 → cartography-0.97.0}/LICENSE +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/README.md +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/__main__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/cli.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/client/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/client/aws/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/client/aws/iam.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/client/core/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/client/core/tx.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/config.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/indexes.cypher +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/aws_ec2_asset_exposure.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/aws_ec2_iaminstance.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/aws_ec2_iaminstanceprofile.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/aws_ec2_keypair_analysis.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/aws_eks_asset_exposure.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/aws_foreign_accounts.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/aws_lambda_ecr.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/aws_s3acl_analysis.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/gcp_compute_asset_inet_exposure.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/gcp_gke_asset_exposure.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/gcp_gke_basic_auth.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/analysis/gsuite_human_link.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_account_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_apigateway_details.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_dns_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_account_access_key_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_apigateway_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_config_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_ec2_launch_configurations_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_ec2_security_groupinfo_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_ecr_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_ecs_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_elastic_ip_addresses_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_elasticache_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_es_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_groups_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_groups_membership_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_groups_policy_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_identity_center_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_internet_gateways_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_kms_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_lambda_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_principals_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_rds_clusters_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_rds_instances_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_rds_snapshots_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_redshift_clusters_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_reserved_instances_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_roles_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_roles_policy_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_s3_acl_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_s3_buckets_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_secrets_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_securityhub_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_snapshots_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_sqs_queues_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_tags_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_tgw_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_users_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_vpc_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_import_vpc_peering_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_ingest_ec2_auto_scaling_groups_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_ingest_load_balancers_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_ingest_load_balancers_v2_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_ingest_subnets_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_kms_details.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_post_ingestion_principals_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/aws_s3_details.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_cosmosdb_cassandra_keyspace_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_cosmosdb_cors_details.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_cosmosdb_mongodb_database_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_cosmosdb_sql_database_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_cosmosdb_table_resources_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_database_account_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_import_disks_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_import_snapshots_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_import_virtual_machines_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_sql_server_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_storage_account_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_subscriptions_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/azure_tenant_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/crowdstrike_import_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/digitalocean_droplet_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/digitalocean_project_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_compute_firewall_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_compute_forwarding_rules_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_compute_instance_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_compute_vpc_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_compute_vpc_subnet_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_crm_folder_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_crm_organization_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_crm_project_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_dns_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_gke_cluster_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gcp_storage_bucket_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/github_org_and_users_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/github_repos_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gsuite_ingest_groups_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/gsuite_ingest_users_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/jamf_import_computers_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/kubernetes_import_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/oci_import_compartments_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/oci_import_groups_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/oci_import_groups_membership_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/oci_import_policies_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/oci_import_users_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/oci_tenancy_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/okta_groups_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/okta_import_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/cleanup/pagerduty_import_cleanup.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/scoped_analysis/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/jobs/scoped_analysis/semgrep_sca_risk_analysis.json +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/data/permission_relationships.yaml +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/__main__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/add_shortcut.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/cli.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/config.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/detect_deviations.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/get_states.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/model.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/reporter.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/serializers.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/shortcut.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/storage.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/driftdetect/util.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/graph/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/graph/cleanupbuilder.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/graph/context.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/graph/job.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/graph/querybuilder.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/graph/statement.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/analysis.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/apigateway.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/config.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/dynamodb.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/auto_scaling_groups.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/elastic_ip_addresses.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/images.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/instances.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/internet_gateways.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/key_pairs.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/launch_templates.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/load_balancer_v2s.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/load_balancers.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/network_acls.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/network_interfaces.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/reserved_instances.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/security_groups.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/snapshots.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/subnets.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/tgw.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/util.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/volumes.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/vpc.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ec2/vpc_peerings.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ecr.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ecs.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/eks.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/elasticache.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/elasticsearch.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/emr.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/iam.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/inspector.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/kms.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/lambda_function.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/organizations.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/permission_relationships.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/rds.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/redshift.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/resourcegroupstaggingapi.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/resources.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/route53.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/s3.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/secretsmanager.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/securityhub.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/sqs.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/ssm.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/util/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/util/arns.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/aws/util/common.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/compute.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/cosmosdb.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/sql.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/storage.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/subscription.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/tenant.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/util/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/azure/util/credentials.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/bigfix/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/bigfix/computers.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/create_indexes.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/crowdstrike/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/crowdstrike/endpoints.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/crowdstrike/spotlight.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/crowdstrike/util.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/digitalocean/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/digitalocean/compute.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/digitalocean/management.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/digitalocean/platform.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/dns.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/duo/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/duo/api_host.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/duo/endpoints.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/duo/groups.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/duo/phones.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/duo/tokens.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/duo/users.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/duo/web_authn_credentials.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/gcp/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/gcp/compute.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/gcp/crm.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/gcp/dns.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/gcp/gke.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/gcp/storage.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/github/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/gsuite/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/gsuite/api.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/jamf/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/jamf/computers.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/jamf/util.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/kandji/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/kandji/devices.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/kubernetes/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/kubernetes/namespaces.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/kubernetes/pods.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/kubernetes/secrets.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/kubernetes/services.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/kubernetes/util.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/lastpass/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/lastpass/users.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/oci/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/oci/iam.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/oci/organizations.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/oci/utils.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/applications.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/awssaml.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/factors.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/groups.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/organization.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/origins.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/roles.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/sync_state.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/users.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/okta/utils.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/pagerduty/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/pagerduty/escalation_policies.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/pagerduty/schedules.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/pagerduty/services.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/pagerduty/teams.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/pagerduty/users.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/pagerduty/vendors.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/semgrep/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/semgrep/dependencies.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/semgrep/deployment.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/semgrep/findings.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/snipeit/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/snipeit/asset.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/snipeit/user.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/intel/snipeit/util.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/dynamodb/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/dynamodb/gsi.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/dynamodb/tables.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/auto_scaling_groups.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/images.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/instances.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/keypairs.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/launch_configurations.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/launch_template_versions.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/launch_templates.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/loadbalancerv2.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/network_acl_rules.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/network_acls.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/networkinterface_instance.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/networkinterfaces.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/privateip_networkinterface.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/reservations.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/securitygroup_instance.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/securitygroup_networkinterface.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/subnet_instance.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/subnet_networkinterface.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ec2/volumes.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/eks/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/eks/clusters.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/emr.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/identitycenter/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/identitycenter/awsidentitycenter.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/identitycenter/awspermissionset.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/identitycenter/awsssouser.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/inspector/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/inspector/findings.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/inspector/packages.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ssm/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ssm/instance_information.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/aws/ssm/instance_patch.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/bigfix/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/bigfix/bigfix_computer.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/bigfix/bigfix_root.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/core/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/core/common.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/core/nodes.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/core/relationships.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/cve/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/cve/cve.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/cve/cve_feed.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/duo/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/duo/api_host.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/duo/endpoint.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/duo/group.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/duo/phone.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/duo/token.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/duo/user.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/duo/web_authn_credential.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/github/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/kandji/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/kandji/device.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/kandji/tenant.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/lastpass/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/lastpass/tenant.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/lastpass/user.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/semgrep/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/semgrep/dependencies.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/semgrep/deployment.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/semgrep/findings.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/semgrep/locations.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/snipeit/__init__.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/snipeit/asset.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/snipeit/tenant.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/models/snipeit/user.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/py.typed +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/stats.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/sync.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography/util.py +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography.egg-info/SOURCES.txt +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography.egg-info/dependency_links.txt +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography.egg-info/entry_points.txt +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography.egg-info/requires.txt +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/cartography.egg-info/top_level.txt +0 -0
- {cartography-0.96.1 → cartography-0.97.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cartography
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.97.0
|
|
4
4
|
Summary: Explore assets and their relationships across your technical infrastructure.
|
|
5
5
|
Home-page: https://www.github.com/cartography-cncf/cartography
|
|
6
6
|
Maintainer: Cartography Contributors
|
|
@@ -2,6 +2,9 @@ import logging
|
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
|
|
4
4
|
import neo4j
|
|
5
|
+
from requests import Session
|
|
6
|
+
from requests.adapters import HTTPAdapter
|
|
7
|
+
from urllib3 import Retry
|
|
5
8
|
|
|
6
9
|
from cartography.config import Config
|
|
7
10
|
from cartography.intel.cve import feed
|
|
@@ -13,28 +16,34 @@ logger = logging.getLogger(__name__)
|
|
|
13
16
|
stat_handler = get_stats_client(__name__)
|
|
14
17
|
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
""
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
cve_api_key: str | None = config.cve_api_key if config.cve_api_key else None
|
|
19
|
+
def _retryable_session() -> Session:
|
|
20
|
+
session = Session()
|
|
21
|
+
retry_policy = Retry(
|
|
22
|
+
total=8,
|
|
23
|
+
connect=1,
|
|
24
|
+
backoff_factor=1,
|
|
25
|
+
status_forcelist=[429, 500, 502, 503, 504],
|
|
26
|
+
allowed_methods=["GET"],
|
|
27
|
+
)
|
|
28
|
+
session.mount("https://", HTTPAdapter(max_retries=retry_policy))
|
|
29
|
+
logger.info(f"Configured session with retry policy: {retry_policy}")
|
|
30
|
+
return session
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
def _sync_year_archives(
|
|
34
|
+
http_session: Session,
|
|
35
|
+
neo4j_session: neo4j.Session,
|
|
36
|
+
config: Config,
|
|
37
|
+
cve_api_key: str | None,
|
|
38
|
+
) -> None:
|
|
31
39
|
existing_years = feed.get_cve_sync_metadata(neo4j_session)
|
|
32
40
|
current_year = datetime.now().year
|
|
33
|
-
for year
|
|
41
|
+
logger.info(f"Syncing CVE data for year archives. Existing years: {existing_years}. Current year: {current_year}")
|
|
42
|
+
for year in range(1999, current_year + 1):
|
|
34
43
|
if year in existing_years:
|
|
35
44
|
continue
|
|
36
45
|
logger.info(f"Syncing CVE data for year {year}")
|
|
37
|
-
cves = feed.get_published_cves_per_year(config.nist_cve_url, str(year), cve_api_key)
|
|
46
|
+
cves = feed.get_published_cves_per_year(http_session, config.nist_cve_url, str(year), cve_api_key)
|
|
38
47
|
feed_metadata = feed.transform_cve_feed(cves)
|
|
39
48
|
feed.load_cve_feed(neo4j_session, [feed_metadata], config.update_tag)
|
|
40
49
|
published_cves = feed.transform_cves(cves)
|
|
@@ -48,10 +57,16 @@ def start_cve_ingestion(
|
|
|
48
57
|
stat_handler=stat_handler,
|
|
49
58
|
)
|
|
50
59
|
|
|
51
|
-
|
|
60
|
+
|
|
61
|
+
def _sync_modified_data(
|
|
62
|
+
http_session: Session,
|
|
63
|
+
neo4j_session: neo4j.Session,
|
|
64
|
+
config: Config,
|
|
65
|
+
cve_api_key: str | None,
|
|
66
|
+
) -> None:
|
|
52
67
|
logger.info("Syncing CVE data for modified data")
|
|
53
68
|
last_modified_date = feed.get_last_modified_cve_date(neo4j_session)
|
|
54
|
-
cves = feed.get_modified_cves(config.nist_cve_url, last_modified_date, cve_api_key)
|
|
69
|
+
cves = feed.get_modified_cves(http_session, config.nist_cve_url, last_modified_date, cve_api_key)
|
|
55
70
|
feed_metadata = feed.transform_cve_feed(cves)
|
|
56
71
|
feed.load_cve_feed(neo4j_session, [feed_metadata], config.update_tag)
|
|
57
72
|
modified_cves = feed.transform_cves(cves)
|
|
@@ -65,4 +80,21 @@ def start_cve_ingestion(
|
|
|
65
80
|
stat_handler=stat_handler,
|
|
66
81
|
)
|
|
67
82
|
|
|
68
|
-
|
|
83
|
+
|
|
84
|
+
@timeit
|
|
85
|
+
def start_cve_ingestion(
|
|
86
|
+
neo4j_session: neo4j.Session, config: Config,
|
|
87
|
+
) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Perform ingestion of CVE data from NIST APIs.
|
|
90
|
+
:param neo4j_session: Neo4J session for database interface
|
|
91
|
+
:param config: A cartography.config object
|
|
92
|
+
:return: None
|
|
93
|
+
"""
|
|
94
|
+
if not config.cve_enabled:
|
|
95
|
+
return
|
|
96
|
+
cve_api_key: str | None = config.cve_api_key if config.cve_api_key else None
|
|
97
|
+
with _retryable_session() as http_session:
|
|
98
|
+
_sync_year_archives(http_session, neo4j_session=neo4j_session, config=config, cve_api_key=cve_api_key)
|
|
99
|
+
_sync_modified_data(http_session, neo4j_session=neo4j_session, config=config, cve_api_key=cve_api_key)
|
|
100
|
+
# CVEs are never deleted, so we don't need to run a cleanup job
|
|
@@ -11,7 +11,7 @@ from typing import List
|
|
|
11
11
|
from typing import Optional
|
|
12
12
|
|
|
13
13
|
import neo4j
|
|
14
|
-
import
|
|
14
|
+
from requests import Session
|
|
15
15
|
|
|
16
16
|
from cartography.client.core.tx import load
|
|
17
17
|
from cartography.client.core.tx import read_list_of_values_tx
|
|
@@ -22,7 +22,6 @@ from cartography.util import timeit
|
|
|
22
22
|
|
|
23
23
|
logger = logging.getLogger(__name__)
|
|
24
24
|
|
|
25
|
-
MAX_RETRIES = 8
|
|
26
25
|
# Connect and read timeouts of 120 seconds each; see https://requests.readthedocs.io/en/master/user/advanced/#timeouts
|
|
27
26
|
CONNECT_AND_READ_TIMEOUT = (30, 120)
|
|
28
27
|
CVE_FEED_ID = "NIST_NVD"
|
|
@@ -68,51 +67,36 @@ def _map_cve_dict(cve_dict: Dict[Any, Any], data: Dict[Any, Any]) -> None:
|
|
|
68
67
|
cve_dict["startIndex"] = data["startIndex"]
|
|
69
68
|
|
|
70
69
|
|
|
71
|
-
def _call_cves_api(url: str, api_key: str | None, params: Dict[str, Any]) -> Dict[Any, Any]:
|
|
72
|
-
|
|
73
|
-
sleep_time = DEFAULT_SLEEP_TIME
|
|
74
|
-
retries = 0
|
|
70
|
+
def _call_cves_api(http_session: Session, url: str, api_key: str | None, params: Dict[str, Any]) -> Dict[Any, Any]:
|
|
71
|
+
total_results = 0
|
|
75
72
|
params["startIndex"] = 0
|
|
76
73
|
params["resultsPerPage"] = RESULTS_PER_PAGE
|
|
77
|
-
headers = {}
|
|
78
|
-
headers["Content-Type"] = "application/json"
|
|
74
|
+
headers = {"Content-Type": "application/json"}
|
|
79
75
|
if api_key:
|
|
76
|
+
sleep_between_requests = DEFAULT_SLEEP_TIME
|
|
80
77
|
headers["apiKey"] = api_key
|
|
81
78
|
else:
|
|
82
|
-
|
|
79
|
+
sleep_between_requests = DELAYED_SLEEP_TIME
|
|
83
80
|
logger.warning(
|
|
84
|
-
f"No NIST NVD API key provided. Increasing sleep time to {
|
|
81
|
+
f"No NIST NVD API key provided. Increasing sleep time to {sleep_between_requests}.",
|
|
85
82
|
)
|
|
86
83
|
results: Dict[Any, Any] = dict()
|
|
87
84
|
|
|
88
|
-
while params["resultsPerPage"] > 0 or params["startIndex"] <
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
)
|
|
93
|
-
res.raise_for_status()
|
|
94
|
-
except requests.exceptions.HTTPError:
|
|
95
|
-
logger.error(
|
|
96
|
-
f"Failed to get CVE data from NIST NVD API {res.status_code} : {res.text}",
|
|
97
|
-
)
|
|
98
|
-
retries += 1
|
|
99
|
-
if retries >= MAX_RETRIES:
|
|
100
|
-
raise
|
|
101
|
-
# Exponential backoff
|
|
102
|
-
sleep_time *= 2
|
|
103
|
-
time.sleep(sleep_time)
|
|
104
|
-
continue
|
|
85
|
+
while params["resultsPerPage"] > 0 or params["startIndex"] < total_results:
|
|
86
|
+
logger.info(f"Calling NIST NVD API at {url} with params {params}")
|
|
87
|
+
res = http_session.get(url, params=params, headers=headers, timeout=CONNECT_AND_READ_TIMEOUT)
|
|
88
|
+
res.raise_for_status()
|
|
105
89
|
data = res.json()
|
|
106
90
|
_map_cve_dict(results, data)
|
|
107
|
-
|
|
91
|
+
total_results = data["totalResults"]
|
|
108
92
|
params["resultsPerPage"] = data["resultsPerPage"]
|
|
109
93
|
params["startIndex"] += data["resultsPerPage"]
|
|
110
|
-
|
|
111
|
-
time.sleep(sleep_time)
|
|
94
|
+
time.sleep(sleep_between_requests)
|
|
112
95
|
return results
|
|
113
96
|
|
|
114
97
|
|
|
115
98
|
def get_cves_in_batches(
|
|
99
|
+
http_session: Session,
|
|
116
100
|
nist_cve_url: str,
|
|
117
101
|
start_date: datetime,
|
|
118
102
|
end_date: datetime,
|
|
@@ -145,7 +129,7 @@ def get_cves_in_batches(
|
|
|
145
129
|
logger.info(
|
|
146
130
|
f"Querying CVE data between {current_start_date} and {current_end_date}",
|
|
147
131
|
)
|
|
148
|
-
batch_cves = _call_cves_api(nist_cve_url, api_key, params)
|
|
132
|
+
batch_cves = _call_cves_api(http_session, nist_cve_url, api_key, params)
|
|
149
133
|
_map_cve_dict(cves, batch_cves)
|
|
150
134
|
current_start_date = current_end_date
|
|
151
135
|
new_end_date = current_start_date + batch_size
|
|
@@ -156,9 +140,8 @@ def get_cves_in_batches(
|
|
|
156
140
|
|
|
157
141
|
|
|
158
142
|
def get_modified_cves(
|
|
159
|
-
nist_cve_url: str, last_modified_date: str, api_key: str | None,
|
|
143
|
+
http_session: Session, nist_cve_url: str, last_modified_date: str, api_key: str | None,
|
|
160
144
|
) -> Dict[Any, Any]:
|
|
161
|
-
cves = dict()
|
|
162
145
|
end_date = datetime.now(tz=timezone.utc)
|
|
163
146
|
start_date = datetime.strptime(last_modified_date, "%Y-%m-%dT%H:%M:%S").replace(
|
|
164
147
|
tzinfo=timezone.utc,
|
|
@@ -168,15 +151,14 @@ def get_modified_cves(
|
|
|
168
151
|
"end": "lastModEndDate",
|
|
169
152
|
}
|
|
170
153
|
cves = get_cves_in_batches(
|
|
171
|
-
nist_cve_url, start_date, end_date, date_param_names, api_key,
|
|
154
|
+
http_session, nist_cve_url, start_date, end_date, date_param_names, api_key,
|
|
172
155
|
)
|
|
173
156
|
return cves
|
|
174
157
|
|
|
175
158
|
|
|
176
159
|
def get_published_cves_per_year(
|
|
177
|
-
nist_cve_url: str, year: str, api_key: str | None,
|
|
160
|
+
http_session: Session, nist_cve_url: str, year: str, api_key: str | None,
|
|
178
161
|
) -> Dict[Any, Any]:
|
|
179
|
-
cves = {}
|
|
180
162
|
start_of_year = datetime.strptime(f"{year}-01-01", "%Y-%m-%d")
|
|
181
163
|
next_year = int(year) + 1
|
|
182
164
|
end_of_next_year = datetime.strptime(f"{next_year}-01-01", "%Y-%m-%d")
|
|
@@ -185,7 +167,7 @@ def get_published_cves_per_year(
|
|
|
185
167
|
"end": "pubEndDate",
|
|
186
168
|
}
|
|
187
169
|
cves = get_cves_in_batches(
|
|
188
|
-
nist_cve_url, start_of_year, end_of_next_year, date_param_names, api_key,
|
|
170
|
+
http_session, nist_cve_url, start_of_year, end_of_next_year, date_param_names, api_key,
|
|
189
171
|
)
|
|
190
172
|
return cves
|
|
191
173
|
|
|
@@ -140,22 +140,40 @@ def _get_repo_collaborators_inner_func(
|
|
|
140
140
|
org: str,
|
|
141
141
|
api_url: str,
|
|
142
142
|
token: str,
|
|
143
|
-
|
|
143
|
+
repo_raw_data: list[dict[str, Any]],
|
|
144
144
|
affiliation: str,
|
|
145
145
|
collab_users: list[dict[str, Any]],
|
|
146
146
|
collab_permission: list[str],
|
|
147
|
-
) ->
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
) -> dict[str, list[UserAffiliationAndRepoPermission]]:
|
|
148
|
+
result: dict[str, list[UserAffiliationAndRepoPermission]] = {}
|
|
149
|
+
|
|
150
|
+
for repo in repo_raw_data:
|
|
151
|
+
repo_name = repo['name']
|
|
152
|
+
repo_url = repo['url']
|
|
153
|
+
|
|
154
|
+
if ((affiliation == 'OUTSIDE' and repo['outsideCollaborators']['totalCount'] == 0) or
|
|
155
|
+
(affiliation == 'DIRECT' and repo['directCollaborators']['totalCount'] == 0)):
|
|
156
|
+
# repo has no collabs of the affiliation type we're looking for, so don't waste time making an API call
|
|
157
|
+
result[repo_url] = []
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
logger.info(f"Loading {affiliation} collaborators for repo {repo_name}.")
|
|
161
|
+
collaborators = _get_repo_collaborators(token, api_url, org, repo_name, affiliation)
|
|
150
162
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
163
|
+
# nodes and edges are expected to always be present given that we only call for them if totalCount is > 0
|
|
164
|
+
# however sometimes GitHub returns None, as in issue 1334 and 1404.
|
|
165
|
+
for collab in collaborators.nodes or []:
|
|
166
|
+
collab_users.append(collab)
|
|
155
167
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
168
|
+
# The `or []` is because `.edges` can be None.
|
|
169
|
+
for perm in collaborators.edges or []:
|
|
170
|
+
collab_permission.append(perm['permission'])
|
|
171
|
+
|
|
172
|
+
result[repo_url] = [
|
|
173
|
+
UserAffiliationAndRepoPermission(user, permission, affiliation)
|
|
174
|
+
for user, permission in zip(collab_users, collab_permission)
|
|
175
|
+
]
|
|
176
|
+
return result
|
|
159
177
|
|
|
160
178
|
|
|
161
179
|
def _get_repo_collaborators_for_multiple_repos(
|
|
@@ -175,39 +193,24 @@ def _get_repo_collaborators_for_multiple_repos(
|
|
|
175
193
|
:param token: The Github API token as string.
|
|
176
194
|
:return: A dictionary of repo URL to list of UserAffiliationAndRepoPermission
|
|
177
195
|
"""
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
backoff_handler,
|
|
197
|
-
)(
|
|
198
|
-
org=org,
|
|
199
|
-
api_url=api_url,
|
|
200
|
-
token=token,
|
|
201
|
-
repo_name=repo_name,
|
|
202
|
-
affiliation=affiliation,
|
|
203
|
-
collab_users=collab_users,
|
|
204
|
-
collab_permission=collab_permission,
|
|
205
|
-
)
|
|
206
|
-
|
|
207
|
-
result[repo_url] = [
|
|
208
|
-
UserAffiliationAndRepoPermission(user, permission, affiliation)
|
|
209
|
-
for user, permission in zip(collab_users, collab_permission)
|
|
210
|
-
]
|
|
196
|
+
logger.info(f'Retrieving repo collaborators for affiliation "{affiliation}" on org "{org}".')
|
|
197
|
+
collab_users: List[dict[str, Any]] = []
|
|
198
|
+
collab_permission: List[str] = []
|
|
199
|
+
|
|
200
|
+
result: dict[str, list[UserAffiliationAndRepoPermission]] = retries_with_backoff(
|
|
201
|
+
_get_repo_collaborators_inner_func,
|
|
202
|
+
TypeError,
|
|
203
|
+
5,
|
|
204
|
+
backoff_handler,
|
|
205
|
+
)(
|
|
206
|
+
org=org,
|
|
207
|
+
api_url=api_url,
|
|
208
|
+
token=token,
|
|
209
|
+
repo_raw_data=repo_raw_data,
|
|
210
|
+
affiliation=affiliation,
|
|
211
|
+
collab_users=collab_users,
|
|
212
|
+
collab_permission=collab_permission,
|
|
213
|
+
)
|
|
211
214
|
return result
|
|
212
215
|
|
|
213
216
|
|
|
@@ -260,8 +263,9 @@ def get(token: str, api_url: str, organization: str) -> List[Dict]:
|
|
|
260
263
|
|
|
261
264
|
|
|
262
265
|
def transform(
|
|
263
|
-
|
|
264
|
-
|
|
266
|
+
repos_json: List[Dict],
|
|
267
|
+
direct_collaborators: dict[str, List[UserAffiliationAndRepoPermission]],
|
|
268
|
+
outside_collaborators: dict[str, List[UserAffiliationAndRepoPermission]],
|
|
265
269
|
) -> Dict:
|
|
266
270
|
"""
|
|
267
271
|
Parses the JSON returned from GitHub API to create data for graph ingestion
|
|
@@ -289,16 +293,24 @@ def transform(
|
|
|
289
293
|
_transform_repo_languages(repo_object['url'], repo_object, transformed_repo_languages)
|
|
290
294
|
_transform_repo_objects(repo_object, transformed_repo_list)
|
|
291
295
|
_transform_repo_owners(repo_object['owner']['url'], repo_object, transformed_repo_owners)
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
296
|
+
|
|
297
|
+
# Allow sync to continue if we didn't have permissions to list collaborators
|
|
298
|
+
repo_url = repo_object['url']
|
|
299
|
+
if repo_url in outside_collaborators:
|
|
300
|
+
_transform_collaborators(
|
|
301
|
+
repo_object['url'],
|
|
302
|
+
outside_collaborators[repo_object['url']],
|
|
303
|
+
transformed_outside_collaborators,
|
|
304
|
+
)
|
|
305
|
+
if repo_url in direct_collaborators:
|
|
306
|
+
_transform_collaborators(
|
|
307
|
+
repo_object['url'],
|
|
308
|
+
direct_collaborators[repo_object['url']],
|
|
309
|
+
transformed_direct_collaborators,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
_transform_requirements_txt(repo_object['requirements'], repo_url, transformed_requirements_files)
|
|
313
|
+
_transform_setup_cfg_requirements(repo_object['setupCfg'], repo_url, transformed_requirements_files)
|
|
302
314
|
results = {
|
|
303
315
|
'repos': transformed_repo_list,
|
|
304
316
|
'repo_languages': transformed_repo_languages,
|
|
@@ -737,12 +749,18 @@ def sync(
|
|
|
737
749
|
"""
|
|
738
750
|
logger.info("Syncing GitHub repos")
|
|
739
751
|
repos_json = get(github_api_key, github_url, organization)
|
|
740
|
-
direct_collabs =
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
752
|
+
direct_collabs: dict[str, list[UserAffiliationAndRepoPermission]] = {}
|
|
753
|
+
outside_collabs: dict[str, list[UserAffiliationAndRepoPermission]] = {}
|
|
754
|
+
try:
|
|
755
|
+
direct_collabs = _get_repo_collaborators_for_multiple_repos(
|
|
756
|
+
repos_json, "DIRECT", organization, github_url, github_api_key,
|
|
757
|
+
)
|
|
758
|
+
outside_collabs = _get_repo_collaborators_for_multiple_repos(
|
|
759
|
+
repos_json, "OUTSIDE", organization, github_url, github_api_key,
|
|
760
|
+
)
|
|
761
|
+
except TypeError:
|
|
762
|
+
# due to permission errors or transient network error or some other nonsense
|
|
763
|
+
logger.warning('Unable to list repo collaborators due to permission errors; continuing on.', exc_info=True)
|
|
746
764
|
repo_data = transform(repos_json, direct_collabs, outside_collabs)
|
|
747
765
|
load(neo4j_session, common_job_parameters, repo_data)
|
|
748
766
|
run_cleanup_job('github_repos_cleanup.json', neo4j_session, common_job_parameters)
|
|
@@ -21,6 +21,9 @@ logger = logging.getLogger(__name__)
|
|
|
21
21
|
RepoPermission = namedtuple('RepoPermission', ['repo_url', 'permission'])
|
|
22
22
|
# A team member's role: https://docs.github.com/en/graphql/reference/enums#teammemberrole
|
|
23
23
|
UserRole = namedtuple('UserRole', ['user_url', 'role'])
|
|
24
|
+
# Unlike the other tuples here, there is no qualification (like 'role' or 'permission') to the relationship.
|
|
25
|
+
# A child team is just a child team: https://docs.github.com/en/graphql/reference/objects#teamconnection
|
|
26
|
+
ChildTeam = namedtuple('ChildTeam', ['team_url'])
|
|
24
27
|
|
|
25
28
|
|
|
26
29
|
def backoff_handler(details: Dict) -> None:
|
|
@@ -53,6 +56,9 @@ def get_teams(org: str, api_url: str, token: str) -> Tuple[PaginatedGraphqlData,
|
|
|
53
56
|
members(membership: IMMEDIATE) {
|
|
54
57
|
totalCount
|
|
55
58
|
}
|
|
59
|
+
childTeams {
|
|
60
|
+
totalCount
|
|
61
|
+
}
|
|
56
62
|
}
|
|
57
63
|
pageInfo{
|
|
58
64
|
endCursor
|
|
@@ -166,6 +172,21 @@ def _get_team_repos(org: str, api_url: str, token: str, team: str) -> PaginatedG
|
|
|
166
172
|
return team_repos
|
|
167
173
|
|
|
168
174
|
|
|
175
|
+
def _get_teams_users_inner_func(
|
|
176
|
+
org: str, api_url: str, token: str, team_name: str,
|
|
177
|
+
user_urls: List[str], user_roles: List[str],
|
|
178
|
+
) -> None:
|
|
179
|
+
logger.info(f"Loading team users for {team_name}.")
|
|
180
|
+
team_users = _get_team_users(org, api_url, token, team_name)
|
|
181
|
+
# The `or []` is because `.nodes` can be None. See:
|
|
182
|
+
# https://docs.github.com/en/graphql/reference/objects#teammemberconnection
|
|
183
|
+
for user in team_users.nodes or []:
|
|
184
|
+
user_urls.append(user['url'])
|
|
185
|
+
# The `or []` is because `.edges` can be None.
|
|
186
|
+
for edge in team_users.edges or []:
|
|
187
|
+
user_roles.append(edge['role'])
|
|
188
|
+
|
|
189
|
+
|
|
169
190
|
def _get_team_users_for_multiple_teams(
|
|
170
191
|
team_raw_data: list[dict[str, Any]],
|
|
171
192
|
org: str,
|
|
@@ -185,21 +206,7 @@ def _get_team_users_for_multiple_teams(
|
|
|
185
206
|
user_urls: List[str] = []
|
|
186
207
|
user_roles: List[str] = []
|
|
187
208
|
|
|
188
|
-
|
|
189
|
-
org: str, api_url: str, token: str, team_name: str,
|
|
190
|
-
user_urls: List[str], user_roles: List[str],
|
|
191
|
-
) -> None:
|
|
192
|
-
logger.info(f"Loading team users for {team_name}.")
|
|
193
|
-
team_users = _get_team_users(org, api_url, token, team_name)
|
|
194
|
-
# The `or []` is because `.nodes` can be None. See:
|
|
195
|
-
# https://docs.github.com/en/graphql/reference/objects#teammemberconnection
|
|
196
|
-
for user in team_users.nodes or []:
|
|
197
|
-
user_urls.append(user['url'])
|
|
198
|
-
# The `or []` is because `.edges` can be None.
|
|
199
|
-
for edge in team_users.edges or []:
|
|
200
|
-
user_roles.append(edge['role'])
|
|
201
|
-
|
|
202
|
-
retries_with_backoff(get_teams_users_inner_func, TypeError, 5, backoff_handler)(
|
|
209
|
+
retries_with_backoff(_get_teams_users_inner_func, TypeError, 5, backoff_handler)(
|
|
203
210
|
org=org, api_url=api_url, token=token, team_name=team_name, user_urls=user_urls, user_roles=user_roles,
|
|
204
211
|
)
|
|
205
212
|
|
|
@@ -252,11 +259,90 @@ def _get_team_users(org: str, api_url: str, token: str, team: str) -> PaginatedG
|
|
|
252
259
|
return team_users
|
|
253
260
|
|
|
254
261
|
|
|
262
|
+
def _get_child_teams_inner_func(
|
|
263
|
+
org: str, api_url: str, token: str, team_name: str, team_urls: List[str],
|
|
264
|
+
) -> None:
|
|
265
|
+
logger.info(f"Loading child teams for {team_name}.")
|
|
266
|
+
child_teams = _get_child_teams(org, api_url, token, team_name)
|
|
267
|
+
# The `or []` is because `.nodes` can be None. See:
|
|
268
|
+
# https://docs.github.com/en/graphql/reference/objects#teammemberconnection
|
|
269
|
+
for cteam in child_teams.nodes or []:
|
|
270
|
+
team_urls.append(cteam['url'])
|
|
271
|
+
# No edges to process here, the GitHub response for child teams has no relevant info in edges.
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _get_child_teams_for_multiple_teams(
|
|
275
|
+
team_raw_data: list[dict[str, Any]],
|
|
276
|
+
org: str,
|
|
277
|
+
api_url: str,
|
|
278
|
+
token: str,
|
|
279
|
+
) -> dict[str, list[ChildTeam]]:
|
|
280
|
+
result: dict[str, list[ChildTeam]] = {}
|
|
281
|
+
for team in team_raw_data:
|
|
282
|
+
team_name = team['slug']
|
|
283
|
+
team_count = team['childTeams']['totalCount']
|
|
284
|
+
|
|
285
|
+
if team_count == 0:
|
|
286
|
+
# This team has no child teams so let's move on
|
|
287
|
+
result[team_name] = []
|
|
288
|
+
continue
|
|
289
|
+
|
|
290
|
+
team_urls: List[str] = []
|
|
291
|
+
|
|
292
|
+
retries_with_backoff(_get_child_teams_inner_func, TypeError, 5, backoff_handler)(
|
|
293
|
+
org=org, api_url=api_url, token=token, team_name=team_name, team_urls=team_urls,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
result[team_name] = [ChildTeam(url) for url in team_urls]
|
|
297
|
+
return result
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _get_child_teams(org: str, api_url: str, token: str, team: str) -> PaginatedGraphqlData:
|
|
301
|
+
team_users_gql = """
|
|
302
|
+
query($login: String!, $team: String!, $cursor: String) {
|
|
303
|
+
organization(login: $login) {
|
|
304
|
+
url
|
|
305
|
+
login
|
|
306
|
+
team(slug: $team) {
|
|
307
|
+
slug
|
|
308
|
+
childTeams(first: 100, after: $cursor) {
|
|
309
|
+
totalCount
|
|
310
|
+
nodes {
|
|
311
|
+
url
|
|
312
|
+
}
|
|
313
|
+
pageInfo {
|
|
314
|
+
endCursor
|
|
315
|
+
hasNextPage
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
rateLimit {
|
|
321
|
+
limit
|
|
322
|
+
cost
|
|
323
|
+
remaining
|
|
324
|
+
resetAt
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
"""
|
|
328
|
+
team_users, _ = fetch_all(
|
|
329
|
+
token,
|
|
330
|
+
api_url,
|
|
331
|
+
org,
|
|
332
|
+
team_users_gql,
|
|
333
|
+
'team',
|
|
334
|
+
resource_inner_type='childTeams',
|
|
335
|
+
team=team,
|
|
336
|
+
)
|
|
337
|
+
return team_users
|
|
338
|
+
|
|
339
|
+
|
|
255
340
|
def transform_teams(
|
|
256
341
|
team_paginated_data: PaginatedGraphqlData,
|
|
257
342
|
org_data: Dict[str, Any],
|
|
258
343
|
team_repo_data: dict[str, list[RepoPermission]],
|
|
259
344
|
team_user_data: dict[str, list[UserRole]],
|
|
345
|
+
team_child_team_data: dict[str, list[ChildTeam]],
|
|
260
346
|
) -> list[dict[str, Any]]:
|
|
261
347
|
result = []
|
|
262
348
|
for team in team_paginated_data.nodes:
|
|
@@ -267,13 +353,15 @@ def transform_teams(
|
|
|
267
353
|
'description': team['description'],
|
|
268
354
|
'repo_count': team['repositories']['totalCount'],
|
|
269
355
|
'member_count': team['members']['totalCount'],
|
|
356
|
+
'child_team_count': team['childTeams']['totalCount'],
|
|
270
357
|
'org_url': org_data['url'],
|
|
271
358
|
'org_login': org_data['login'],
|
|
272
359
|
}
|
|
273
360
|
repo_permissions = team_repo_data[team_name]
|
|
274
361
|
user_roles = team_user_data[team_name]
|
|
362
|
+
child_teams = team_child_team_data[team_name]
|
|
275
363
|
|
|
276
|
-
if not repo_permissions and not user_roles:
|
|
364
|
+
if not repo_permissions and not user_roles and not child_teams:
|
|
277
365
|
result.append(repo_info)
|
|
278
366
|
continue
|
|
279
367
|
|
|
@@ -289,6 +377,15 @@ def transform_teams(
|
|
|
289
377
|
repo_info_copy = repo_info.copy()
|
|
290
378
|
repo_info_copy[role] = user_url
|
|
291
379
|
result.append(repo_info_copy)
|
|
380
|
+
if child_teams:
|
|
381
|
+
for (team_url,) in child_teams:
|
|
382
|
+
repo_info_copy = repo_info.copy()
|
|
383
|
+
# GitHub speaks of team-team relationships as 'child teams', as in the graphql query
|
|
384
|
+
# or here: https://docs.github.com/en/graphql/reference/enums#teammembershiptype
|
|
385
|
+
# We label the relationship as 'MEMBER_OF_TEAM' here because it is in line with
|
|
386
|
+
# other similar relationships in Cartography.
|
|
387
|
+
repo_info_copy['MEMBER_OF_TEAM'] = team_url
|
|
388
|
+
result.append(repo_info_copy)
|
|
292
389
|
return result
|
|
293
390
|
|
|
294
391
|
|
|
@@ -325,7 +422,8 @@ def sync_github_teams(
|
|
|
325
422
|
teams_paginated, org_data = get_teams(organization, github_url, github_api_key)
|
|
326
423
|
team_repos = _get_team_repos_for_multiple_teams(teams_paginated.nodes, organization, github_url, github_api_key)
|
|
327
424
|
team_users = _get_team_users_for_multiple_teams(teams_paginated.nodes, organization, github_url, github_api_key)
|
|
328
|
-
|
|
425
|
+
team_children = _get_child_teams_for_multiple_teams(teams_paginated.nodes, organization, github_url, github_api_key)
|
|
426
|
+
processed_data = transform_teams(teams_paginated, org_data, team_repos, team_users, team_children)
|
|
329
427
|
load_team_repos(neo4j_session, processed_data, common_job_parameters['UPDATE_TAG'], org_data['url'])
|
|
330
428
|
common_job_parameters['org_url'] = org_data['url']
|
|
331
429
|
cleanup(neo4j_session, common_job_parameters)
|
|
@@ -90,6 +90,7 @@ def get_users(token: str, api_url: str, organization: str) -> Tuple[List[Dict],
|
|
|
90
90
|
2. data on the owning GitHub organization
|
|
91
91
|
see tests.data.github.users.GITHUB_USER_DATA for shape of both
|
|
92
92
|
"""
|
|
93
|
+
logger.info(f"Retrieving users from GitHub organization {organization}")
|
|
93
94
|
users, org = fetch_all(
|
|
94
95
|
token,
|
|
95
96
|
api_url,
|
|
@@ -112,6 +113,7 @@ def get_enterprise_owners(token: str, api_url: str, organization: str) -> Tuple[
|
|
|
112
113
|
3. data on the owning GitHub organization
|
|
113
114
|
see tests.data.github.users.GITHUB_ENTERPRISE_OWNER_DATA for shape
|
|
114
115
|
"""
|
|
116
|
+
logger.info(f"Retrieving enterprise owners from GitHub organization {organization}")
|
|
115
117
|
owners, org = fetch_all(
|
|
116
118
|
token,
|
|
117
119
|
api_url,
|
|
@@ -142,10 +144,13 @@ def transform_users(user_data: List[Dict], owners_data: List[Dict], org_data: Di
|
|
|
142
144
|
|
|
143
145
|
users_dict = {}
|
|
144
146
|
for user in user_data:
|
|
147
|
+
# all members get the 'MEMBER_OF' relationship
|
|
145
148
|
processed_user = deepcopy(user['node'])
|
|
146
|
-
processed_user['role'] = user['role']
|
|
147
149
|
processed_user['hasTwoFactorEnabled'] = user['hasTwoFactorEnabled']
|
|
148
150
|
processed_user['MEMBER_OF'] = org_data['url']
|
|
151
|
+
# admins get a second relationship expressing them as such
|
|
152
|
+
if user['role'] == 'ADMIN':
|
|
153
|
+
processed_user['ADMIN_OF'] = org_data['url']
|
|
149
154
|
users_dict[processed_user['url']] = processed_user
|
|
150
155
|
|
|
151
156
|
owners_dict = {}
|
|
@@ -163,7 +163,8 @@ def fetch_all(
|
|
|
163
163
|
|
|
164
164
|
if retry >= retries:
|
|
165
165
|
logger.error(
|
|
166
|
-
f"GitHub: Could not retrieve page of resource `{resource_type}` due to HTTP error
|
|
166
|
+
f"GitHub: Could not retrieve page of resource `{resource_type}` due to HTTP error "
|
|
167
|
+
f"after {retry} retries. Raising exception.",
|
|
167
168
|
exc_info=True,
|
|
168
169
|
)
|
|
169
170
|
raise exc
|