From 23f150983178bedb51b71f2cfc1dd1e4792982aa Mon Sep 17 00:00:00 2001 From: Jacob Levy <129657918+jlevypaloalto@users.noreply.github.com> Date: Mon, 26 Feb 2024 16:10:15 +0200 Subject: [PATCH] Fix AWSRecreateSG EC2 breaking change bug (#32962) * fixed * remove print * update docker + RN * add BC warning * remove duplicate * fixed unit-tests * newline --- .../ReleaseNotes/1_1_16.md | 9 + .../Scripts/AWSRecreateSG/AWSRecreateSG.py | 37 +--- .../Scripts/AWSRecreateSG/AWSRecreateSG.yml | 2 +- .../AWSRecreateSG/AWSRecreateSG_test.py | 57 ++---- .../Scripts/AWSRecreateSG/test_data/sample.py | 163 +++++++----------- .../pack_metadata.json | 2 +- 6 files changed, 90 insertions(+), 180 deletions(-) create mode 100644 Packs/AWS-Enrichment-Remediation/ReleaseNotes/1_1_16.md diff --git a/Packs/AWS-Enrichment-Remediation/ReleaseNotes/1_1_16.md b/Packs/AWS-Enrichment-Remediation/ReleaseNotes/1_1_16.md new file mode 100644 index 000000000000..627b0169c684 --- /dev/null +++ b/Packs/AWS-Enrichment-Remediation/ReleaseNotes/1_1_16.md @@ -0,0 +1,9 @@ + +#### Scripts + +##### AWSRecreateSG + +- Fixed an issue where the script failed with the latest version of the `AWS - EC2` integration. +- Updated the Docker image to: *demisto/python3:3.10.13.87159*. + +**NOTE:** The `AWS - EC2` integration version must be greater than `1.4.0` for this script to run. diff --git a/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG.py b/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG.py index 2890f2ebae7a..c221214a2a79 100644 --- a/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG.py +++ b/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG.py @@ -1,7 +1,6 @@ import demistomock as demisto # noqa: F401 from CommonServerPython import * # noqa: F401 - from typing import Any import traceback from random import randint @@ -9,28 +8,6 @@ ROLE_SESSION_NAME = "xsoar-session" -def get_context_path(context: dict, path: str): - """Get a context output ignoring the DT suffix. - - Args: - context (dict): The context output with DT paths as keys. - path (str): The outputs prefix path without the DT transform under which the required data is held. - - Return: - (Any): The context data under the prefix. - - Example: - >>> output = demisto.executeCommand('aws-ec2-describe-addresses') - >>> output - {'Contents': {'path.to.data(val.Id && val.Id == obj.Id)': [1, 2, 3, 4]}} - >>> get_context_path(output, 'path.to.data') - [1, 2, 3, 4] - """ - return context.get( - next((key for key in context if key.partition('(')[0] == path), None) - ) - - def split_rule(rule: dict, port: int, protocol: str) -> list[dict]: """ If there are rules with ranges of ports, split them up @@ -93,7 +70,7 @@ def sg_fix(sg_info: list, port: int, protocol: str, assume_role: str, instance_t Returns: Dict: Dict of the new SG to be used """ - info = get_context_path(sg_info[0]['Contents'], 'AWS.EC2.SecurityGroups')[0] # type: ignore + info = dict_safe_get(sg_info, (0, 'Contents', 0)) recreate_list = [] # Keep track of change in SG or not. change = False @@ -109,13 +86,6 @@ def sg_fix(sg_info: list, port: int, protocol: str, assume_role: str, instance_t and rule['IpProtocol'] == protocol ): change = True - elif ( - rule["FromPort"] == port and port == rule["ToPort"] - and any(d["CidrIp"] == "0.0.0.0/0" for d in rule["IpRanges"]) - and rule["IpProtocol"] == protocol - ): - # If condition to check for Quad 0 in the rules list for matching port. - change = True elif ( rule['FromPort'] <= port and port <= rule['ToPort'] and any(d["CidrIp"] == "0.0.0.0/0" for d in rule["IpRanges"]) @@ -160,7 +130,7 @@ def sg_fix(sg_info: list, port: int, protocol: str, assume_role: str, instance_t new_sg = demisto.executeCommand("aws-ec2-create-security-group", cmd_args) if isError(new_sg): raise ValueError('Error on creating new security group') - new_id = new_sg[0]['Contents']['AWS.EC2.SecurityGroups']['GroupId'] + new_id = dict_safe_get(new_sg, (0, 'Contents', 'GroupId')) for item in recreate_list: cmd_args = {"groupId": new_id, "IpPermissionsFull": item, "using": instance_to_use} if assume_role: @@ -293,8 +263,7 @@ def instance_info(instance_id: str, public_ip: str, assume_role: str, region: st # Need a for loop in case multiple AWS-EC2 integrations are configured. match = False for instance in instance_info: - # Check if returned error, in the case of multiple integration instances only one should pass. - interfaces = get_context_path(instance.get('Contents'), 'AWS.EC2.Instances')[0].get('NetworkInterfaces') # type: ignore + interfaces = dict_safe_get(instance, ('Contents', 0, 'NetworkInterfaces')) if not isError(instance) and interfaces: mapping_dict = {} for interface in interfaces: diff --git a/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG.yml b/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG.yml index edccc4cc15b3..f9f15a05f2c9 100644 --- a/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG.yml +++ b/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG.yml @@ -51,7 +51,7 @@ dependson: - AWS - EC2|||aws-ec2-authorize-security-group-egress-rule - AWS - EC2|||aws-ec2-revoke-security-group-ingress-rule - AWS - EC2|||aws-ec2-revoke-security-group-egress-rule -dockerimage: demisto/python3:3.10.13.84405 +dockerimage: demisto/python3:3.10.13.87159 enabled: true name: AWSRecreateSG runas: DBotWeakRole diff --git a/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG_test.py b/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG_test.py index e18a3162abc1..0597bb3ba21f 100644 --- a/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG_test.py +++ b/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/AWSRecreateSG_test.py @@ -72,9 +72,8 @@ def test_sg_fix(mocker): - Checks the output of the helper function with the expected output. """ from AWSRecreateSG import sg_fix - from test_data.sample import SG_INFO - new_sg = [{'Type': 1, 'Contents': {'AWS.EC2.SecurityGroups': {'GroupId': 'sg-00000000000000001'}}}] - mocker.patch.object(demisto, "executeCommand", return_value=new_sg) + from test_data.sample import SG_INFO, NEW_SG + mocker.patch.object(demisto, "executeCommand", return_value=NEW_SG) args = {"sg_info": SG_INFO, "port": 22, "protocol": "tcp", "assume_role": "test_role", "instance_to_use": "AWS - EC2", "region": "us-east-1"} result = sg_fix(**args) @@ -92,15 +91,13 @@ def test_determine_excessive_access(mocker): - Checks the output of the helper function with the expected output. """ from AWSRecreateSG import determine_excessive_access - from test_data.sample import SG_INFO - new_sg = [{'Type': 1, 'Contents': {'AWS.EC2.SecurityGroups': {'GroupId': 'sg-00000000000000001'}}}] + from test_data.sample import SG_INFO, NEW_SG - def executeCommand(name, args): - if name == "aws-ec2-describe-security-groups": - return SG_INFO - elif name == "aws-ec2-create-security-group": - return new_sg - return None + def executeCommand(name, *_): + return { + "aws-ec2-describe-security-groups": SG_INFO, + "aws-ec2-create-security-group": NEW_SG + }.get(name) mocker.patch.object(demisto, "executeCommand", side_effect=executeCommand) args = {"int_sg_mapping": {'eni-00000000000000000': ['sg-00000000000000000']}, "port": 22, @@ -120,38 +117,18 @@ def test_aws_recreate_sg(mocker): - Checks the output of the function with the expected output. """ from AWSRecreateSG import aws_recreate_sg - from test_data.sample import SG_INFO, INSTANCE_INFO - new_sg = [{'Type': 1, 'Contents': {'AWS.EC2.SecurityGroups': {'GroupId': 'sg-00000000000000001'}}}] - - def executeCommand(name, args): - if name == "aws-ec2-describe-security-groups": - return SG_INFO - elif name == "aws-ec2-create-security-group": - return new_sg - elif name == "aws-ec2-describe-instances": - return INSTANCE_INFO - return None + from test_data.sample import SG_INFO, INSTANCE_INFO, NEW_SG - mocker.patch.object(demisto, "executeCommand", side_effect=executeCommand) + def execute_command(command, *_): + return { + "aws-ec2-describe-security-groups": SG_INFO, + "aws-ec2-create-security-group": NEW_SG, + "aws-ec2-describe-instances": INSTANCE_INFO + }.get(command) + + mocker.patch.object(demisto, "executeCommand", side_effect=execute_command) args = {"instance_id": "fake-instance-id", "public_ip": "1.1.1.1", "port": "22", "protocol": "tcp"} command_results = aws_recreate_sg(args) readable_output = command_results.readable_output correct_output = "For interface eni-00000000000000000: \r\nreplaced SG sg-00000000000000000 with sg-00000000000000001 \r\n" assert readable_output == correct_output - - -def test_get_context_path(): - """ - Given: - An output from demisto.excuteCommand('some-command')['Context'] - When: - Calling demisto.excuteCommand. - Then: - Get the context output. - """ - from AWSRecreateSG import get_context_path - - outputs = {'path.to.data(dt_path)': [1, 2, 3, 4]} - - assert get_context_path(outputs, 'path.to.data') == [1, 2, 3, 4] - assert get_context_path(outputs, 'wrong.path') is None diff --git a/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/test_data/sample.py b/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/test_data/sample.py index ccd2e3d7340b..acbaacb9ecd6 100644 --- a/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/test_data/sample.py +++ b/Packs/AWS-Enrichment-Remediation/Scripts/AWSRecreateSG/test_data/sample.py @@ -1,105 +1,61 @@ SG_INFO = [ { "Type": 1, - "Contents": { - "AWS.EC2.SecurityGroups(val.GroupId === obj.GroupId)": [ + "Contents": [ + { + "Description": "sldkjdlskfjs", + "GroupId": "sg-00000000000000001", + "GroupName": "demo-sg", + "IpPermissions": [ { - "Description": "sldkjdlskfjs", - "GroupId": "sg-00000000000000001", - "GroupName": "demo-sg", - "IpPermissions": [ + "FromPort": 0, + "IpProtocol": "tcp", + "IpRanges": [ { - "FromPort": 0, - "IpProtocol": "tcp", - "IpRanges": [ - { - "CidrIp": "0.0.0.0/0" - } - ], - "Ipv6Ranges": [], - "PrefixListIds": [], - "ToPort": 65535, - "UserIdGroupPairs": [] - }, + "CidrIp": "0.0.0.0/0" + } + ], + "Ipv6Ranges": [], + "PrefixListIds": [], + "ToPort": 65535, + "UserIdGroupPairs": [] + }, + { + "FromPort": 22, + "IpProtocol": "tcp", + "IpRanges": [ { - "FromPort": 22, - "IpProtocol": "tcp", - "IpRanges": [ - { - "CidrIp": "0.0.0.0/0" - } - ], - "Ipv6Ranges": [], - "PrefixListIds": [], - "ToPort": 22, - "UserIdGroupPairs": [] + "CidrIp": "0.0.0.0/0" } ], - "IpPermissionsEgress": [ + "Ipv6Ranges": [], + "PrefixListIds": [], + "ToPort": 22, + "UserIdGroupPairs": [] + } + ], + "IpPermissionsEgress": [ + { + "FromPort": 0, + "IpProtocol": "tcp", + "IpRanges": [ { - "FromPort": 0, - "IpProtocol": "tcp", - "IpRanges": [ - { - "CidrIp": "0.0.0.0/0" - } - ], - "Ipv6Ranges": [], - "PrefixListIds": [], - "ToPort": 65535, - "UserIdGroupPairs": [] + "CidrIp": "0.0.0.0/0" } ], - "OwnerId": "717007404259", - "Region": "us-east-1", - "VpcId": "vpc-061c242911e464170" + "Ipv6Ranges": [], + "PrefixListIds": [], + "ToPort": 65535, + "UserIdGroupPairs": [] } - ] - }, + ], + "OwnerId": "717007404259", + "Region": "us-east-1", + "VpcId": "vpc-061c242911e464170" + } + ], "HumanReadable": "### AWS EC2 SecurityGroups\n|Description|GroupId|GroupName|OwnerId|Region|VpcId|\n|---|---|---|---|---|---|\n| sldkjdlskfjs | sg-0408c2745d3d13b15 | demo-sg | 717007404259 | us-east-1 | vpc-061c242911e464170 |\n", "ImportantEntryContext": "None", - "EntryContext": { - "AWS.EC2.SecurityGroups(val.GroupId === obj.GroupId)": [ - { - "Description": "sldkjdlskfjs", - "GroupId": "sg-0408c2745d3d13b15", - "GroupName": "demo-sg", - "IpPermissions": [ - { - "FromPort": 0, - "IpProtocol": "tcp", - "IpRanges": [ - { - "CidrIp": "0.0.0.0/0" - } - ], - "Ipv6Ranges": "None", - "PrefixListIds": "None", - "ToPort": 65535, - "UserIdGroupPairs": "None" - } - ], - "IpPermissionsEgress": [ - { - "FromPort": 0, - "IpProtocol": "tcp", - "IpRanges": [ - { - "CidrIp": "0.0.0.0/0" - } - ], - "Ipv6Ranges": "None", - "PrefixListIds": "None", - "ToPort": 65535, - "UserIdGroupPairs": "None" - } - ], - "OwnerId": "717007404259", - "Region": "us-east-1", - "VpcId": "vpc-061c242911e464170" - } - ] - } } ] INSTANCE_INFO = [ @@ -108,25 +64,24 @@ "instance": "AWS - EC2" }, "Type": 1, - "Contents": { - "AWS.EC2.Instances(val.InstanceId === obj.InstanceId)": [ + "Contents": [ + { + "NetworkInterfaces": [ { - "NetworkInterfaces": [ + "Association": { + "PublicIp": "1.1.1.1" + }, + "Groups": [ { - "Association": { - "PublicIp": "1.1.1.1" - }, - "Groups": [ - { - "GroupId": "sg-00000000000000000", - "GroupName": "sg-name" - } - ], - "NetworkInterfaceId": "eni-00000000000000000" + "GroupId": "sg-00000000000000000", + "GroupName": "sg-name" } - ] + ], + "NetworkInterfaceId": "eni-00000000000000000" } - ] - } + ] + } + ] } ] +NEW_SG = [{'Type': 1, 'Contents': {'GroupId': 'sg-00000000000000001'}}] diff --git a/Packs/AWS-Enrichment-Remediation/pack_metadata.json b/Packs/AWS-Enrichment-Remediation/pack_metadata.json index 02e202cd9549..dafe86614af4 100644 --- a/Packs/AWS-Enrichment-Remediation/pack_metadata.json +++ b/Packs/AWS-Enrichment-Remediation/pack_metadata.json @@ -2,7 +2,7 @@ "name": "AWS Enrichment and Remediation", "description": "Playbooks using multiple AWS content packs for enrichment and remediation purposes", "support": "xsoar", - "currentVersion": "1.1.15", + "currentVersion": "1.1.16", "author": "Cortex XSOAR", "url": "https://www.paloaltonetworks.com/cortex", "email": "",