Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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",
Comment thread
sedovolosiy marked this conversation as resolved.
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()
Original file line number Diff line number Diff line change
@@ -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
Comment thread
sedovolosiy marked this conversation as resolved.
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
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

if TYPE_CHECKING:
import requests

from TIPCommon.types import ChronicleSOAR


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Loading
Loading