Adding Entities from Source Case to Current Case in Action

I am currently working on a custom action to copy entities from a source case to the current case. Despite the source case (ID: 18545, which I added as a parameter) having entities, the action reports that zero entities were added. Here's the code I'm using:

 

from SiemplifyAction import SiemplifyAction
from Siemplify import Siemplify
from SiemplifyUtils import output_handler

@output_handler
def main():
    siemplify_action = SiemplifyAction()

    # Retrieve the source case ID from the action parameters
    source_case_id = siemplify_action.extract_action_param(param_name="source_case_id", is_mandatory=True)

    # Get the current case
    current_case = siemplify_action.case

    # Create an instance of Siemplify to access get_case_by_id
    siemplify = Siemplify()

    # Retrieve the source case
    try:
        source_case = siemplify._get_case_by_id(int(source_case_id))
    except Exception as e:
        siemplify_action.LOGGER.error(f"Error retrieving case with ID {source_case_id}: {str(e)}")
        siemplify_action.end(f"Could not retrieve case with ID {source_case_id}.", False)
        return

    source_entities = source_case.get("entities", [])

    # Add entities to the current case
    for entity in source_entities:
        siemplify_action.add_entity_to_case(
            entity_identifier=entity.identifier,
            entity_type=entity.entity_type,
            is_internal=entity.is_internal,
            is_suspicious=entity.is_suspicious,
            is_enriched=entity.is_enriched,
            is_vulnerable=entity.is_vulnerable,
            properties=entity.properties
        )

    siemplify_action.end(f"Added {len(source_entities)} entities from case {source_case_id} to the current case.", True)

if __name__ == "__main__":
    main()

 

Issue:

The output message is:

Added 0 entities from case 18545 to the current case.

However, I have confirmed that case 18545 contains entities. I suspect the problem might be with the line:

 

 

 

source_entities = source_case.get("entities", [])

 

 

 

 Perhaps the key "entities" is incorrect or not returning the expected data. I've also tried using get_case_by_id, but it results in an error, so I'm using the private method _get_case_by_id for now.

Question:

  • Is "entities" the correct key to access the list of entities from a case object retrieved via _get_case_by_id?

  • If not, what is the correct way to access the entities from a source case?

  • Are there any best practices for copying entities from one case to another?

Any guidance or suggestions would be greatly appreciated.

Thank you!

Solved Solved
0 6 250
1 ACCEPTED SOLUTION

I managed to find the solution, which is the following:

 

 

from SiemplifyAction import SiemplifyAction
from SiemplifyUtils import output_handler
import json

@output_handler
def main():
    siemplify = SiemplifyAction()
    source_case_id = siemplify.extract_action_param(param_name="source_case_id", is_mandatory=True)

    try:
        # Get the full case as a dictionary
        source_case = siemplify._get_case_by_id(int(source_case_id))
        # Print the complete content of the case for debugging
        print(json.dumps(source_case, indent=4))
    except Exception as e:
        siemplify.LOGGER.error(f"Error retrieving the case with ID {source_case_id}: {str(e)}")
        siemplify.end(f"Failed to retrieve the case with ID {source_case_id}.", False)
        return

    # List to store the found entities
    found_entities = []

    # Iterate over the alerts in the case
    for alert in source_case.get("cyber_alerts", []):
        for entity in alert.get("domain_entities", []):
            identifier = entity.get("identifier")
            entity_type = entity.get("entity_type")
            if identifier and entity_type:
                found_entities.append((identifier, entity_type))

    # Remove duplicates
    unique_entities = list(set(found_entities))

    if not unique_entities:
        siemplify.end(f"No entities found in case {source_case_id}.", True)
        return

    # Add the entities to the current case
    added_count = 0
    for identifier, entity_type in unique_entities:
        try:
            siemplify.add_entity_to_case(
                entity_identifier=identifier,
                entity_type=entity_type,
                is_internal=False,
                is_suspicous=False,
                is_enriched=False,
                is_vulnerable=False,
                properties={}
            )
            added_count += 1
        except Exception as e:
            siemplify.LOGGER.error(f"Failed to add entity {identifier} to the current case: {str(e)}")

    siemplify.end(f"Successfully added {added_count} entities from case {source_case_id} to the current case.", True)

if __name__ == "__main__":
    main()

 

 

