Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
b81582a
Create an Observable type instead
pyth0n1c Jul 28, 2023
db06257
Improve validation to check
pyth0n1c Jul 29, 2023
7b3b5da
More enhancements to catch
pyth0n1c Jul 29, 2023
ea5c86f
Fixing template detection which was
pyth0n1c Jul 29, 2023
9e591a3
Fix testEndToEndWorkflow
pyth0n1c Jul 29, 2023
dc9fa25
Fix and update poetry.lock
pyth0n1c Jul 29, 2023
081fd7a
Fix bug where lookup folder was not generated properly in app.
pyth0n1c Aug 1, 2023
3838937
Do not require validation between observable
pyth0n1c Aug 1, 2023
816f4f7
Significantly improved validation
pyth0n1c Aug 2, 2023
174a0c3
Progress on implementing --mode changes
pyth0n1c Aug 3, 2023
06dd1a4
Better support for getting modified content
pyth0n1c Aug 3, 2023
1c3a8a8
Proper support for contentctl test checking
pyth0n1c Aug 3, 2023
c11bf16
Improve error message
pyth0n1c Aug 14, 2023
e0d3b23
Huge number of overhauls and updates in
pyth0n1c Aug 18, 2023
8bf2d47
Fixes to incorrect fields called for objects
pyth0n1c Aug 18, 2023
29c1848
Clean up container when done.
pyth0n1c Aug 21, 2023
79b7fbd
Fix typo in variable name
pyth0n1c Aug 28, 2023
32bb047
Fix missing sid when there is an error.
pyth0n1c Aug 28, 2023
5008a5c
Improve build command
Sep 8, 2023
ecebb0e
contentctl config files
Sep 8, 2023
2ef4d89
Better support for the --num_containers command line argument. Initia…
pyth0n1c Sep 8, 2023
b770bb5
Should now accept command line
pyth0n1c Sep 9, 2023
9dc343c
contentctl config files
Sep 11, 2023
dba2ad8
Fix so that notable validation
pyth0n1c Sep 12, 2023
90717ee
Throw errors during build
pyth0n1c Sep 12, 2023
aeb2841
Updated .gitignore
Sep 14, 2023
451c114
Updated test parser
Sep 14, 2023
21568f1
Updated test parser
Sep 14, 2023
5421566
Merge branch 'improve_notable_validation' into merge_changes
Sep 15, 2023
f6d6541
merged multiple branches
Sep 15, 2023
fe69f2d
merged branches
Sep 15, 2023
11b434a
Make sure that testing with
pyth0n1c Sep 20, 2023
d23fed5
improve report command
Sep 20, 2023
d82df79
Merge branch 'merge_changes' of github.com:splunk/contentctl into mer…
Sep 20, 2023
b8648be
Remove dependency on
pyth0n1c Sep 20, 2023
8ec13ab
Minor patch to flush stdout.
pyth0n1c Sep 21, 2023
926c4c0
Support for skipping experimental
pyth0n1c Sep 22, 2023
8e7f646
Add convert command
Sep 25, 2023
ef75c23
improvments
Sep 25, 2023
be13985
improvements
Sep 25, 2023
8200682
improvements
Sep 25, 2023
84dc835
added sonarqube
Sep 26, 2023
b4914ba
remove sonarqube
Sep 26, 2023
00f9219
Improvements to make sure diffing changes
pyth0n1c Sep 28, 2023
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
Binary file added .DS_Store
Binary file not shown.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ dist/*
apps*
test_results*
attack_data*

security_content/

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down
25 changes: 25 additions & 0 deletions contentctl/actions/convert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

import sys
import shutil
import os

from dataclasses import dataclass

from contentctl.input.sigma_converter import *
from contentctl.output.yml_output import YmlOutput

@dataclass(frozen=True)
class ConvertInputDto:
sigma_converter_input_dto: SigmaConverterInputDto
output_path : str


class Convert:

def execute(self, input_dto: ConvertInputDto) -> None:
sigma_converter_output_dto = SigmaConverterOutputDto([])
sigma_converter = SigmaConverter(sigma_converter_output_dto)
sigma_converter.execute(input_dto.sigma_converter_input_dto)

yml_output = YmlOutput()
yml_output.writeDetections(sigma_converter_output_dto.detections, input_dto.output_path)
22 changes: 8 additions & 14 deletions contentctl/actions/detection_testing/DetectionTestingManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ def sigint_handler(signum, frame):
signal.signal(signal.SIGINT, sigint_handler)

with concurrent.futures.ThreadPoolExecutor(
max_workers=self.input_dto.config.num_containers,
max_workers=len(self.input_dto.config.infrastructure_config.infrastructures),
) as instance_pool, concurrent.futures.ThreadPoolExecutor(
max_workers=len(self.input_dto.views)
) as view_runner, concurrent.futures.ThreadPoolExecutor(
max_workers=self.input_dto.config.num_containers,
max_workers=len(self.input_dto.config.infrastructure_config.infrastructures),
) as view_shutdowner:

# Start all the views
Expand Down Expand Up @@ -151,39 +151,33 @@ def sigint_handler(signum, frame):
def create_DetectionTestingInfrastructureObjects(self):
import sys

for index in range(self.input_dto.config.num_containers):
instanceConfig = deepcopy(self.input_dto.config)
instanceConfig.api_port += index * 2
instanceConfig.hec_port += index * 2
instanceConfig.web_ui_port += index

instanceConfig.container_name = instanceConfig.container_name % (index,)
for infrastructure in self.input_dto.config.infrastructure_config.infrastructures:

if (
self.input_dto.config.target_infrastructure
self.input_dto.config.infrastructure_config.infrastructure_type
== DetectionTestingTargetInfrastructure.container
):

self.detectionTestingInfrastructureObjects.append(
DetectionTestingInfrastructureContainer(
config=instanceConfig, sync_obj=self.output_dto
global_config=self.input_dto.config, infrastructure=infrastructure, sync_obj=self.output_dto
)
)

elif (
self.input_dto.config.target_infrastructure
self.input_dto.config.infrastructure_config.infrastructure_type
== DetectionTestingTargetInfrastructure.server
):

self.detectionTestingInfrastructureObjects.append(
DetectionTestingInfrastructureServer(
config=instanceConfig, sync_obj=self.output_dto
global_config=self.input_dto.config, infrastructure=infrastructure, sync_obj=self.output_dto
)
)

else:

print(
f"Unsupported target infrastructure '{self.input_dto.config.target_infrastructure}'"
f"Unsupported target infrastructure '{self.input_dto.config.infrastructure_config.infrastructure_type}'"
)
sys.exit(1)
132 changes: 65 additions & 67 deletions contentctl/actions/detection_testing/GitHubService.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ def get_all_content(self, director: DirectorOutputDto) -> DirectorOutputDto:
self.get_macros(director),
self.get_lookups(director),
[],
[]
)

def get_stories(self, director: DirectorOutputDto) -> list[Story]:
Expand Down Expand Up @@ -137,15 +138,63 @@ def get_detections_changed(self, director: DirectorOutputDto) -> list[Detection]
f"Error: self.repo must be initialized before getting changed detections."
)
)
raise (Exception("not implemented"))
return []

differences = self.repo.git.diff("--name-status", f"origin/{self.config.version_control_config.main_branch}").split("\n")
new_content = []
modified_content = []
deleted_content = []
for difference in differences:
mode, filename = difference.split("\t")
if mode == "A":
new_content.append(filename)
elif mode == "M":
modified_content.append(filename)
elif mode == "D":
deleted_content.append(filename)
else:
raise Exception(f"Unknown mode in determining differences: {difference}")

#Changes to detections, macros, and lookups should trigger a re-test for anything which uses them
changed_lookups_list = list(filter(lambda x: x.startswith("lookups"), new_content+modified_content))
changed_lookups = set()

#We must account for changes to the lookup yml AND for the underlying csv
for lookup in changed_lookups_list:
if lookup.endswith(".csv"):
lookup = lookup.replace(".csv", ".yml")
changed_lookups.add(lookup)

# At some point we should account for macros which contain other macros...
changed_macros = set(filter(lambda x: x.startswith("macros"), new_content+modified_content))
changed_macros_and_lookups = set([str(pathlib.Path(filename).absolute()) for filename in changed_lookups.union(changed_macros)])

changed_detections = set(filter(lambda x: x.startswith("detections"), new_content+modified_content))

#Check and see if content that has been modified uses any of the changed macros or lookups
for detection in director.detections:
deps = set([content.file_path for content in detection.get_content_dependencies()])
if not deps.isdisjoint(changed_macros_and_lookups):
changed_detections.add(detection.file_path)

return Detection.get_detections_from_filenames(changed_detections, director.detections)

def __init__(self, config: TestConfig):
self.repo = None

self.requested_detections: list[pathlib.Path] = []
self.config = config

if config.mode == DetectionTestingMode.selected:
if config.version_control_config is not None:
self.repo = git.Repo(config.version_control_config.repo_path)
else:
self.repo = None


if config.mode == DetectionTestingMode.changes:
if self.repo is None:
raise Exception("You are using detection mode 'changes', but the app does not have a version_control_config in contentctl_test.yml.")
return
elif config.mode == DetectionTestingMode.all:
return
elif config.mode == DetectionTestingMode.selected:
if config.detections_list is None or len(config.detections_list) < 1:
raise (
Exception(
Expand All @@ -171,63 +220,12 @@ def __init__(self, config: TestConfig):
pathlib.Path(detection_file_name)
for detection_file_name in config.detections_list
]
return

elif config.mode == DetectionTestingMode.changes:
# Changes is ONLY possible if the app is version controlled
# in a github repo. Ensure that this is the case and, if not
# raise an exception
raise (Exception("Mode [changes] is not yet supported."))
try:
repo = git.Repo(config.repo_path)
except Exception as e:
raise (
Exception(
f"Error: detection mode [{config.mode}] REQUIRES that [{config.repo_path}] is a git repository, but it is not."
)
)
if config.main_branch == config.test_branch:
raise (
Exception(
f"Error: test_branch [{config.test_branch}] is the same as the main_branch [{config.main_branch}]. When using mode [{config.mode}], these two branches MUST be different."
)
)

# Ensure that the test branch is checked out
if self.repo.active_branch.name != config.test_branch:
raise (
Exception(
f"Error: detection mode [{config.mode}] REQUIRES that the test_branch [{config.test_branch}] be checked out at the beginning of the test, but it is not."
)
)

# Ensure that the base branch exists

if Utils.validate_git_branch_name(
config.repo_path, "NO_URL", config.main_branch
):
return

elif config.mode == DetectionTestingMode.all:
return

else:
raise (
Exception(
f"Unsupported detection testing mode [{config.mode}]. Supported detection testing modes are [{DetectionTestingMode._member_names_}]"
)
)

def __init2__(self, config: TestConfig):

self.repo = git.Repo(config.repo_path)

if self.repo.active_branch.name != config.test_branch:
print(
f"Error - test_branch is '{config.test_branch}', but the current active branch in '{config.repo_path}' is '{self.repo.active_branch}'. Checking out the branch you specified..."
)
self.repo.git.checkout(config.test_branch)

self.config = config
raise Exception(f"Unsupported detection testing mode [{config.mode}]. "\
"Supported detection testing modes are [{DetectionTestingMode._member_names_}]")
return


def clone_project(self, url, project, branch):
LOGGER.info(f"Clone Security Content Project")
Expand All @@ -252,7 +250,7 @@ def get_detections_to_test(
]
if ignore_deprecated:
director.detections = [
d for d in director.detections if not (d.deprecated == True)
d for d in director.detections if not (d.status == "deprecated")
]
if ignore_ssa:
director.detections = [
Expand Down Expand Up @@ -352,29 +350,29 @@ def get_all_modified_content(
# Because we have not passed -all as a kwarg, we will have a MAX of one commit returned:
# https://gitpython.readthedocs.io/en/stable/reference.html?highlight=merge_base#git.repo.base.Repo.merge_base
base_commits = self.repo.merge_base(
self.config.main_branch, self.config.test_branch
self.config.version_control_config.main_branch, self.config.version_control_config.test_branch
)
if len(base_commits) == 0:
raise (
Exception(
f"Error, main branch '{self.config.main_branch}' and test branch '{self.config.test_branch}' do not share a common ancestor"
f"Error, main branch '{self.config.version_control_config.main_branch}' and test branch '{self.config.version_control_config.test_branch}' do not share a common ancestor"
)
)
base_commit = base_commits[0]
if base_commit is None:
raise (
Exception(
f"Error, main branch '{self.config.main_branch}' and test branch '{self.config.test_branch}' common ancestor commit was 'None'"
f"Error, main branch '{self.config.version_control_config.main_branch}' and test branch '{self.config.version_control_config.test_branch}' common ancestor commit was 'None'"
)
)

all_changes = base_commit.diff(
self.config.test_branch, paths=[str(path) for path in paths]
self.config.version_control_config.test_branch, paths=[str(path) for path in paths]
)

# distill changed files down to the paths of added or modified files
all_changes_paths = [
os.path.join(self.config.repo_path, change.b_path)
os.path.join(self.config.version_control_config.repo_path, change.b_path)
for change in all_changes
if change.change_type in ["M", "A"]
]
Expand Down
Loading