diff --git a/content/response_integrations/third_party/partner/wiz/actions/add_comment_to_issue.py b/content/response_integrations/third_party/partner/wiz/actions/add_comment_to_issue.py index b84613346..470c940de 100644 --- a/content/response_integrations/third_party/partner/wiz/actions/add_comment_to_issue.py +++ b/content/response_integrations/third_party/partner/wiz/actions/add_comment_to_issue.py @@ -19,11 +19,7 @@ from TIPCommon.base.action import Action from TIPCommon.extraction import extract_action_param -from ..core import action_init -from ..core import api_client -from ..core import constants -from ..core import exceptions - +from ..core import action_init, api_client, constants, exceptions if TYPE_CHECKING: from typing import NoReturn diff --git a/content/response_integrations/third_party/partner/wiz/actions/get_issue_details.py b/content/response_integrations/third_party/partner/wiz/actions/get_issue_details.py index b40687811..04ccac333 100644 --- a/content/response_integrations/third_party/partner/wiz/actions/get_issue_details.py +++ b/content/response_integrations/third_party/partner/wiz/actions/get_issue_details.py @@ -19,10 +19,7 @@ from TIPCommon.base.action import Action from TIPCommon.extraction import extract_action_param -from ..core import action_init -from ..core import api_client -from ..core import constants -from ..core import exceptions +from ..core import action_init, api_client, constants, exceptions if TYPE_CHECKING: from typing import NoReturn diff --git a/content/response_integrations/third_party/partner/wiz/actions/ignore_issue.py b/content/response_integrations/third_party/partner/wiz/actions/ignore_issue.py index 0d8dedaf3..9ca1ce48f 100644 --- a/content/response_integrations/third_party/partner/wiz/actions/ignore_issue.py +++ b/content/response_integrations/third_party/partner/wiz/actions/ignore_issue.py @@ -20,10 +20,7 @@ from TIPCommon.extraction import extract_action_param from TIPCommon.validation import ParameterValidator -from ..core import action_init -from ..core import api_client -from ..core import constants -from ..core import exceptions +from ..core import action_init, api_client, constants, exceptions if TYPE_CHECKING: from typing import NoReturn diff --git a/content/response_integrations/third_party/partner/wiz/actions/list_resource_vulnerability_findings.py b/content/response_integrations/third_party/partner/wiz/actions/list_resource_vulnerability_findings.py new file mode 100644 index 000000000..eb25c63cd --- /dev/null +++ b/content/response_integrations/third_party/partner/wiz/actions/list_resource_vulnerability_findings.py @@ -0,0 +1,186 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from TIPCommon.base.action import Action +from TIPCommon.extraction import extract_action_param + +from ..core import action_init, api_client, constants + +if TYPE_CHECKING: + from typing import NoReturn + + from TIPCommon.types import SingleJson + + +class ListResourceVulnerabilityFindings(Action): + def __init__(self) -> None: + super().__init__(constants.LIST_RESOURCE_VULNERABILITY_FINDINGS_SCRIPT_NAME) + + def _extract_action_parameters(self) -> None: + self.params.resource_names = extract_action_param( + self.soar_action, + param_name="Resource Name", + is_mandatory=True, + print_value=True, + ) + self.params.severity = extract_action_param( + self.soar_action, + param_name="Severity", + is_mandatory=False, + print_value=True, + ) + self.params.related_issue_severity = extract_action_param( + self.soar_action, + param_name="Related Issue Severity", + is_mandatory=False, + print_value=True, + ) + self.params.has_fix = extract_action_param( + self.soar_action, + param_name="Has Fix", + is_mandatory=False, + print_value=True, + default_value="Select One", + ) + self.params.has_public_exploit = extract_action_param( + self.soar_action, + param_name="Has Public Exploit", + is_mandatory=False, + print_value=True, + default_value="Select One", + ) + self.params.cve_ids = extract_action_param( + self.soar_action, + param_name="CVE IDs", + is_mandatory=False, + print_value=True, + ) + self.params.max_findings = extract_action_param( + self.soar_action, + param_name="Max Findings To Return", + is_mandatory=False, + print_value=True, + default_value=str(constants.DEFAULT_MAX_FINDINGS), + ) + + def _init_api_clients(self) -> api_client.WizApiClient: + return action_init.create_api_client(self.soar_action) + + def _perform_action(self, _: Any) -> None: + resource_list = [r.strip() for r in self.params.resource_names.split(",") if r.strip()] + severity_list = None + if self.params.severity: + severity_list = [ + s.strip().upper() for s in self.params.severity.split(",") if s.strip() + ] + + cve_ids_list = None + if self.params.cve_ids: + cve_ids_list = [ + c.strip() for c in self.params.cve_ids.split(",") if c.strip() + ] + + related_issue_severity_list = None + if self.params.related_issue_severity: + related_issue_severity_list = [ + s.strip().upper() + for s in self.params.related_issue_severity.split(",") + if s.strip() + ] + + has_fix = self._map_ddl_to_bool(self.params.has_fix) + has_exploit = self._map_ddl_to_bool(self.params.has_public_exploit) + + max_findings = constants.DEFAULT_MAX_FINDINGS + + try: + val = int(self.params.max_findings) + if 0 < val <= constants.DEFAULT_MAX_FINDINGS: + max_findings = val + else: + self.logger.warning( + f"Max Findings ({val}) is out of bounds. " + f"Defaulting to {constants.DEFAULT_MAX_FINDINGS}." + ) + except (ValueError, TypeError): + self.logger.warning( + "Invalid or empty Max Findings value. " + f"Defaulting to {constants.DEFAULT_MAX_FINDINGS}." + ) + + all_findings = [] + resources_with_findings = [] + for resource in resource_list: + self.logger.info(f"Fetching findings for resource: {resource}") + findings = self.api_client.get_resource_vulnerability_findings( + resource_name=resource, + severity=severity_list, + has_fix=has_fix, + has_exploit=has_exploit, + cve_ids=cve_ids_list, + related_issue_severity=related_issue_severity_list, + first=max_findings, + ) + if findings: + resources_with_findings.append(resource) + all_findings.append({ + "Entity": resource, + "EntityResult": [f.to_json() for f in findings] + }) + + self.json_results: SingleJson = all_findings + + if not all_findings: + if len(resource_list) == 1: + self.output_message = ( + "No vulnerabilities found that match provided filters " + f"for the following resources in Wiz: {resource_list[0]}" + ) + else: + self.output_message = ( + "No vulnerabilities found that match provided filters " + "for the provided resources." + ) + else: + self.output_message = ( + "Successfully found vulnerabilities that match provided filters " + f"for the following resources in Wiz: {', '.join(resources_with_findings)}" + ) + + def _map_ddl_to_bool(self, value: str) -> bool | None: + """Map DDL string value to boolean or None. + + Args: + value: The string value to map ('Yes', 'No', or others). + + Returns: + True if 'Yes', False if 'No', None otherwise. + """ + if value == "Yes": + return True + if value == "No": + return False + return None + + +def main() -> NoReturn: + ListResourceVulnerabilityFindings().run() + + +if __name__ == "__main__": + main() diff --git a/content/response_integrations/third_party/partner/wiz/actions/list_resource_vulnerability_findings.yaml b/content/response_integrations/third_party/partner/wiz/actions/list_resource_vulnerability_findings.yaml new file mode 100644 index 000000000..ff8f76a53 --- /dev/null +++ b/content/response_integrations/third_party/partner/wiz/actions/list_resource_vulnerability_findings.yaml @@ -0,0 +1,52 @@ +creator: admin +description: Use the List Resource Vulnerability Findings action to retrieve a list of vulnerability findings associated with specific resources in Wiz. Results are returned as a JSON object keyed by resource name. +dynamic_results_metadata: +- result_example_path: resources/list_resource_vulnerability_findings_JsonResult_example.json + result_name: JsonResult + show_result: true +integration_identifier: Wiz +name: List Resource Vulnerability Findings +parameters: +- default_value: '' + description: A comma-separated list of resource names to retrieve findings for (for example, demos.azurecr.io/sb-nginx@6d06ab19, demo-m138-event-handler-00001-9qs). + is_mandatory: true + name: Resource Name + type: string +- default_value: '' + description: The technical severity levels of the vulnerability findings to return. + is_mandatory: false + name: Severity + type: string +- default_value: '' + description: The severity levels of the Wiz Issues associated with the vulnerability findings to return. + is_mandatory: false + name: Related Issue Severity + type: string +- default_value: 'Select One' + description: "The criteria used to filter the vulnerability findings to return based on whether a fix or patch is available. Select One: Returns all findings. Yes: Only returns findings with an available fix. No: Only returns findings without an available fix." + is_mandatory: false + name: Has Fix + optional_values: + - Select One + - 'Yes' + - 'No' + type: ddl +- default_value: 'Select One' + description: "The criteria used to filter the vulnerability findings to return based on whether a known public exploit exists. Select One: Returns all vulnerability findings. Yes: Only returns findings with a known public exploit. No: Only returns findings without a known public exploit." + is_mandatory: false + name: Has Public Exploit + optional_values: + - Select One + - 'Yes' + - 'No' + type: ddl +- default_value: '' + description: A comma-separated list of CVE IDs used to filter the vulnerability findings to return. + is_mandatory: false + name: CVE IDs + type: string +- default_value: '500' + description: "The maximum number of vulnerability findings to return for each resource. Maximum: 500." + is_mandatory: false + name: Max Findings To Return + type: string diff --git a/content/response_integrations/third_party/partner/wiz/actions/ping.py b/content/response_integrations/third_party/partner/wiz/actions/ping.py index 1f534a95a..2bfe234bc 100644 --- a/content/response_integrations/third_party/partner/wiz/actions/ping.py +++ b/content/response_integrations/third_party/partner/wiz/actions/ping.py @@ -18,9 +18,8 @@ from TIPCommon.base.action import Action -from ..core import action_init +from ..core import action_init, api_client from ..core.constants import INTEGRATION_NAME, PING_SCRIPT_NAME -from ..core import api_client if TYPE_CHECKING: from typing import NoReturn diff --git a/content/response_integrations/third_party/partner/wiz/actions/reopen_issue.py b/content/response_integrations/third_party/partner/wiz/actions/reopen_issue.py index 1356477ee..45ebe6066 100644 --- a/content/response_integrations/third_party/partner/wiz/actions/reopen_issue.py +++ b/content/response_integrations/third_party/partner/wiz/actions/reopen_issue.py @@ -19,10 +19,7 @@ from TIPCommon.base.action import Action from TIPCommon.extraction import extract_action_param -from ..core import action_init -from ..core import api_client -from ..core import constants -from ..core import exceptions +from ..core import action_init, api_client, constants, exceptions if TYPE_CHECKING: from typing import NoReturn diff --git a/content/response_integrations/third_party/partner/wiz/actions/resolve_issue.py b/content/response_integrations/third_party/partner/wiz/actions/resolve_issue.py index a319142bc..4ac3aaebd 100644 --- a/content/response_integrations/third_party/partner/wiz/actions/resolve_issue.py +++ b/content/response_integrations/third_party/partner/wiz/actions/resolve_issue.py @@ -20,10 +20,7 @@ from TIPCommon.extraction import extract_action_param from TIPCommon.validation import ParameterValidator -from ..core import action_init -from ..core import api_client -from ..core import constants -from ..core import exceptions +from ..core import action_init, api_client, constants, exceptions if TYPE_CHECKING: from typing import NoReturn diff --git a/content/response_integrations/third_party/partner/wiz/core/action_init.py b/content/response_integrations/third_party/partner/wiz/core/action_init.py index 7331e1462..56237aba1 100644 --- a/content/response_integrations/third_party/partner/wiz/core/action_init.py +++ b/content/response_integrations/third_party/partner/wiz/core/action_init.py @@ -23,7 +23,6 @@ if TYPE_CHECKING: import requests - from TIPCommon.types import ChronicleSOAR diff --git a/content/response_integrations/third_party/partner/wiz/core/api_client.py b/content/response_integrations/third_party/partner/wiz/core/api_client.py index f082c2b75..03cb38111 100644 --- a/content/response_integrations/third_party/partner/wiz/core/api_client.py +++ b/content/response_integrations/third_party/partner/wiz/core/api_client.py @@ -14,17 +14,12 @@ from __future__ import annotations -from typing import NamedTuple, TYPE_CHECKING -import requests +from typing import TYPE_CHECKING, NamedTuple +import requests from TIPCommon.base.interfaces import Apiable -from . import api_utils -from . import auth_manager -from . import constants -from . import datamodels -from . import data_parser -from . import query_builder +from . import api_utils, auth_manager, constants, data_parser, datamodels, query_builder if TYPE_CHECKING: from collections.abc import Mapping @@ -235,3 +230,50 @@ def resolve_issue( api_utils.validate_response(response=response) return self.parser.build_update_issue_object(response.json()) + + def get_resource_vulnerability_findings( + self, + resource_name: str, + severity: list[str] | None = None, + has_fix: bool | None = None, + has_exploit: bool | None = None, + cve_ids: list[str] | None = None, + related_issue_severity: list[str] | None = None, + first: int = 100, + ) -> list[datamodels.VulnerabilityFinding]: + """Get vulnerability findings for a specific resource. + + Args: + resource_name (str): The name of the resource. + severity (list[str] | None): Filter by severity levels. + has_fix (bool | None): Filter by fix availability. + has_exploit (bool | None): Filter by exploit availability. + cve_ids (list[str] | None): Filter by CVE IDs. + related_issue_severity (list[str] | None): Filter by related issue severity. + first (int): Max findings to return. + + Returns: + list[datamodels.VulnerabilityFinding]: A list of VulnerabilityFinding objects. + """ + query_builder_instance = query_builder.VulnerabilityFindingsQueryBuilder( + resource_name=resource_name, + severity=severity, + has_fix=has_fix, + has_exploit=has_exploit, + cve_ids=cve_ids, + related_issue_severity=related_issue_severity, + first=first, + ) + + url: str = api_utils.get_full_url( + api_root=self.api_root, + url_id="graphql", + ) + response: requests.Response = self.session.post( + url=url, + json=query_builder_instance.build_query(), + ) + api_utils.validate_response(response=response) + + nodes = response.json().get("data", {}).get("vulnerabilityFindings", {}).get("nodes", []) + return [self.parser.build_vulnerability_finding_object(node) for node in nodes] diff --git a/content/response_integrations/third_party/partner/wiz/core/api_utils.py b/content/response_integrations/third_party/partner/wiz/core/api_utils.py index 1ef182b5c..10b9de459 100644 --- a/content/response_integrations/third_party/partner/wiz/core/api_utils.py +++ b/content/response_integrations/third_party/partner/wiz/core/api_utils.py @@ -15,8 +15,8 @@ from __future__ import annotations from typing import TYPE_CHECKING - from urllib.parse import urljoin + import requests from .constants import ENDPOINTS, ISSUE_NOT_FOUND_ERRORS, UNAUTHORIZED_STATUS_CODE diff --git a/content/response_integrations/third_party/partner/wiz/core/auth_manager.py b/content/response_integrations/third_party/partner/wiz/core/auth_manager.py index f24711322..2760b67c4 100644 --- a/content/response_integrations/third_party/partner/wiz/core/auth_manager.py +++ b/content/response_integrations/third_party/partner/wiz/core/auth_manager.py @@ -14,14 +14,13 @@ from __future__ import annotations -from typing import TYPE_CHECKING import dataclasses +from typing import TYPE_CHECKING from TIPCommon.base.interfaces import Authable from TIPCommon.base.utils import CreateSession -from . import api_utils -from . import constants +from . import api_utils, constants if TYPE_CHECKING: from collections.abc import Mapping diff --git a/content/response_integrations/third_party/partner/wiz/core/constants.py b/content/response_integrations/third_party/partner/wiz/core/constants.py index e5af445ce..1758d7736 100644 --- a/content/response_integrations/third_party/partner/wiz/core/constants.py +++ b/content/response_integrations/third_party/partner/wiz/core/constants.py @@ -27,6 +27,11 @@ IGNORE_ISSUE_SCRIPT_NAME: str = f"{INTEGRATION_NAME} - Ignore Issue" REOPEN_ISSUE_SCRIPT_NAME: str = f"{INTEGRATION_NAME} - Reopen Issue" RESOLVE_ISSUE_SCRIPT_NAME: str = f"{INTEGRATION_NAME} - Resolve Issue" +LIST_RESOURCE_VULNERABILITY_FINDINGS_SCRIPT_NAME: str = ( + f"{INTEGRATION_NAME} - List Resource Vulnerability Findings" +) + +DEFAULT_MAX_FINDINGS: int = 500 ENDPOINTS: Mapping[str, str] = { diff --git a/content/response_integrations/third_party/partner/wiz/core/data_parser.py b/content/response_integrations/third_party/partner/wiz/core/data_parser.py index 0fb2eaddd..b30ae225f 100644 --- a/content/response_integrations/third_party/partner/wiz/core/data_parser.py +++ b/content/response_integrations/third_party/partner/wiz/core/data_parser.py @@ -55,8 +55,22 @@ def build_issue_comment_object(issue_json: SingleJson) -> datamodels.IssueCommen issue_json (SingleJson): The JSON data containing commented issue details. Returns: - datamodels.Issue: An Issue object containing the details of the commented issue. + datamodels.IssueComment: The commented issue details. """ return datamodels.IssueComment.from_json( issue_json.get("data", {}).get("createIssueNote", {}).get("issueNote", {}) ) + + +def build_vulnerability_finding_object( + finding_json: SingleJson, +) -> datamodels.VulnerabilityFinding: + """Build a VulnerabilityFinding object from the provided JSON data. + + Args: + finding_json (SingleJson): The JSON data containing finding details. + + Returns: + datamodels.VulnerabilityFinding: A VulnerabilityFinding object. + """ + return datamodels.VulnerabilityFinding.from_json(finding_json) diff --git a/content/response_integrations/third_party/partner/wiz/core/datamodels.py b/content/response_integrations/third_party/partner/wiz/core/datamodels.py index 86be2cb58..051aa2101 100644 --- a/content/response_integrations/third_party/partner/wiz/core/datamodels.py +++ b/content/response_integrations/third_party/partner/wiz/core/datamodels.py @@ -14,9 +14,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import dataclasses +from typing import TYPE_CHECKING if TYPE_CHECKING: from TIPCommon.base.interfaces.logger import ScriptLogger @@ -59,3 +58,20 @@ class IssueComment(BaseModel): def from_json(cls, json_data: SingleJson) -> IssueComment: """Create an IssueComment instance from JSON data.""" return cls(raw_data=json_data, comment_id=json_data["id"]) + + +@dataclasses.dataclass(slots=True) +class VulnerabilityFinding(BaseModel): + finding_id: str + name: str + severity: str + + @classmethod + def from_json(cls, json_data: SingleJson) -> VulnerabilityFinding: + """Create a VulnerabilityFinding instance from JSON data.""" + return cls( + raw_data=json_data, + finding_id=json_data["id"], + name=json_data.get("name", ""), + severity=json_data.get("severity", ""), + ) diff --git a/content/response_integrations/third_party/partner/wiz/core/query_builder.py b/content/response_integrations/third_party/partner/wiz/core/query_builder.py index 5675b8abf..51c79aa09 100644 --- a/content/response_integrations/third_party/partner/wiz/core/query_builder.py +++ b/content/response_integrations/third_party/partner/wiz/core/query_builder.py @@ -14,9 +14,8 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import dataclasses +from typing import TYPE_CHECKING from graphql_query import Argument, Field, Operation, Query, Variable @@ -190,3 +189,81 @@ def build_mutation(self) -> SingleJson: ).render(), "variables": variable_data, } + + +@dataclasses.dataclass(slots=True) +class VulnerabilityFindingsQueryBuilder: + resource_name: str + severity: list[str] | None = None + has_fix: bool | None = None + has_exploit: bool | None = None + cve_ids: list[str] | None = None + related_issue_severity: list[str] | None = None + first: int = 100 + + def build_fields(self) -> list[Field]: + """Build the fields required in the vulnerability findings query.""" + return [ + Field( + name="nodes", + fields=[ + "id", + "portalUrl", + "name", + "CVEDescription", + "CVSSSeverity", + "score", + "severity", + "status", + "hasExploit", + "remediation", + Field( + name="vulnerableAsset", + fields=["... on VulnerableAssetBase { id type name }"], + ), + ], + ) + ] + + def build_query(self) -> SingleJson: + """Builds the GraphQL query payload for listing vulnerability findings.""" + filter_var = Variable(name="filterBy", type="VulnerabilityFindingFilters") + first_var = Variable(name="first", type="Int") + + query = Query( + name="vulnerabilityFindings", + arguments=[ + Argument(name="filterBy", value=filter_var), + Argument(name="first", value=first_var), + ], + fields=self.build_fields(), + ) + + operation = Operation( + type="query", + name="VulnerabilityFindingsPage", + variables=[filter_var, first_var], + queries=[query], + ) + + filter_by = { + "assetName": {"equals": self.resource_name} + } + if self.severity: + filter_by["severity"] = self.severity + if self.has_fix is not None: + filter_by["hasFix"] = self.has_fix + if self.has_exploit is not None: + filter_by["hasExploit"] = self.has_exploit + if self.cve_ids: + filter_by["vulnerabilityExternalIdV2"] = self.cve_ids + if self.related_issue_severity: + filter_by["relatedIssueSeverity"] = self.related_issue_severity + + return { + "query": operation.render(), + "variables": { + "first": self.first, + "filterBy": filter_by, + }, + } diff --git a/content/response_integrations/third_party/partner/wiz/pyproject.toml b/content/response_integrations/third_party/partner/wiz/pyproject.toml index 7e6ae4fd6..7f846a3f0 100644 --- a/content/response_integrations/third_party/partner/wiz/pyproject.toml +++ b/content/response_integrations/third_party/partner/wiz/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "Wiz" -version = "4.0" +version = "5.0" description = "Wiz connects to every cloud environment, scans every layer, and covers every aspect of your cloud security - including elements that normally require installing agents. Its comprehensive approach has all of these cloud security solutions built in. In case of any queries, please reach out to win.support@wiz.io" requires-python = ">=3.11" dependencies = [ diff --git a/content/response_integrations/third_party/partner/wiz/release_notes.yaml b/content/response_integrations/third_party/partner/wiz/release_notes.yaml index 35c7287b8..ba620bf09 100644 --- a/content/response_integrations/third_party/partner/wiz/release_notes.yaml +++ b/content/response_integrations/third_party/partner/wiz/release_notes.yaml @@ -23,3 +23,9 @@ item_type: Integration publish_time: '2026-03-18' ticket_number: '' +- description: New Action Added - List Resource Vulnerability Findings. + integration_version: 5.0 + item_name: Wiz + item_type: Integration + publish_time: '2026-04-14' + ticket_number: '494154345' diff --git a/content/response_integrations/third_party/partner/wiz/resources/list_resource_vulnerability_findings_JsonResult_example.json b/content/response_integrations/third_party/partner/wiz/resources/list_resource_vulnerability_findings_JsonResult_example.json new file mode 100644 index 000000000..29388a51c --- /dev/null +++ b/content/response_integrations/third_party/partner/wiz/resources/list_resource_vulnerability_findings_JsonResult_example.json @@ -0,0 +1,92 @@ +[ + { + "Entity": "test-vm-instance-01", + "EntityResult": [ + { + "id": "11111111-1111-1111-1111-111111111111", + "portalUrl": "https://app.wiz.io/explorer/vulnerability-findings#~(entity~(~'11111111-1111-1111-1111-111111111111*2cSECURITY_TOOL_FINDING))", + "name": "CVE-2022-25972", + "CVEDescription": "An out-of-bounds write vulnerability exists in the gif2h5 functionality of HDF5 Group libhdf5 1.10.4. A specially-crafted GIF file can lead to code execution. An attacker can provide a malicious file to trigger this vulnerability.", + "CVSSSeverity": "HIGH", + "score": 7.8, + "severity": "LOW", + "status": "OPEN", + "hasExploit": true, + "remediation": null, + "vulnerableAsset": { + "id": "00000000-0000-0000-0000-000000000001", + "type": "VIRTUAL_MACHINE", + "name": "test-vm-instance-01" + } + }, + { + "id": "22222222-2222-2222-2222-222222222222", + "portalUrl": "https://app.wiz.io/explorer/vulnerability-findings#~(entity~(~'22222222-2222-2222-2222-222222222222*2cSECURITY_TOOL_FINDING))", + "name": "CVE-2015-3243", + "CVEDescription": "rsyslog uses weak permissions for generating log files, which allows local users to obtain sensitive information by reading files in /var/log/cron.", + "CVSSSeverity": "MEDIUM", + "score": 5.5, + "severity": "LOW", + "status": "OPEN", + "hasExploit": false, + "remediation": null, + "vulnerableAsset": { + "id": "00000000-0000-0000-0000-000000000001", + "type": "VIRTUAL_MACHINE", + "name": "test-vm-instance-01" + } + }, + { + "id": "33333333-3333-3333-3333-333333333333", + "portalUrl": "https://app.wiz.io/explorer/vulnerability-findings#~(entity~(~'33333333-3333-3333-3333-333333333333*2cSECURITY_TOOL_FINDING))", + "name": "CVE-2021-46242", + "CVEDescription": "HDF5 v1.13.1-1 was discovered to contain a heap-use-after free via the component H5AC_unpin_entry.", + "CVSSSeverity": "HIGH", + "score": 8.8, + "severity": "LOW", + "status": "OPEN", + "hasExploit": true, + "remediation": null, + "vulnerableAsset": { + "id": "00000000-0000-0000-0000-000000000001", + "type": "VIRTUAL_MACHINE", + "name": "test-vm-instance-01" + } + }, + { + "id": "44444444-4444-4444-4444-444444444444", + "portalUrl": "https://app.wiz.io/explorer/vulnerability-findings#~(entity~(~'44444444-4444-4444-4444-444444444444*2cSECURITY_TOOL_FINDING))", + "name": "CVE-2018-14034", + "CVEDescription": "An issue was discovered in the HDF HDF5 1.8.20 library. There is an out of bounds read in the function H5O_pline_reset in H5Opline.c.", + "CVSSSeverity": "HIGH", + "score": 8.8, + "severity": "LOW", + "status": "OPEN", + "hasExploit": false, + "remediation": null, + "vulnerableAsset": { + "id": "00000000-0000-0000-0000-000000000001", + "type": "VIRTUAL_MACHINE", + "name": "test-vm-instance-01" + } + }, + { + "id": "55555555-5555-5555-5555-555555555555", + "portalUrl": "https://app.wiz.io/explorer/vulnerability-findings#~(entity~(~'55555555-5555-5555-5555-555555555555*2cSECURITY_TOOL_FINDING))", + "name": "CVE-2018-20673", + "CVEDescription": "The demangle_template function in cplus-dem.c in GNU libiberty, as distributed in GNU Binutils 2.31.1, contains an integer overflow vulnerability (for \"Create an array for saving the template argument values\") that can trigger a heap-based buffer overflow, as demonstrated by nm.", + "CVSSSeverity": "MEDIUM", + "score": 5.5, + "severity": "LOW", + "status": "OPEN", + "hasExploit": true, + "remediation": null, + "vulnerableAsset": { + "id": "00000000-0000-0000-0000-000000000001", + "type": "VIRTUAL_MACHINE", + "name": "test-vm-instance-01" + } + } + ] + } +] diff --git a/content/response_integrations/third_party/partner/wiz/tests/common.py b/content/response_integrations/third_party/partner/wiz/tests/common.py index f6591431f..cb29dc9b9 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/common.py +++ b/content/response_integrations/third_party/partner/wiz/tests/common.py @@ -14,10 +14,9 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import dataclasses import pathlib +from typing import TYPE_CHECKING from integration_testing.common import get_def_file_content diff --git a/content/response_integrations/third_party/partner/wiz/tests/conftest.py b/content/response_integrations/third_party/partner/wiz/tests/conftest.py index d1ccffe17..0725acbe4 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/conftest.py +++ b/content/response_integrations/third_party/partner/wiz/tests/conftest.py @@ -15,19 +15,18 @@ from __future__ import annotations import pytest - -from TIPCommon.base.utils import CreateSession from integration_testing.common import use_live_api +from integration_testing.logger import Logger from integration_testing.request import MockRequest from integration_testing.requests.response import MockResponse -from integration_testing.logger import Logger +from TIPCommon.base.utils import CreateSession + +from wiz.core.api_client import ApiParameters, WizApiClient -from wiz.core.api_client import WizApiClient, ApiParameters from .common import CONFIG from .core.product import Wiz from .core.session import WizSession - pytest_plugins = ("integration_testing.conftest",) diff --git a/content/response_integrations/third_party/partner/wiz/tests/core/product.py b/content/response_integrations/third_party/partner/wiz/tests/core/product.py index ad76d3391..0c2c31a48 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/core/product.py +++ b/content/response_integrations/third_party/partner/wiz/tests/core/product.py @@ -30,6 +30,7 @@ class Wiz: def __init__(self) -> None: self._issues: MutableMapping[str, Issue] = {} self._comments: MutableMapping[str, common.Comment] = {} + self._vulnerabilities: MutableMapping[str, list[SingleJson]] = {} def add_issue(self, issue: Issue) -> None: self._issues[issue.issue_id] = issue @@ -48,3 +49,14 @@ def add_comment(self, comment: common.Comment) -> None: def cleanup_issues(self) -> None: self._issues = {} + + def add_vulnerability(self, resource_name: str, vuln: SingleJson) -> None: + if resource_name not in self._vulnerabilities: + self._vulnerabilities[resource_name] = [] + self._vulnerabilities[resource_name].append(vuln) + + def get_vulnerabilities(self, resource_name: str) -> list[SingleJson]: + return self._vulnerabilities.get(resource_name, []) + + def cleanup_vulnerabilities(self) -> None: + self._vulnerabilities = {} diff --git a/content/response_integrations/third_party/partner/wiz/tests/core/session.py b/content/response_integrations/third_party/partner/wiz/tests/core/session.py index ee6ceb89d..e76093d6d 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/core/session.py +++ b/content/response_integrations/third_party/partner/wiz/tests/core/session.py @@ -14,21 +14,22 @@ from __future__ import annotations -from typing import TYPE_CHECKING - from collections.abc import Iterable +from typing import TYPE_CHECKING from integration_testing import router from integration_testing.request import MockRequest from integration_testing.requests.response import MockResponse from integration_testing.requests.session import MockSession, RouteFunction +from tests.core.product import Wiz from wiz.core import constants + from .. import common -from tests.core.product import Wiz if TYPE_CHECKING: from TIPCommon.types import SingleJson + from wiz.core import datamodels @@ -55,11 +56,28 @@ def graphql_query(self, request: MockRequest) -> MockResponse: return self._resolve_issue() + if "query VulnerabilityFindingsPage" in query: + return self._get_vulnerability_findings(request) + return MockResponse( content={"errors": [{"message": "Invalid query"}]}, status_code=400, ) + def _get_vulnerability_findings(self, request: MockRequest) -> MockResponse: + """Handle get vulnerability findings request's response.""" + variables = request.kwargs["json"].get("variables", {}) + filter_by = variables.get("filterBy", {}) + asset_name_filter = filter_by.get("assetName", {}) + resource_name = asset_name_filter.get("equals") + + vulns = self._product.get_vulnerabilities(resource_name) + + return MockResponse( + content={"data": {"vulnerabilityFindings": {"nodes": vulns}}}, + status_code=200, + ) + def _get_issue_details(self) -> MockResponse: """Handle get issue details request's response.""" issue: datamodels.Issue = self._product.get_issues()[0] diff --git a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_add_comment_to_issue.py b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_add_comment_to_issue.py index bd517cf03..5ef383592 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_add_comment_to_issue.py +++ b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_add_comment_to_issue.py @@ -14,20 +14,19 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import copy +from typing import TYPE_CHECKING -from TIPCommon.base.action import ExecutionState -from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from integration_testing.platform.script_output import MockActionOutput from integration_testing.set_meta import set_metadata - -from wiz.actions import add_comment_to_issue -from wiz.core import constants, data_parser from tests import common from tests.core.product import Wiz from tests.core.session import WizSession +from TIPCommon.base.action import ExecutionState +from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput + +from wiz.actions import add_comment_to_issue +from wiz.core import constants, data_parser if TYPE_CHECKING: from collections.abc import Mapping diff --git a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_get_issue_details.py b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_get_issue_details.py index 41d3dc028..0ae7eb513 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_get_issue_details.py +++ b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_get_issue_details.py @@ -14,20 +14,20 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import copy +from typing import TYPE_CHECKING -from TIPCommon.base.action import ExecutionState -from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from integration_testing.platform.script_output import MockActionOutput from integration_testing.set_meta import set_metadata +from tests.core.product import Wiz +from tests.core.session import WizSession +from TIPCommon.base.action import ExecutionState +from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from wiz.actions import get_issue_details from wiz.core import constants + from .. import common -from tests.core.product import Wiz -from tests.core.session import WizSession if TYPE_CHECKING: from collections.abc import Mapping @@ -60,6 +60,7 @@ def test_get_issue_details_action_success( ) -> None: wiz.cleanup_issues() issue: datamodels.Issue = copy.deepcopy(ISSUE) + wiz.add_issue(issue) get_issue_details.main() assert len(script_session.request_history) == 2 diff --git a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_ignore_issue.py b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_ignore_issue.py index 66931718d..61ba8c4ab 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_ignore_issue.py +++ b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_ignore_issue.py @@ -14,20 +14,20 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import copy +from typing import TYPE_CHECKING -from TIPCommon.base.action import ExecutionState -from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from integration_testing.platform.script_output import MockActionOutput from integration_testing.set_meta import set_metadata +from tests.core.product import Wiz +from tests.core.session import WizSession +from TIPCommon.base.action import ExecutionState +from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from wiz.actions import ignore_issue from wiz.core import constants, data_parser + from .. import common -from tests.core.product import Wiz -from tests.core.session import WizSession if TYPE_CHECKING: from collections.abc import Mapping diff --git a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_list_resource_vulnerability_findings.py b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_list_resource_vulnerability_findings.py new file mode 100644 index 000000000..929f350a9 --- /dev/null +++ b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_list_resource_vulnerability_findings.py @@ -0,0 +1,139 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from typing import TYPE_CHECKING + +from integration_testing.platform.script_output import MockActionOutput +from integration_testing.set_meta import set_metadata +from tests.core.product import Wiz +from tests.core.session import WizSession +from TIPCommon.base.action import ExecutionState +from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput + +from wiz.actions import list_resource_vulnerability_findings + +from .. import common + +if TYPE_CHECKING: + from collections.abc import Mapping + + +RESOURCE_NAME = "demo-vm-1" +VULN_DATA = { + "id": "11111111-1111-1111-1111-111111111111", + "portalUrl": "https://app.wiz.io/explorer/vulnerability-findings#~(entity~(~'11111111-1111-1111-1111-111111111111*2cSECURITY_TOOL_FINDING))", + "name": "CVE-2022-25972", + "CVEDescription": ( + "An out-of-bounds write vulnerability exists in the gif2h5 functionality of " + "HDF5 Group libhdf5 1.10.4. A specially-crafted GIF file can lead to code execution. " + "An attacker can provide a malicious file to trigger this vulnerability." + ), + "CVSSSeverity": "HIGH", + "score": 7.8, + "severity": "LOW", + "status": "OPEN", + "hasExploit": True, + "remediation": None, + "vulnerableAsset": { + "id": "00000000-0000-0000-0000-000000000001", + "type": "VIRTUAL_MACHINE", + "name": RESOURCE_NAME + } +} + +SUCCESS_OUTPUT_MESSAGE = ( + "Successfully found vulnerabilities that match provided filters " + f"for the following resources in Wiz: {RESOURCE_NAME}" +) + +DEFAULT_PARAMETERS: Mapping[str, str] = { + "Resource Name": RESOURCE_NAME, + "Severity": "High", + "Max Findings To Return": "100" +} + + +@set_metadata(integration_config=common.CONFIG, parameters=DEFAULT_PARAMETERS) +def test_list_resource_vulnerability_findings_success( + wiz: Wiz, + script_session: WizSession, + action_output: MockActionOutput, +) -> None: + wiz.cleanup_vulnerabilities() + wiz.add_vulnerability(RESOURCE_NAME, VULN_DATA) + + list_resource_vulnerability_findings.main() + + assert len(script_session.request_history) == 2 # Auth + GraphQL + graphql_record = script_session.request_history[1] + query = graphql_record.request.kwargs["json"]["query"] + assert "nodes" in query + + assert action_output.results == ActionOutput( + output_message=SUCCESS_OUTPUT_MESSAGE, + result_value=True, + execution_state=ExecutionState.COMPLETED, + json_output=ActionJsonOutput( + json_result=[{"Entity": RESOURCE_NAME, "EntityResult": [VULN_DATA]}] + ), + ) + + +@set_metadata(integration_config=common.CONFIG, parameters=DEFAULT_PARAMETERS) +def test_list_resource_vulnerability_findings_no_findings( + wiz: Wiz, + script_session: WizSession, + action_output: MockActionOutput, +) -> None: + wiz.cleanup_vulnerabilities() + + list_resource_vulnerability_findings.main() + + assert len(script_session.request_history) == 2 # Auth + GraphQL + assert action_output.results == ActionOutput( + output_message=( + "No vulnerabilities found that match provided filters " + f"for the following resources in Wiz: {RESOURCE_NAME}" + ), + result_value=True, + execution_state=ExecutionState.COMPLETED, + json_output=None, + ) + + +@set_metadata( + integration_config=common.CONFIG, + parameters={**DEFAULT_PARAMETERS, "Resource Name": "resource1,resource2"}, +) +def test_list_resource_vulnerability_findings_multiple_resources( + wiz: Wiz, + script_session: WizSession, + action_output: MockActionOutput, +) -> None: + wiz.cleanup_vulnerabilities() + + list_resource_vulnerability_findings.main() + + assert len(script_session.request_history) == 3 # Auth + 2 GraphQL requests + assert action_output.results == ActionOutput( + output_message=( + "No vulnerabilities found that match provided filters " + "for the provided resources." + ), + result_value=True, + execution_state=ExecutionState.COMPLETED, + json_output=None, + ) diff --git a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_ping.py b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_ping.py index 19a65c799..bb49347e5 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_ping.py +++ b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_ping.py @@ -14,20 +14,20 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import copy +from typing import TYPE_CHECKING -from TIPCommon.base.action import ExecutionState -from TIPCommon.base.data_models import ActionOutput from integration_testing.platform.script_output import MockActionOutput from integration_testing.set_meta import set_metadata +from tests.core.product import Wiz +from tests.core.session import WizSession +from TIPCommon.base.action import ExecutionState +from TIPCommon.base.data_models import ActionOutput from wiz.actions import ping from wiz.core import constants + from .. import common -from tests.core.product import Wiz -from tests.core.session import WizSession if TYPE_CHECKING: from wiz.core import datamodels diff --git a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_reopen_issue.py b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_reopen_issue.py index 8a393d20c..76d554bbb 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_reopen_issue.py +++ b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_reopen_issue.py @@ -14,20 +14,20 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import copy +from typing import TYPE_CHECKING -from TIPCommon.base.action import ExecutionState -from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from integration_testing.platform.script_output import MockActionOutput from integration_testing.set_meta import set_metadata +from tests.core.product import Wiz +from tests.core.session import WizSession +from TIPCommon.base.action import ExecutionState +from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from wiz.actions import reopen_issue from wiz.core import constants, data_parser + from .. import common -from tests.core.product import Wiz -from tests.core.session import WizSession if TYPE_CHECKING: from collections.abc import Mapping diff --git a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_resolve_issue.py b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_resolve_issue.py index 0ac6b286e..641c7a542 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_resolve_issue.py +++ b/content/response_integrations/third_party/partner/wiz/tests/test_actions/test_resolve_issue.py @@ -14,20 +14,20 @@ from __future__ import annotations -from typing import TYPE_CHECKING - import copy +from typing import TYPE_CHECKING -from TIPCommon.base.action import ExecutionState -from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from integration_testing.platform.script_output import MockActionOutput from integration_testing.set_meta import set_metadata +from tests.core.product import Wiz +from tests.core.session import WizSession +from TIPCommon.base.action import ExecutionState +from TIPCommon.base.data_models import ActionJsonOutput, ActionOutput from wiz.actions import resolve_issue from wiz.core import constants, data_parser + from .. import common -from tests.core.product import Wiz -from tests.core.session import WizSession if TYPE_CHECKING: from collections.abc import Mapping diff --git a/content/response_integrations/third_party/partner/wiz/tests/test_manager.py b/content/response_integrations/third_party/partner/wiz/tests/test_manager.py index b7eb41f1d..7a230ce4b 100644 --- a/content/response_integrations/third_party/partner/wiz/tests/test_manager.py +++ b/content/response_integrations/third_party/partner/wiz/tests/test_manager.py @@ -14,20 +14,23 @@ from __future__ import annotations -from typing import TYPE_CHECKING import copy +from typing import TYPE_CHECKING import pytest + from wiz.core import constants from wiz.core.exceptions import InvalidCredsError, IssueNotFoundError + from . import common if TYPE_CHECKING: - from wiz.core.api_client import WizApiClient - from wiz.core.datamodels import Issue from tests.core.product import Wiz from tests.core.session import WizSession + from wiz.core.api_client import WizApiClient + from wiz.core.datamodels import Issue + ISSUE: Issue = common.ISSUE @@ -43,6 +46,8 @@ def test_ping_success( ) -> None: """Test ping success. + + Verify that the test_connectivity method successfully pings the Wiz API and returns a 200 status code. diff --git a/content/response_integrations/third_party/partner/wiz/uv.lock b/content/response_integrations/third_party/partner/wiz/uv.lock index cde475dc9..33b2400ac 100644 --- a/content/response_integrations/third_party/partner/wiz/uv.lock +++ b/content/response_integrations/third_party/partner/wiz/uv.lock @@ -1251,7 +1251,7 @@ wheels = [ [[package]] name = "wiz" -version = "4.0" +version = "5.0" source = { virtual = "." } dependencies = [ { name = "environmentcommon" }, diff --git a/content/response_integrations/third_party/partner/wiz/widgets/list_resource_vulnerability_findings.html b/content/response_integrations/third_party/partner/wiz/widgets/list_resource_vulnerability_findings.html new file mode 100644 index 000000000..838b70b43 --- /dev/null +++ b/content/response_integrations/third_party/partner/wiz/widgets/list_resource_vulnerability_findings.html @@ -0,0 +1,1200 @@ + + + + + + + + + + + Wiz - List Resource Vulnerability Findings + + + + +
+
+
+
+

+ +
+
+
+

+

+
+
+ + + +
+
+ + + +
+
+
+
+
+ + + + + diff --git a/content/response_integrations/third_party/partner/wiz/widgets/list_resource_vulnerability_findings.yaml b/content/response_integrations/third_party/partner/wiz/widgets/list_resource_vulnerability_findings.yaml new file mode 100644 index 000000000..165b1f5a5 --- /dev/null +++ b/content/response_integrations/third_party/partner/wiz/widgets/list_resource_vulnerability_findings.yaml @@ -0,0 +1,26 @@ +title: Wiz - List Resource Vulnerability Findings +type: html +scope: alert +action_identifier: List Resource Vulnerability Findings +description: This widget highlights the most important items in List Resource Vulnerability Findings +data_definition: + html_height: 400 + safe_rendering: false + widget_definition_scope: both + type: html +condition_group: + conditions: + - field_name: '[{stepInstanceName}.JsonResult]' + value: '{stepInstanceName}' + match_type: not_contains + - field_name: '[{stepInstanceName}.JsonResult]' + value: '' + match_type: is_not_empty + - field_name: '[{stepInstanceName}.JsonResult]' + value: '{}' + match_type: not_equal + - field_name: '[{stepInstanceName}.JsonResult]' + value: '[]' + match_type: not_equal + logical_operator: and +default_size: half_width diff --git a/packages/mp/src/mp/build_project/restructure/integrations/code.py b/packages/mp/src/mp/build_project/restructure/integrations/code.py index df07d029b..efcb58599 100644 --- a/packages/mp/src/mp/build_project/restructure/integrations/code.py +++ b/packages/mp/src/mp/build_project/restructure/integrations/code.py @@ -71,3 +71,5 @@ def _restructure_code(self, dir_name: str) -> None: file for file in out_dir.iterdir() if mp.core.file_utils.is_python_file(file) } mp.core.code_manipulation.restructure_scripts_imports(files) + if files: # we will skip linter all project files if there are no python files + mp.core.code_manipulation.format_python_files(files) diff --git a/packages/mp/src/mp/core/file_utils/common/file_utils.py b/packages/mp/src/mp/core/file_utils/common/file_utils.py index 319d0754b..3f7dade1a 100644 --- a/packages/mp/src/mp/core/file_utils/common/file_utils.py +++ b/packages/mp/src/mp/core/file_utils/common/file_utils.py @@ -100,6 +100,9 @@ def flatten_dir(path: Path, dest: Path) -> None: """ if path.is_file() and is_valid_source_path(path): + if path.suffix == ".pyc": + return + new_path: Path = dest / path.name if new_path.exists(): if new_path.name in VALID_REPEATED_FILES: @@ -111,6 +114,9 @@ def flatten_dir(path: Path, dest: Path) -> None: shutil.copyfile(path, new_path) elif path.is_dir() and is_valid_source_path(path): + if path.name == "__pycache__": + return + for child in path.iterdir(): flatten_dir(child, dest) diff --git a/packages/mp/src/mp/dev_env/api.py b/packages/mp/src/mp/dev_env/api.py index 546f98960..718fccf49 100644 --- a/packages/mp/src/mp/dev_env/api.py +++ b/packages/mp/src/mp/dev_env/api.py @@ -55,6 +55,7 @@ def __init__( self.api_key = api_key self.session = requests.Session() self.token = None + self.session.verify = False if api_key is not None: if username is not None or password is not None: