From 82dab3aa081f3187551eaf97dbba73117abd3dac Mon Sep 17 00:00:00 2001 From: avitav Date: Tue, 14 Apr 2026 15:47:35 +0000 Subject: [PATCH] feat(GitSync): Remap playbook view roles by name Fixes playbook pull failures due to non-portable role IDs by mapping roles by name to destination IDs. Missing roles are removed. --- .../power_ups/git_sync/core/GitSyncManager.py | 41 +++++++++++++++++++ .../git_sync/core/SiemplifyApiClient.py | 8 ++++ .../power_ups/git_sync/pyproject.toml | 2 +- .../power_ups/git_sync/release_notes.yaml | 6 +++ .../power_ups/git_sync/uv.lock | 2 +- 5 files changed, 57 insertions(+), 2 deletions(-) diff --git a/content/response_integrations/power_ups/git_sync/core/GitSyncManager.py b/content/response_integrations/power_ups/git_sync/core/GitSyncManager.py index 2eb2e2dc9..2e3725813 100644 --- a/content/response_integrations/power_ups/git_sync/core/GitSyncManager.py +++ b/content/response_integrations/power_ups/git_sync/core/GitSyncManager.py @@ -640,6 +640,7 @@ def update_local_workflow(self, workflow: Workflow) -> None: """Update an existing workflow in the platform.""" self.logger.info(f"Updating existing workflow '{workflow.name}'") self._adjust_workflow_ids(workflow) + self._remap_workflow_roles(workflow) self.api.save_playbook(workflow.raw_data) self._save_workflow_mod_time_to_context(workflow) self.logger.info(f"Workflow '{workflow.name}' was updated successfully") @@ -658,10 +659,43 @@ def install_new_workflow(self, workflow: Workflow) -> None: self.logger.info(f"Installing new workflow '{workflow.name}'") self._define_workflow_as_new(workflow) self._process_steps(workflow) + self._remap_workflow_roles(workflow) self.api.save_playbook(workflow.raw_data) self._save_workflow_mod_time_to_context(workflow) self.logger.info(f"New workflow '{workflow.name}' was installed successfully") + def _remap_workflow_roles(self, workflow: Workflow) -> None: + """Remap the role IDs of a workflow overviewTemplate based on the roles available in the system. + + Args: + workflow: The workflow object to remap roles for. + """ + if not workflow.raw_data.get("overviewTemplates"): + return + + roles_map = { + role["name"]: role["id"] + for role in self._soc_roles + if "name" in role and "id" in role + } + + for template in workflow.raw_data["overviewTemplates"]: + role_names = template.pop("roleNames", None) + if not role_names: + continue + + valid_role_ids = [] + for role_name in role_names: + if role_name in roles_map: + valid_role_ids.append(roles_map[role_name]) + else: + self.logger.warn( + f"Role '{role_name}' for view '{template.get('name')}' in workflow " + f"'{workflow.name}' does not exist in the destination system. It will be removed." + ) + + template["roles"] = valid_role_ids + def _process_steps( self, workflow: Workflow, @@ -793,6 +827,13 @@ def _installed_playbooks(self) -> dict[str, dict[str, Any]]: } return self._cache.get("playbooks") + @property + def _soc_roles(self) -> list[dict[str, Any]]: + """Currently configured SOC roles""" + if "soc_roles" not in self._cache: + self._cache["soc_roles"] = self.api.get_soc_roles() + return self._cache.get("soc_roles") + @property def _playbook_categories(self) -> dict: """Currently configured playbook categories""" diff --git a/content/response_integrations/power_ups/git_sync/core/SiemplifyApiClient.py b/content/response_integrations/power_ups/git_sync/core/SiemplifyApiClient.py index 8c904b5f6..ee502bc53 100644 --- a/content/response_integrations/power_ups/git_sync/core/SiemplifyApiClient.py +++ b/content/response_integrations/power_ups/git_sync/core/SiemplifyApiClient.py @@ -501,6 +501,14 @@ def get_playbook_categories(self): self.validate_response(res) return res.json() + def get_soc_roles(self) -> list[dict]: + """Get the SOC roles from the platform. + + Returns: + A list of SOC roles. + """ + return self.get_page_results("socroles/getSocRoles") + def get_simulated_cases(self): res = self.session.get("attackssimulator/GetCustomCases") self.validate_response(res) diff --git a/content/response_integrations/power_ups/git_sync/pyproject.toml b/content/response_integrations/power_ups/git_sync/pyproject.toml index aaa301fec..0d9300e08 100644 --- a/content/response_integrations/power_ups/git_sync/pyproject.toml +++ b/content/response_integrations/power_ups/git_sync/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "GitSync" -version = "44.0" +version = "45.0" description = "Sync Google SecOps integrations, playbooks, and settings with a GitHub, BitBucket or GitLab instance" requires-python = ">=3.11,<3.12" dependencies = [ diff --git a/content/response_integrations/power_ups/git_sync/release_notes.yaml b/content/response_integrations/power_ups/git_sync/release_notes.yaml index e83a28790..10ad76a88 100644 --- a/content/response_integrations/power_ups/git_sync/release_notes.yaml +++ b/content/response_integrations/power_ups/git_sync/release_notes.yaml @@ -479,3 +479,9 @@ item_type: Integration publish_time: '2026-03-18' ticket_number: '' +- description: Remap playbook view roles by name to destination IDs. + integration_version: 45.0 + item_name: GitSync + item_type: Integration + publish_time: '2026-04-20' + ticket_number: '' diff --git a/content/response_integrations/power_ups/git_sync/uv.lock b/content/response_integrations/power_ups/git_sync/uv.lock index fcaaaf5ee..605c506ed 100644 --- a/content/response_integrations/power_ups/git_sync/uv.lock +++ b/content/response_integrations/power_ups/git_sync/uv.lock @@ -309,7 +309,7 @@ wheels = [ [[package]] name = "gitsync" -version = "44.0" +version = "45.0" source = { virtual = "." } dependencies = [ { name = "dulwich" },