diff --git a/.gitignore b/.gitignore index 3113ce1..b0a557c 100644 --- a/.gitignore +++ b/.gitignore @@ -199,3 +199,6 @@ cython_debug/ local/ test.ipynb test.py + +# claude code +CLAUDE.md diff --git a/config/pipeline_config_default.yaml b/config/pipeline_config_default.yaml index 0475495..4271752 100644 --- a/config/pipeline_config_default.yaml +++ b/config/pipeline_config_default.yaml @@ -68,8 +68,6 @@ detectors: NewValueComboDetector: method_type: new_value_combo_detector auto_config: False - params: - comb_size: 3 events: 1: test: diff --git a/docs/detectors.md b/docs/detectors.md index 5dad456..5e17c0c 100644 --- a/docs/detectors.md +++ b/docs/detectors.md @@ -172,7 +172,7 @@ The `set_configuration()` method queries the tracker results and generates the f def set_configuration(self): variables = {} for event_id, tracker in self.auto_conf_persistency.get_events_data().items(): - stable_vars = tracker.get_variables_by_classification("STABLE") + stable_vars = tracker.get_features_by_classification("STABLE") variables[event_id] = stable_vars config_dict = generate_detector_config( diff --git a/docs/detectors/combo.md b/docs/detectors/combo.md index 0272668..1440167 100644 --- a/docs/detectors/combo.md +++ b/docs/detectors/combo.md @@ -18,7 +18,7 @@ detectors: method_type: new_value_combo_detector auto_config: False params: - comb_size: 3 + max_combo_size: 3 events: 1: test: diff --git a/src/detectmatelibrary/common/_config/_compile.py b/src/detectmatelibrary/common/_config/_compile.py index 5198629..fca1a27 100644 --- a/src/detectmatelibrary/common/_config/_compile.py +++ b/src/detectmatelibrary/common/_config/_compile.py @@ -142,7 +142,7 @@ def generate_detector_config( detector_name: Name of the detector, used as the base instance_id. method_type: Type of detection method (e.g., "new_value_detector"). **additional_params: Additional parameters for the detector's params - dict (e.g., comb_size=3). + dict (e.g., max_combo_size=3). Returns: Dictionary with structure compatible with detector config classes. @@ -162,7 +162,7 @@ def generate_detector_config( variable_selection={1: [("username", "src_ip"), ("var_0", "var_1")]}, detector_name="MyDetector", method_type="new_value_combo_detector", - comb_size=2, + max_combo_size=2, ) """ var_pattern = re.compile(r"^var_(\d+)$") diff --git a/src/detectmatelibrary/detectors/new_value_combo_detector.py b/src/detectmatelibrary/detectors/new_value_combo_detector.py index 0445863..9078dee 100644 --- a/src/detectmatelibrary/detectors/new_value_combo_detector.py +++ b/src/detectmatelibrary/detectors/new_value_combo_detector.py @@ -55,7 +55,10 @@ class NewValueComboDetectorConfig(CoreDetectorConfig): events: EventsConfig | dict[str, Any] = {} global_instances: Dict[str, _EventInstance] = {} - comb_size: int = 2 + + max_combo_size: int = 3 + use_stable_vars: bool = True + use_static_vars: bool = False class NewValueComboDetector(CoreDetector): @@ -162,7 +165,7 @@ def configure(self, input_: ParserSchema) -> None: # type: ignore named_variables=input_["logFormatVariables"], ) - def set_configuration(self, max_combo_size: int = 3) -> None: + def set_configuration(self, max_combo_size: int | None = None) -> None: """Set the detector configuration based on the stability of variable combinations. @@ -172,17 +175,18 @@ def set_configuration(self, max_combo_size: int = 3) -> None: 3. Re-ingest all events to learn the stability of these combos (testing all possible combos right away would explode combinatorially). """ + config = cast(NewValueComboDetectorConfig, self.config) # run WITH auto_conf_persistency variable_combos = {} for event_id, tracker in self.auto_conf_persistency.get_events_data().items(): - stable_vars = tracker.get_variables_by_classification("STABLE") # type: ignore + stable_vars = tracker.get_features_by_classification("STABLE") # type: ignore if len(stable_vars) > 1: variable_combos[event_id] = stable_vars config_dict = generate_detector_config( variable_selection=variable_combos, detector_name=self.name, method_type=self.config.method_type, - comb_size=max_combo_size + max_combo_size=max_combo_size or config.max_combo_size ) # Update the config object from the dictionary instead of replacing it self.config = NewValueComboDetectorConfig.from_dict(config_dict, self.name) @@ -199,15 +203,21 @@ def set_configuration(self, max_combo_size: int = 3) -> None: # rerun to set final config WITH auto_conf_persistency_combos combo_selection = {} for event_id, tracker in self.auto_conf_persistency_combos.get_events_data().items(): - stable_combos = tracker.get_variables_by_classification("STABLE") # type: ignore + stable_combos = [] + if self.config.use_stable_vars: + stable_combos = tracker.get_features_by_classification("STABLE") # type: ignore + static_combos = [] + if self.config.use_static_vars: + static_combos = tracker.get_features_by_classification("STATIC") # type: ignore + combos = stable_combos + static_combos # Keep combos as tuples - each will become a separate config entry - if len(stable_combos) >= 1: - combo_selection[event_id] = stable_combos + if len(combos) > 0: + combo_selection[event_id] = combos config_dict = generate_detector_config( variable_selection=combo_selection, detector_name=self.name, method_type=self.config.method_type, - comb_size=max_combo_size + max_combo_size=max_combo_size or self.config.max_combo_size ) # Update the config object from the dictionary instead of replacing it self.config = NewValueComboDetectorConfig.from_dict(config_dict, self.name) diff --git a/src/detectmatelibrary/detectors/new_value_detector.py b/src/detectmatelibrary/detectors/new_value_detector.py index eab00c6..2c3db66 100644 --- a/src/detectmatelibrary/detectors/new_value_detector.py +++ b/src/detectmatelibrary/detectors/new_value_detector.py @@ -24,6 +24,8 @@ class NewValueDetectorConfig(CoreDetectorConfig): events: EventsConfig | dict[str, Any] = {} global_instances: Dict[str, _EventInstance] = {} + use_stable_vars: bool = True + use_static_vars: bool = True class NewValueDetector(CoreDetector): @@ -118,8 +120,15 @@ def configure(self, input_: ParserSchema) -> None: # type: ignore def set_configuration(self) -> None: variables = {} for event_id, tracker in self.auto_conf_persistency.get_events_data().items(): - stable_vars = tracker.get_variables_by_classification("STABLE") # type: ignore - variables[event_id] = stable_vars + stable = [] + if self.config.use_stable_vars: + stable = tracker.get_features_by_classification("STABLE") # type: ignore + static = [] + if self.config.use_static_vars: + static = tracker.get_features_by_classification("STATIC") # type: ignore + vars_ = stable + static + if len(vars_) > 0: + variables[event_id] = vars_ config_dict = generate_detector_config( variable_selection=variables, detector_name=self.name, diff --git a/src/detectmatelibrary/utils/persistency/event_data_structures/trackers/stability/stability_tracker.py b/src/detectmatelibrary/utils/persistency/event_data_structures/trackers/stability/stability_tracker.py index a37ab3d..f29a78e 100644 --- a/src/detectmatelibrary/utils/persistency/event_data_structures/trackers/stability/stability_tracker.py +++ b/src/detectmatelibrary/utils/persistency/event_data_structures/trackers/stability/stability_tracker.py @@ -73,7 +73,7 @@ class MultiStabilityTracker(MultiTracker): """Tracks multiple features (e.g. variables or variable combos) using individual trackers.""" - def get_variables_by_classification( + def get_features_by_classification( self, classification_type: Literal["INSUFFICIENT_DATA", "STATIC", "RANDOM", "STABLE", "UNSTABLE"] ) -> List[str]: @@ -99,9 +99,9 @@ def __init__(self, converter_function: Callable[[Any], Any] = lambda x: x) -> No converter_function=converter_function, ) - def get_variables_by_classification( + def get_features_by_classification( self, classification_type: Literal["INSUFFICIENT_DATA", "STATIC", "RANDOM", "STABLE", "UNSTABLE"] ) -> List[str]: """Get a list of variable names that are classified as the given type.""" - return self.multi_tracker.get_variables_by_classification(classification_type) + return self.multi_tracker.get_features_by_classification(classification_type) diff --git a/tests/test_detectors/test_new_value_combo_detector.py b/tests/test_detectors/test_new_value_combo_detector.py index af45bd7..d3c123a 100644 --- a/tests/test_detectors/test_new_value_combo_detector.py +++ b/tests/test_detectors/test_new_value_combo_detector.py @@ -9,8 +9,6 @@ from detectmatelibrary.utils.aux import time_test_mode -import pytest - # Set time test mode for consistent timestamps time_test_mode() @@ -21,7 +19,7 @@ "method_type": "new_value_combo_detector", "auto_config": False, "params": { - "comb_size": 4 + "max_combo_size": 4 }, "events": { 1: { @@ -38,7 +36,7 @@ "method_type": "new_value_combo_detector", "auto_config": False, "params": { - "comb_size": 2 + "max_combo_size": 2 }, "events": { 1: { @@ -72,7 +70,7 @@ def test_custom_config_initialization(self): detector = NewValueComboDetector(name="CustomInit", config=config) assert detector.name == "CustomInit" - assert detector.config.comb_size == 4 + assert detector.config.max_combo_size == 4 class TestNewValueComboDetectorTraining: @@ -215,14 +213,14 @@ def test_generate_detector_config_basic(self): variable_selection=variable_selection, detector_name="TestDetector", method_type="new_value_combo_detector", - comb_size=2 + max_combo_size=2 ) assert "detectors" in config_dict assert "TestDetector" in config_dict["detectors"] detector_config = config_dict["detectors"]["TestDetector"] assert detector_config["method_type"] == "new_value_combo_detector" - assert detector_config["params"]["comb_size"] == 2 + assert detector_config["params"]["max_combo_size"] == 2 assert len(detector_config["events"]) == 1 def test_generate_detector_config_multiple_events(self): @@ -291,7 +289,7 @@ def test_set_configuration_updates_config(self): # Verify config was updated assert detector.config.events is not None - assert detector.config.comb_size == 2 + assert detector.config.max_combo_size == 2 def test_configuration_workflow(self): """Test complete configuration workflow like in notebook.""" @@ -362,8 +360,8 @@ def test_set_configuration_with_combo_size(self): # Set configuration with specific combo size detector.set_configuration(max_combo_size=4) - # Verify comb_size was updated - assert detector.config.comb_size == 4 + # Verify max_combo_size was updated + assert detector.config.max_combo_size == 4 def test_configuration_with_no_stable_variables(self): """Test configuration when no stable variables are found.""" @@ -551,14 +549,8 @@ def test_configure_only_selects_stable_event_types(self): "MatcherParser": { "method_type": "matcher_parser", "auto_config": False, - "log_format": "type= msg=audit\\(