cartography 0.102.0rc1__py3-none-any.whl → 0.103.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cartography might be problematic. Click here for more details.
- cartography/__main__.py +1 -2
- cartography/_version.py +2 -2
- cartography/cli.py +302 -253
- cartography/client/core/tx.py +39 -18
- cartography/config.py +4 -0
- cartography/driftdetect/__main__.py +1 -2
- cartography/driftdetect/add_shortcut.py +10 -2
- cartography/driftdetect/cli.py +71 -75
- cartography/driftdetect/detect_deviations.py +7 -3
- cartography/driftdetect/get_states.py +20 -8
- cartography/driftdetect/model.py +5 -5
- cartography/driftdetect/serializers.py +8 -6
- cartography/driftdetect/storage.py +2 -2
- cartography/graph/cleanupbuilder.py +35 -15
- cartography/graph/job.py +46 -17
- cartography/graph/querybuilder.py +165 -80
- cartography/graph/statement.py +35 -26
- cartography/intel/analysis.py +4 -1
- cartography/intel/aws/__init__.py +114 -55
- cartography/intel/aws/apigateway.py +134 -63
- cartography/intel/aws/cloudtrail.py +127 -0
- cartography/intel/aws/config.py +56 -20
- cartography/intel/aws/dynamodb.py +108 -40
- cartography/intel/aws/ec2/__init__.py +2 -2
- cartography/intel/aws/ec2/auto_scaling_groups.py +181 -78
- cartography/intel/aws/ec2/elastic_ip_addresses.py +41 -13
- cartography/intel/aws/ec2/images.py +49 -20
- cartography/intel/aws/ec2/instances.py +234 -136
- cartography/intel/aws/ec2/internet_gateways.py +40 -11
- cartography/intel/aws/ec2/key_pairs.py +44 -20
- cartography/intel/aws/ec2/launch_templates.py +101 -59
- cartography/intel/aws/ec2/load_balancer_v2s.py +104 -39
- cartography/intel/aws/ec2/load_balancers.py +82 -42
- cartography/intel/aws/ec2/network_acls.py +89 -65
- cartography/intel/aws/ec2/network_interfaces.py +146 -87
- cartography/intel/aws/ec2/reserved_instances.py +45 -16
- cartography/intel/aws/ec2/route_tables.py +327 -0
- cartography/intel/aws/ec2/security_groups.py +71 -21
- cartography/intel/aws/ec2/snapshots.py +61 -22
- cartography/intel/aws/ec2/subnets.py +54 -18
- cartography/intel/aws/ec2/tgw.py +100 -34
- cartography/intel/aws/ec2/util.py +1 -1
- cartography/intel/aws/ec2/volumes.py +69 -41
- cartography/intel/aws/ec2/vpc.py +37 -12
- cartography/intel/aws/ec2/vpc_peerings.py +83 -24
- cartography/intel/aws/ecr.py +88 -32
- cartography/intel/aws/ecs.py +83 -47
- cartography/intel/aws/eks.py +55 -29
- cartography/intel/aws/elasticache.py +42 -18
- cartography/intel/aws/elasticsearch.py +57 -20
- cartography/intel/aws/emr.py +61 -23
- cartography/intel/aws/iam.py +401 -145
- cartography/intel/aws/iam_instance_profiles.py +22 -22
- cartography/intel/aws/identitycenter.py +71 -37
- cartography/intel/aws/inspector.py +159 -89
- cartography/intel/aws/kms.py +92 -38
- cartography/intel/aws/lambda_function.py +103 -34
- cartography/intel/aws/organizations.py +30 -10
- cartography/intel/aws/permission_relationships.py +133 -51
- cartography/intel/aws/rds.py +249 -85
- cartography/intel/aws/redshift.py +107 -46
- cartography/intel/aws/resourcegroupstaggingapi.py +120 -66
- cartography/intel/aws/resources.py +53 -44
- cartography/intel/aws/route53.py +108 -61
- cartography/intel/aws/s3.py +168 -83
- cartography/intel/aws/s3accountpublicaccessblock.py +157 -0
- cartography/intel/aws/secretsmanager.py +24 -12
- cartography/intel/aws/securityhub.py +20 -9
- cartography/intel/aws/sns.py +166 -0
- cartography/intel/aws/sqs.py +60 -28
- cartography/intel/aws/ssm.py +70 -30
- cartography/intel/aws/util/arns.py +7 -7
- cartography/intel/aws/util/common.py +31 -4
- cartography/intel/azure/__init__.py +78 -19
- cartography/intel/azure/compute.py +101 -27
- cartography/intel/azure/cosmosdb.py +496 -170
- cartography/intel/azure/sql.py +296 -105
- cartography/intel/azure/storage.py +322 -113
- cartography/intel/azure/subscription.py +39 -23
- cartography/intel/azure/tenant.py +13 -4
- cartography/intel/azure/util/credentials.py +95 -55
- cartography/intel/bigfix/__init__.py +2 -2
- cartography/intel/bigfix/computers.py +93 -65
- cartography/intel/create_indexes.py +3 -2
- cartography/intel/crowdstrike/__init__.py +11 -9
- cartography/intel/crowdstrike/endpoints.py +5 -1
- cartography/intel/crowdstrike/spotlight.py +8 -3
- cartography/intel/cve/__init__.py +46 -13
- cartography/intel/cve/feed.py +48 -12
- cartography/intel/digitalocean/__init__.py +22 -13
- cartography/intel/digitalocean/compute.py +75 -108
- cartography/intel/digitalocean/management.py +44 -80
- cartography/intel/digitalocean/platform.py +48 -43
- cartography/intel/dns.py +36 -10
- cartography/intel/duo/__init__.py +21 -16
- cartography/intel/duo/api_host.py +14 -9
- cartography/intel/duo/endpoints.py +50 -45
- cartography/intel/duo/groups.py +18 -14
- cartography/intel/duo/phones.py +37 -34
- cartography/intel/duo/tokens.py +26 -23
- cartography/intel/duo/users.py +54 -50
- cartography/intel/duo/web_authn_credentials.py +30 -25
- cartography/intel/entra/__init__.py +25 -7
- cartography/intel/entra/ou.py +112 -0
- cartography/intel/entra/users.py +69 -63
- cartography/intel/gcp/__init__.py +185 -49
- cartography/intel/gcp/compute.py +418 -231
- cartography/intel/gcp/crm.py +96 -43
- cartography/intel/gcp/dns.py +60 -19
- cartography/intel/gcp/gke.py +72 -38
- cartography/intel/gcp/iam.py +61 -41
- cartography/intel/gcp/storage.py +84 -55
- cartography/intel/github/__init__.py +13 -11
- cartography/intel/github/repos.py +270 -137
- cartography/intel/github/teams.py +170 -88
- cartography/intel/github/users.py +70 -39
- cartography/intel/github/util.py +36 -34
- cartography/intel/gsuite/__init__.py +47 -26
- cartography/intel/gsuite/api.py +73 -30
- cartography/intel/jamf/__init__.py +19 -1
- cartography/intel/jamf/computers.py +30 -7
- cartography/intel/jamf/util.py +7 -2
- cartography/intel/kandji/__init__.py +6 -3
- cartography/intel/kandji/devices.py +14 -8
- cartography/intel/kubernetes/namespaces.py +7 -4
- cartography/intel/kubernetes/pods.py +7 -4
- cartography/intel/kubernetes/services.py +8 -4
- cartography/intel/lastpass/__init__.py +2 -2
- cartography/intel/lastpass/users.py +23 -12
- cartography/intel/oci/__init__.py +44 -11
- cartography/intel/oci/iam.py +134 -38
- cartography/intel/oci/organizations.py +13 -6
- cartography/intel/oci/utils.py +43 -20
- cartography/intel/okta/__init__.py +66 -15
- cartography/intel/okta/applications.py +42 -20
- cartography/intel/okta/awssaml.py +93 -33
- cartography/intel/okta/factors.py +16 -4
- cartography/intel/okta/groups.py +56 -29
- cartography/intel/okta/organization.py +5 -1
- cartography/intel/okta/origins.py +6 -2
- cartography/intel/okta/roles.py +15 -5
- cartography/intel/okta/users.py +20 -8
- cartography/intel/okta/utils.py +6 -4
- cartography/intel/pagerduty/__init__.py +8 -7
- cartography/intel/pagerduty/escalation_policies.py +18 -6
- cartography/intel/pagerduty/schedules.py +12 -4
- cartography/intel/pagerduty/services.py +11 -4
- cartography/intel/pagerduty/teams.py +8 -3
- cartography/intel/pagerduty/users.py +3 -1
- cartography/intel/pagerduty/vendors.py +3 -1
- cartography/intel/semgrep/__init__.py +24 -6
- cartography/intel/semgrep/dependencies.py +50 -28
- cartography/intel/semgrep/deployment.py +3 -1
- cartography/intel/semgrep/findings.py +42 -18
- cartography/intel/snipeit/__init__.py +17 -3
- cartography/intel/snipeit/asset.py +12 -6
- cartography/intel/snipeit/user.py +8 -5
- cartography/intel/snipeit/util.py +9 -4
- cartography/models/aws/apigateway.py +21 -17
- cartography/models/aws/apigatewaycertificate.py +28 -22
- cartography/models/aws/apigatewayresource.py +28 -20
- cartography/models/aws/apigatewaystage.py +33 -25
- cartography/models/aws/cloudtrail/__init__.py +0 -0
- cartography/models/aws/cloudtrail/trail.py +61 -0
- cartography/models/aws/dynamodb/gsi.py +30 -22
- cartography/models/aws/dynamodb/tables.py +25 -17
- cartography/models/aws/ec2/auto_scaling_groups.py +102 -82
- cartography/models/aws/ec2/images.py +36 -34
- cartography/models/aws/ec2/instances.py +51 -45
- cartography/models/aws/ec2/keypair.py +21 -16
- cartography/models/aws/ec2/keypair_instance.py +28 -21
- cartography/models/aws/ec2/launch_configurations.py +30 -26
- cartography/models/aws/ec2/launch_template_versions.py +48 -38
- cartography/models/aws/ec2/launch_templates.py +21 -17
- cartography/models/aws/ec2/load_balancer_listeners.py +27 -23
- cartography/models/aws/ec2/load_balancers.py +47 -37
- cartography/models/aws/ec2/network_acl_rules.py +38 -30
- cartography/models/aws/ec2/network_acls.py +38 -29
- cartography/models/aws/ec2/networkinterface_instance.py +52 -39
- cartography/models/aws/ec2/networkinterfaces.py +53 -37
- cartography/models/aws/ec2/privateip_networkinterface.py +32 -22
- cartography/models/aws/ec2/reservations.py +18 -14
- cartography/models/aws/ec2/route_table_associations.py +97 -0
- cartography/models/aws/ec2/route_tables.py +128 -0
- cartography/models/aws/ec2/routes.py +85 -0
- cartography/models/aws/ec2/securitygroup_instance.py +29 -20
- cartography/models/aws/ec2/securitygroup_networkinterface.py +24 -15
- cartography/models/aws/ec2/subnet_instance.py +24 -19
- cartography/models/aws/ec2/subnet_networkinterface.py +40 -31
- cartography/models/aws/ec2/volumes.py +47 -40
- cartography/models/aws/eks/clusters.py +23 -21
- cartography/models/aws/emr.py +32 -30
- cartography/models/aws/iam/instanceprofile.py +33 -24
- cartography/models/aws/identitycenter/awsidentitycenter.py +18 -14
- cartography/models/aws/identitycenter/awspermissionset.py +37 -29
- cartography/models/aws/identitycenter/awsssouser.py +23 -21
- cartography/models/aws/inspector/findings.py +77 -65
- cartography/models/aws/inspector/packages.py +35 -29
- cartography/models/aws/s3/__init__.py +0 -0
- cartography/models/aws/s3/account_public_access_block.py +51 -0
- cartography/models/aws/sns/__init__.py +0 -0
- cartography/models/aws/sns/topic.py +50 -0
- cartography/models/aws/ssm/instance_information.py +51 -39
- cartography/models/aws/ssm/instance_patch.py +32 -26
- cartography/models/bigfix/bigfix_computer.py +42 -38
- cartography/models/bigfix/bigfix_root.py +3 -3
- cartography/models/core/common.py +12 -10
- cartography/models/core/nodes.py +5 -2
- cartography/models/core/relationships.py +14 -6
- cartography/models/crowdstrike/hosts.py +37 -35
- cartography/models/cve/cve.py +34 -32
- cartography/models/cve/cve_feed.py +6 -6
- cartography/models/digitalocean/__init__.py +0 -0
- cartography/models/digitalocean/account.py +21 -0
- cartography/models/digitalocean/droplet.py +56 -0
- cartography/models/digitalocean/project.py +48 -0
- cartography/models/duo/api_host.py +3 -3
- cartography/models/duo/endpoint.py +43 -41
- cartography/models/duo/group.py +14 -14
- cartography/models/duo/phone.py +27 -27
- cartography/models/duo/token.py +16 -16
- cartography/models/duo/user.py +46 -44
- cartography/models/duo/web_authn_credential.py +27 -19
- cartography/models/entra/ou.py +48 -0
- cartography/models/entra/tenant.py +24 -18
- cartography/models/entra/user.py +64 -48
- cartography/models/gcp/iam.py +23 -23
- cartography/models/github/orgs.py +5 -4
- cartography/models/github/teams.py +37 -31
- cartography/models/github/users.py +34 -23
- cartography/models/kandji/device.py +22 -16
- cartography/models/kandji/tenant.py +6 -4
- cartography/models/lastpass/tenant.py +3 -3
- cartography/models/lastpass/user.py +32 -28
- cartography/models/semgrep/dependencies.py +36 -24
- cartography/models/semgrep/deployment.py +5 -5
- cartography/models/semgrep/findings.py +58 -42
- cartography/models/semgrep/locations.py +27 -21
- cartography/models/snipeit/asset.py +30 -21
- cartography/models/snipeit/tenant.py +6 -4
- cartography/models/snipeit/user.py +19 -12
- cartography/stats.py +3 -3
- cartography/sync.py +107 -31
- cartography/util.py +84 -62
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/METADATA +3 -14
- cartography-0.103.0rc1.dist-info/RECORD +396 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/WHEEL +1 -1
- cartography-0.102.0rc1.dist-info/RECORD +0 -377
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/entry_points.txt +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/licenses/LICENSE +0 -0
- {cartography-0.102.0rc1.dist-info → cartography-0.103.0rc1.dist-info}/top_level.txt +0 -0
cartography/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,531 @@ 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",
|
|
522
523
|
type=str,
|
|
523
524
|
default=None,
|
|
524
|
-
help=(
|
|
525
|
-
'The name of environment variable containing the Duo api key'
|
|
526
|
-
),
|
|
525
|
+
help=("The name of environment variable containing the Duo api key"),
|
|
527
526
|
)
|
|
528
527
|
parser.add_argument(
|
|
529
|
-
|
|
528
|
+
"--duo-api-secret-env-var",
|
|
530
529
|
type=str,
|
|
531
530
|
default=None,
|
|
532
|
-
help=(
|
|
533
|
-
'The name of environment variable containing the Duo api secret'
|
|
534
|
-
),
|
|
531
|
+
help=("The name of environment variable containing the Duo api secret"),
|
|
535
532
|
)
|
|
536
533
|
parser.add_argument(
|
|
537
|
-
|
|
534
|
+
"--duo-api-hostname",
|
|
538
535
|
type=str,
|
|
539
536
|
default=None,
|
|
540
|
-
help=(
|
|
541
|
-
'The Duo api hostname'
|
|
542
|
-
),
|
|
537
|
+
help=("The Duo api hostname"),
|
|
543
538
|
)
|
|
544
539
|
parser.add_argument(
|
|
545
|
-
|
|
540
|
+
"--semgrep-app-token-env-var",
|
|
546
541
|
type=str,
|
|
547
542
|
default=None,
|
|
548
543
|
help=(
|
|
549
|
-
|
|
550
|
-
|
|
544
|
+
"The name of environment variable containing the Semgrep app token key. "
|
|
545
|
+
"Required if you are using the Semgrep intel module. Ignored otherwise."
|
|
551
546
|
),
|
|
552
547
|
)
|
|
553
548
|
parser.add_argument(
|
|
554
|
-
|
|
549
|
+
"--semgrep-dependency-ecosystems",
|
|
555
550
|
type=str,
|
|
556
551
|
default=None,
|
|
557
552
|
help=(
|
|
558
|
-
|
|
553
|
+
"Comma-separated list of language ecosystems for which dependencies will be retrieved from Semgrep. "
|
|
559
554
|
'For example, a value of "gomod,npm" will retrieve Go and NPM dependencies. '
|
|
560
|
-
|
|
561
|
-
|
|
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."
|
|
562
557
|
),
|
|
563
558
|
)
|
|
564
559
|
parser.add_argument(
|
|
565
|
-
|
|
560
|
+
"--snipeit-base-uri",
|
|
566
561
|
type=str,
|
|
567
562
|
default=None,
|
|
568
563
|
help=(
|
|
569
|
-
|
|
570
|
-
|
|
564
|
+
"Your SnipeIT base URI"
|
|
565
|
+
"Required if you are using the SnipeIT intel module. Ignored otherwise."
|
|
571
566
|
),
|
|
572
567
|
)
|
|
573
568
|
parser.add_argument(
|
|
574
|
-
|
|
569
|
+
"--snipeit-token-env-var",
|
|
575
570
|
type=str,
|
|
576
571
|
default=None,
|
|
577
|
-
help=
|
|
572
|
+
help="The name of an environment variable containing token with which to authenticate to SnipeIT.",
|
|
578
573
|
)
|
|
579
574
|
parser.add_argument(
|
|
580
|
-
|
|
575
|
+
"--snipeit-tenant-id",
|
|
581
576
|
type=str,
|
|
582
577
|
default=None,
|
|
583
|
-
help=
|
|
578
|
+
help="An ID for the SnipeIT tenant.",
|
|
584
579
|
)
|
|
585
580
|
|
|
586
581
|
return parser
|
|
@@ -596,17 +591,20 @@ class CLI:
|
|
|
596
591
|
config: argparse.Namespace = self.parser.parse_args(argv)
|
|
597
592
|
# Logging config
|
|
598
593
|
if config.verbose:
|
|
599
|
-
logging.getLogger(
|
|
594
|
+
logging.getLogger("cartography").setLevel(logging.DEBUG)
|
|
600
595
|
elif config.quiet:
|
|
601
|
-
logging.getLogger(
|
|
596
|
+
logging.getLogger("cartography").setLevel(logging.WARNING)
|
|
602
597
|
else:
|
|
603
|
-
logging.getLogger(
|
|
598
|
+
logging.getLogger("cartography").setLevel(logging.INFO)
|
|
604
599
|
logger.debug("Launching cartography with CLI configuration: %r", vars(config))
|
|
605
600
|
# Neo4j config
|
|
606
601
|
if config.neo4j_user:
|
|
607
602
|
config.neo4j_password = None
|
|
608
603
|
if config.neo4j_password_prompt:
|
|
609
|
-
logger.info(
|
|
604
|
+
logger.info(
|
|
605
|
+
"Reading password for Neo4j user '%s' interactively.",
|
|
606
|
+
config.neo4j_user,
|
|
607
|
+
)
|
|
610
608
|
config.neo4j_password = getpass.getpass()
|
|
611
609
|
elif config.neo4j_password_env_var:
|
|
612
610
|
logger.debug(
|
|
@@ -616,7 +614,9 @@ class CLI:
|
|
|
616
614
|
)
|
|
617
615
|
config.neo4j_password = os.environ.get(config.neo4j_password_env_var)
|
|
618
616
|
if not config.neo4j_password:
|
|
619
|
-
logger.warning(
|
|
617
|
+
logger.warning(
|
|
618
|
+
"Neo4j username was provided but a password could not be found.",
|
|
619
|
+
)
|
|
620
620
|
else:
|
|
621
621
|
config.neo4j_password = None
|
|
622
622
|
|
|
@@ -629,44 +629,65 @@ class CLI:
|
|
|
629
629
|
# No need to store the returned value; we're using this for input validation.
|
|
630
630
|
parse_and_validate_aws_requested_syncs(config.aws_requested_syncs)
|
|
631
631
|
|
|
632
|
+
# AWS regions
|
|
633
|
+
if config.aws_regions:
|
|
634
|
+
# No need to store the returned value; we're using this for input validation.
|
|
635
|
+
parse_and_validate_aws_regions(config.aws_regions)
|
|
636
|
+
|
|
632
637
|
# Azure config
|
|
633
638
|
if config.azure_sp_auth and config.azure_client_secret_env_var:
|
|
634
639
|
logger.debug(
|
|
635
640
|
"Reading Client Secret for Azure Service Principal Authentication from environment variable %s",
|
|
636
641
|
config.azure_client_secret_env_var,
|
|
637
642
|
)
|
|
638
|
-
config.azure_client_secret = os.environ.get(
|
|
643
|
+
config.azure_client_secret = os.environ.get(
|
|
644
|
+
config.azure_client_secret_env_var,
|
|
645
|
+
)
|
|
639
646
|
else:
|
|
640
647
|
config.azure_client_secret = None
|
|
641
648
|
|
|
642
649
|
# Entra config
|
|
643
|
-
if
|
|
650
|
+
if (
|
|
651
|
+
config.entra_tenant_id
|
|
652
|
+
and config.entra_client_id
|
|
653
|
+
and config.entra_client_secret_env_var
|
|
654
|
+
):
|
|
644
655
|
logger.debug(
|
|
645
656
|
"Reading Client Secret for Entra Authentication from environment variable %s",
|
|
646
657
|
config.entra_client_secret_env_var,
|
|
647
658
|
)
|
|
648
|
-
config.entra_client_secret = os.environ.get(
|
|
659
|
+
config.entra_client_secret = os.environ.get(
|
|
660
|
+
config.entra_client_secret_env_var
|
|
661
|
+
)
|
|
649
662
|
else:
|
|
650
663
|
config.entra_client_secret = None
|
|
651
664
|
|
|
652
665
|
# Okta config
|
|
653
666
|
if config.okta_org_id and config.okta_api_key_env_var:
|
|
654
|
-
logger.debug(
|
|
667
|
+
logger.debug(
|
|
668
|
+
f"Reading API key for Okta from environment variable {config.okta_api_key_env_var}",
|
|
669
|
+
)
|
|
655
670
|
config.okta_api_key = os.environ.get(config.okta_api_key_env_var)
|
|
656
671
|
else:
|
|
657
672
|
config.okta_api_key = None
|
|
658
673
|
|
|
659
674
|
# GitHub config
|
|
660
675
|
if config.github_config_env_var:
|
|
661
|
-
logger.debug(
|
|
676
|
+
logger.debug(
|
|
677
|
+
f"Reading config string for GitHub from environment variable {config.github_config_env_var}",
|
|
678
|
+
)
|
|
662
679
|
config.github_config = os.environ.get(config.github_config_env_var)
|
|
663
680
|
else:
|
|
664
681
|
config.github_config = None
|
|
665
682
|
|
|
666
683
|
# DigitalOcean config
|
|
667
684
|
if config.digitalocean_token_env_var:
|
|
668
|
-
logger.debug(
|
|
669
|
-
|
|
685
|
+
logger.debug(
|
|
686
|
+
f"Reading token for DigitalOcean from env variable {config.digitalocean_token_env_var}",
|
|
687
|
+
)
|
|
688
|
+
config.digitalocean_token = os.environ.get(
|
|
689
|
+
config.digitalocean_token_env_var,
|
|
690
|
+
)
|
|
670
691
|
else:
|
|
671
692
|
config.digitalocean_token = None
|
|
672
693
|
|
|
@@ -697,11 +718,11 @@ class CLI:
|
|
|
697
718
|
config.kandji_token_env_var,
|
|
698
719
|
)
|
|
699
720
|
config.kandji_token = os.environ.get(config.kandji_token_env_var)
|
|
700
|
-
elif os.environ.get(
|
|
721
|
+
elif os.environ.get("KANDJI_TOKEN"):
|
|
701
722
|
logger.debug(
|
|
702
723
|
"Reading Kandji API token from environment variable 'KANDJI_TOKEN'.",
|
|
703
724
|
)
|
|
704
|
-
config.kandji_token = os.environ.get(
|
|
725
|
+
config.kandji_token = os.environ.get("KANDJI_TOKEN")
|
|
705
726
|
else:
|
|
706
727
|
logger.warning("A Kandji base URI was provided but a token was not.")
|
|
707
728
|
config.kandji_token = None
|
|
@@ -711,13 +732,15 @@ class CLI:
|
|
|
711
732
|
|
|
712
733
|
if config.statsd_enabled:
|
|
713
734
|
logger.debug(
|
|
714
|
-
f
|
|
735
|
+
f"statsd enabled. Sending metrics to server {config.statsd_host}:{config.statsd_port}. "
|
|
715
736
|
f'Metrics have prefix "{config.statsd_prefix}".',
|
|
716
737
|
)
|
|
717
738
|
|
|
718
739
|
# Pagerduty config
|
|
719
740
|
if config.pagerduty_api_key_env_var:
|
|
720
|
-
logger.debug(
|
|
741
|
+
logger.debug(
|
|
742
|
+
f"Reading API key for PagerDuty from environment variable {config.pagerduty_api_key_env_var}",
|
|
743
|
+
)
|
|
721
744
|
config.pagerduty_api_key = os.environ.get(config.pagerduty_api_key_env_var)
|
|
722
745
|
else:
|
|
723
746
|
config.pagerduty_api_key = None
|
|
@@ -727,7 +750,9 @@ class CLI:
|
|
|
727
750
|
logger.debug(
|
|
728
751
|
f"Reading API key for Crowdstrike from environment variable {config.crowdstrike_client_id_env_var}",
|
|
729
752
|
)
|
|
730
|
-
config.crowdstrike_client_id = os.environ.get(
|
|
753
|
+
config.crowdstrike_client_id = os.environ.get(
|
|
754
|
+
config.crowdstrike_client_id_env_var,
|
|
755
|
+
)
|
|
731
756
|
else:
|
|
732
757
|
config.crowdstrike_client_id = None
|
|
733
758
|
|
|
@@ -735,36 +760,54 @@ class CLI:
|
|
|
735
760
|
logger.debug(
|
|
736
761
|
f"Reading API key for Crowdstrike from environment variable {config.crowdstrike_client_secret_env_var}",
|
|
737
762
|
)
|
|
738
|
-
config.crowdstrike_client_secret = os.environ.get(
|
|
763
|
+
config.crowdstrike_client_secret = os.environ.get(
|
|
764
|
+
config.crowdstrike_client_secret_env_var,
|
|
765
|
+
)
|
|
739
766
|
else:
|
|
740
767
|
config.crowdstrike_client_secret = None
|
|
741
768
|
|
|
742
769
|
# GSuite config
|
|
743
770
|
if config.gsuite_tokens_env_var:
|
|
744
|
-
logger.debug(
|
|
771
|
+
logger.debug(
|
|
772
|
+
f"Reading config string for GSuite from environment variable {config.gsuite_tokens_env_var}",
|
|
773
|
+
)
|
|
745
774
|
config.gsuite_config = os.environ.get(config.gsuite_tokens_env_var)
|
|
746
775
|
else:
|
|
747
776
|
config.gsuite_tokens_env_var = None
|
|
748
777
|
|
|
749
778
|
# Lastpass config
|
|
750
779
|
if config.lastpass_cid_env_var:
|
|
751
|
-
logger.debug(
|
|
780
|
+
logger.debug(
|
|
781
|
+
f"Reading CID for Lastpass from environment variable {config.lastpass_cid_env_var}",
|
|
782
|
+
)
|
|
752
783
|
config.lastpass_cid = os.environ.get(config.lastpass_cid_env_var)
|
|
753
784
|
else:
|
|
754
785
|
config.lastpass_cid = None
|
|
755
786
|
if config.lastpass_provhash_env_var:
|
|
756
|
-
logger.debug(
|
|
787
|
+
logger.debug(
|
|
788
|
+
f"Reading provhash for Lastpass from environment variable {config.lastpass_provhash_env_var}",
|
|
789
|
+
)
|
|
757
790
|
config.lastpass_provhash = os.environ.get(config.lastpass_provhash_env_var)
|
|
758
791
|
else:
|
|
759
792
|
config.lastpass_provhash = None
|
|
760
793
|
|
|
761
794
|
# BigFix config
|
|
762
|
-
if
|
|
763
|
-
|
|
795
|
+
if (
|
|
796
|
+
config.bigfix_username
|
|
797
|
+
and config.bigfix_password_env_var
|
|
798
|
+
and config.bigfix_root_url
|
|
799
|
+
):
|
|
800
|
+
logger.debug(
|
|
801
|
+
f"Reading BigFix password from environment variable {config.bigfix_password_env_var}",
|
|
802
|
+
)
|
|
764
803
|
config.bigfix_password = os.environ.get(config.bigfix_password_env_var)
|
|
765
804
|
|
|
766
805
|
# Duo config
|
|
767
|
-
if
|
|
806
|
+
if (
|
|
807
|
+
config.duo_api_key_env_var
|
|
808
|
+
and config.duo_api_secret_env_var
|
|
809
|
+
and config.duo_api_hostname
|
|
810
|
+
):
|
|
768
811
|
logger.debug(
|
|
769
812
|
f"Reading Duo api key and secret from environment variables {config.duo_api_key_env_var}"
|
|
770
813
|
f", {config.duo_api_secret_env_var}",
|
|
@@ -777,7 +820,9 @@ class CLI:
|
|
|
777
820
|
|
|
778
821
|
# Semgrep config
|
|
779
822
|
if config.semgrep_app_token_env_var:
|
|
780
|
-
logger.debug(
|
|
823
|
+
logger.debug(
|
|
824
|
+
f"Reading Semgrep App Token from environment variable {config.semgrep_app_token_env_var}",
|
|
825
|
+
)
|
|
781
826
|
config.semgrep_app_token = os.environ.get(config.semgrep_app_token_env_var)
|
|
782
827
|
else:
|
|
783
828
|
config.semgrep_app_token = None
|
|
@@ -787,7 +832,9 @@ class CLI:
|
|
|
787
832
|
|
|
788
833
|
# CVE feed config
|
|
789
834
|
if config.cve_api_key_env_var:
|
|
790
|
-
logger.debug(
|
|
835
|
+
logger.debug(
|
|
836
|
+
f"Reading NVD CVE API key environment variable {config.cve_api_key_env_var}",
|
|
837
|
+
)
|
|
791
838
|
config.cve_api_key = os.environ.get(config.cve_api_key_env_var)
|
|
792
839
|
else:
|
|
793
840
|
config.cve_api_key = None
|
|
@@ -800,11 +847,11 @@ class CLI:
|
|
|
800
847
|
config.snipeit_token_env_var,
|
|
801
848
|
)
|
|
802
849
|
config.snipeit_token = os.environ.get(config.snipeit_token_env_var)
|
|
803
|
-
elif os.environ.get(
|
|
850
|
+
elif os.environ.get("SNIPEIT_TOKEN"):
|
|
804
851
|
logger.debug(
|
|
805
852
|
"Reading SnipeIT API token from environment variable 'SNIPEIT_TOKEN'.",
|
|
806
853
|
)
|
|
807
|
-
config.snipeit_token = os.environ.get(
|
|
854
|
+
config.snipeit_token = os.environ.get("SNIPEIT_TOKEN")
|
|
808
855
|
else:
|
|
809
856
|
logger.warning("A SnipeIT base URI was provided but a token was not.")
|
|
810
857
|
config.kandji_token = None
|
|
@@ -829,12 +876,14 @@ def main(argv=None):
|
|
|
829
876
|
:return: The return code.
|
|
830
877
|
"""
|
|
831
878
|
logging.basicConfig(level=logging.INFO)
|
|
832
|
-
logging.getLogger(
|
|
833
|
-
logging.getLogger(
|
|
834
|
-
logging.getLogger(
|
|
835
|
-
logging.getLogger(
|
|
836
|
-
logging.getLogger(
|
|
837
|
-
logging.getLogger(
|
|
879
|
+
logging.getLogger("botocore").setLevel(logging.WARNING)
|
|
880
|
+
logging.getLogger("googleapiclient").setLevel(logging.WARNING)
|
|
881
|
+
logging.getLogger("neo4j").setLevel(logging.WARNING)
|
|
882
|
+
logging.getLogger("azure.identity").setLevel(logging.WARNING)
|
|
883
|
+
logging.getLogger("httpx").setLevel(logging.WARNING)
|
|
884
|
+
logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(
|
|
885
|
+
logging.WARNING
|
|
886
|
+
)
|
|
838
887
|
|
|
839
888
|
argv = argv if argv is not None else sys.argv[1:]
|
|
840
|
-
sys.exit(CLI(prog=
|
|
889
|
+
sys.exit(CLI(prog="cartography").main(argv))
|