Skip to content

Commit 4a21abc

Browse files
authored
Merge pull request #1 from bridgecrewio/master
a
2 parents d60360b + 1e88a5d commit 4a21abc

File tree

74 files changed

+5003
-3427
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+5003
-3427
lines changed

CHANGELOG.md

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
11
# CHANGELOG
22

3-
## [Unreleased](https://github.com/bridgecrewio/checkov/compare/2.2.43...HEAD)
3+
## [Unreleased](https://github.com/bridgecrewio/checkov/compare/2.2.50...HEAD)
4+
5+
## [2.2.50](https://github.com/bridgecrewio/checkov/compare/2.2.44...2.2.50) - 2022-11-13
6+
7+
### Feature
8+
9+
- **general:** add reporting contributor metrics - [#3823](https://github.com/bridgecrewio/checkov/pull/3823)
10+
- **terraform:** add CKV NCP rules about access key hard coding - [#3820](https://github.com/bridgecrewio/checkov/pull/3820)
11+
- **terraform:** NSGRulePortAccessRestricted - Remove the condition for dynamic blocks - [#3862](https://github.com/bridgecrewio/checkov/pull/3862)
12+
13+
### Bug Fix
14+
15+
- **kubernetes:** handle empty spec object in k8s templates - [#3865](https://github.com/bridgecrewio/checkov/pull/3865)
16+
- **openapi:** fixed error in invalid openapi template - [#3863](https://github.com/bridgecrewio/checkov/pull/3863)
17+
- **terraform:** app_service Upgrade tests and add web app resources - [#3838](https://github.com/bridgecrewio/checkov/pull/3838)
18+
- **terraform:** Handled nested unrendered vars - [#3853](https://github.com/bridgecrewio/checkov/pull/3853)
19+
20+
## [2.2.44](https://github.com/bridgecrewio/checkov/compare/2.2.43...2.2.44) - 2022-11-11
21+
22+
### Bug Fix
23+
24+
- **terraform:** fix an issue with dynamics replacing a whole block - [#3846](https://github.com/bridgecrewio/checkov/pull/3846)
425

526
## [2.2.43](https://github.com/bridgecrewio/checkov/compare/2.2.38...2.2.43) - 2022-11-10
627

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
checkov==2.2.44
1+
checkov==2.2.50

admissioncontroller/k8s/deployment.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ spec:
3030
capabilities:
3131
drop:
3232
- ALL
33-
image: bridgecrew/whorf@sha256:22f96f7076607f9ee15ac57a309c67d410d39b5a82cd4db68236943b75de3a38
33+
image: bridgecrew/whorf@sha256:1fef8ebb4749aa43f4abe5dd0f95513aeb1da6ce88b4e0201adc36e4007803fb
3434
imagePullPolicy: Always
3535
resources:
3636
limits:

checkov/common/bridgecrew/bc_source.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@
22

33

44
class SourceType:
5-
__slots__ = ("name", "upload_results")
5+
__slots__ = ("name", "upload_results", "report_contributor_metrics")
66

7-
def __init__(self, name: str, upload_results: bool):
7+
def __init__(self, name: str, upload_results: bool, report_contributor_metrics: bool = False):
88
self.name = name
99
self.upload_results = upload_results
10+
self.report_contributor_metrics = report_contributor_metrics
1011

1112

1213
@dataclass
@@ -28,11 +29,11 @@ class BCSourceType:
2829
BCSourceType.JETBRAINS: SourceType(BCSourceType.JETBRAINS, False),
2930
BCSourceType.CLI: SourceType(BCSourceType.CLI, True),
3031
BCSourceType.KUBERNETES_WORKLOADS: SourceType(BCSourceType.KUBERNETES_WORKLOADS, True),
31-
BCSourceType.GITHUB_ACTIONS: SourceType(BCSourceType.GITHUB_ACTIONS, True),
3232
BCSourceType.DISABLED: SourceType(BCSourceType.VSCODE, False),
33-
BCSourceType.CODEBUILD: SourceType(BCSourceType.CODEBUILD, True),
34-
BCSourceType.JENKINS: SourceType(BCSourceType.JENKINS, True),
35-
BCSourceType.CIRCLECI: SourceType(BCSourceType.CIRCLECI, True),
33+
BCSourceType.GITHUB_ACTIONS: SourceType(BCSourceType.GITHUB_ACTIONS, True, report_contributor_metrics=True),
34+
BCSourceType.CODEBUILD: SourceType(BCSourceType.CODEBUILD, True, report_contributor_metrics=True),
35+
BCSourceType.JENKINS: SourceType(BCSourceType.JENKINS, True, report_contributor_metrics=True),
36+
BCSourceType.CIRCLECI: SourceType(BCSourceType.CIRCLECI, True, report_contributor_metrics=True),
3637
BCSourceType.ADMISSION_CONTROLLER: SourceType(BCSourceType.ADMISSION_CONTROLLER, False)
3738
}
3839

checkov/common/checks_infra/solvers/attribute_solvers/base_attribute_solver.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,24 @@ def run(self, graph_connector: DiGraph) -> Tuple[List[Dict[str, Any]], List[Dict
5353
return passed_vertices, failed_vertices, unknown_vertices
5454

5555
def get_operation(self, vertex: Dict[str, Any]) -> Optional[bool]:
56-
attr_val = vertex.get(self.attribute) # type:ignore[arg-type] # due to attribute can be None
5756
# if this value contains an underendered variable, then we cannot evaluate value checks,
5857
# and will return None (for UNKNOWN)
5958
# handle edge cases in some policies that explicitly look for blank values
60-
if self.is_value_attribute_check and self._is_variable_dependant(attr_val, vertex['source_']) \
61-
and self.value != '':
62-
return None
59+
# we also need to check the attribute stack - e.g., if they are looking for tags.component, but tags = local.tags,
60+
# then we actually need to see if tags is variable dependent as well
61+
attr_parts = self.attribute.split('.') # type:ignore[union-attr] # due to attribute can be None (but not really)
62+
attr_to_check = None
63+
for attr in attr_parts:
64+
attr_to_check = f'{attr_to_check}.{attr}' if attr_to_check else attr
65+
value_to_check = vertex.get(attr_to_check)
66+
67+
# we can only check is_attribute_value_check when evaluating the full attribute
68+
# for example, if we have a policy that says "tags.component exists", and tags = local.tags, then
69+
# we need to check if tags is variable dependent even though this is a not value_attribute check
70+
if (attr_to_check != self.attribute or self.is_value_attribute_check) \
71+
and self._is_variable_dependant(value_to_check, vertex['source_']) \
72+
and self.value != '':
73+
return None
6374

6475
if self.attribute and (self.is_jsonpath_check or re.match(WILDCARD_PATTERN, self.attribute)):
6576
attribute_matches = self.get_attribute_matches(vertex)

checkov/contributor_metrics.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from __future__ import annotations
2+
import logging
3+
import json
4+
import subprocess # nosec
5+
from checkov.common.util.http_utils import request_wrapper
6+
from checkov.common.bridgecrew.platform_integration import BcPlatformIntegration
7+
from typing import Any
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
def report_contributor_metrics(repository: str, bc_integration: BcPlatformIntegration) -> None: # ignore: type
13+
request_body = parse_gitlog(repository)
14+
if request_body:
15+
response = request_wrapper(
16+
"POST", f"{bc_integration.api_url}/api/v1/contributors/report",
17+
headers=bc_integration.get_default_headers("POST"), data=json.dumps(request_body)
18+
)
19+
if response.status_code < 300:
20+
logging.info(f"Successfully uploaded contributor metrics with status: {response.status_code}")
21+
else:
22+
logging.info(f"Failed to upload contributor metrics with: {response.status_code} - {response.reason}")
23+
24+
25+
def parse_gitlog(repository: str) -> dict[str, Any] | None:
26+
process = subprocess.Popen(['git', 'shortlog', '-ne', '--all', '--since', '"90 days ago"', '--pretty=commit-%ct', '--reverse'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) # nosec
27+
out, err = process.communicate()
28+
if err:
29+
logger.info(f"Failed to collect contributor metrics due to: {err}") # type: ignore
30+
return None
31+
# split per contributor
32+
list_of_contributors = out.decode('utf-8').split('\n\n')
33+
return {"repository": repository,
34+
"contributors": list(map(lambda contributor: process_contributor(contributor),
35+
list(filter(lambda x: x, list_of_contributors))
36+
))
37+
}
38+
39+
40+
def process_contributor(contributor: str) -> str:
41+
splittedList = contributor.split('\n')
42+
user = splittedList[0]
43+
commit = splittedList[1]
44+
return user[:user.find('(')] + commit[commit.find('-') + 1:]

checkov/kubernetes/checks/resource/base_root_container_check.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@ def extract_spec(self, conf: Dict[str, Any]) -> Dict:
3535
if "spec" in conf:
3636
spec = conf["spec"]
3737
elif conf['kind'] == 'CronJob':
38-
if "spec" in conf and "jobTemplate" in conf["spec"] and "spec" in conf["spec"]["jobTemplate"] and conf["spec"]["jobTemplate"]["spec"] and "template" in conf["spec"]["jobTemplate"]["spec"] and "spec" in conf["spec"]["jobTemplate"]["spec"]["template"]:
38+
if "spec" in conf and \
39+
isinstance(conf["spec"], dict) and \
40+
"jobTemplate" in conf["spec"] and \
41+
"spec" in conf["spec"]["jobTemplate"] and \
42+
conf["spec"]["jobTemplate"]["spec"] and \
43+
"template" in conf["spec"]["jobTemplate"]["spec"] and \
44+
"spec" in conf["spec"]["jobTemplate"]["spec"]["template"]:
3945
spec = conf["spec"]["jobTemplate"]["spec"]["template"]["spec"]
4046
else:
4147
inner_spec = self.get_inner_entry(conf, "spec")

checkov/kubernetes/checks/resource/base_spec_check.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,6 @@ def wrapper(self, conf, entity_type=None):
5050
@staticmethod
5151
def get_inner_entry(conf: Dict[str, Any], entry_name: str) -> Dict[str, Any]:
5252
spec = {}
53-
if conf.get("spec") and conf.get("spec").get("template"):
53+
if conf.get("spec") and isinstance(conf["spec"], dict) and conf.get("spec").get("template"):
5454
spec = conf.get("spec").get("template").get(entry_name, {})
5555
return spec

checkov/kubernetes/checks/resource/k8s/Seccomp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def scan_spec_conf(self, conf):
3737
metadata = conf["metadata"]
3838
elif conf['kind'] == 'CronJob':
3939
if "spec" in conf:
40-
if "jobTemplate" in conf["spec"]:
40+
if isinstance(conf["spec"], dict) and "jobTemplate" in conf["spec"]:
4141
if "spec" in conf["spec"]["jobTemplate"]:
4242
if conf["spec"]["jobTemplate"]["spec"] and "template" in conf["spec"]["jobTemplate"]["spec"]:
4343
if "metadata" in conf["spec"]["jobTemplate"]["spec"]["template"]:

checkov/main.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
from checkov.common.util.ext_argument_parser import ExtArgumentParser
4444
from checkov.common.util.runner_dependency_handler import RunnerDependencyHandler
4545
from checkov.common.util.type_forcers import convert_str_to_bool
46+
from checkov.contributor_metrics import report_contributor_metrics
4647
from checkov.dockerfile.runner import Runner as dockerfile_runner
4748
from checkov.github.runner import Runner as github_configuration_runner
4849
from checkov.github_actions.runner import Runner as github_actions_runner
@@ -249,6 +250,15 @@ def run(banner: str = checkov_banner, argv: List[str] = sys.argv[1:]) -> Optiona
249250
source_version=source_version,
250251
repo_branch=config.branch,
251252
prisma_api_url=config.prisma_api_url)
253+
254+
should_run_contributor_reporting = os.getenv('RUN_CONTRIBUTOR_REPORTING', False)
255+
# TODO : Replace the following condition with: if source.report_contributor_metrics and config.repo_id and config.prisma_api_url:
256+
if should_run_contributor_reporting:
257+
try: # collect contributor info and upload
258+
report_contributor_metrics(config.repo_id, bc_integration)
259+
except Exception as e:
260+
logger.warning(f"Unable to report contributor metrics due to: {e}")
261+
252262
except MaxRetryError:
253263
return None
254264
except Exception:

checkov/openapi/checks/resource/v2/SecurityRequirement.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ def scan_openapi_conf(self, conf: dict[str, Any], entity_type: str) -> tuple[Che
4141
for op_name, op_val in http_method.items():
4242
if self.is_start_end_line(op_name):
4343
continue
44+
if not isinstance(op_val, dict):
45+
return CheckResult.FAILED, conf
4446
if not self.check_security_conf(op_val, security_definitions):
4547
return CheckResult.FAILED, op_val["security"]
4648

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from os.path import dirname, basename, isfile, join
2+
import glob
3+
4+
modules = glob.glob(join(dirname(__file__), "*.py"))
5+
__all__ = [basename(f)[:-3] for f in modules if isfile(f) and not f.endswith("__init__.py")]
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import re
2+
from typing import Dict, List, Any
3+
4+
from checkov.common.models.enums import CheckResult, CheckCategories
5+
from checkov.terraform.checks.provider.base_check import BaseProviderCheck
6+
from checkov.common.models.consts import access_key_pattern, secret_key_pattern
7+
8+
9+
class NCPCredentials(BaseProviderCheck):
10+
def __init__(self) -> None:
11+
name = "Ensure no hard coded NCP access key and secret key exists in provider"
12+
id = "CKV_NCP_17"
13+
supported_provider = ("ncloud",)
14+
categories = (CheckCategories.SECRETS,)
15+
super().__init__(name=name, id=id, categories=categories, supported_provider=supported_provider)
16+
17+
def scan_provider_conf(self, conf: Dict[str, List[Any]]) -> CheckResult:
18+
"""
19+
see: https://registry.terraform.io/providers/NaverCloudPlatform/ncloud/latest/docs
20+
"""
21+
result = CheckResult.PASSED
22+
if self.secret_found(conf, "access_key", access_key_pattern):
23+
result = CheckResult.FAILED
24+
if self.secret_found(conf, "secret_key", secret_key_pattern):
25+
result = CheckResult.FAILED
26+
return result
27+
28+
def secret_found(self, conf: Dict[str, List[Any]], field: str, pattern: str) -> bool:
29+
if field in conf.keys():
30+
value = conf[field][0]
31+
if isinstance(value, str) and re.match(pattern, value) is not None:
32+
conf[f'{self.id}_secret_{field}'] = value
33+
return True
34+
return False
35+
36+
37+
check = NCPCredentials()

checkov/terraform/checks/resource/azure/AppServiceAuthentication.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
from checkov.common.models.enums import CheckCategories
1+
from checkov.common.models.enums import CheckCategories, CheckResult
22
from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceValueCheck
33

44

55
class AppServiceAuthentication(BaseResourceValueCheck):
66
def __init__(self):
77
name = "Ensure App Service Authentication is set on Azure App Service"
88
id = "CKV_AZURE_13"
9-
supported_resources = ['azurerm_app_service']
10-
categories = [CheckCategories.GENERAL_SECURITY]
11-
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
9+
supported_resources = ('azurerm_app_service', 'azurerm_linux_web_app', 'azurerm_windows_web_app')
10+
categories = (CheckCategories.GENERAL_SECURITY,)
11+
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources,
12+
missing_block_result=CheckResult.FAILED)
1213

1314
def get_inspected_key(self):
1415
return 'auth_settings/[0]/enabled/[0]'
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from checkov.common.models.enums import CheckCategories, CheckResult
2+
from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceValueCheck
3+
4+
5+
class AppServiceClientCertificate(BaseResourceValueCheck):
6+
def __init__(self):
7+
name = "Ensure the web app has 'Client Certificates (Incoming client certificates)' set"
8+
id = "CKV_AZURE_17"
9+
supported_resources = ('azurerm_app_service', 'azurerm_linux_web_app', 'azurerm_windows_web_app')
10+
categories = (CheckCategories.NETWORKING,)
11+
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources,
12+
missing_block_result=CheckResult.FAILED)
13+
14+
def get_inspected_key(self):
15+
if self.entity_type == 'azurerm_app_service':
16+
return 'client_cert_enabled/[0]'
17+
else:
18+
return 'client_certificate_enabled/[0]'
19+
20+
21+
check = AppServiceClientCertificate()
Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
from checkov.terraform.checks.resource.base_resource_value_check import BaseResourceValueCheck
2-
from checkov.common.models.enums import CheckCategories
2+
from checkov.common.models.enums import CheckCategories, CheckResult
33

44

55
class AppServiceDetailedErrorMessagesEnabled(BaseResourceValueCheck):
66
def __init__(self):
77
name = "Ensure that App service enables detailed error messages"
88
id = "CKV_AZURE_65"
9-
supported_resources = ['azurerm_app_service']
10-
categories = [CheckCategories.LOGGING]
11-
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
9+
supported_resources = ('azurerm_app_service', 'azurerm_linux_web_app', 'azurerm_windows_web_app')
10+
categories = (CheckCategories.LOGGING,)
11+
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources,
12+
missing_block_result=CheckResult.FAILED)
1213

1314
def get_inspected_key(self):
14-
return "logs/[0]/detailed_error_messages_enabled"
15+
if self.entity_type == 'azurerm_app_service':
16+
return "logs/[0]/detailed_error_messages_enabled"
17+
else:
18+
return "logs/[0]/detailed_error_messages"
1519

1620

1721
check = AppServiceDetailedErrorMessagesEnabled()

checkov/terraform/checks/resource/azure/AppServiceDisallowCORS.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ class AppServiceDisallowCORS(BaseResourceNegativeValueCheck):
66
def __init__(self):
77
name = "Ensure that CORS disallows every resource to access app services"
88
id = "CKV_AZURE_57"
9-
supported_resources = ['azurerm_app_service']
10-
categories = [CheckCategories.GENERAL_SECURITY]
11-
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources, missing_attribute_result=CheckResult.PASSED)
9+
supported_resources = ('azurerm_app_service', 'azurerm_linux_web_app', 'azurerm_windows_web_app')
10+
categories = (CheckCategories.GENERAL_SECURITY,)
11+
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources,
12+
missing_attribute_result=CheckResult.PASSED)
1213

1314
def get_inspected_key(self):
1415
return 'site_config/[0]/cors/[0]/allowed_origins'

checkov/terraform/checks/resource/azure/AppServiceEnableFailedRequest.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ class AppServiceEnableFailedRequest(BaseResourceValueCheck):
66
def __init__(self):
77
name = "Ensure that App service enables failed request tracing"
88
id = "CKV_AZURE_66"
9-
supported_resources = ['azurerm_app_service']
10-
categories = [CheckCategories.LOGGING]
9+
supported_resources = ('azurerm_linux_web_app', 'azurerm_windows_web_app', 'azurerm_app_service')
10+
categories = (CheckCategories.LOGGING,)
1111
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
1212

1313
def get_inspected_key(self):
14-
return 'logs/[0]/failed_request_tracing_enabled'
14+
if self.entity_type == "azurerm_app_service":
15+
return 'logs/[0]/failed_request_tracing_enabled'
16+
else:
17+
return 'logs/[0]/failed_request_tracing'
1518

1619

1720
check = AppServiceEnableFailedRequest()

checkov/terraform/checks/resource/azure/AppServiceFTPSState.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ class AppServiceFTPSState(BaseResourceValueCheck):
66
def __init__(self):
77
name = "Ensure FTP deployments are disabled"
88
id = "CKV_AZURE_78"
9-
supported_resources = ['azurerm_app_service']
10-
categories = [CheckCategories.APPLICATION_SECURITY]
9+
supported_resources = ('azurerm_app_service', 'azurerm_linux_web_app', 'azurerm_windows_web_app')
10+
categories = (CheckCategories.APPLICATION_SECURITY,)
1111
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
1212

1313
def get_inspected_key(self):

checkov/terraform/checks/resource/azure/AppServiceHTTPSOnly.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ class AppServiceHTTPSOnly(BaseResourceValueCheck):
66
def __init__(self):
77
name = "Ensure web app redirects all HTTP traffic to HTTPS in Azure App Service"
88
id = "CKV_AZURE_14"
9-
supported_resources = ['azurerm_app_service']
10-
categories = [CheckCategories.NETWORKING]
9+
supported_resources = ('azurerm_app_service', 'azurerm_linux_web_app', 'azurerm_windows_web_app')
10+
categories = (CheckCategories.NETWORKING,)
1111
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
1212

1313
def get_inspected_key(self):

checkov/terraform/checks/resource/azure/AppServiceHttpLoggingEnabled.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ class AppServiceHttpLoggingEnabled(BaseResourceValueCheck):
77
def __init__(self):
88
name = "Ensure that App service enables HTTP logging"
99
id = "CKV_AZURE_63"
10-
supported_resources = ['azurerm_app_service']
11-
categories = [CheckCategories.LOGGING]
10+
supported_resources = ('azurerm_app_service', 'azurerm_linux_web_app', 'azurerm_windows_web_app')
11+
categories = (CheckCategories.LOGGING,)
1212
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
1313

1414
def get_inspected_key(self):

0 commit comments

Comments
 (0)