Skip to content
Merged
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
2 changes: 1 addition & 1 deletion packages/mp/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "mp"
version = "1.25.6"
version = "1.25.7"
description = "General Purpose CLI tool for Google SecOps Marketplace"
readme = "README.md"
authors = [
Expand Down
3 changes: 2 additions & 1 deletion packages/mp/src/mp/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,14 @@
"Git Verify SSL",
"Siemplify Verify SSL",
}
LONG_DESCRIPTION_MAX_LENGTH: int = 2_200
LONG_DESCRIPTION_MAX_LENGTH: int = 2050
SHORT_DESCRIPTION_MAX_LENGTH: int = 2050
DISPLAY_NAME_MAX_LENGTH: int = 150
MAX_PARAMETERS_LENGTH: int = 50
PARAM_NAME_MAX_LENGTH: int = 150
PARAM_NAME_MAX_WORDS: int = 13
MINIMUM_SCRIPT_VERSION: float = 1.0
MAX_SCRIPT_RESULT_NAME_LENGTH: int = 100

# ------------------ Playbooks ------------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,9 @@ class ActionMetadata(ComponentMetadata[BuiltActionMetadata, NonBuiltActionMetada
]
default_result_value: str | None
creator: str
script_result_name: str
script_result_name: Annotated[
str, pydantic.Field(max_length=mp.core.constants.MAX_SCRIPT_RESULT_NAME_LENGTH)
]
simulation_data_json: str
version: Annotated[
pydantic.PositiveFloat,
Expand Down Expand Up @@ -185,7 +187,7 @@ def _from_built(cls, file_name: str, built: BuiltActionMetadata) -> Self:
for drm in built.get("DynamicResultsMetadata", []) or []
],
integration_identifier=built["IntegrationIdentifier"],
is_async=built.get("IsAsync", False),
is_async=v if (v := built.get("IsAsync")) is not None else False,
is_custom=built.get("IsCustom", False),
is_enabled=built.get("IsEnabled", True),
name=built["Name"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import mp.core.constants
import mp.core.utils
import mp.core.validators
from mp.core import exclusions
from mp.core.data_models.abc import Buildable, RepresentableEnum


Expand Down Expand Up @@ -85,7 +84,6 @@ class ActionParameter(
str,
pydantic.Field(
max_length=mp.core.constants.PARAM_NAME_MAX_LENGTH,
pattern=exclusions.get_param_display_name_regex(),
),
pydantic.AfterValidator(mp.core.validators.validate_param_name),
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import mp.core.constants
import mp.core.utils
import mp.core.validators
from mp.core import exclusions
from mp.core.data_models.abc import Buildable, RepresentableEnum
from mp.core.data_models.integrations.script.parameter import ScriptParamType

Expand Down Expand Up @@ -58,7 +57,6 @@ class ConnectorParameter(
str,
pydantic.Field(
max_length=mp.core.constants.PARAM_NAME_MAX_LENGTH,
pattern=exclusions.get_param_display_name_regex(),
),
pydantic.AfterValidator(mp.core.validators.validate_param_name),
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ class IntegrationMetadata(
str,
pydantic.Field(
max_length=mp.core.constants.DISPLAY_NAME_MAX_LENGTH,
pattern=exclusions.get_script_display_name_regex(),
),
]
identifier: Annotated[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ class IntegrationParameter(Buildable[BuiltIntegrationParameter, NonBuiltIntegrat
str,
pydantic.Field(
max_length=mp.core.constants.PARAM_NAME_MAX_LENGTH,
pattern=exclusions.get_param_display_name_regex(),
),
pydantic.AfterValidator(mp.core.validators.validate_param_name),
]
Expand All @@ -74,7 +73,7 @@ def _from_built(cls, built: BuiltIntegrationParameter) -> Self:
return cls(
name=built["PropertyName"],
default_value=built["Value"],
description=built.get("PropertyDescription", ""),
description=v if (v := built.get("PropertyDescription")) is not None else "",
is_mandatory=built.get("IsMandatory", False),
type_=ScriptParamType(int(built["PropertyType"])),
integration_identifier=built["IntegrationIdentifier"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ class JobMetadata(ComponentMetadata[BuiltJobMetadata, NonBuiltJobMetadata]):
str,
pydantic.Field(
max_length=mp.core.constants.DISPLAY_NAME_MAX_LENGTH,
pattern=exclusions.get_script_display_name_regex(),
),
]
parameters: Annotated[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import mp.core.constants
import mp.core.utils
import mp.core.validators
from mp.core import exclusions
from mp.core.data_models.abc import Buildable
from mp.core.data_models.integrations.script.parameter import ScriptParamType

Expand All @@ -48,7 +47,6 @@ class JobParameter(Buildable[BuiltJobParameter, NonBuiltJobParameter]):
str,
pydantic.Field(
max_length=mp.core.constants.PARAM_NAME_MAX_LENGTH,
pattern=exclusions.get_param_display_name_regex(),
),
pydantic.AfterValidator(mp.core.validators.validate_param_name),
]
Expand All @@ -64,7 +62,7 @@ class JobParameter(Buildable[BuiltJobParameter, NonBuiltJobParameter]):
def _from_built(cls, built: BuiltJobParameter) -> Self:
return cls(
name=built["Name"],
description=built.get("Description", ""),
description=v if (v := built.get("Description")) is not None else "",
is_mandatory=built["IsMandatory"],
type_=ScriptParamType(int(built["Type"])),
default_value=built.get("DefaultValue"),
Expand Down
12 changes: 12 additions & 0 deletions packages/mp/src/mp/core/exclusions.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ def _build_regex_from_list(excluded_names: list[str], base_regex: str) -> str:
def get_script_display_name_regex() -> str:
"""Build and return the script display name regex.

Returns:
The script display name regex.

"""
data: dict[str, Any] = _load_exclusions_data()
excluded_names: list[str] = data.get("excluded_script_display_names_for_regex", [])
return _build_regex_from_list(excluded_names, r"^[A-Za-z0-9-_,\s]+$")


def get_strict_script_display_name_regex() -> str:
"""Build and return the strict (validation only) script display name regex.

Returns:
The script display name regex.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .dependency_provider_validation import DependencyProviderValidation
from .disabled_validation import NoDisabledComponentsInIntegrationValidation
from .documentation_link_validation import IntegrationHasDocumentationLinkValidation
from .fields_validation import FieldsValidation
from .integration_ssl_validation import SslParameterExistsInIntegrationValidation
from .mapping_rules_validation import IntegrationHasMappingRulesIfHasConnectorValidation
from .ping_validation import IntegrationHasPingActionValidation
Expand Down Expand Up @@ -59,6 +60,7 @@ def _get_non_priority_validations() -> list[Validator]:
IntegrationHasDocumentationLinkValidation(),
ConnectorsHasDocumentationLinkValidation(),
PythonVersionValidation(),
FieldsValidation(),
]


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Copyright 2025 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

import dataclasses
import re
from typing import TYPE_CHECKING

from mp.core.data_models.integrations.integration import Integration
from mp.core.exceptions import NonFatalValidationError
from mp.core.exclusions import get_param_display_name_regex, get_strict_script_display_name_regex

if TYPE_CHECKING:
from pathlib import Path

from mp.core.data_models.integrations.action.metadata import ActionMetadata
from mp.core.data_models.integrations.action.parameter import ActionParameter
from mp.core.data_models.integrations.connector.metadata import ConnectorMetadata
from mp.core.data_models.integrations.connector.parameter import ConnectorParameter
from mp.core.data_models.integrations.integration_meta.metadata import IntegrationMetadata
from mp.core.data_models.integrations.integration_meta.parameter import IntegrationParameter
from mp.core.data_models.integrations.job.metadata import JobMetadata
from mp.core.data_models.integrations.job.parameter import JobParameter

METADATA_NAME_REGEX: str = get_strict_script_display_name_regex()
PARAM_NAME_REGEX: str = get_param_display_name_regex()


@dataclasses.dataclass(slots=True, frozen=True)
class FieldsValidation:
name: str = "Fields Validation"

@staticmethod
def run(integration_path: Path) -> None:
"""Strict integration fields names.

Args:
integration_path: The path of the integration to validate.

Raises:
NonFatalValidationError: If the integration doesn't have a documentation link.

"""
integration: Integration = Integration.from_non_built_path(integration_path)

result: list[str] = []
result.extend(_validate_action_metadata(list(integration.actions_metadata.values())))
result.extend(_validate_connector_metadata(list(integration.connectors_metadata.values())))
result.extend(_validate_job_metadata(list(integration.jobs_metadata.values())))
result.extend(_integration_metadata(integration.metadata))

if result:
raise NonFatalValidationError("\n".join(result))


def _validate_action_metadata(all_actions_metadata: list[ActionMetadata]) -> list[str]:
result: list[str] = []
for metadata in all_actions_metadata:
if not re.match(METADATA_NAME_REGEX, metadata.name):
result.append(
f"Action name: {metadata.name} does not match the regex: {METADATA_NAME_REGEX}"
)
result.extend(_validate_action_parameters(metadata.parameters))

return result


def _validate_action_parameters(action_parameters: list[ActionParameter]) -> list[str]:
result: list[str] = [
f"Action Parameter name: {parameter.name} does not match the regex: {PARAM_NAME_REGEX}"
for parameter in action_parameters
if not re.match(PARAM_NAME_REGEX, parameter.name)
]
return result


def _validate_connector_metadata(all_connectors_metadata: list[ConnectorMetadata]) -> list[str]:
result: list[str] = []
for metadata in all_connectors_metadata:
if not re.match(METADATA_NAME_REGEX, metadata.name):
result.append(
f"Connector name: {metadata.name} does not match the regex: {METADATA_NAME_REGEX}"
)
result.extend(_validate_connector_parameters(metadata.parameters))
return result


def _validate_connector_parameters(connector_parameters: list[ConnectorParameter]) -> list[str]:
result: list[str] = [
f"Connector Parameter name: {parameter.name} does not match the regex: {PARAM_NAME_REGEX}"
for parameter in connector_parameters
if not re.match(PARAM_NAME_REGEX, parameter.name)
]
return result


def _validate_job_metadata(all_jobs_metadata: list[JobMetadata]) -> list[str]:
result: list[str] = []
for metadata in all_jobs_metadata:
if not re.match(METADATA_NAME_REGEX, metadata.name):
result.append(
f"Job name: {metadata.name} does not match the regex: {METADATA_NAME_REGEX}"
)
result.extend(_validate_job_parameters(metadata.parameters))
return result


def _validate_job_parameters(job_parameters: list[JobParameter]) -> list[str]:
result: list[str] = [
f"Job Parameter name: {parameter.name} does not match the regex: {PARAM_NAME_REGEX}"
for parameter in job_parameters
if not re.match(PARAM_NAME_REGEX, parameter.name)
]
return result


def _integration_metadata(integration_metadata: IntegrationMetadata) -> list[str]:
result: list[str] = []
if not re.match(METADATA_NAME_REGEX, integration_metadata.name):
result.append(
f"Integration name: {integration_metadata.name} "
f"does not match the regex: {METADATA_NAME_REGEX}\n"
)
result.extend(_integration_parameters(integration_metadata.parameters))

return result


def _integration_parameters(integration_parameters: list[IntegrationParameter]) -> list[str]:
result: list[str] = [
f"Integration Parameter name: {parameter.name} does not match the regex: {PARAM_NAME_REGEX}"
for parameter in integration_parameters
if not re.match(PARAM_NAME_REGEX, parameter.name)
]
return result
Loading
Loading