Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/set-min-version' into set-min-ve…
Browse files Browse the repository at this point in the history
…rsion
  • Loading branch information
yaakovpraisler committed Jan 29, 2025
2 parents 79124c3 + 0905679 commit 66c8993
Show file tree
Hide file tree
Showing 56 changed files with 1,838 additions and 257 deletions.
4 changes: 4 additions & 0 deletions .changelog/4658.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changes:
- description: Added the **test-use-case** command to test use-case flows on cloud machines.
type: internal
pr_number: 4658
6 changes: 6 additions & 0 deletions .changelog/4769.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
changes:
- description: Added new *VC100* validation. Validates that version_config file schema is valid.
type: internal
- description: Added new *VC101* validation. Validates that versions in version_config file are continuous.
type: internal
pr_number: 4769
4 changes: 4 additions & 0 deletions .changelog/4778.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changes:
- description: Fixed an issue where the ***demisto-sdk validate*** command ran on the remote branch instead of the local current branch by default.
type: fix
pr_number: 4778
4 changes: 0 additions & 4 deletions .changelog/4779.yml

This file was deleted.

4 changes: 0 additions & 4 deletions .changelog/4781.yml

This file was deleted.

4 changes: 0 additions & 4 deletions .changelog/4782.yml

This file was deleted.

