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
cartography/cli.py
CHANGED
|
@@ -8,10 +8,10 @@ from typing import Optional
|
|
|
8
8
|
import cartography.config
|
|
9
9
|
import cartography.sync
|
|
10
10
|
import cartography.util
|
|
11
|
+
from cartography.intel.aws.util.common import parse_and_validate_aws_regions
|
|
11
12
|
from cartography.intel.aws.util.common import parse_and_validate_aws_requested_syncs
|
|
12
13
|
from cartography.intel.semgrep.dependencies import parse_and_validate_semgrep_ecosystems
|
|
13
14
|
|
|
14
|
-
|
|
15
15
|
logger = logging.getLogger(__name__)
|
|
16
16
|
|
|
17
17
|
|
|
@@ -23,7 +23,11 @@ class CLI:
|
|
|
23
23
|
:param prog: The name of the command line program. This will be displayed in usage and help output.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
def __init__(
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
sync: Optional[cartography.sync.Sync] = None,
|
|
29
|
+
prog: Optional[str] = None,
|
|
30
|
+
):
|
|
27
31
|
self.sync = sync if sync else cartography.sync.build_default_sync()
|
|
28
32
|
self.prog = prog
|
|
29
33
|
self.parser = self._build_parser()
|
|
@@ -47,540 +51,582 @@ class CLI:
|
|
|
47
51
|
"instances, use auth when communicating with Neo4j, sync data from multiple AWS accounts, and execute "
|
|
48
52
|
"arbitrary analysis jobs after the conclusion of the sync."
|
|
49
53
|
),
|
|
50
|
-
epilog=
|
|
54
|
+
epilog="For more documentation please visit: https://github.com/cartography-cncf/cartography",
|
|
51
55
|
)
|
|
52
56
|
parser.add_argument(
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
action=
|
|
56
|
-
help=
|
|
57
|
+
"-v",
|
|
58
|
+
"--verbose",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Enable verbose logging for cartography.",
|
|
57
61
|
)
|
|
58
62
|
parser.add_argument(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
action=
|
|
62
|
-
help=
|
|
63
|
+
"-q",
|
|
64
|
+
"--quiet",
|
|
65
|
+
action="store_true",
|
|
66
|
+
help="Restrict cartography logging to warnings and errors only.",
|
|
63
67
|
)
|
|
64
68
|
parser.add_argument(
|
|
65
|
-
|
|
69
|
+
"--neo4j-uri",
|
|
66
70
|
type=str,
|
|
67
|
-
default=
|
|
71
|
+
default="bolt://localhost:7687",
|
|
68
72
|
help=(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
"A valid Neo4j URI to sync against. See "
|
|
74
|
+
"https://neo4j.com/docs/api/python-driver/current/driver.html#uri for complete documentation on the "
|
|
75
|
+
"structure of a Neo4j URI."
|
|
72
76
|
),
|
|
73
77
|
)
|
|
74
78
|
parser.add_argument(
|
|
75
|
-
|
|
79
|
+
"--neo4j-user",
|
|
76
80
|
type=str,
|
|
77
81
|
default=None,
|
|
78
|
-
help=
|
|
82
|
+
help="A username with which to authenticate to Neo4j.",
|
|
79
83
|
)
|
|
80
84
|
parser.add_argument(
|
|
81
|
-
|
|
85
|
+
"--neo4j-password-env-var",
|
|
82
86
|
type=str,
|
|
83
87
|
default=None,
|
|
84
|
-
help=
|
|
88
|
+
help="The name of an environment variable containing a password with which to authenticate to Neo4j.",
|
|
85
89
|
)
|
|
86
90
|
parser.add_argument(
|
|
87
|
-
|
|
88
|
-
action=
|
|
91
|
+
"--neo4j-password-prompt",
|
|
92
|
+
action="store_true",
|
|
89
93
|
help=(
|
|
90
|
-
|
|
91
|
-
|
|
94
|
+
"Present an interactive prompt for a password with which to authenticate to Neo4j. This parameter "
|
|
95
|
+
"supersedes other methods of supplying a Neo4j password."
|
|
92
96
|
),
|
|
93
97
|
)
|
|
94
98
|
parser.add_argument(
|
|
95
|
-
|
|
99
|
+
"--neo4j-max-connection-lifetime",
|
|
96
100
|
type=int,
|
|
97
101
|
default=3600,
|
|
98
102
|
help=(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
"Time in seconds for the Neo4j driver to consider a TCP connection alive. cartography default = 3600, "
|
|
104
|
+
"which is the same as the Neo4j driver default. See "
|
|
105
|
+
"https://neo4j.com/docs/driver-manual/1.7/client-applications/#driver-config-connection-pool-management"
|
|
106
|
+
"."
|
|
103
107
|
),
|
|
104
108
|
)
|
|
105
109
|
parser.add_argument(
|
|
106
|
-
|
|
110
|
+
"--neo4j-database",
|
|
107
111
|
type=str,
|
|
108
112
|
default=None,
|
|
109
113
|
help=(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
114
|
+
"The name of the database in Neo4j to connect to. If not specified, uses the config settings of your "
|
|
115
|
+
"Neo4j database itself to infer which database is set to default. "
|
|
116
|
+
"See https://neo4j.com/docs/api/python-driver/4.4/api.html#database."
|
|
113
117
|
),
|
|
114
118
|
)
|
|
115
119
|
parser.add_argument(
|
|
116
|
-
|
|
120
|
+
"--selected-modules",
|
|
117
121
|
type=str,
|
|
118
122
|
default=None,
|
|
119
123
|
help=(
|
|
120
124
|
'Comma-separated list of cartography top-level modules to sync. Example 1: "aws,gcp" to run AWS and GCP'
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
125
|
+
"modules. See the full list available in source code at cartography.sync. "
|
|
126
|
+
"If not specified, cartography by default will run all modules available and log warnings when it "
|
|
127
|
+
"does not find credentials configured for them. "
|
|
124
128
|
# TODO remove this mention about the create-indexes module when everything is using auto-indexes.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
"We recommend that you always specify the `create-indexes` module first in this list. "
|
|
130
|
+
"If you specify the `analysis` module, we recommend that you include it as the LAST item of this list, "
|
|
131
|
+
"(because it does not make sense to perform analysis on an empty/out-of-date graph)."
|
|
128
132
|
),
|
|
129
133
|
)
|
|
130
134
|
# TODO add the below parameters to a 'sync' subparser
|
|
131
135
|
parser.add_argument(
|
|
132
|
-
|
|
136
|
+
"--update-tag",
|
|
133
137
|
type=int,
|
|
134
138
|
default=None,
|
|
135
139
|
help=(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
140
|
+
"A unique tag to apply to all Neo4j nodes and relationships created or updated during the sync run. "
|
|
141
|
+
"This tag is used by cleanup jobs to identify nodes and relationships that are stale and need to be "
|
|
142
|
+
"removed from the graph. By default, cartography will use a UNIX timestamp as the update tag."
|
|
139
143
|
),
|
|
140
144
|
)
|
|
141
145
|
parser.add_argument(
|
|
142
|
-
|
|
143
|
-
action=
|
|
146
|
+
"--aws-sync-all-profiles",
|
|
147
|
+
action="store_true",
|
|
144
148
|
help=(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
"Enable AWS sync for all discovered named profiles. When this parameter is supplied cartography will "
|
|
150
|
+
"discover all configured AWS named profiles (see "
|
|
151
|
+
"https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html) and run the AWS sync "
|
|
148
152
|
'job for each profile not named "default". If this parameter is not supplied, cartography will use the '
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
153
|
+
"default AWS credentials available in your environment to run the AWS sync once. When using this "
|
|
154
|
+
"parameter it is suggested that you create an AWS config file containing a named profile for each AWS "
|
|
155
|
+
"account you want to sync and use the AWS_CONFIG_FILE environment variable to point to that config "
|
|
156
|
+
"file (see https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html). cartography "
|
|
157
|
+
"respects the AWS CLI/SDK environment variables and does not override them."
|
|
154
158
|
),
|
|
155
159
|
)
|
|
156
160
|
parser.add_argument(
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
"--aws-regions",
|
|
162
|
+
type=str,
|
|
163
|
+
default=None,
|
|
159
164
|
help=(
|
|
160
|
-
'
|
|
161
|
-
|
|
165
|
+
'[EXPERIMENTAL!] Comma-separated list of AWS regions to sync. Example: specify "us-east-1,us-east-2" '
|
|
166
|
+
"to sync US East 1 and 2. Note that this syncs the same regions in ALL accounts and it is currently "
|
|
167
|
+
"not possible to specify different regions per account. "
|
|
168
|
+
"CAUTION: if you previously synced assets from regions that are _not_ included in your current list, "
|
|
169
|
+
"those assets will be _deleted_ during this sync. "
|
|
170
|
+
'This is because cartography\'s cleanup process uses "lastupdated" and "account id" to determine data '
|
|
171
|
+
"freshness and not regions. So, if a previously synced region is missing in the current sync, "
|
|
172
|
+
"Cartography assumes the associated assets are stale and removes them. "
|
|
173
|
+
"Default behavior: If `--aws-regions` is not specified, cartography will _autodiscover_ the "
|
|
174
|
+
"regions supported by each account being synced."
|
|
162
175
|
),
|
|
163
176
|
)
|
|
164
177
|
parser.add_argument(
|
|
165
|
-
|
|
166
|
-
action=
|
|
178
|
+
"--aws-best-effort-mode",
|
|
179
|
+
action="store_true",
|
|
167
180
|
help=(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
'https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm) and run the OCI sync '
|
|
171
|
-
'job for each profile not named "DEFAULT". If this parameter is not supplied, cartography will use the '
|
|
172
|
-
'default OCI credentials available in your environment to run the OCI sync once.'
|
|
181
|
+
"Enable AWS sync best effort mode when syncing AWS accounts. This will allow cartography to continue "
|
|
182
|
+
"syncing other accounts and delay raising an exception until the very end."
|
|
173
183
|
),
|
|
174
184
|
)
|
|
175
185
|
parser.add_argument(
|
|
176
|
-
|
|
177
|
-
action=
|
|
186
|
+
"--oci-sync-all-profiles",
|
|
187
|
+
action="store_true",
|
|
178
188
|
help=(
|
|
179
|
-
|
|
180
|
-
|
|
189
|
+
"Enable OCI sync for all discovered named profiles. When this parameter is supplied cartography will "
|
|
190
|
+
"discover all configured OCI named profiles (see "
|
|
191
|
+
"https://docs.oracle.com/en-us/iaas/Content/API/Concepts/sdkconfig.htm) and run the OCI sync "
|
|
192
|
+
'job for each profile not named "DEFAULT". If this parameter is not supplied, cartography will use the '
|
|
193
|
+
"default OCI credentials available in your environment to run the OCI sync once."
|
|
181
194
|
),
|
|
182
195
|
)
|
|
183
196
|
parser.add_argument(
|
|
184
|
-
|
|
185
|
-
action=
|
|
197
|
+
"--azure-sync-all-subscriptions",
|
|
198
|
+
action="store_true",
|
|
186
199
|
help=(
|
|
187
|
-
|
|
200
|
+
"Enable Azure sync for all discovered subscriptions. When this parameter is supplied cartography will "
|
|
201
|
+
"discover all configured Azure subscriptions."
|
|
188
202
|
),
|
|
189
203
|
)
|
|
190
204
|
parser.add_argument(
|
|
191
|
-
|
|
205
|
+
"--azure-sp-auth",
|
|
206
|
+
action="store_true",
|
|
207
|
+
help=("Use Service Principal authentication for Azure sync."),
|
|
208
|
+
)
|
|
209
|
+
parser.add_argument(
|
|
210
|
+
"--azure-tenant-id",
|
|
192
211
|
type=str,
|
|
193
212
|
default=None,
|
|
194
|
-
help=(
|
|
195
|
-
'Azure Tenant Id for Service Principal Authentication.'
|
|
196
|
-
),
|
|
213
|
+
help=("Azure Tenant Id for Service Principal Authentication."),
|
|
197
214
|
)
|
|
198
215
|
parser.add_argument(
|
|
199
|
-
|
|
216
|
+
"--azure-client-id",
|
|
200
217
|
type=str,
|
|
201
218
|
default=None,
|
|
202
|
-
help=(
|
|
203
|
-
'Azure Client Id for Service Principal Authentication.'
|
|
204
|
-
),
|
|
219
|
+
help=("Azure Client Id for Service Principal Authentication."),
|
|
205
220
|
)
|
|
206
221
|
parser.add_argument(
|
|
207
|
-
|
|
222
|
+
"--azure-client-secret-env-var",
|
|
208
223
|
type=str,
|
|
209
224
|
default=None,
|
|
210
225
|
help=(
|
|
211
|
-
|
|
226
|
+
"The name of environment variable containing Azure Client Secret for Service Principal Authentication."
|
|
212
227
|
),
|
|
213
228
|
)
|
|
214
229
|
parser.add_argument(
|
|
215
|
-
|
|
230
|
+
"--entra-tenant-id",
|
|
216
231
|
type=str,
|
|
217
232
|
default=None,
|
|
218
|
-
help=(
|
|
219
|
-
'Entra Tenant Id for Service Principal Authentication.'
|
|
220
|
-
),
|
|
233
|
+
help=("Entra Tenant Id for Service Principal Authentication."),
|
|
221
234
|
)
|
|
222
235
|
parser.add_argument(
|
|
223
|
-
|
|
236
|
+
"--entra-client-id",
|
|
224
237
|
type=str,
|
|
225
238
|
default=None,
|
|
226
|
-
help=(
|
|
227
|
-
'Entra Client Id for Service Principal Authentication.'
|
|
228
|
-
),
|
|
239
|
+
help=("Entra Client Id for Service Principal Authentication."),
|
|
229
240
|
)
|
|
230
241
|
parser.add_argument(
|
|
231
|
-
|
|
242
|
+
"--entra-client-secret-env-var",
|
|
232
243
|
type=str,
|
|
233
244
|
default=None,
|
|
234
245
|
help=(
|
|
235
|
-
|
|
246
|
+
"The name of environment variable containing Entra Client Secret for Service Principal Authentication."
|
|
236
247
|
),
|
|
237
248
|
)
|
|
238
249
|
parser.add_argument(
|
|
239
|
-
|
|
250
|
+
"--aws-requested-syncs",
|
|
240
251
|
type=str,
|
|
241
252
|
default=None,
|
|
242
253
|
help=(
|
|
243
254
|
'Comma-separated list of AWS resources to sync. Example 1: "ecr,s3,ec2:instance" for ECR, S3, and all '
|
|
244
|
-
|
|
245
|
-
|
|
255
|
+
"EC2 instance resources. See the full list available in source code at cartography.intel.aws.resources."
|
|
256
|
+
" If not specified, cartography by default will run all AWS sync modules available."
|
|
246
257
|
),
|
|
247
258
|
)
|
|
248
259
|
parser.add_argument(
|
|
249
|
-
|
|
260
|
+
"--analysis-job-directory",
|
|
250
261
|
type=str,
|
|
251
262
|
default=None,
|
|
252
263
|
help=(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
264
|
+
"A path to a directory containing analysis jobs to run at the conclusion of the sync. cartography will "
|
|
265
|
+
"discover all JSON files in the given directory (and its subdirectories) and pass them to the GraphJob "
|
|
266
|
+
"API to execute against the graph. This allows you to apply data transformation and augmentation at "
|
|
267
|
+
"the end of a sync run without writing code. cartography does not guarantee the order in which the "
|
|
268
|
+
"jobs are executed."
|
|
258
269
|
),
|
|
259
270
|
)
|
|
260
271
|
parser.add_argument(
|
|
261
|
-
|
|
272
|
+
"--okta-org-id",
|
|
262
273
|
type=str,
|
|
263
274
|
default=None,
|
|
264
275
|
help=(
|
|
265
|
-
|
|
276
|
+
"Okta organizational id to sync. Required if you are using the Okta intel module. Ignored otherwise."
|
|
266
277
|
),
|
|
267
278
|
)
|
|
268
279
|
parser.add_argument(
|
|
269
|
-
|
|
280
|
+
"--okta-api-key-env-var",
|
|
270
281
|
type=str,
|
|
271
282
|
default=None,
|
|
272
283
|
help=(
|
|
273
|
-
|
|
274
|
-
|
|
284
|
+
"The name of an environment variable containing a key with which to auth to the Okta API."
|
|
285
|
+
"Required if you are using the Okta intel module. Ignored otherwise."
|
|
275
286
|
),
|
|
276
287
|
)
|
|
277
288
|
parser.add_argument(
|
|
278
|
-
|
|
289
|
+
"--okta-saml-role-regex",
|
|
279
290
|
type=str,
|
|
280
291
|
default=r"^aws\#\S+\#(?{{role}}[\w\-]+)\#(?{{accountid}}\d+)$",
|
|
281
292
|
help=(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
293
|
+
"The regex used to map Okta groups to AWS roles when using okta as a SAML provider."
|
|
294
|
+
"The regex is the one entered in Step 5: Enabling Group Based Role Mapping in Okta"
|
|
295
|
+
"https://saml-doc.okta.com/SAML_Docs/How-to-Configure-SAML-2.0-for-Amazon-Web-Service#c-step5"
|
|
296
|
+
"The regex must contain the {{role}} and {{accountid}} tags"
|
|
286
297
|
),
|
|
287
298
|
)
|
|
288
299
|
parser.add_argument(
|
|
289
|
-
|
|
300
|
+
"--github-config-env-var",
|
|
290
301
|
type=str,
|
|
291
302
|
default=None,
|
|
292
303
|
help=(
|
|
293
|
-
|
|
294
|
-
|
|
304
|
+
"The name of an environment variable containing a Base64 encoded GitHub config object."
|
|
305
|
+
"Required if you are using the GitHub intel module. Ignored otherwise."
|
|
295
306
|
),
|
|
296
307
|
)
|
|
297
308
|
parser.add_argument(
|
|
298
|
-
|
|
309
|
+
"--digitalocean-token-env-var",
|
|
299
310
|
type=str,
|
|
300
311
|
default=None,
|
|
301
312
|
help=(
|
|
302
|
-
|
|
303
|
-
|
|
313
|
+
"The name of an environment variable containing a DigitalOcean access token."
|
|
314
|
+
"Required if you are using the DigitalOcean intel module. Ignored otherwise."
|
|
304
315
|
),
|
|
305
316
|
)
|
|
306
317
|
parser.add_argument(
|
|
307
|
-
|
|
318
|
+
"--permission-relationships-file",
|
|
308
319
|
type=str,
|
|
309
320
|
default="cartography/data/permission_relationships.yaml",
|
|
310
321
|
help=(
|
|
311
|
-
|
|
312
|
-
|
|
322
|
+
"The path to the permission relationships mapping file."
|
|
323
|
+
"If omitted the default permission relationships will be created"
|
|
313
324
|
),
|
|
314
325
|
)
|
|
315
326
|
parser.add_argument(
|
|
316
|
-
|
|
327
|
+
"--jamf-base-uri",
|
|
317
328
|
type=str,
|
|
318
329
|
default=None,
|
|
319
330
|
help=(
|
|
320
|
-
|
|
321
|
-
|
|
331
|
+
"Your Jamf base URI, e.g. https://hostname.com/JSSResource."
|
|
332
|
+
"Required if you are using the Jamf intel module. Ignored otherwise."
|
|
322
333
|
),
|
|
323
334
|
)
|
|
324
335
|
parser.add_argument(
|
|
325
|
-
|
|
336
|
+
"--jamf-user",
|
|
326
337
|
type=str,
|
|
327
338
|
default=None,
|
|
328
|
-
help=
|
|
339
|
+
help="A username with which to authenticate to Jamf.",
|
|
329
340
|
)
|
|
330
341
|
parser.add_argument(
|
|
331
|
-
|
|
342
|
+
"--jamf-password-env-var",
|
|
332
343
|
type=str,
|
|
333
344
|
default=None,
|
|
334
|
-
help=
|
|
345
|
+
help="The name of an environment variable containing a password with which to authenticate to Jamf.",
|
|
335
346
|
)
|
|
336
347
|
parser.add_argument(
|
|
337
|
-
|
|
348
|
+
"--kandji-base-uri",
|
|
338
349
|
type=str,
|
|
339
350
|
default=None,
|
|
340
351
|
help=(
|
|
341
|
-
|
|
342
|
-
|
|
352
|
+
"Your Kandji base URI, e.g. https://company.api.kandji.io."
|
|
353
|
+
"Required if you are using the Kandji intel module. Ignored otherwise."
|
|
343
354
|
),
|
|
344
355
|
)
|
|
345
356
|
parser.add_argument(
|
|
346
|
-
|
|
357
|
+
"--kandji-tenant-id",
|
|
347
358
|
type=str,
|
|
348
359
|
default=None,
|
|
349
360
|
help=(
|
|
350
|
-
|
|
351
|
-
|
|
361
|
+
"Your Kandji tenant id e.g. company."
|
|
362
|
+
"Required using the Kandji intel module. Ignored otherwise."
|
|
352
363
|
),
|
|
353
364
|
)
|
|
354
365
|
parser.add_argument(
|
|
355
|
-
|
|
366
|
+
"--kandji-token-env-var",
|
|
356
367
|
type=str,
|
|
357
368
|
default=None,
|
|
358
|
-
help=
|
|
369
|
+
help="The name of an environment variable containing token with which to authenticate to Kandji.",
|
|
359
370
|
)
|
|
360
371
|
parser.add_argument(
|
|
361
|
-
|
|
372
|
+
"--k8s-kubeconfig",
|
|
362
373
|
default=None,
|
|
363
374
|
type=str,
|
|
364
375
|
help=(
|
|
365
|
-
|
|
376
|
+
"The path to kubeconfig file specifying context to access K8s cluster(s)."
|
|
366
377
|
),
|
|
367
378
|
)
|
|
368
379
|
parser.add_argument(
|
|
369
|
-
|
|
380
|
+
"--nist-cve-url",
|
|
370
381
|
type=str,
|
|
371
|
-
default=
|
|
382
|
+
default="https://services.nvd.nist.gov/rest/json/cves/2.0/",
|
|
372
383
|
help=(
|
|
373
|
-
|
|
384
|
+
"The base url for the NIST CVE data. Default = https://services.nvd.nist.gov/rest/json/cves/2.0/"
|
|
374
385
|
),
|
|
375
386
|
)
|
|
376
387
|
parser.add_argument(
|
|
377
|
-
|
|
378
|
-
action=
|
|
379
|
-
help=(
|
|
380
|
-
'If set, CVE data will be synced from NIST.'
|
|
381
|
-
),
|
|
388
|
+
"--cve-enabled",
|
|
389
|
+
action="store_true",
|
|
390
|
+
help=("If set, CVE data will be synced from NIST."),
|
|
382
391
|
)
|
|
383
392
|
parser.add_argument(
|
|
384
|
-
|
|
393
|
+
"--cve-api-key-env-var",
|
|
385
394
|
type=str,
|
|
386
395
|
default=None,
|
|
387
|
-
help=(
|
|
388
|
-
'If set, uses the provided NIST NVD API v2.0 key.'
|
|
389
|
-
),
|
|
396
|
+
help=("If set, uses the provided NIST NVD API v2.0 key."),
|
|
390
397
|
)
|
|
391
398
|
parser.add_argument(
|
|
392
|
-
|
|
393
|
-
action=
|
|
399
|
+
"--statsd-enabled",
|
|
400
|
+
action="store_true",
|
|
394
401
|
help=(
|
|
395
|
-
|
|
402
|
+
"If set, enables sending metrics using statsd to a server of your choice."
|
|
396
403
|
),
|
|
397
404
|
)
|
|
398
405
|
parser.add_argument(
|
|
399
|
-
|
|
406
|
+
"--statsd-prefix",
|
|
400
407
|
type=str,
|
|
401
|
-
default=
|
|
408
|
+
default="",
|
|
402
409
|
help=(
|
|
403
|
-
|
|
410
|
+
"The string to prefix statsd metrics with. Only used if --statsd-enabled is on. Default = empty string."
|
|
404
411
|
),
|
|
405
412
|
)
|
|
406
413
|
parser.add_argument(
|
|
407
|
-
|
|
414
|
+
"--statsd-host",
|
|
408
415
|
type=str,
|
|
409
|
-
default=
|
|
416
|
+
default="127.0.0.1",
|
|
410
417
|
help=(
|
|
411
|
-
|
|
418
|
+
"The IP address of your statsd server. Only used if --statsd-enabled is on. Default = 127.0.0.1."
|
|
412
419
|
),
|
|
413
420
|
)
|
|
414
421
|
parser.add_argument(
|
|
415
|
-
|
|
422
|
+
"--statsd-port",
|
|
416
423
|
type=int,
|
|
417
424
|
default=8125,
|
|
418
425
|
help=(
|
|
419
|
-
|
|
426
|
+
"The port of your statsd server. Only used if --statsd-enabled is on. Default = UDP 8125."
|
|
420
427
|
),
|
|
421
428
|
)
|
|
422
429
|
parser.add_argument(
|
|
423
|
-
|
|
430
|
+
"--pagerduty-api-key-env-var",
|
|
424
431
|
type=str,
|
|
425
432
|
default=None,
|
|
426
433
|
help=(
|
|
427
|
-
|
|
434
|
+
"The name of environment variable containing the pagerduty API key for authentication."
|
|
428
435
|
),
|
|
429
436
|
)
|
|
430
437
|
parser.add_argument(
|
|
431
|
-
|
|
438
|
+
"--pagerduty-request-timeout",
|
|
432
439
|
type=int,
|
|
433
440
|
default=None,
|
|
434
|
-
help=(
|
|
435
|
-
'Seconds to timeout for pagerduty API sessions.'
|
|
436
|
-
),
|
|
441
|
+
help=("Seconds to timeout for pagerduty API sessions."),
|
|
437
442
|
)
|
|
438
443
|
parser.add_argument(
|
|
439
|
-
|
|
444
|
+
"--crowdstrike-client-id-env-var",
|
|
440
445
|
type=str,
|
|
441
446
|
default=None,
|
|
442
447
|
help=(
|
|
443
|
-
|
|
448
|
+
"The name of environment variable containing the crowdstrike client id for authentication."
|
|
444
449
|
),
|
|
445
450
|
)
|
|
446
451
|
parser.add_argument(
|
|
447
|
-
|
|
452
|
+
"--crowdstrike-client-secret-env-var",
|
|
448
453
|
type=str,
|
|
449
454
|
default=None,
|
|
450
455
|
help=(
|
|
451
|
-
|
|
456
|
+
"The name of environment variable containing the crowdstrike secret key for authentication."
|
|
452
457
|
),
|
|
453
458
|
)
|
|
454
459
|
parser.add_argument(
|
|
455
|
-
|
|
460
|
+
"--crowdstrike-api-url",
|
|
456
461
|
type=str,
|
|
457
462
|
default=None,
|
|
458
463
|
help=(
|
|
459
|
-
|
|
464
|
+
"The crowdstrike URL, if using self-hosted. Defaults to the public crowdstrike API URL otherwise."
|
|
460
465
|
),
|
|
461
466
|
)
|
|
462
467
|
parser.add_argument(
|
|
463
|
-
|
|
468
|
+
"--gsuite-auth-method",
|
|
464
469
|
type=str,
|
|
465
|
-
default=
|
|
466
|
-
choices=[
|
|
470
|
+
default="delegated",
|
|
471
|
+
choices=["delegated", "oauth", "default"],
|
|
467
472
|
help=(
|
|
468
473
|
'GSuite authentication method. Can be "delegated" for service account or "oauth" for OAuth. '
|
|
469
474
|
'"Default" best if using gcloud CLI.'
|
|
470
475
|
),
|
|
471
476
|
)
|
|
472
477
|
parser.add_argument(
|
|
473
|
-
|
|
478
|
+
"--gsuite-tokens-env-var",
|
|
474
479
|
type=str,
|
|
475
|
-
default=
|
|
480
|
+
default="GSUITE_GOOGLE_APPLICATION_CREDENTIALS",
|
|
476
481
|
help=(
|
|
477
|
-
|
|
482
|
+
"The name of environment variable containing secrets for GSuite authentication."
|
|
478
483
|
),
|
|
479
484
|
)
|
|
480
485
|
parser.add_argument(
|
|
481
|
-
|
|
486
|
+
"--lastpass-cid-env-var",
|
|
482
487
|
type=str,
|
|
483
488
|
default=None,
|
|
484
489
|
help=(
|
|
485
|
-
|
|
490
|
+
"The name of environment variable containing the Lastpass CID for authentication."
|
|
486
491
|
),
|
|
487
492
|
)
|
|
488
493
|
parser.add_argument(
|
|
489
|
-
|
|
494
|
+
"--lastpass-provhash-env-var",
|
|
490
495
|
type=str,
|
|
491
496
|
default=None,
|
|
492
497
|
help=(
|
|
493
|
-
|
|
498
|
+
"The name of environment variable containing the Lastpass provhash for authentication."
|
|
494
499
|
),
|
|
495
500
|
)
|
|
496
501
|
parser.add_argument(
|
|
497
|
-
|
|
502
|
+
"--bigfix-username",
|
|
498
503
|
type=str,
|
|
499
504
|
default=None,
|
|
500
|
-
help=(
|
|
501
|
-
'The BigFix username for authentication.'
|
|
502
|
-
),
|
|
505
|
+
help=("The BigFix username for authentication."),
|
|
503
506
|
)
|
|
504
507
|
parser.add_argument(
|
|
505
|
-
|
|
508
|
+
"--bigfix-password-env-var",
|
|
506
509
|
type=str,
|
|
507
510
|
default=None,
|
|
508
511
|
help=(
|
|
509
|
-
|
|
512
|
+
"The name of environment variable containing the BigFix password for authentication."
|
|
510
513
|
),
|
|
511
514
|
)
|
|
512
515
|
parser.add_argument(
|
|
513
|
-
|
|
516
|
+
"--bigfix-root-url",
|
|
514
517
|
type=str,
|
|
515
518
|
default=None,
|
|
516
|
-
help=(
|
|
517
|
-
'The BigFix Root URL, a.k.a the BigFix API URL'
|
|
518
|
-
),
|
|
519
|
+
help=("The BigFix Root URL, a.k.a the BigFix API URL"),
|
|
519
520
|
)
|
|
520
521
|
parser.add_argument(
|
|
521
|
-
|
|
522
|
+
"--duo-api-key-env-var",
|
|
523
|
+
type=str,
|
|
524
|
+
default=None,
|
|
525
|
+
help=("The name of environment variable containing the Duo api key"),
|
|
526
|
+
)
|
|
527
|
+
parser.add_argument(
|
|
528
|
+
"--duo-api-secret-env-var",
|
|
529
|
+
type=str,
|
|
530
|
+
default=None,
|
|
531
|
+
help=("The name of environment variable containing the Duo api secret"),
|
|
532
|
+
)
|
|
533
|
+
parser.add_argument(
|
|
534
|
+
"--duo-api-hostname",
|
|
535
|
+
type=str,
|
|
536
|
+
default=None,
|
|
537
|
+
help=("The Duo api hostname"),
|
|
538
|
+
)
|
|
539
|
+
parser.add_argument(
|
|
540
|
+
"--semgrep-app-token-env-var",
|
|
522
541
|
type=str,
|
|
523
542
|
default=None,
|
|
524
543
|
help=(
|
|
525
|
-
|
|
544
|
+
"The name of environment variable containing the Semgrep app token key. "
|
|
545
|
+
"Required if you are using the Semgrep intel module. Ignored otherwise."
|
|
526
546
|
),
|
|
527
547
|
)
|
|
528
548
|
parser.add_argument(
|
|
529
|
-
|
|
549
|
+
"--semgrep-dependency-ecosystems",
|
|
530
550
|
type=str,
|
|
531
551
|
default=None,
|
|
532
552
|
help=(
|
|
533
|
-
|
|
553
|
+
"Comma-separated list of language ecosystems for which dependencies will be retrieved from Semgrep. "
|
|
554
|
+
'For example, a value of "gomod,npm" will retrieve Go and NPM dependencies. '
|
|
555
|
+
"See the full list of supported ecosystems in source code at cartography.intel.semgrep.dependencies. "
|
|
556
|
+
"Required if you are using the Semgrep dependencies intel module. Ignored otherwise."
|
|
534
557
|
),
|
|
535
558
|
)
|
|
536
559
|
parser.add_argument(
|
|
537
|
-
|
|
560
|
+
"--snipeit-base-uri",
|
|
538
561
|
type=str,
|
|
539
562
|
default=None,
|
|
540
563
|
help=(
|
|
541
|
-
|
|
564
|
+
"Your SnipeIT base URI"
|
|
565
|
+
"Required if you are using the SnipeIT intel module. Ignored otherwise."
|
|
542
566
|
),
|
|
543
567
|
)
|
|
544
568
|
parser.add_argument(
|
|
545
|
-
|
|
569
|
+
"--snipeit-token-env-var",
|
|
570
|
+
type=str,
|
|
571
|
+
default=None,
|
|
572
|
+
help="The name of an environment variable containing token with which to authenticate to SnipeIT.",
|
|
573
|
+
)
|
|
574
|
+
parser.add_argument(
|
|
575
|
+
"--snipeit-tenant-id",
|
|
576
|
+
type=str,
|
|
577
|
+
default=None,
|
|
578
|
+
help="An ID for the SnipeIT tenant.",
|
|
579
|
+
)
|
|
580
|
+
parser.add_argument(
|
|
581
|
+
"--cloudflare-token-env-var",
|
|
582
|
+
type=str,
|
|
583
|
+
default=None,
|
|
584
|
+
help="The name of an environment variable containing ApiKey with which to authenticate to Cloudflare.",
|
|
585
|
+
)
|
|
586
|
+
parser.add_argument(
|
|
587
|
+
"--tailscale-token-env-var",
|
|
546
588
|
type=str,
|
|
547
589
|
default=None,
|
|
548
590
|
help=(
|
|
549
|
-
|
|
550
|
-
|
|
591
|
+
"The name of an environment variable containing a Tailscale API token."
|
|
592
|
+
"Required if you are using the Tailscale intel module. Ignored otherwise."
|
|
551
593
|
),
|
|
552
594
|
)
|
|
553
595
|
parser.add_argument(
|
|
554
|
-
|
|
596
|
+
"--tailscale-org",
|
|
555
597
|
type=str,
|
|
556
598
|
default=None,
|
|
557
599
|
help=(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
'See the full list of supported ecosystems in source code at cartography.intel.semgrep.dependencies. '
|
|
561
|
-
'Required if you are using the Semgrep dependencies intel module. Ignored otherwise.'
|
|
600
|
+
"The name of the Tailscale organization to sync. "
|
|
601
|
+
"Required if you are using the Tailscale intel module. Ignored otherwise."
|
|
562
602
|
),
|
|
563
603
|
)
|
|
564
604
|
parser.add_argument(
|
|
565
|
-
|
|
605
|
+
"--tailscale-base-url",
|
|
566
606
|
type=str,
|
|
567
|
-
default=
|
|
607
|
+
default="https://api.tailscale.com/api/v2",
|
|
568
608
|
help=(
|
|
569
|
-
|
|
570
|
-
|
|
609
|
+
"The base URL for the Tailscale API. "
|
|
610
|
+
"Required if you are using the Tailscale intel module. Ignored otherwise."
|
|
571
611
|
),
|
|
572
612
|
)
|
|
573
613
|
parser.add_argument(
|
|
574
|
-
|
|
614
|
+
"--openai-apikey-env-var",
|
|
575
615
|
type=str,
|
|
576
616
|
default=None,
|
|
577
|
-
help=
|
|
617
|
+
help=(
|
|
618
|
+
"The name of an environment variable containing a OpenAI API Key."
|
|
619
|
+
"Required if you are using the OpenAI intel module. Ignored otherwise."
|
|
620
|
+
),
|
|
578
621
|
)
|
|
579
622
|
parser.add_argument(
|
|
580
|
-
|
|
623
|
+
"--openai-org-id",
|
|
581
624
|
type=str,
|
|
582
625
|
default=None,
|
|
583
|
-
help=
|
|
626
|
+
help=(
|
|
627
|
+
"The ID of the OpenAI organization to sync. "
|
|
628
|
+
"Required if you are using the OpenAI intel module. Ignored otherwise."
|
|
629
|
+
),
|
|
584
630
|
)
|
|
585
631
|
|
|
586
632
|
return parser
|
|
@@ -596,17 +642,20 @@ class CLI:
|
|
|
596
642
|
config: argparse.Namespace = self.parser.parse_args(argv)
|
|
597
643
|
# Logging config
|
|
598
644
|
if config.verbose:
|
|
599
|
-
logging.getLogger(
|
|
645
|
+
logging.getLogger("cartography").setLevel(logging.DEBUG)
|
|
600
646
|
elif config.quiet:
|
|
601
|
-
logging.getLogger(
|
|
647
|
+
logging.getLogger("cartography").setLevel(logging.WARNING)
|
|
602
648
|
else:
|
|
603
|
-
logging.getLogger(
|
|
649
|
+
logging.getLogger("cartography").setLevel(logging.INFO)
|
|
604
650
|
logger.debug("Launching cartography with CLI configuration: %r", vars(config))
|
|
605
651
|
# Neo4j config
|
|
606
652
|
if config.neo4j_user:
|
|
607
653
|
config.neo4j_password = None
|
|
608
654
|
if config.neo4j_password_prompt:
|
|
609
|
-
logger.info(
|
|
655
|
+
logger.info(
|
|
656
|
+
"Reading password for Neo4j user '%s' interactively.",
|
|
657
|
+
config.neo4j_user,
|
|
658
|
+
)
|
|
610
659
|
config.neo4j_password = getpass.getpass()
|
|
611
660
|
elif config.neo4j_password_env_var:
|
|
612
661
|
logger.debug(
|
|
@@ -616,7 +665,9 @@ class CLI:
|
|
|
616
665
|
)
|
|
617
666
|
config.neo4j_password = os.environ.get(config.neo4j_password_env_var)
|
|
618
667
|
if not config.neo4j_password:
|
|
619
|
-
logger.warning(
|
|
668
|
+
logger.warning(
|
|
669
|
+
"Neo4j username was provided but a password could not be found.",
|
|
670
|
+
)
|
|
620
671
|
else:
|
|
621
672
|
config.neo4j_password = None
|
|
622
673
|
|
|
@@ -629,44 +680,65 @@ class CLI:
|
|
|
629
680
|
# No need to store the returned value; we're using this for input validation.
|
|
630
681
|
parse_and_validate_aws_requested_syncs(config.aws_requested_syncs)
|
|
631
682
|
|
|
683
|
+
# AWS regions
|
|
684
|
+
if config.aws_regions:
|
|
685
|
+
# No need to store the returned value; we're using this for input validation.
|
|
686
|
+
parse_and_validate_aws_regions(config.aws_regions)
|
|
687
|
+
|
|
632
688
|
# Azure config
|
|
633
689
|
if config.azure_sp_auth and config.azure_client_secret_env_var:
|
|
634
690
|
logger.debug(
|
|
635
691
|
"Reading Client Secret for Azure Service Principal Authentication from environment variable %s",
|
|
636
692
|
config.azure_client_secret_env_var,
|
|
637
693
|
)
|
|
638
|
-
config.azure_client_secret = os.environ.get(
|
|
694
|
+
config.azure_client_secret = os.environ.get(
|
|
695
|
+
config.azure_client_secret_env_var,
|
|
696
|
+
)
|
|
639
697
|
else:
|
|
640
698
|
config.azure_client_secret = None
|
|
641
699
|
|
|
642
700
|
# Entra config
|
|
643
|
-
if
|
|
701
|
+
if (
|
|
702
|
+
config.entra_tenant_id
|
|
703
|
+
and config.entra_client_id
|
|
704
|
+
and config.entra_client_secret_env_var
|
|
705
|
+
):
|
|
644
706
|
logger.debug(
|
|
645
707
|
"Reading Client Secret for Entra Authentication from environment variable %s",
|
|
646
708
|
config.entra_client_secret_env_var,
|
|
647
709
|
)
|
|
648
|
-
config.entra_client_secret = os.environ.get(
|
|
710
|
+
config.entra_client_secret = os.environ.get(
|
|
711
|
+
config.entra_client_secret_env_var
|
|
712
|
+
)
|
|
649
713
|
else:
|
|
650
714
|
config.entra_client_secret = None
|
|
651
715
|
|
|
652
716
|
# Okta config
|
|
653
717
|
if config.okta_org_id and config.okta_api_key_env_var:
|
|
654
|
-
logger.debug(
|
|
718
|
+
logger.debug(
|
|
719
|
+
f"Reading API key for Okta from environment variable {config.okta_api_key_env_var}",
|
|
720
|
+
)
|
|
655
721
|
config.okta_api_key = os.environ.get(config.okta_api_key_env_var)
|
|
656
722
|
else:
|
|
657
723
|
config.okta_api_key = None
|
|
658
724
|
|
|
659
725
|
# GitHub config
|
|
660
726
|
if config.github_config_env_var:
|
|
661
|
-
logger.debug(
|
|
727
|
+
logger.debug(
|
|
728
|
+
f"Reading config string for GitHub from environment variable {config.github_config_env_var}",
|
|
729
|
+
)
|
|
662
730
|
config.github_config = os.environ.get(config.github_config_env_var)
|
|
663
731
|
else:
|
|
664
732
|
config.github_config = None
|
|
665
733
|
|
|
666
734
|
# DigitalOcean config
|
|
667
735
|
if config.digitalocean_token_env_var:
|
|
668
|
-
logger.debug(
|
|
669
|
-
|
|
736
|
+
logger.debug(
|
|
737
|
+
f"Reading token for DigitalOcean from env variable {config.digitalocean_token_env_var}",
|
|
738
|
+
)
|
|
739
|
+
config.digitalocean_token = os.environ.get(
|
|
740
|
+
config.digitalocean_token_env_var,
|
|
741
|
+
)
|
|
670
742
|
else:
|
|
671
743
|
config.digitalocean_token = None
|
|
672
744
|
|
|
@@ -697,11 +769,11 @@ class CLI:
|
|
|
697
769
|
config.kandji_token_env_var,
|
|
698
770
|
)
|
|
699
771
|
config.kandji_token = os.environ.get(config.kandji_token_env_var)
|
|
700
|
-
elif os.environ.get(
|
|
772
|
+
elif os.environ.get("KANDJI_TOKEN"):
|
|
701
773
|
logger.debug(
|
|
702
774
|
"Reading Kandji API token from environment variable 'KANDJI_TOKEN'.",
|
|
703
775
|
)
|
|
704
|
-
config.kandji_token = os.environ.get(
|
|
776
|
+
config.kandji_token = os.environ.get("KANDJI_TOKEN")
|
|
705
777
|
else:
|
|
706
778
|
logger.warning("A Kandji base URI was provided but a token was not.")
|
|
707
779
|
config.kandji_token = None
|
|
@@ -711,13 +783,15 @@ class CLI:
|
|
|
711
783
|
|
|
712
784
|
if config.statsd_enabled:
|
|
713
785
|
logger.debug(
|
|
714
|
-
f
|
|
786
|
+
f"statsd enabled. Sending metrics to server {config.statsd_host}:{config.statsd_port}. "
|
|
715
787
|
f'Metrics have prefix "{config.statsd_prefix}".',
|
|
716
788
|
)
|
|
717
789
|
|
|
718
790
|
# Pagerduty config
|
|
719
791
|
if config.pagerduty_api_key_env_var:
|
|
720
|
-
logger.debug(
|
|
792
|
+
logger.debug(
|
|
793
|
+
f"Reading API key for PagerDuty from environment variable {config.pagerduty_api_key_env_var}",
|
|
794
|
+
)
|
|
721
795
|
config.pagerduty_api_key = os.environ.get(config.pagerduty_api_key_env_var)
|
|
722
796
|
else:
|
|
723
797
|
config.pagerduty_api_key = None
|
|
@@ -727,7 +801,9 @@ class CLI:
|
|
|
727
801
|
logger.debug(
|
|
728
802
|
f"Reading API key for Crowdstrike from environment variable {config.crowdstrike_client_id_env_var}",
|
|
729
803
|
)
|
|
730
|
-
config.crowdstrike_client_id = os.environ.get(
|
|
804
|
+
config.crowdstrike_client_id = os.environ.get(
|
|
805
|
+
config.crowdstrike_client_id_env_var,
|
|
806
|
+
)
|
|
731
807
|
else:
|
|
732
808
|
config.crowdstrike_client_id = None
|
|
733
809
|
|
|
@@ -735,36 +811,54 @@ class CLI:
|
|
|
735
811
|
logger.debug(
|
|
736
812
|
f"Reading API key for Crowdstrike from environment variable {config.crowdstrike_client_secret_env_var}",
|
|
737
813
|
)
|
|
738
|
-
config.crowdstrike_client_secret = os.environ.get(
|
|
814
|
+
config.crowdstrike_client_secret = os.environ.get(
|
|
815
|
+
config.crowdstrike_client_secret_env_var,
|
|
816
|
+
)
|
|
739
817
|
else:
|
|
740
818
|
config.crowdstrike_client_secret = None
|
|
741
819
|
|
|
742
820
|
# GSuite config
|
|
743
821
|
if config.gsuite_tokens_env_var:
|
|
744
|
-
logger.debug(
|
|
822
|
+
logger.debug(
|
|
823
|
+
f"Reading config string for GSuite from environment variable {config.gsuite_tokens_env_var}",
|
|
824
|
+
)
|
|
745
825
|
config.gsuite_config = os.environ.get(config.gsuite_tokens_env_var)
|
|
746
826
|
else:
|
|
747
827
|
config.gsuite_tokens_env_var = None
|
|
748
828
|
|
|
749
829
|
# Lastpass config
|
|
750
830
|
if config.lastpass_cid_env_var:
|
|
751
|
-
logger.debug(
|
|
831
|
+
logger.debug(
|
|
832
|
+
f"Reading CID for Lastpass from environment variable {config.lastpass_cid_env_var}",
|
|
833
|
+
)
|
|
752
834
|
config.lastpass_cid = os.environ.get(config.lastpass_cid_env_var)
|
|
753
835
|
else:
|
|
754
836
|
config.lastpass_cid = None
|
|
755
837
|
if config.lastpass_provhash_env_var:
|
|
756
|
-
logger.debug(
|
|
838
|
+
logger.debug(
|
|
839
|
+
f"Reading provhash for Lastpass from environment variable {config.lastpass_provhash_env_var}",
|
|
840
|
+
)
|
|
757
841
|
config.lastpass_provhash = os.environ.get(config.lastpass_provhash_env_var)
|
|
758
842
|
else:
|
|
759
843
|
config.lastpass_provhash = None
|
|
760
844
|
|
|
761
845
|
# BigFix config
|
|
762
|
-
if
|
|
763
|
-
|
|
846
|
+
if (
|
|
847
|
+
config.bigfix_username
|
|
848
|
+
and config.bigfix_password_env_var
|
|
849
|
+
and config.bigfix_root_url
|
|
850
|
+
):
|
|
851
|
+
logger.debug(
|
|
852
|
+
f"Reading BigFix password from environment variable {config.bigfix_password_env_var}",
|
|
853
|
+
)
|
|
764
854
|
config.bigfix_password = os.environ.get(config.bigfix_password_env_var)
|
|
765
855
|
|
|
766
856
|
# Duo config
|
|
767
|
-
if
|
|
857
|
+
if (
|
|
858
|
+
config.duo_api_key_env_var
|
|
859
|
+
and config.duo_api_secret_env_var
|
|
860
|
+
and config.duo_api_hostname
|
|
861
|
+
):
|
|
768
862
|
logger.debug(
|
|
769
863
|
f"Reading Duo api key and secret from environment variables {config.duo_api_key_env_var}"
|
|
770
864
|
f", {config.duo_api_secret_env_var}",
|
|
@@ -777,7 +871,9 @@ class CLI:
|
|
|
777
871
|
|
|
778
872
|
# Semgrep config
|
|
779
873
|
if config.semgrep_app_token_env_var:
|
|
780
|
-
logger.debug(
|
|
874
|
+
logger.debug(
|
|
875
|
+
f"Reading Semgrep App Token from environment variable {config.semgrep_app_token_env_var}",
|
|
876
|
+
)
|
|
781
877
|
config.semgrep_app_token = os.environ.get(config.semgrep_app_token_env_var)
|
|
782
878
|
else:
|
|
783
879
|
config.semgrep_app_token = None
|
|
@@ -787,7 +883,9 @@ class CLI:
|
|
|
787
883
|
|
|
788
884
|
# CVE feed config
|
|
789
885
|
if config.cve_api_key_env_var:
|
|
790
|
-
logger.debug(
|
|
886
|
+
logger.debug(
|
|
887
|
+
f"Reading NVD CVE API key environment variable {config.cve_api_key_env_var}",
|
|
888
|
+
)
|
|
791
889
|
config.cve_api_key = os.environ.get(config.cve_api_key_env_var)
|
|
792
890
|
else:
|
|
793
891
|
config.cve_api_key = None
|
|
@@ -800,11 +898,11 @@ class CLI:
|
|
|
800
898
|
config.snipeit_token_env_var,
|
|
801
899
|
)
|
|
802
900
|
config.snipeit_token = os.environ.get(config.snipeit_token_env_var)
|
|
803
|
-
elif os.environ.get(
|
|
901
|
+
elif os.environ.get("SNIPEIT_TOKEN"):
|
|
804
902
|
logger.debug(
|
|
805
903
|
"Reading SnipeIT API token from environment variable 'SNIPEIT_TOKEN'.",
|
|
806
904
|
)
|
|
807
|
-
config.snipeit_token = os.environ.get(
|
|
905
|
+
config.snipeit_token = os.environ.get("SNIPEIT_TOKEN")
|
|
808
906
|
else:
|
|
809
907
|
logger.warning("A SnipeIT base URI was provided but a token was not.")
|
|
810
908
|
config.kandji_token = None
|
|
@@ -812,6 +910,33 @@ class CLI:
|
|
|
812
910
|
logger.warning("A SnipeIT base URI was not provided.")
|
|
813
911
|
config.snipeit_base_uri = None
|
|
814
912
|
|
|
913
|
+
# Tailscale config
|
|
914
|
+
if config.tailscale_token_env_var:
|
|
915
|
+
logger.debug(
|
|
916
|
+
f"Reading Tailscale API token from environment variable {config.tailscale_token_env_var}",
|
|
917
|
+
)
|
|
918
|
+
config.tailscale_token = os.environ.get(config.tailscale_token_env_var)
|
|
919
|
+
else:
|
|
920
|
+
config.tailscale_token = None
|
|
921
|
+
|
|
922
|
+
# Cloudflare config
|
|
923
|
+
if config.cloudflare_token_env_var:
|
|
924
|
+
logger.debug(
|
|
925
|
+
f"Reading Cloudflare ApiKey from environment variable {config.cloudflare_token_env_var}",
|
|
926
|
+
)
|
|
927
|
+
config.cloudflare_token = os.environ.get(config.cloudflare_token_env_var)
|
|
928
|
+
else:
|
|
929
|
+
config.cloudflare_token = None
|
|
930
|
+
|
|
931
|
+
# OpenAI config
|
|
932
|
+
if config.openai_apikey_env_var:
|
|
933
|
+
logger.debug(
|
|
934
|
+
f"Reading OpenAI API key from environment variable {config.openai_apikey_env_var}",
|
|
935
|
+
)
|
|
936
|
+
config.openai_apikey = os.environ.get(config.openai_apikey_env_var)
|
|
937
|
+
else:
|
|
938
|
+
config.openai_apikey = None
|
|
939
|
+
|
|
815
940
|
# Run cartography
|
|
816
941
|
try:
|
|
817
942
|
return cartography.sync.run_with_config(self.sync, config)
|
|
@@ -829,12 +954,14 @@ def main(argv=None):
|
|
|
829
954
|
:return: The return code.
|
|
830
955
|
"""
|
|
831
956
|
logging.basicConfig(level=logging.INFO)
|
|
832
|
-
logging.getLogger(
|
|
833
|
-
logging.getLogger(
|
|
834
|
-
logging.getLogger(
|
|
835
|
-
logging.getLogger(
|
|
836
|
-
logging.getLogger(
|
|
837
|
-
logging.getLogger(
|
|
957
|
+
logging.getLogger("botocore").setLevel(logging.WARNING)
|
|
958
|
+
logging.getLogger("googleapiclient").setLevel(logging.WARNING)
|
|
959
|
+
logging.getLogger("neo4j").setLevel(logging.WARNING)
|
|
960
|
+
logging.getLogger("azure.identity").setLevel(logging.WARNING)
|
|
961
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
962
|
+
logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(
|
|
963
|
+
logging.WARNING
|
|
964
|
+
)
|
|
838
965
|
|
|
839
966
|
argv = argv if argv is not None else sys.argv[1:]
|
|
840
|
-
sys.exit(CLI(prog=
|
|
967
|
+
sys.exit(CLI(prog="cartography").main(argv))
|