I used the method _get_case_by_id to get the full information of the source case, whose id is located in source_case_id, as a dictionary. Then I printed the full case data with print(json.dumps(source_case, indent=4))).

From that printed information, I extracted the identifier (the entity's unique value) and the entity_type and made a list with them. And finally, I added the entities of that list to the Test case, which in my case was empty. By doing this, the entities of the case source_case_id will be copied in the Test case

View solution in original post

6 REPLIES 6

@mikelmi01 Just wanted to comment on here quickly in case it helps before I get a chance to test this later, but I believe entities are stored underneath alerts, not cases. So if you wanted all of the entities for a case, and not just for a specific alert within a case, you'd have to loop through the alerts in that (source) case for the entities.

I see this occurring in the SiemplifyAction soar-sdk on GitHub. Which makes me believe you just need to loop through the alerts within the case for the entities instead of trying to pull the entities from the case directly.

Here is a small snippet from the GitHub code where they are doing that exact thing. Again this is just a hunch without actually testing.

def _load_target_entities(self):
        """
        load target entities
        if in alert context - run only on alert entities. if not - run on all case entities.
        :return: {dict} target_entities
        """
        target_entities = {}
        all_entities = []

        if self.current_alert == None:
            #no current alert context
            all_entities = [entity for alert in self.case.alerts for entity in alert.entities]
        else:
            #current alert context
            all_entities = self.current_alert.entities
        
        if not self.support_old_entities:
            for entity in all_entities:
                if (entity.identifier, entity.entity_type) in self.target_entity_ids and (entity.identifier, entity.entity_type) not in target_entities:
                    target_entities[(entity.identifier, entity.entity_type)] = entity
        else:
             for entity in all_entities:
                if entity.identifier in self.target_entity_ids and entity.identifier not in target_entities:
                    target_entities[entity.identifier] = entity
                    
        self._target_entities = list(target_entities.values())

 

Done some changes in your code give this a try 

from SiemplifyAction import SiemplifyAction
from Siemplify import Siemplify
from SiemplifyUtils import output_handler

@output_handler
def main():
    siemplify_action = SiemplifyAction()
    siemplify = Siemplify()

    source_case_id = siemplify_action.extract_action_param(param_name="source_case_id", is_mandatory=True)

    try:
        # Get full case including alerts
        source_case = siemplify.get_case_by_id(int(source_case_id))
    except Exception as e:
        siemplify_action.LOGGER.error(f"Failed to retrieve case {source_case_id}: {str(e)}")
        siemplify_action.end(f"Failed to retrieve source case.", False)
        return

    source_entities = []

    # Extract all entities from all alerts in the case
    for alert in source_case.alerts:
        if hasattr(alert, "entities"):
            source_entities.extend(alert.entities)

    if not source_entities:
        siemplify_action.end(f"No entities found in case {source_case_id}.", True)
        return

    added_count = 0
    for entity in source_entities:
        siemplify_action.add_entity_to_case(
            entity_identifier=entity.identifier,
            entity_type=entity.entity_type,
            is_internal=entity.is_internal,
            is_suspicious=entity.is_suspicious,
            is_enriched=entity.is_enriched,
            is_vulnerable=entity.is_vulnerable,
            properties=entity.properties
        )
        added_count += 1

    siemplify_action.end(f"Added {added_count} entities from case {source_case_id} to the current case.", True)

if __name__ == "__main__":
    main()

Hey @Chand1 

Thanks for the help! However, it gives me this error now:

Failed to retrieve case 20301: 'Siemplify' object has no attribute 'get_case_by_id'

 I tried changing the atribute to '_get_case_by_id'. That way, it doesn't give me the mentioned error, but no I have this one:

STDOUT:
STDERR:
Traceback (most recent call last):
  File "/opt/siemplify/siemplify_server/bin/Scripting/PythonSDK/IntegrationsVirtualEnvironment/#/Perseus_V6.0/0ggzcajx.y1e/2p5vhib1.u54.py", line 47, in <module>
    main()
  File "/opt/siemplify/siemplify_server/bin/Scripting/PythonSDK/SiemplifyUtils.py", line 96, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/opt/siemplify/siemplify_server/bin/Scripting/PythonSDK/IntegrationsVirtualEnvironment/#/Perseus_V6.0/0ggzcajx.y1e/2p5vhib1.u54.py", line 23, in main
    for alert in source_case.alerts:
                 ^^^^^^^^^^^^^^^^^^
AttributeError: 'dict' object has no attribute 'alerts'

 

On line 14, it's still called 'siemplify', but all those should be 'simpleify_action', I think one var was just missed

Rename like the others and try Chand1's suggestion again

It still gives me the same error. Here is the code with Chand1's suggestion but with the siemplify in line 14 changed to siemplify_action.

 

from SiemplifyAction import SiemplifyAction
from Siemplify import Siemplify
from SiemplifyUtils import output_handler

@output_handler
def main():
    siemplify_action = SiemplifyAction()
    siemplify = Siemplify()

    source_case_id = siemplify_action.extract_action_param(param_name="source_case_id", is_mandatory=True)

    try:
        # Get full case including alerts
        source_case = siemplify_action.get_case_by_id(int(source_case_id))
    except Exception as e:
        siemplify_action.LOGGER.error(f"Failed to retrieve case {source_case_id}: {str(e)}")
        siemplify_action.end(f"Failed to retrieve source case.", False)
        return

    source_entities = []

    # Extract all entities from all alerts in the case
    for alert in source_case.alerts:
        if hasattr(alert, "entities"):
            source_entities.extend(alert.entities)

    if not source_entities:
        siemplify_action.end(f"No entities found in case {source_case_id}.", True)
        return

    added_count = 0
    for entity in source_entities:
        siemplify_action.add_entity_to_case(
            entity_identifier=entity.identifier,
            entity_type=entity.entity_type,
            is_internal=entity.is_internal,
            is_suspicious=entity.is_suspicious,
            is_enriched=entity.is_enriched,
            is_vulnerable=entity.is_vulnerable,
            properties=entity.properties
        )
        added_count += 1

    siemplify_action.end(f"Added {added_count} entities from case {source_case_id} to the current case.", True)

if __name__ == "__main__":
    main()

 

 But I still get the same problem in the Debug Output:

 

Failed to retrieve case 20301: 'SiemplifyAction' object has no attribute 'get_case_by_id'

 

The 20301 is the value of parameter source_case_id.

I managed to find the solution, which is the following:

 

 

from SiemplifyAction import SiemplifyAction
from SiemplifyUtils import output_handler
import json

@output_handler
def main():
    siemplify = SiemplifyAction()
    source_case_id = siemplify.extract_action_param(param_name="source_case_id", is_mandatory=True)

    try:
        # Get the full case as a dictionary
        source_case = siemplify._get_case_by_id(int(source_case_id))
        # Print the complete content of the case for debugging
        print(json.dumps(source_case, indent=4))
    except Exception as e:
        siemplify.LOGGER.error(f"Error retrieving the case with ID {source_case_id}: {str(e)}")
        siemplify.end(f"Failed to retrieve the case with ID {source_case_id}.", False)
        return

    # List to store the found entities
    found_entities = []

    # Iterate over the alerts in the case
    for alert in source_case.get("cyber_alerts", []):
        for entity in alert.get("domain_entities", []):
            identifier = entity.get("identifier")
            entity_type = entity.get("entity_type")
            if identifier and entity_type:
                found_entities.append((identifier, entity_type))

    # Remove duplicates
    unique_entities = list(set(found_entities))

    if not unique_entities:
        siemplify.end(f"No entities found in case {source_case_id}.", True)
        return

    # Add the entities to the current case
    added_count = 0
    for identifier, entity_type in unique_entities:
        try:
            siemplify.add_entity_to_case(
                entity_identifier=identifier,
                entity_type=entity_type,
                is_internal=False,
                is_suspicous=False,
                is_enriched=False,
                is_vulnerable=False,
                properties={}
            )
            added_count += 1
        except Exception as e:
            siemplify.LOGGER.error(f"Failed to add entity {identifier} to the current case: {str(e)}")

    siemplify.end(f"Successfully added {added_count} entities from case {source_case_id} to the current case.", True)

if __name__ == "__main__":
    main()

 

 

I used the method _get_case_by_id to get the full information of the source case, whose id is located in source_case_id, as a dictionary. Then I printed the full case data with print(json.dumps(source_case, indent=4))).

From that printed information, I extracted the identifier (the entity's unique value) and the entity_type and made a list with them. And finally, I added the entities of that list to the Test case, which in my case was empty. By doing this, the entities of the case source_case_id will be copied in the Test case