15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
# Changelog
## 1.34.1 (2025-01-28)
### Feature
* Added support for `command_examples` paths to the `validate-content-paths` in the ***demisto-sdk pre-commit*** hook. [#4775](https://github.com/demisto/demisto-sdk/pull/4775)
* Added shorter flags *-i* for the *--input* option and *-o* for the *--output* option in the ***demisto-sdk split*** command to enhance usability. [#4785](https://github.com/demisto/demisto-sdk/pull/4785)

### Fix
* Fixed *ST111* validation error message. [#4779](https://github.com/demisto/demisto-sdk/pull/4779)
* Fixed an issue in *RN114* validation where the validation's error will no longer be raised when the latest playbook release notes format is used. [#4782](https://github.com/demisto/demisto-sdk/pull/4782)
* Fixed an issue where *RN111* validation would fail when a docker entry message was missing. [#4781](https://github.com/demisto/demisto-sdk/pull/4781)

### Internal
* Fixed an issue in the Docker proxy mechanism where ISO 8601 timestamps with nanosecond precision were not properly parsed. [#4783](https://github.com/demisto/demisto-sdk/pull/4783)
* The mocking mechanism in the ***demisto-sdk test-content*** command will be removed in upcoming versions of the demisto-sdk. [#4772](https://github.com/demisto/demisto-sdk/pull/4772)


## 1.34.0 (2025-01-21)
### Internal
* Reintroduce support for GAR DockerHub proxy when running in a Gitlab CI environment. [#4655](https://github.com/demisto/demisto-sdk/pull/4655)
Expand Down
1 change: 1 addition & 0 deletions TestSuite/pack.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ def __init__(self, packs_dir: Path, name: str, repo):
"keywords": [],
}
)
self.version_config = JSONBased(self._pack_path, "version_config", "")
self.author_image = File(
tmp_path=self._pack_path / "Author_image.png", repo_path=repo.path
)
Expand Down
18 changes: 18 additions & 0 deletions demisto_sdk/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,12 @@ def register_commands(_args: list[str] = []): # noqa: C901
)(error_code)

if command_name == "test-content" or register_all:
message = typer.style(
"Warning: The mocking mechanism will be removed in the next release of the Demisto SDK.",
fg=typer.colors.RED,
)
typer.echo(message)

from demisto_sdk.commands.test_content.content_test_setup import test_content

app.command(
Expand Down Expand Up @@ -493,6 +499,18 @@ def register_commands(_args: list[str] = []): # noqa: C901
help="This command generates a test playbook from integration/script YAML arguments.",
)(generate_test_playbook)

if command_name == "test-use-case" or register_all:
from demisto_sdk.commands.test_content.test_use_case.test_use_case_setup import (
run_test_use_case,
)

app.command(
name="test-use-case",
hidden=True,
no_args_is_help=True,
help="Test Use Cases.",
)(run_test_use_case)


# Register relevant commands to Demisto-SDK app based on command-line arguments.
args = sys.argv[1:]
Expand Down
35 changes: 35 additions & 0 deletions demisto_sdk/commands/common/clients/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from functools import lru_cache
from typing import Optional

from _pytest.fixtures import SubRequest
from urllib3.exceptions import MaxRetryError

from demisto_sdk.commands.common.clients.configs import (
Expand All @@ -27,6 +28,7 @@
DEMISTO_PASSWORD,
DEMISTO_USERNAME,
DEMISTO_VERIFY_SSL,
PROJECT_ID,
MarketplaceVersions,
)
from demisto_sdk.commands.common.logger import logger
Expand Down Expand Up @@ -132,6 +134,7 @@ def get_client_from_server_type(
auth_id: Optional[str] = None,
username: Optional[str] = None,
password: Optional[str] = None,
project_id: Optional[str] = None,
verify_ssl: Optional[bool] = None,
raise_if_server_not_healthy: bool = True,
) -> XsoarClient:
Expand All @@ -144,6 +147,7 @@ def get_client_from_server_type(
auth_id: the auth ID, if not provided will take from XSIAM_AUTH_ID env var
username: the username to authenticate, relevant only for xsoar on prem
password: the password to authenticate, relevant only for xsoar on prem
project_id: the project id of the current cloud machine.
verify_ssl: whether in each request SSL should be verified, True if yes, False if not,
if verify_ssl = None, will take the SSL verification from DEMISTO_VERIFY_SSL env var
raise_if_server_not_healthy: whether to raise an exception if the server is not healthy
Expand All @@ -156,6 +160,7 @@ def get_client_from_server_type(
_auth_id = auth_id or os.getenv(AUTH_ID)
_username = username or os.getenv(DEMISTO_USERNAME, "")
_password = password or os.getenv(DEMISTO_PASSWORD, "")
_project_id = project_id or os.getenv(PROJECT_ID, "")
_verify_ssl = (
verify_ssl
if verify_ssl is not None
Expand Down Expand Up @@ -188,6 +193,7 @@ def get_client_from_server_type(
api_key=_api_key,
auth_id=_auth_id,
verify_ssl=_verify_ssl,
project_id=_project_id,
),
should_validate_server_type=should_validate_server_type,
raise_if_server_not_healthy=raise_if_server_not_healthy,
Expand All @@ -202,6 +208,7 @@ def get_client_from_server_type(
api_key=_api_key,
auth_id=_auth_id,
verify_ssl=_verify_ssl,
project_id=_project_id,
),
should_validate_server_type=should_validate_server_type,
raise_if_server_not_healthy=raise_if_server_not_healthy,
Expand Down Expand Up @@ -232,3 +239,31 @@ def get_client_from_server_type(
f"make sure the {DEMISTO_BASE_URL}, {DEMISTO_KEY}, {AUTH_ID} are defined properly"
)
raise


# =================== Playbook Flow Tests =================


def parse_str_to_dict(input_str: str):
"""Internal function to convert a string representing a dictionary into an actual dictionary.
Args:
input_str (str): A string in the format 'key1=value1,key2=value2'.
Returns:
dict: A dictionary with the parsed key-value pairs.
"""
x = dict(pair.split("=") for pair in input_str.split(",") if "=" in pair)
logger.info(x.get("base_url", "no base url"))
return dict(pair.split("=") for pair in input_str.split(",") if "=" in pair)


def get_client_conf_from_pytest_request(request: SubRequest):
# Manually parse command-line argument
for arg in request.config.invocation_params.args:
if isinstance(arg, str) and arg.startswith("--client_conf="):
logger.debug("Parsing --client_conf argument")
client_conf = arg.replace("--client_conf=", "")
return parse_str_to_dict(client_conf)
# If a client data was not provided, we proceed to use default.
return None
4 changes: 4 additions & 0 deletions demisto_sdk/commands/common/clients/configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
DEMISTO_PASSWORD,
DEMISTO_USERNAME,
DEMISTO_VERIFY_SSL,
PROJECT_ID,
XSIAM_COLLECTOR_TOKEN,
XSIAM_TOKEN,
)
Expand Down Expand Up @@ -72,6 +73,9 @@ def __eq__(self, other):

class XsoarSaasClientConfig(XsoarClientConfig):
auth_id: str = Field(default=os.getenv(AUTH_ID), description="XSOAR/XSIAM Auth ID")
project_id: str = Field(
default=os.getenv(PROJECT_ID), description="XSOAR/XSIAM Project ID"
)

@root_validator()
def validate_auth_params(cls, values: Dict[str, Any]):
Expand Down
97 changes: 92 additions & 5 deletions demisto_sdk/commands/common/clients/xsiam/xsiam_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,9 @@ def push_to_dataset(
endpoint = urljoin(self.server_config.base_api_url, "logs/v1/event")
additional_headers = {
"authorization": self.server_config.collector_token,
"content-type": (
"application/json"
if data_format.casefold == "json"
else "text/plain"
),
"content-type": "application/json"
if data_format.casefold == "json"
else "text/plain",
"content-encoding": "gzip",
}
token_type = "collector_token"
Expand Down Expand Up @@ -199,3 +197,92 @@ def get_ioc_rules(self):
)

return response

"""
#############################
Alerts related methods
#############################
"""

def create_alert_from_json(self, json_content: dict) -> int:
alert_payload = {"request_data": {"alert": json_content}}
endpoint = urljoin(
self.server_config.base_api_url, "/public_api/v1/alerts/create_alert"
)
res = self._xdr_client.post(endpoint, json=alert_payload)
alert_data = self._process_response(res, res.status_code, 200)
return alert_data["reply"]

def get_internal_alert_id(self, alert_external_id: str) -> int:
data = self.search_alerts(
filters=[
{
"field": "external_id_list",
"operator": "in",
"value": [alert_external_id],
}
]
)
return data["alerts"][0]["alert_id"]

def update_alert(self, alert_id: Union[str, list[str]], updated_data: dict) -> dict:
"""
Args:
alert_id (str | list[str]): alert ids to edit.
updated_data (dict): The data to update the alerts with. https://cortex-panw.stoplight.io/docs/cortex-xsiam-1/rpt3p1ne2bwfe-update-alerts
"""
alert_payload = {
"request_data": {"update_data": updated_data, "alert_id_list": alert_id}
}
endpoint = urljoin(
self.server_config.base_api_url, "/public_api/v1/alerts/update_alerts"
)
res = self._xdr_client.post(endpoint, json=alert_payload)
alert_data = self._process_response(res, res.status_code, 200)
return alert_data

def search_alerts(
self,
filters: list = None,
search_from: int = None,
search_to: int = None,
sort: dict = None,
) -> dict:
"""
filters should be a list of dicts contains field, operator, value.
For example:
[{field: alert_id_list, operator: in, value: [1,2,3,4]}]
Allowed values for fields - alert_id_list, alert_source, severity, creation_time
"""
body = {
"request_data": {
"filters": filters,
"search_from": search_from,
"search_to": search_to,
"sort": sort,
}
}
endpoint = urljoin(
self.server_config.base_api_url, "/public_api/v1/alerts/get_alerts/"
)
res = self._xdr_client.post(endpoint, json=body)
return self._process_response(res, res.status_code, 200)["reply"]

def search_alerts_by_uuid(self, alert_uuids: list = None, filters: list = None):
alert_uuids = alert_uuids or []
alert_ids: list = []
res = self.search_alerts(filters=filters)
alerts: list = res.get("alerts") # type: ignore
count: int = res.get("result_count") # type: ignore

while len(alerts) > 0 and len(alert_uuids) > len(alert_ids):
for alert in alerts:
for uuid in alert_uuids:
if alert.get("description").endswith(uuid):
alert_ids.append(alert.get("alert_id"))

res = self.search_alerts(filters=filters, search_from=count)
alerts = res.get("alerts") # type: ignore
count = res.get("result_count") # type: ignore

return alert_ids
37 changes: 37 additions & 0 deletions demisto_sdk/commands/common/clients/xsoar/xsoar_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,28 @@ def external_base_url(self) -> str:
# url that its purpose is to expose apis of integrations outside from xsoar/xsiam
return self.server_config.config.base_api_url

"""
#############################
Helper methods
#############################
"""

def _process_response(self, response, status_code, expected_status=200):
"""Process the HTTP response coming from the XSOAR client."""
if status_code == expected_status:
if response:
try:
return response.json()
except json.JSONDecodeError:
error = response.text
err_msg = f"Failed to parse json response - with status code {response.status_code}"
err_msg += f"\n{error}" if error else ""
logger.error(err_msg)
response.raise_for_status()
else:
error_message = f"Expected status {expected_status}, but got {status_code}. Response: {response}"
raise Exception(error_message)

"""
#############################
marketplace related methods
Expand Down Expand Up @@ -1306,3 +1328,18 @@ def poll_playbook_state(
else None
),
)

def get_playbook_data(self, playbook_id: int) -> dict:
playbook_endpoint = f"/playbook/{playbook_id}"

response, status_code, _ = self._xsoar_client.generic_request(
playbook_endpoint, method="GET", accept="application/json"
)
return self._process_response(response, status_code, 200)

def update_playbook_input(self, playbook_id: str, new_inputs: dict):
saving_inputs_path = f"/playbook/inputs/{playbook_id}"
response, status_code, _ = self._xsoar_client.generic_request(
saving_inputs_path, method="POST", body={"inputs": new_inputs}
)
return self._process_response(response, status_code, 200)
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def __init__(
"Content-Type": "application/json",
}
)
self.project_id = config.project_id
super().__init__(
config,
client=client,
Expand Down
5 changes: 4 additions & 1 deletion demisto_sdk/commands/common/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
XSIAM_TOKEN = "XSIAM_TOKEN"
XSIAM_COLLECTOR_TOKEN = "XSIAM_COLLECTOR_TOKEN"
DEMISTO_VERIFY_SSL = "DEMISTO_VERIFY_SSL"
PROJECT_ID = "PROJECT_ID"

# Logging
DEMISTO_SDK_LOG_FILE_PATH = "DEMISTO_SDK_LOG_FILE_PATH"
Expand Down Expand Up @@ -943,6 +944,7 @@ class FileType(StrEnum):
GIT_IGNORE_FILE_NAME = ".gitignore"

CONF_JSON_FILE_NAME = "conf.json"
VERSION_CONFIG_FILE_NAME = "version_config.json"

PYTHON_TEST_REGEXES = [PACKS_SCRIPT_TEST_PY_REGEX, PACKS_INTEGRATION_TEST_PY_REGEX]

Expand Down Expand Up @@ -1259,7 +1261,7 @@ class FileType(StrEnum):
VALIDATION_USING_GIT_IGNORABLE_DATA = (
"Pipfile",
"Pipfile.lock",
"command_examples",
"command_examples.txt",
"pack_metadata.json",
"testdata",
"test_data",
Expand Down Expand Up @@ -2218,6 +2220,7 @@ class PlaybookTaskType(StrEnum):
# Test types:
TEST_PLAYBOOKS = "TestPlaybooks"
TEST_MODELING_RULES = "TestModelingRules"
TEST_USE_CASES = "TestUseCases"

PB_RELEASE_NOTES_FORMAT = {
"This playbook addresses the following alerts:": 5,
Expand Down
Loading

0 comments on commit 66c8993

Please sign in to comment.