diff --git a/cluster-info.txt b/cluster-info.txt new file mode 100644 index 000000000..fd96ac708 --- /dev/null +++ b/cluster-info.txt @@ -0,0 +1,2 @@ +- `fcd1957` feat(export-controller): limit CSV delimiter to printable ASCII +- `fcc6ffb` fix(export-controller): do not allow alfa-numeric CSV delimiters \ No newline at end of file diff --git a/cluster.json b/cluster.json new file mode 100644 index 000000000..4822a0b25 --- /dev/null +++ b/cluster.json @@ -0,0 +1,25 @@ +{ + "id": "C013", + "title": "Restrict CSV delimiter pattern to printable non-alphanumeric ASCII", + "services": [ + "gooddata-automation-client", + "gooddata-export-client" + ], + "commits": [ + { + "sha": "fcd19575046d93ed0c58976ceccf16d3f9e198f4", + "author": "Vladim\u00edr Kro\u010d\u00e1k", + "author_email": "vladimir.krocak@gooddata.com", + "message": "feat(export-controller): limit CSV delimiter to printable ASCII" + }, + { + "sha": "fcc6ffb550c40f93db098662056ac819c41cb1a7", + "author": "Vladim\u00edr Kro\u010d\u00e1k", + "author_email": "vladimir.krocak@gooddata.com", + "message": "fix(export-controller): do not allow alfa-numeric CSV delimiters" + } + ], + "diff": "--- a/gooddata-automation-client.json\n+++ b/gooddata-automation-client.json\n@@ DashboardTabularExportRequest.csvDelimiter @@\n- \"pattern\": \"^[^\\\\r\\\\n\\\"]$\",\n+ \"pattern\": \"^[\\\\t !#$%&()*+\\\\-.,/:;<=>?@\\\\[\\\\\\\\]^_{|}~]$\",\n--- a/gooddata-export-client.json\n+++ b/gooddata-export-client.json\n@@ TabularExportRequest.csvDelimiter @@\n- \"pattern\": \"^[^\\\\r\\\\n\\\"]$\",\n+ \"pattern\": \"^[\\\\t !#$%&()*+\\\\-.,/:;<=>?@\\\\[\\\\\\\\]^_{|}~]$\"", + "jira_tickets": [], + "sdk_impact": "modification" +} \ No newline at end of file diff --git a/packages/gooddata-sdk/src/gooddata_sdk/catalog/export/request.py b/packages/gooddata-sdk/src/gooddata_sdk/catalog/export/request.py index 7cb9cc8a2..af6f32617 100644 --- a/packages/gooddata-sdk/src/gooddata_sdk/catalog/export/request.py +++ b/packages/gooddata-sdk/src/gooddata_sdk/catalog/export/request.py @@ -1,6 +1,7 @@ # (C) 2023 GoodData Corporation from __future__ import annotations +import re from typing import Literal from attrs import define @@ -16,6 +17,9 @@ GrandTotalsPosition = Literal["pinnedBottom", "pinnedTop", "bottom", "top"] +# Pattern matching printable non-alphanumeric ASCII characters (tab, space, and special chars) +_CSV_DELIMITER_PATTERN = re.compile(r"^[\t !#$%&()*+\-.,/:;<=>?@\[\\\]^_{|}~]$") + @define(kw_only=True) class ExportCustomLabel(Base): @@ -51,6 +55,15 @@ class ExportSettings(Base): merge_headers: bool show_filters: bool grand_totals_position: GrandTotalsPosition | None = None + delimiter: str | None = None + + def __attrs_post_init__(self) -> None: + if self.delimiter is not None and not _CSV_DELIMITER_PATTERN.match(self.delimiter): + raise ValueError( + f"delimiter '{self.delimiter}' is not a valid CSV delimiter. " + "It must be a single printable non-alphanumeric ASCII character " + "(tab, space, or one of: !#$%&()*+\\-.,/:;<=>?@[\\\\]^_{{|}}~)." + ) @staticmethod def client_class() -> type[ApiSettings]: diff --git a/result.json b/result.json new file mode 100644 index 000000000..96c13b7f9 --- /dev/null +++ b/result.json @@ -0,0 +1,10 @@ +{ + "status": "implemented", + "cluster_id": "C013", + "summary": "Added `delimiter: str | None = None` field to `ExportSettings` in the SDK wrapper (`catalog/export/request.py`) to expose CSV delimiter functionality from the underlying `Settings.delimiter` API field. Added `__attrs_post_init__` validation using the new restricted pattern `^[\\t !#$%&()*+\\-.,/:;<=>?@\\[\\\\\\]^_{|}~]$` (printable non-alphanumeric ASCII only, excluding alphanumeric chars and control chars like CR/LF). Also added a module-level `_CSV_DELIMITER_PATTERN` regex constant and `import re`. The `Base.to_api()` serialization automatically includes `delimiter` when set and excludes it when `None`. No changes to `__init__.py` needed (ExportSettings was already exported).", + "files_changed": [ + "packages/gooddata-sdk/src/gooddata_sdk/catalog/export/request.py" + ], + "reason": "", + "cost_usd": 1.2988483499999997 +} \ No newline at end of file