Skip to content

V.1.0.2 #14

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
requires = ["wheel", 'setuptools==64.0.0']
[project]
name = "soarsdk"
version = "1.0.1"
version = "1.0.2"
description = "API Wrapper and Objects for Splunk SOAR"
readme = "README.md"
license = { file= "license.txt"}
Expand Down
12 changes: 11 additions & 1 deletion src/soarsdk/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
Pin,
PhantomObject,
Playbook,
Indicator
)


Expand Down Expand Up @@ -863,6 +864,15 @@ def get_artifacts(self, params: dict = {}) -> list[Artifact]:
)
return [Artifact(**artifact) for artifact in artifacts]

def get_indicator_by_value(self, indicator_value) -> Indicator:
"""Returns an Indicator object based on it's value"""
params: dict = {"indicator_value": indicator_value}

indicator: Indicator = self._handle_request(
method="GET", url="indicator_by_value?", params=params, return_data_only=False
)
return Indicator(**indicator)

def get_playbooks(self, params: dict = {}) -> list[Playbook]:
"""Returns a list of Playbook objects based on the REST parameters provided"""
playbooks: list[dict] = self._handle_request(
Expand Down Expand Up @@ -1054,7 +1064,7 @@ def _handle_request(
return_data_only: bool = kwargs.get("return_data_only", False)

for key, value in params.items():
if key not in ["start_time", "sort", "order"] and "__in" not in key:
if key not in ["start_time", "sort", "order", "indicator_value"] and "__in" not in key:
if isinstance(value, str):
params[key]: str = json.dumps(value)

Expand Down
15 changes: 15 additions & 0 deletions src/soarsdk/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,20 @@ def __hash__(self):
"""@todo look through previous scripts to find appropriate hashing for equality comparisons"""
return hash(self.name + self.label + self.id)

class Indicator(PhantomObject):
def __init__(self, **kwargs):
super().__init__()
self.id: int = kwargs.get("id")
self.value: str = kwargs.get("value")
self.value_hash: str = kwargs.get("value_hash")
self.tenant: int = kwargs.get("tenant")
self.tags: list = kwargs.get("tags", [])
self.earliest_time: str = kwargs.get("earliest_time")
self.latest_time: str = kwargs.get("latest_time")
self.open_events: int = kwargs.get("open_events")
self.total_events: int = kwargs.get("total_events")
self.severity_counts = kwargs.get("severity_counts", [])


class Asset(PhantomObject):
def __init__(self, **kwargs):
Expand Down Expand Up @@ -391,6 +405,7 @@ def __init__(self, **kwargs):
self.comments: list[str] = kwargs.get("comments", [])
self.notes: list[Note] = kwargs.get("notes", [])
self.data: list[dict] = kwargs.get("data", {})
self.due_time: str = kwargs.get("due_time")

@property
def artifact_count(self) -> int:
Expand Down
37 changes: 36 additions & 1 deletion tests/sample_objects.json
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,40 @@
"parent_container": null,
"parent_artifact": null
}
]
],
"indicator": {
"tags": [
"exclude_from_analysis"
],
"id": 163,
"value": "www.internalwebsite.com",
"value_hash": "d5342b206053b160e4dc3e98eeac7248f084cdfbbecd0e234fa121187f95a51a",
"tenant": 0,
"earliest_time": "2023-12-28T00:53:51.316373Z",
"latest_time": "2023-12-28T19:42:34.375667Z",
"open_events": 15,
"total_events": 15,
"severity_counts": [
{
"name": "informational",
"count": 0
},
{
"name": "critical",
"count": 0
},
{
"name": "high",
"count": 0
},
{
"name": "low",
"count": 15
},
{
"name": "medium",
"count": 0
}
]
}
}
25 changes: 21 additions & 4 deletions tests/test_PhantomClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import requests
import soarsdk
from soarsdk.client import PhantomClient
from soarsdk.objects import Artifact, Container, Playbook, Action, Pin, Asset, App
from soarsdk.objects import Artifact, Container, Indicator, Playbook, Action, Pin, Asset, App
from soarsdk.exceptions import *
from soarsdk.objects import PhantomObject
from unittest.mock import Mock
Expand All @@ -30,10 +30,15 @@ def setUp(self) -> None:
)

self.mock_container = Container(name="test", label="foobar")
self.test_data = json.load(open("tests/sample_objects.json"))

with open('tests/sample_objects.json') as f:
test_data = json.load(f)

self.test_data = test_data
self.test_artifacts = self.test_data["artifacts"]
self.test_containers = self.test_data["containers"]

self.test_indicator = self.test_data["indicator"]

@patch("requests.Session.post")
def test_create_container_throws_invalid_exception(self, mock_post):
mock_response = Mock()
Expand Down Expand Up @@ -144,6 +149,12 @@ def get_mock_artifacts_response(self) -> Mock:
mock_get_response.status_code = 200
mock_get_response.json.return_value: dict = self.test_artifacts
return mock_get_response

def get_mock_indicator_response(self) -> Mock:
mock_get_response = Mock()
mock_get_response.status_code = 200
mock_get_response.json.return_value: dict = self.test_indicator
return mock_get_response

def get_mock_containers_response(self) -> Mock:
mock_get_response = Mock()
Expand Down Expand Up @@ -254,7 +265,7 @@ def test_create_artifact(self, mock_artifacts, mock_post, mock_get):
test_artifact: Artifact = Artifact(name="Test Artifact", label="Test Artifact")
self.phantom.create_artifacts(test_container, test_artifact)
self.assertEqual(test_artifact.container_id, test_container.id)

@patch("requests.Session.delete")
def test_delete_artifact(self, mock_delete):
mock_delete_response = Mock()
Expand Down Expand Up @@ -329,6 +340,12 @@ def test_get_artifacts(self, mock_get):
mock_get.return_value = self.get_mock_artifacts_response()
for artifact in artifacts:
self.assertIsInstance(artifact, Artifact)

@patch("requests.Session.get")
def test_get_indicator_by_value(self, mock_get):
mock_get.return_value = self.get_mock_indicator_response()
indicator: Indicator = self.phantom.get_indicator_by_value("anything")
self.assertIsInstance(indicator, Indicator)

def test_update_container_values_none_id(self):
bad_container: Container = Container(name="test", label="foobar")
Expand Down