diff --git a/README.md b/README.md
index 72dd47f..e3cd21b 100644
--- a/README.md
+++ b/README.md
@@ -1,29 +1,50 @@
Adaptive Misuse Detection System (AMIDES)
-The Adaptive Misuse Detection System (AMIDES) extends conventional rule matching of SIEM systems by applying machine learning components that aim to detect attacks evading existing SIEM rules as well as otherwise undetected attack variants. It learns from SIEM rules and historical benign events and can thus estimate which SIEM rule was tried to be evaded. A brief overview of AMIDES is given in [Overview](#overview).
+> ### TL;DR
+>
+> AMIDES extends conventional rule matching of SIEM systems by machine learning components that aim to detect
+> attacks evading existing SIEM rules as well as otherwise undetected attack variants. It learns from SIEM rules
+> and historical benign events and can thus estimate which SIEM rule was tried to be evaded.
+>
+> To run AMIDES and all the experiments from its [paper](#documentation), execute the following commands as a *__non-root user__* on a *__Linux machine__* with `docker` installed:
+>
+>```bash
+> git clone https://github.com/fkie-cad/amides.git
+> cd amides
+> ./build_image.sh
+> ./run_experiments.sh
+> cd amides/plots
+>```
+
+This repository contains the source code, and initial training and validation data which enables to train and validate models for AMIDES. The `amides` Python package contains additional modules and scripts that help to evaluate the model's classification performance and create meaningful visualizations that help users to assess the evaluation results.
+
+For operational use, AMIDES is integrated into [Logprep](https://logprep.readthedocs.io/en/latest/user_manual/configuration/processor.html#amides), a pipeline-based log message preprocessor also written in Python. The `amides` package also contains additional scripts that help to prepare models for the operational use with Logprep. For more information on how to prepare AMIDES models for Logprep, please read [here](#preparing-models-for-logprep).
-This repository contains the source code of the `amides` Python package. The package contains the modules and scripts that enable to train and validate models for AMIDES, evaluate the model's classification performance, and create meaningful visualizations that help users to assess the evaluation results. Additionally, the repository contains initial training and validation data that enables to build and evaluate models for AMIDES.
+## Overview
-For operational use, AMIDES is integrated into [Logprep](https://logprep.readthedocs.io/en/latest/user_manual/configuration/processor.html#amides), a pipeline-based log message preprocessor also written in Python. The package also contains additional scripts that help to prepare models for the operational use with Logprep. For more information on how to prepare AMIDES models for Logprep, please read [here](#preparing-models-for-logprep).
+Core of the Adaptive Misuse Detection System (AMIDES) are the misuse classification and rule attribution components. Both components employ machine learning models. While the misuse classification component employs a single binary classifier, the rule attribution component makes use of multiple binary classifiers that work as a multi-classifier.
-## Overview
+During training, AMIDES' machine learning models for both the misuse classification and rule attribution components are trained using a set of SIEM detection rules and historical benign events taken from an organization's corporate network.
![amides_architecture](./docs/amides.png)
-AMIDES is trained using a set of SIEM detection rules and historical benign events taken from an organization's corporate network.
-During operation, incoming events are passed to the rule matching component and the feature extraction component, which transforms the events into feature vectors. The features required for vectorization have been learned during the training phase. The feature vectors are then passed to the misuse classification component, which classifies events as malicious or benign. In case of a malicious result, the feature vector is passed to the rule attribution component, which generates a ranked list of SIEM rules potentially evaded by the event. In the final step, potential alerts of the rule matching and both machine learning components are merged into a single alert by the alert generation component.
+During operation, incoming events are passed to the rule matching component and the feature extraction component, which transforms the events into feature vectors. The features used for vectorization have been learned during the training phase. The feature vectors are then passed to the misuse classification component, which classifies events as malicious or benign. In case of a malicious result, the feature vector is passed to the rule attribution component, which generates a ranked list of SIEM rules potentially evaded by the event. In the final step, potential alerts of the rule matching and both machine learning components are merged into a single alert by the alert generation component.
## System Requirements
-AMIDES was developed and tested on Linux using Python 3.10. Before attempting to use `amides`, make sure you have
+AMIDES was developed and tested on Linux using Python 3.10. It can be run by either installing it (onto your local system/locally) or using the provided docker quickstart environment. Before attempting to install and run `amides`, make sure you have
-- Physical or virtual host with a Linux-based OS
+- A physical or virtual host with a Linux-based OS
- A minimum of 8 GB of RAM
- At least 2 GB of HDD space
-- Python 3.10 (or newer)
-- jq
-The repository contains a `Dockerfile` that creates a quickstart environment for the `amides` package. For testing purposes, we highly recommend to use the quickstart environment. Building and using the environment has been tested with `docker 20.10`.
+### Docker Quickstart Environment
+
+The repository contains a `Dockerfile` that creates a quickstart environment for AMIDES. The created docker image comes with the `amides` package and all its requirements installed. Building and using the environment has been tested with `docker 20.10`. For testing purposes, we highly recommend to use the quickstart environment.
+
+### Local Installation
+
+To directly execute AMIDES on your machine, the `amides` package can be installed either system-wide or into a virtual environment. As AMIDES was developed and tested on Python 3.10, we encourage to use Python greater than or equal to version 3.10. To execute the [experiments](#running-experiments) using your local installation, the command-line JSON processor[`jq`](https://jqlang.github.io/jq/) is also required.
## Accessing Code and Initial Data
@@ -90,7 +111,7 @@ After the environment has been created, activate it by executing
source /bin/activate
```
-To install the `amides` package and all its dependencies, change into the `amides` directory and execute
+To install the `amides` package and all its dependencies, change into the `amides/amides` directory and execute
```bash
pip install -r requirements.txt
@@ -199,7 +220,7 @@ After parameters have been established and the model has been fit, an additional
By executing
```bash
-./bin/train.py --benign-samples "../data/socbed/process_creation/train" --events-dir "../data/sigma/events/windows/process_creation" --rules-dir "../data/sigma/rules/windows/process_creation" --type "misuse" --malicious-samples-type "rule_filters" --search-params --cv 5 --mcc-scaling --mcc-threshold 0.5 --result-name "misuse_model" --out-dir "models/process_creation"
+./bin/train.py --benign-samples "../data/socbed/process_creation/train" --events-dir "../data/sigma/events/windows/process_creation" --rules-dir "../data/sigma/rules/windows/process_creation" --model-type "misuse" --malicious-samples-type "rule_filters" --search-params --cv 5 --mcc-scaling --mcc-threshold 0.5 --result-name "misuse_model" --out-dir "models/process_creation"
```
a misuse classification models is trained using the benign command lines in `../data/socbed/process_creation/train` and the SIEM rule filters in `./data/sigma/events/windows/process_creation`.
@@ -245,7 +266,7 @@ trains and optimizes a misuse classification model using 10% of the evasions as
Tainted share and tainted seed values are held by `TrainingResult` objects. When the model is validated, `validate.py` takes the tainted seed and share values to remove the evasions already used for training. Evaluation of tainted training models is performed by `eval_mcc_scaling.py` in the same way as other validation results.
-Visualising precision and recall of the `EvaluationResult` objects of multiple tainted training results can be done with the `plot_multi_tainted.py` script. An optional base result without any tainting can be tainted using the `--base-result` flag
+Visualizing precision and recall of the `EvaluationResult` objects of multiple tainted training results can be done with the `plot_multi_tainted.py` script. An optional base result without any tainting can be tainted using the `--base-result` flag
```bash
./bin/plot_multi_tainted.py --base-result "models/process_creation/valid_rslt_misuse_model.zip" --low-tainted "models/process_creation/tainted/10/eval_rslt_misuse_model_tainted.zip" --out-dir "plots"
@@ -258,7 +279,7 @@ Rule attribution models are also generated using `train.py`. Creating a rule att
To build a rule attribution model, the script is started with the `--mode=attribution` option. The process of training rule attribution models can be parallelized. `train.py` supports the `--num-subprocesses` option to specify the number of sub-processes used for training the single rule models. To create a rule attribution model of the benign command lines and the SIEM rule data in `data/`, execute
```bash
-./bin/train.py --benign-samples "../data/socbed/process_creation/train" --events-dir "../data/sigma/events/windows/process_creation" --rules-dir "../data/sigma/rules/windows/process_creation" --type "attribution" --malicious-samples-type "rule_filters" --search-params --search-method "GridSearch" --mcc-scaling --mcc-threshold 0.5 --result-name "attr_model" --out-dir "models/process_creation"
+./bin/train.py --benign-samples "../data/socbed/process_creation/train" --events-dir "../data/sigma/events/windows/process_creation" --rules-dir "../data/sigma/rules/windows/process_creation" --model-type "attribution" --malicious-samples-type "rule_filters" --search-params --mcc-scaling --mcc-threshold 0.5 --result-name "attr_model" --out-dir "models/process_creation"
```
The rule models are gathered by a `MultiTrainingResult` object, where each entry is a `TrainingResult` object itself.
@@ -277,7 +298,7 @@ The mapping can be provided as .json file by the `--rules-evasions` flag. In thi
Alternatively, the mapping is automatically built from the evasion and rule data specified by `events_dir` and `rules_dir` by executing
```bash
-./bin/eval_attr.py --multi-result "models/process_creation/multi_train_rslt_attr_model.zip" --events-dir ../data/sigma/events/windows/process_creation --rules-dir "../data/sigma/rules/windows"
+./bin/eval_attr.py --multi-result "models/process_creation/multi_train_rslt_attr_model.zip" --events-dir ../data/sigma/events/windows/process_creation --rules-dir "../data/sigma/rules/windows/process_creation"
```
Results of the rule attribution evaluation are encapsulated in `RuleAttributionEvaluationResult` instances, which are also pickled.
@@ -300,10 +321,12 @@ Models for the operational use of AMIDES' misuse classification and rule attribu
## Documentation
-The corresponding research paper describes AMIDES in more detail:
+The corresponding research paper describes AMIDES and its evaluation results in more detail:
R. Uetz, M. Herzog, L. Hackländer, S. Schwarz, and M. Henze, "You Cannot Escape Me: Detecting Evasions of SIEM Rules in Enterprise Networks,"
-in *Proceedings of the 33rd USENIX Security Symposium (USENIX Security)*, 2024. [[Conference Website](https://www.usenix.org/conference/usenixsecurity24/presentation/uetz)] [[Prepublication PDF](https://www.usenix.org/system/files/sec23winter-prepub-112-uetz.pdf)]
+in *Proceedings of the 33rd USENIX Security Symposium (USENIX Security)*, 2024. [[arXiv](https://arxiv.org/pdf/2311.10197)]
+
+Artifacts play a crucial role when it comes to availability and reproducibility of results presented in scientific papers. The code, data, and experiments described in this repository were submitted to the official USENIX Security Symposium's Artifact Evaluation (AE). The AE committee awarded the submission with all of the available badges (i.e. "Artifacts Available", "Artifacts Functional", and "Results Reproduced"). More information on the artifact submission can be found in the "Artifact Appendix" of the publication.
## License
diff --git a/amides/amides/data.py b/amides/amides/data.py
index 137a43e..6783ca0 100644
--- a/amides/amides/data.py
+++ b/amides/amides/data.py
@@ -1,5 +1,6 @@
"""This module provides classes and functions to hold and prepare datasets for the training and validation process.
"""
+
from abc import ABC, abstractmethod
import numpy as np
@@ -39,20 +40,15 @@ def __init__(self, samples, labels, label_names=None, feature_info=None):
ValueError
If size of data and labels are not equal.
"""
- if not (
- isinstance(samples, np.ndarray) or isinstance(samples, sparse.csr_matrix)
- ):
- raise TypeError(
- "samples is not of the required type np.ndarray or sparse.csr_matrix"
- )
+ if not (isinstance(samples, np.ndarray) or isinstance(samples, sparse.csr_matrix)):
+ raise TypeError("samples is not of the required type np.ndarray or sparse.csr_matrix")
if not isinstance(labels, np.ndarray):
raise TypeError("'labels' is not of the required type 'np.ndarray'")
if samples.shape[0] != labels.shape[0]:
raise ValueError(
- "Number of data points in samples and number of labels"
- " should be equal"
+ "Number of data points in samples and number of labels" " should be equal"
)
self._samples = samples
@@ -75,9 +71,7 @@ def samples(self):
@samples.setter
def samples(self, samples):
- if not (
- isinstance(samples, np.ndarray) or isinstance(samples, sparse.csr_matrix)
- ):
+ if not (isinstance(samples, np.ndarray) or isinstance(samples, sparse.csr_matrix)):
raise TypeError("data is not of the required type np.ndarray or csr_matrix")
self._samples = samples
@@ -93,9 +87,7 @@ def labels(self, labels):
raise TypeError("labels is not of the required type np.ndarray")
if labels.shape[0] != self._samples.shape[0]:
- raise ValueError(
- "Number of labels and number of datapoints should be equal"
- )
+ raise ValueError("Number of labels and number of datapoints should be equal")
self._labels = labels
@@ -163,14 +155,10 @@ def stack_horizontally(self, bunch):
samples = bunch.samples
if self._samples.shape[0] != samples.shape[0]:
- raise ValueError(
- "Number of provided data points does not match" " number of samples"
- )
+ raise ValueError("Number of provided data points does not match" " number of samples")
if self._samples.ndim == 1:
- self._samples = np.reshape(
- self._samples, newshape=(self._samples.shape[0], 1)
- )
+ self._samples = np.reshape(self._samples, newshape=(self._samples.shape[0], 1))
if samples.ndim == 1:
samples = np.reshape(samples, newshape=(samples.shape[0], 1))
@@ -281,18 +269,12 @@ def create_info_dict(self):
return info
- def _create_split_bunches(
- self, positive_sample_indices_splits, negative_sample_indices_splits
- ):
+ def _create_split_bunches(self, positive_sample_indices_splits, negative_sample_indices_splits):
split_bunches = []
- indices_splits = zip(
- positive_sample_indices_splits, negative_sample_indices_splits
- )
+ indices_splits = zip(positive_sample_indices_splits, negative_sample_indices_splits)
for positive_sample_split, negative_sample_split in indices_splits:
- indices_split = np.concatenate(
- [positive_sample_split, negative_sample_split]
- )
+ indices_split = np.concatenate([positive_sample_split, negative_sample_split])
current_sample_split = np.take(self._samples, indices_split, axis=0)
current_labels_split = np.take(self._labels, indices_split, axis=0)
@@ -306,15 +288,11 @@ def _create_split_bunches(
def _create_random_split(self, elements, num_splits):
try:
- random_elements = np.random.choice(
- elements, size=len(elements), replace=False
- )
+ random_elements = np.random.choice(elements, size=len(elements), replace=False)
splits = np.array_split(random_elements, num_splits, axis=0)
return splits
except ValueError:
- raise DataError(
- f"Could not split elements into {num_splits} parts"
- ) from ValueError
+ raise DataError(f"Could not split elements into {num_splits} parts") from ValueError
def _gather_class_info(self):
num_positive_samples = np.count_nonzero(self._labels == 1)
@@ -371,10 +349,7 @@ class are labeled with '0' per default, labels of the second class are labeled
if not isinstance(elements_class_b, list):
raise TypeError("elements_class_b is not of the required type list")
- if not (
- isinstance(class_labels, tuple)
- and list(map(type, class_labels)) == [int, int]
- ):
+ if not (isinstance(class_labels, tuple) and list(map(type, class_labels)) == [int, int]):
raise TypeError("class_labels is not of the required type Tuple[int, int]")
labels, samples = [], []
@@ -506,9 +481,7 @@ def file_name(self):
File name used when pickling objects.
"""
- file_name = (
- self.name if self.name.startswith("tt_split") else f"tt_split_{self.name}"
- )
+ file_name = self.name if self.name.startswith("tt_split") else f"tt_split_{self.name}"
return file_name
@@ -535,12 +508,8 @@ def stack_horizontally(self, data_split):
def create_info_dict(self):
info = {
- "train_data": self._data["train"].create_info_dict()
- if self._data["train"]
- else None,
- "test_data": self._data["test"].create_info_dict()
- if self._data["test"]
- else None,
+ "train_data": self._data["train"].create_info_dict() if self._data["train"] else None,
+ "test_data": self._data["test"].create_info_dict() if self._data["test"] else None,
"name": self.name,
}
@@ -599,11 +568,7 @@ def file_name(self):
File name used when pickling objects.
"""
- file_name = (
- self.name
- if self.name.startswith("ttv_split")
- else f"ttv_split_{ self.name}"
- )
+ file_name = self.name if self.name.startswith("ttv_split") else f"ttv_split_{ self.name}"
return file_name
@@ -631,15 +596,9 @@ def stack_horizontally(self, data_split):
def create_info_dict(self):
info = {
- "train_data": self._data["train"].create_info_dict()
- if self._data["train"]
- else None,
- "test_data": self._data["test"].create_info_dict()
- if self._data["test"]
- else None,
- "valid_data": self._data["valid"].create_info_dict()
- if self._data["valid"]
- else None,
+ "train_data": self._data["train"].create_info_dict() if self._data["train"] else None,
+ "test_data": self._data["test"].create_info_dict() if self._data["test"] else None,
+ "valid_data": self._data["valid"].create_info_dict() if self._data["valid"] else None,
"name": self.name,
}
@@ -694,9 +653,7 @@ def __init__(
self._feature_extractors = feature_extractors if feature_extractors else []
self._name = name
self._timestamp = (
- timestamp
- if timestamp is not None
- else get_current_timestamp("%Y%m%d_%H%M%S")
+ timestamp if timestamp is not None else get_current_timestamp("%Y%m%d_%H%M%S")
)
@property
@@ -787,11 +744,7 @@ def file_name(self):
"""
- file_name = (
- self.name
- if self.name.startswith("train_rslt")
- else f"train_rslt_{self._name}"
- )
+ file_name = self.name if self.name.startswith("train_rslt") else f"train_rslt_{self._name}"
if self._timestamp:
file_name = f"{file_name}_{self.timestamp}"
@@ -813,11 +766,11 @@ def create_info_dict(self):
"tainted_share": self._tainted_share,
"tainted_seed": self._tainted_seed,
"scaler": self._create_scaler_info() if self._scaler else None,
- "feature_extractors": [
- extractor.__class__.__name__ for extractor in self._feature_extractors
- ]
- if self._feature_extractors
- else None,
+ "feature_extractors": (
+ [extractor.__class__.__name__ for extractor in self._feature_extractors]
+ if self._feature_extractors
+ else None
+ ),
"name": self.name,
"timestamp": self._timestamp,
}
@@ -893,11 +846,7 @@ def predict(self):
return self._predict
def file_name(self):
- file_name = (
- self.name
- if self.name.startswith("valid_rslt")
- else f"valid_rslt_{self.name}"
- )
+ file_name = self.name if self.name.startswith("valid_rslt") else f"valid_rslt_{self.name}"
if self._timestamp:
file_name = f"{file_name}_{self._timestamp}"
@@ -924,9 +873,7 @@ def __init__(self, name=None, timestamp=None, benign_training_data=None):
"""
self._name = name
self._timestamp = (
- timestamp
- if timestamp is not None
- else get_current_timestamp("%Y%m%d_%H%M%S")
+ timestamp if timestamp is not None else get_current_timestamp("%Y%m%d_%H%M%S")
)
self._results = {}
diff --git a/amides/bin/train.py b/amides/bin/train.py
index add94cb..c72e354 100755
--- a/amides/bin/train.py
+++ b/amides/bin/train.py
@@ -77,7 +77,7 @@
output_dir = None
model_type = None
-malicious_samples_type = "rule_filter"
+malicious_samples_type = "rule_filters"
vectorization = "tfidf"
tokenization = "comma_separation"
@@ -106,6 +106,7 @@
tainted_sample_seedings = []
num_subprocesses = 1
+num_jobs = 1
num_iterations = 1
save_data = True
@@ -159,9 +160,7 @@ def load_model_params():
def prepare_multi_train_result(train_results, iteration):
- multi_train_result = MultiTrainingResult(
- name=f"{result_name}_{iteration}", timestamp=""
- )
+ multi_train_result = MultiTrainingResult(name=f"{result_name}_{iteration}", timestamp="")
for result in train_results.values():
multi_train_result.add_result(result)
@@ -214,17 +213,11 @@ def create_vectorizer():
tokenizer = TokenizerFactory.create(tokenization)
if vectorization == "count":
- return CountVectorizer(
- tokenizer=tokenizer, analyzer=ngram_mode, ngram_range=ngram_range
- )
+ return CountVectorizer(tokenizer=tokenizer, analyzer=ngram_mode, ngram_range=ngram_range)
elif vectorization == "tfidf":
- return TfidfVectorizer(
- tokenizer=tokenizer, analyzer=ngram_mode, ngram_range=ngram_range
- )
+ return TfidfVectorizer(tokenizer=tokenizer, analyzer=ngram_mode, ngram_range=ngram_range)
elif vectorization == "hashing":
- return HashingVectorizer(
- tokenizer=tokenizer, analyzer=ngram_mode, ngram_range=ngram_range
- )
+ return HashingVectorizer(tokenizer=tokenizer, analyzer=ngram_mode, ngram_range=ngram_range)
elif vectorization == "binary_count":
return CountVectorizer(
tokenizer=tokenizer,
@@ -311,9 +304,7 @@ def create_tainted_sample_list(rule_set_data: RuleSetDataset, seed: int):
return []
-def train_samples(
- benign_samples: list, tainted_samples: list, malicious_samples: list
-) -> dict:
+def train_samples(benign_samples: list, tainted_samples: list, malicious_samples: list) -> dict:
for sample in benign_samples:
yield sample
@@ -398,9 +389,7 @@ def _train_models(dataset_queue: multiprocessing.Queue, train_results: dict):
_logger.info("Training model for %s", rule_dataset.name)
try:
- train_data, feature_extractor = prepare_training_data(
- rule_dataset, taint_seed
- )
+ train_data, feature_extractor = prepare_training_data(rule_dataset, taint_seed)
train_result = train_model(train_data, feature_extractor, taint_seed)
except (RuleDatasetError, ValueError) as err:
_logger.error(err)
@@ -411,9 +400,7 @@ def _train_models(dataset_queue: multiprocessing.Queue, train_results: dict):
@execution_time
-def create_attribution_model(
- rule_set_data: RuleSetDataset, iteration: int
-) -> MultiTrainingResult:
+def create_attribution_model(rule_set_data: RuleSetDataset, iteration: int) -> MultiTrainingResult:
manager = multiprocessing.Manager()
train_results = manager.dict()
dataset_queue = multiprocessing.Queue()
@@ -422,9 +409,7 @@ def create_attribution_model(
for _ in range(num_subprocesses):
workers.append(
- multiprocessing.Process(
- target=_train_models, args=(dataset_queue, train_results)
- )
+ multiprocessing.Process(target=_train_models, args=(dataset_queue, train_results))
)
workers[-1].start()
@@ -443,9 +428,7 @@ def create_attribution_model(
@execution_time
-def create_misuse_model(
- rule_set_data: RuleSetDataset, iteration: int
-) -> TrainingResult:
+def create_misuse_model(rule_set_data: RuleSetDataset, iteration: int) -> TrainingResult:
_logger.info("Creating misuse model for %s", rule_set_data)
taint_seed = tainted_sample_seedings[iteration]
train_data, feature_extractor = prepare_training_data(rule_set_data, taint_seed)
@@ -572,9 +555,7 @@ def parse_args_and_options(parser: argparse.ArgumentParser):
output_dir = args.out_dir
else:
output_dir = os.path.join(os.getcwd(), "models")
- _logger.warning(
- "No output dir for results data specified. Using %s", output_dir
- )
+ _logger.warning("No output dir for results data specified. Using %s", output_dir)
init_dumper()
@@ -643,7 +624,7 @@ def main():
"--malicious-samples-type",
type=str,
action="store",
- choices=["rule_filters, matches"],
+ choices=["rule_filters", "matches"],
help="Specifies the type of malicious samples used for training",
)
parser.add_argument(
@@ -762,9 +743,7 @@ def main():
action="store",
help="Specifies the result files base name",
)
- parser.add_argument(
- "--config", type=str, action="store", help="Path to config file."
- )
+ parser.add_argument("--config", type=str, action="store", help="Path to config file.")
parse_args_and_options(parser)
create_model()
diff --git a/amides/tests/unit/test_events.py b/amides/tests/unit/test_events.py
index 085225c..1e0c299 100644
--- a/amides/tests/unit/test_events.py
+++ b/amides/tests/unit/test_events.py
@@ -31,17 +31,16 @@ def sigma_path():
class TestEvents:
def test_init(self):
- assert Events("process_creation", EventType.PROCESS_CREATION)
- assert Events("powershell", EventType.POWERSHELL)
- assert Events("registry", EventType.REGISTRY)
- assert Events("proxy_web", EventType.PROXY_WEB)
+ assert Events(EventType.PROCESS_CREATION, "process_creation")
+ assert Events(EventType.POWERSHELL, "powershell")
+ assert Events(EventType.REGISTRY, "registry")
+ assert Events(EventType.PROXY_WEB, "proxy_web")
event_paths = [
(pc_events_json_path(), 20),
(pc_events_jsonl_path(), 20),
(powershell_events_jsonl_path(), 30),
]
-
@pytest.mark.parametrize("events_path,num_events", event_paths)
def test_load_from_dir(self, events_path, num_events):
events = Events(EventType.PROCESS_CREATION)
diff --git a/run_experiments.sh b/run_experiments.sh
index 6c3ae63..39af824 100755
--- a/run_experiments.sh
+++ b/run_experiments.sh
@@ -8,7 +8,7 @@ mkdir -p $PWD/amides/models
mkdir -p $PWD/amides/plots
echo "########## Starting AMIDES experiments container '$AMIDES_EXPERIMENTS_CONTAINER' ... ##########"
-docker run --rm --name $AMIDES_EXPERIMENTS_CONTAINER --interactive --tty --user docker-user --mount type=bind,source=$PWD/amides/models,target=/home/docker-user/amides/models --mount type=bind,source=$PWD/amides/plots,target=/home/docker-user/amides/plots --mount type=bind,source=$PWD/data,target=/home/docker-user/data $AMIDES_IMAGE ./experiments.sh
+docker run --rm --name $AMIDES_EXPERIMENTS_CONTAINER --interactive --tty --user docker-user --mount type=bind,source="${PWD}/amides/models",target="/home/docker-user/amides/models" --mount type=bind,source="${PWD}/amides/plots",target="/home/docker-user/amides/plots" --mount type=bind,source="${PWD}/data",target="/home/docker-user/data" $AMIDES_IMAGE ./experiments.sh
if [ $? -eq 0 ]; then
echo "########## Successfully executed AMIDES experiments container '$AMIDES_EXPERIMENTS_CONTAINER' ##########"
else
diff --git a/start_env.sh b/start_env.sh
index 69aa845..c48635e 100755
--- a/start_env.sh
+++ b/start_env.sh
@@ -8,7 +8,7 @@ mkdir -p $PWD/amides/models
mkdir -p $PWD/amides/plots
echo "########## Starting AMIDES environment container '$AMIDES_ENV_CONTAINER'... ##########"
-docker run --name $AMIDES_ENV_CONTAINER --interactive --rm --tty --user docker-user --mount type=bind,source=$PWD/amides/models,target=/home/docker-user/amides/models --mount type=bind,source=$PWD/amides/plots,target=/home/docker-user/amides/plots --mount type=bind,source=$PWD/data,target=/home/docker-user/data $AMIDES_IMAGE /bin/bash
+docker run --name $AMIDES_ENV_CONTAINER --interactive --rm --tty --user docker-user --mount type=bind,source="${PWD}/amides/models",target="/home/docker-user/amides/models" --mount type=bind,source="${PWD}/amides/plots",target="/home/docker-user/amides/plots" --mount type=bind,source="${PWD}/data",target="/home/docker-user/data" $AMIDES_IMAGE "/bin/bash"
if [ $? -eq 0 ]; then
echo "########## Successfully executed AMIDES environment container '$AMIDES_ENV_CONTAINER' ##########"
else