diff --git a/src/robusta/core/reporting/base.py b/src/robusta/core/reporting/base.py index f4b88afa0..45a1c3f0b 100644 --- a/src/robusta/core/reporting/base.py +++ b/src/robusta/core/reporting/base.py @@ -16,6 +16,7 @@ from robusta.core.model.env_vars import ROBUSTA_UI_DOMAIN from robusta.core.reporting.consts import FindingSource, FindingSubjectType, FindingType from robusta.integrations.kubernetes.api_client_utils import get_namespace_labels +from robusta.utils.common import encode_url from robusta.utils.scope import BaseScopeMatcher @@ -361,6 +362,7 @@ def add_enrichment( def add_link(self, link: Link, suppress_warning: bool = False) -> None: if self.dirty and not suppress_warning: logging.warning("Updating a finding after it was added to the event is not allowed!") + link.url = encode_url(link.url) self.links.append(link) def add_video_link(self, video_link: Link, suppress_warning: bool = False) -> None: diff --git a/src/robusta/core/sinks/transformer.py b/src/robusta/core/sinks/transformer.py index c3417b1ee..a361022b8 100644 --- a/src/robusta/core/sinks/transformer.py +++ b/src/robusta/core/sinks/transformer.py @@ -1,12 +1,13 @@ import logging import re -import urllib.parse from typing import List, Optional, Union import markdown2 from fpdf import FPDF from fpdf.fonts import FontFace +from robusta.utils.common import encode_url + try: from tabulate import tabulate except ImportError: @@ -122,9 +123,8 @@ def to_github_markdown(markdown_data: str, add_angular_brackets: bool = True) -> # take only the data between the first '<' and last '>' splits = match[1:-1].split("|") if len(splits) == 2: # don't replace unexpected strings - parsed_url = urllib.parse.urlparse(splits[0]) - parsed_url = parsed_url._replace(path=urllib.parse.quote_plus(parsed_url.path, safe="/")) - replacement = f"[{splits[1]}]({OPENING_ANGULAR}{parsed_url.geturl()}{CLOSING_ANGULAR})" + encoded_url = encode_url(splits[0]) + replacement = f"[{splits[1]}]({OPENING_ANGULAR}{encoded_url}{CLOSING_ANGULAR})" markdown_data = markdown_data.replace(match, replacement) return re.sub(r"\*([^\*]*)\*", r"**\1**", markdown_data) diff --git a/src/robusta/utils/common.py b/src/robusta/utils/common.py index d350294eb..a164f741d 100644 --- a/src/robusta/utils/common.py +++ b/src/robusta/utils/common.py @@ -1,4 +1,5 @@ import re +import urllib.parse from typing import List from hikaru import DiffDetail, HikaruBase @@ -41,3 +42,23 @@ def duplicate_without_fields(obj: HikaruBase, omitted_fields: List[str]): pass # in case the field doesn't exist on this object return duplication + + +def encode_url(url: str) -> str: + """ + Encode a URL so that it can be safely used in contexts where special characters must be escaped. + """ + if not url: + return "" + + parsed_url = urllib.parse.urlsplit(url) + + encoded_path = urllib.parse.quote(parsed_url.path, safe="/%") + encoded_query = urllib.parse.quote_plus(parsed_url.query, safe="=&%") + encoded_fragment = urllib.parse.quote(parsed_url.fragment, safe="%") + + return parsed_url._replace( + path=encoded_path, + query=encoded_query, + fragment=encoded_fragment + ).geturl()