Skip to content

Commit

Permalink
Merge 6ed64ec into 4e131a3
Browse files Browse the repository at this point in the history
  • Loading branch information
maimorag committed Jun 6, 2024
2 parents 4e131a3 + 6ed64ec commit 3ad583d
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 4 deletions.
4 changes: 4 additions & 0 deletions .changelog/4324.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changes:
- description: Converted the RM108 validation to the new format. This validation verifies that images in the readme and description files are relative and stored in doc_files.
type: internal
pr_number: 4275
4 changes: 2 additions & 2 deletions demisto_sdk/commands/validate/sdk_validation_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ ignorable_errors = [
"MP106",
"PA113", "PA116", "PA124", "PA125", "PA127", "PA129",
"PB104", "PB105", "PB106", "PB107", "PB110", "PB111", "PB114", "PB115", "PB116", "PB118", "PB119", "PB121",
"RM100", "RM102", "RM104", "RM106", "RM108", "RM110", "RM112", "RM113",
"RM100", "RM102", "RM104", "RM106", "RM110", "RM112", "RM113",
"RP102", "RP104",
"SC100", "SC105", "SC106",
"IM111",
Expand All @@ -33,7 +33,7 @@ select = [
"DO100", "DO101", "DO102", "DO103", "DO104", "DO105", "DO106",
"DS100", "DS107",
"SC100", "SC105", "SC106", "SC109",
"RM101", "RN103", "RM104", "RM105", "RM106", "RM109", "RM113", "RM114",
"RM101", "RN103", "RM104", "RM105", "RM106", "RM108", "RM109", "RM113", "RM114",
"CL100",
"GF100", "GF101", "GF102",
"IF100", "IF101", "IF102", "IF103", "IF104", "IF105", "IF106", "IF116",
Expand Down
108 changes: 108 additions & 0 deletions demisto_sdk/commands/validate/tests/RM_validators_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@
from demisto_sdk.commands.validate.validators.RM_validators.RM106_is_contain_demisto_word import (
IsContainDemistoWordValidator,
)
from demisto_sdk.commands.validate.validators.RM_validators.RM108_check_image_path_integration import (
ImagePathIntegrationValidator,
)
from demisto_sdk.commands.validate.validators.RM_validators.RM108_check_image_path_only_readme import (
ImagePathOnlyReadMeValidator,
)
from demisto_sdk.commands.validate.validators.RM_validators.RM109_is_readme_exists import (
IsReadmeExistsValidator,
)
Expand Down Expand Up @@ -434,3 +440,105 @@ def test_IsContainDemistoWordValidator_is_invalid():
results = IsContainDemistoWordValidator().is_valid(content_items)
assert len(results) == 1
assert results[0].message == expected_msg


def test_ImagePathIntegrationValidator_is_valid_valid_case():
"""
Given
content_items.
- Pack with valid readme and valid description contain only relative paths.
When
- Calling the ImagePathIntegrationValidator is_valid function.
Then
- Make sure that the pack isn't failing.
"""
content_items = [
create_integration_object(
readme_content="![Example Image](../doc_files/image.png)",
description_content="valid description ![Example Image](../doc_files/image.png)",
),
]
assert not ImagePathIntegrationValidator().is_valid(content_items)


def test_ImagePathIntegrationValidator_is_valid_invalid_case():
"""
Given
content_items.
- Pack with:
1. invalid readme that contains absolute path.
2. description contains relative path that saved not under dec_files.
demisto_sdk/commands/validate/sdk_validation_config.toml
When
- Calling the ImagePathIntegrationValidator is_valid function.
Then
- Make sure that the pack is failing.
"""
content_items = [
create_integration_object(
readme_content=" Readme contains absolute path:\n 'Here is an image:\n"
" ![Example Image](https://www.example.com/images/example_image.jpg)",
description_content="valid description ![Example Image](../../content/image.jpg)",
),
]
expected = (
"Invalid image path(s) detected. Please use relative paths instead in the following links:"
"\nhttps://www.example.com/images/example_image.jpgRelative image paths found outside the pack's "
"doc_files directory. Please move the following images to the doc_files directory:"
"\n../../content/image.jpg. See https://xsoar.pan.dev/docs/integrations/integration-docs#images"
" for further info on how to add images to pack markdown files."
)
result = ImagePathIntegrationValidator().is_valid(content_items)
assert result[0].message == expected


def test_ImagePathOnlyReadMeValidator_is_valid_valid_case():
"""
Given
content_items.
- Pack with valid readme contains only relative paths.
When
- Calling the ImagePathIntegrationValidator is_valid function.
Then
- Make sure that the pack isn't failing.
"""
content_items = [
create_integration_object(
readme_content="![Example Image](../doc_files/image.png)",
),
]
assert not ImagePathOnlyReadMeValidator().is_valid(content_items)


def test_ImagePathOnlyReadMeValidator_is_valid_invalid_case():
"""
Given
content_items.
- Pack with:
1. invalid readme that contains absolute path and contains
relative path that saved not under dec_files.
When
- Calling the ImagePathOnlyReadMeValidator is_valid function.
Then
- Make sure that the pack is failing.
"""
content_items = [
create_integration_object(
readme_content=" Readme contains absolute path:\n 'Here is an image:\n"
" ![Example Image](https://www.example.com/images/example_image.jpg)"
" ![Example Image](../../content/image.jpg)",
),
]
expected = (
"Invalid image path(s) detected. Please use relative paths instead in the following links:"
"\nhttps://www.example.com/images/example_image.jpgRelative image paths found outside the pack's"
" doc_files directory. Please move the following images to the doc_files"
" directory:\n../../content/image.jpg."
" See https://xsoar.pan.dev/docs/integrations/integration-docs#images for further info on"
" how to add images to pack markdown files."
)
result = ImagePathOnlyReadMeValidator().is_valid(content_items)
assert result[0].message == expected
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ def is_valid(self, content_items: Iterable[ContentTypes]) -> List[ValidationResu
return [
ValidationResult(
validator=self,
message=self.error_message.format(", ".join(lines_contain_demsito)),
message=self.error_message.format(", ".join(lines_contain_demisto)),
content_object=content_item,
)
for content_item in content_items
if (
lines_contain_demsito := check_text_content_contain_sub_text(
lines_contain_demisto := check_text_content_contain_sub_text(
sub_text_list=["demisto"],
is_lower=True,
text=content_item.readme.file_content,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
from __future__ import annotations

import re
from abc import ABC
from typing import Iterable, List

from demisto_sdk.commands.common.constants import (
DOC_FILE_IMAGE_REGEX,
HTML_IMAGE_LINK_REGEX,
URL_IMAGE_LINK_REGEX,
)
from demisto_sdk.commands.content_graph.objects.content_item import ContentItem
from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFileType
from demisto_sdk.commands.validate.validators.base_validator import (
BaseValidator,
ValidationResult,
)

ContentTypes = ContentItem


class ImagePathValidator(BaseValidator, ABC):
error_code = "RM108"
description = (
"This validation verifies that images in the readme and description files are"
" relative and stored in doc_files."
)
rationale = (
"Using relative references to files in the repo folder enhances security by reducing reliance"
" on external links, minimizing the risk of link manipulation or redirection attacks. "
)
error_message = (
"{}. See https://xsoar.pan.dev/docs/integrations/integration-docs#images for further info on"
" how to add images to pack markdown files."
)
related_file_type = [RelatedFileType.README, RelatedFileType.DESCRIPTION_File]

def is_valid(self, content_items: Iterable[ContentTypes]) -> List[ValidationResult]:
return [
ValidationResult(
validator=self,
message=self.error_message.format(error_message),
content_object=content_item,
)
for content_item in content_items
if (error_message := self.validate_content_items(content_item))
]

def validate_content_items(self, content_item):
"""Check if the content items are valid by passing verify_absolute_images_not_exist and verify_relative_saved_in_doc_files.
Arguments:
content_item {ContentTypes} -- The content item to check.
Returns:
str -- The error message if the content item isn't valid.
"""
raise NotImplementedError("Subclasses must implement this method.")

def verify_absolute_images_not_exist(self, content_item) -> str:
"""Check if the content item contains exists image paths.
Arguments:
content_item {ContentTypes} -- The content item to check.
Returns:
str -- The error message if the content item contains absolute paths.
"""
absolute_links = re.findall(
URL_IMAGE_LINK_REGEX + r"|" + HTML_IMAGE_LINK_REGEX,
content_item,
re.IGNORECASE | re.MULTILINE,
)
if absolute_links:
absolute_links = [
absolute_link[1] if absolute_link[0] else absolute_link[2]
for absolute_link in absolute_links
]

return (
"Invalid image path(s) detected. Please use relative paths instead in the following links:\n"
+ "\n".join(absolute_links)
)
return ""

def verify_relative_saved_in_doc_files(self, content_item) -> str:
"""Check if the content item contains exists image paths.
Arguments:
content_item {ContentTypes} -- The content item to check.
Returns:
str -- The error message if the content item contains relative images that are not saved in the
pack's doc_files folder.
"""
relative_images = re.findall(
r"(\!\[.*?\])\(((?!http).*?)\)$"
+ r"|"
+ r'(<img.*?src\s*=\s*"((?!http).*?)")',
content_item,
re.IGNORECASE | re.MULTILINE,
)
relative_images = [
match[1] if match[0] else match[2] for match in relative_images
]
invalid_links = [
rel_img
for rel_img in relative_images
if not re.match(DOC_FILE_IMAGE_REGEX, rel_img)
]
if invalid_links:
return (
"Relative image paths found outside the pack's doc_files directory."
" Please move the following images to the doc_files directory:\n"
+ "\n".join(invalid_links)
)
return ""
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from __future__ import annotations

from demisto_sdk.commands.content_graph.objects.integration import Integration
from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFileType
from demisto_sdk.commands.validate.validators.base_validator import (
BaseValidator,
)
from demisto_sdk.commands.validate.validators.RM_validators.RM108_check_image_path import (
ImagePathValidator,
)

ContentTypes = Integration


class ImagePathIntegrationValidator(ImagePathValidator, BaseValidator[ContentTypes]):
related_file_type = [RelatedFileType.README, RelatedFileType.DESCRIPTION_File]

def validate_content_items(self, content_item: ContentTypes) -> str:
"""Check if the content items are valid by passing verify_absolute_images_not_exist and verify_relative_saved_in_doc_files.
Arguments:
content_item {ContentTypes} -- The content item to check.
Returns:
str -- The error message if the content item isn't valid.
"""
error_message = (
self.verify_absolute_images_not_exist(content_item.readme.file_content)
+ self.verify_relative_saved_in_doc_files(content_item.readme.file_content)
+ self.verify_absolute_images_not_exist(
content_item.description_file.file_content
)
+ self.verify_relative_saved_in_doc_files(
content_item.description_file.file_content
)
)
return error_message
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from __future__ import annotations

from typing import Union

from demisto_sdk.commands.content_graph.objects.pack import Pack
from demisto_sdk.commands.content_graph.objects.playbook import Playbook
from demisto_sdk.commands.content_graph.objects.script import Script
from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFileType
from demisto_sdk.commands.validate.validators.base_validator import (
BaseValidator,
)
from demisto_sdk.commands.validate.validators.RM_validators.RM108_check_image_path import (
ImagePathValidator,
)

ContentTypes = Union[Script, Playbook, Pack]


class ImagePathOnlyReadMeValidator(ImagePathValidator, BaseValidator[ContentTypes]):
related_file_type = [RelatedFileType.README]

def validate_content_items(self, content_item: ContentTypes) -> str:
"""Check if the content items are valid.
Arguments:
content_item {ContentTypes} -- The content item to check.
Returns:
str -- The error message if the content item isn't valid.
"""
error_message = self.verify_absolute_images_not_exist(
content_item.readme.file_content
) + self.verify_relative_saved_in_doc_files(content_item.readme.file_content)
return error_message

0 comments on commit 3ad583d

Please sign in to comment.