Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix AWSRecreateSG EC2 breaking change bug #32962

Merged
merged 8 commits into from Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions 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.
@@ -1,36 +1,13 @@
import demistomock as demisto # noqa: F401
from CommonServerPython import * # noqa: F401


from typing import Any
import traceback
from random import randint

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
Expand Down Expand 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
Expand All @@ -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 (
jlevypaloalto marked this conversation as resolved.
Show resolved Hide resolved
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"])
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
Expand Up @@ -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
Expand Down
Expand Up @@ -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)
Expand All @@ -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,
Expand All @@ -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
@@ -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 = [
Expand All @@ -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'}}]
2 changes: 1 addition & 1 deletion Packs/AWS-Enrichment-Remediation/pack_metadata.json
Expand Up @@ -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": "",
Expand Down