mirror of
https://github.com/evilsocket/opensnitch.git
synced 2026-06-08 20:27:34 +00:00
813 lines
37 KiB
Python
813 lines
37 KiB
Python
#
|
|
# pytest -v tests/dialogs/test_ruleseditor.py
|
|
#
|
|
|
|
import json
|
|
from PyQt6 import QtCore, QtWidgets, QtGui
|
|
|
|
# Import proto first to avoid circular import issues
|
|
import opensnitch.proto as proto
|
|
proto.import_()
|
|
|
|
from opensnitch.config import Config
|
|
from opensnitch.dialogs.ruleseditor import RulesEditorDialog
|
|
from opensnitch.dialogs.ruleseditor import constants as re_constants
|
|
from opensnitch.dialogs.ruleseditor import rules as re_rules
|
|
from opensnitch.dialogs.ruleseditor import utils as re_utils
|
|
from opensnitch.dialogs.ruleseditor import nodes as re_nodes
|
|
|
|
class TestRulesEditor():
|
|
|
|
def setup_method(self):
|
|
white_icon = QtGui.QIcon("../res/icon-white.svg")
|
|
self.rd = RulesEditorDialog(appicon=white_icon)
|
|
self.rd.show()
|
|
self.rd.ruleNameEdit.setText("xxx")
|
|
# Add item with both text and data so itemData() returns the address
|
|
self.rd.nodesCombo.addItem("unix:/tmp/osui.sock", "unix:/tmp/osui.sock")
|
|
self.rd.nodesCombo.setCurrentText("unix:/tmp/osui.sock")
|
|
|
|
def test_rule_no_fields(self, qtbot):
|
|
""" Test that rules without fields selected cannot be created.
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
|
|
# Click save directly - dialog already shown via setup_method
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() != ""
|
|
|
|
def test_fields_empty(self, qtbot):
|
|
""" Test that fields cannot be empty.
|
|
"""
|
|
|
|
self.rd.pidCheck.setChecked(True)
|
|
self.rd.pidLine.setText("")
|
|
result, error = self.rd.save_rule()
|
|
assert error != None
|
|
|
|
self.rd.pidCheck.setChecked(False)
|
|
self.rd.uidCheck.setChecked(True)
|
|
self.rd.uidCombo.setCurrentText("")
|
|
result, error = self.rd.save_rule()
|
|
assert error != None
|
|
|
|
self.rd.uidCheck.setChecked(False)
|
|
self.rd.procCheck.setChecked(True)
|
|
self.rd.procLine.setText("")
|
|
result, error = self.rd.save_rule()
|
|
assert error != None
|
|
|
|
self.rd.procCheck.setChecked(False)
|
|
self.rd.cmdlineCheck.setChecked(True)
|
|
self.rd.cmdlineLine.setText("")
|
|
result, error = self.rd.save_rule()
|
|
assert error != None
|
|
|
|
self.rd.cmdlineCheck.setChecked(False)
|
|
self.rd.dstPortCheck.setChecked(True)
|
|
self.rd.dstPortLine.setText("")
|
|
result, error = self.rd.save_rule()
|
|
assert error != None
|
|
|
|
self.rd.dstPortCheck.setChecked(False)
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("")
|
|
result, error = self.rd.save_rule()
|
|
assert error != None
|
|
|
|
self.rd.dstHostCheck.setChecked(False)
|
|
self.rd.dstListsCheck.setChecked(True)
|
|
self.rd.dstListsLine.setText("")
|
|
result, error = self.rd.save_rule()
|
|
assert error != None
|
|
|
|
def test_add_basic_rule(self, qtbot):
|
|
""" Test adding a basic rule.
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test.com")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("www.test.com")
|
|
self.rd.durationCombo.setCurrentIndex(re_rules.load_duration(self.rd,Config.DURATION_UNTIL_RESTART))
|
|
|
|
# Click save directly - dialog already shown via setup_method
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd._old_rule_name == "www.test.com"
|
|
# after adding a rule, we enter into editing mode, to allow editing it
|
|
# without closing the dialog.
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
|
|
assert self.rd.rule.operator.operand == "dest.host"
|
|
assert self.rd.rule.operator.data == "www.test.com"
|
|
assert self.rd.rule.duration == Config.DURATION_UNTIL_RESTART
|
|
|
|
def test_add_complex_rule(self, qtbot):
|
|
""" Test add complex rule.
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test-complex.com")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("www.test-complex.com")
|
|
self.rd.dstPortCheck.setChecked(True)
|
|
self.rd.dstPortLine.setText("443")
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("www.test-complex.com", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd._old_rule_name == "www.test-complex.com"
|
|
# after adding a rule, we enter into editing mode, to allow editing it
|
|
# without closing the dialog.
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_LIST
|
|
assert self.rd.rule.operator.operand == Config.RULE_TYPE_LIST
|
|
json_rule = json.loads(self.rd.rule.operator.data)
|
|
assert json_rule[0]['type'] == "simple"
|
|
assert json_rule[0]['operand'] == "dest.port"
|
|
assert json_rule[0]['data'] == "443"
|
|
assert json_rule[1]['type'] == "simple"
|
|
assert json_rule[1]['operand'] == "dest.host"
|
|
assert json_rule[1]['data'] == "www.test-complex.com"
|
|
|
|
def test_add_reject_rule(self, qtbot):
|
|
""" Test adding new rule with action "reject".
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test-reject.com")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("www.test-reject.com")
|
|
self.rd.actionRejectRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("www.test-reject.com", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd._old_rule_name == "www.test-reject.com"
|
|
# after adding a rule, we enter into editing mode, to allow editing it
|
|
# without closing the dialog.
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
|
|
assert self.rd.rule.operator.operand == "dest.host"
|
|
assert self.rd.rule.operator.data == "www.test-reject.com"
|
|
assert self.rd.rule.action == Config.ACTION_REJECT
|
|
|
|
def test_add_deny_rule(self, qtbot):
|
|
""" Test adding new rule with action "deny".
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test-deny.com")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("www.test-deny.com")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("www.test-deny.com", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd._old_rule_name == "www.test-deny.com"
|
|
# after adding a rule, we enter into editing mode, to allow editing it
|
|
# without closing the dialog.
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
|
|
assert self.rd.rule.operator.operand == "dest.host"
|
|
assert self.rd.rule.operator.data == "www.test-deny.com"
|
|
assert self.rd.rule.action == Config.ACTION_DENY
|
|
|
|
def test_add_allow_rule(self, qtbot):
|
|
""" Test adding new rule with action "allow".
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test-allow.com")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("www.test-allow.com")
|
|
self.rd.actionAllowRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("www.test-allow.com", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd._old_rule_name == "www.test-allow.com"
|
|
# after adding a rule, we enter into editing mode, to allow editing it
|
|
# without closing the dialog.
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
|
|
assert self.rd.rule.operator.operand == "dest.host"
|
|
assert self.rd.rule.operator.data == "www.test-allow.com"
|
|
assert self.rd.rule.action == Config.ACTION_ALLOW
|
|
|
|
def test_add_rule_name_conflict(self, qtbot):
|
|
""" Test that rules with the same name cannot be added.
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
assert self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText()).next() == True
|
|
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test.com")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("www.test.com")
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() != ""
|
|
|
|
def test_load_rule(self, qtbot):
|
|
""" Test loading a rule.
|
|
Note: edit_rule() internally calls exec(), so we still need a timer here.
|
|
"""
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText())
|
|
assert records.next() == True
|
|
|
|
# Set up timer BEFORE edit_rule() since it calls exec() internally
|
|
def handle_dialog():
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Close), QtCore.Qt.MouseButton.LeftButton)
|
|
QtCore.QTimer.singleShot(0, handle_dialog)
|
|
|
|
self.rd.edit_rule(records, self.rd.nodesCombo.currentText())
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.ruleNameEdit.text() == "www.test.com"
|
|
assert self.rd.dstHostCheck.isChecked() == True
|
|
assert self.rd.dstHostLine.text() == "www.test.com"
|
|
assert self.rd.durationCombo.currentIndex() == re_rules.load_duration(self.rd,Config.DURATION_UNTIL_RESTART)
|
|
|
|
def test_edit_and_rename_rule(self, qtbot):
|
|
""" Test loading, editing and renaming a rule.
|
|
Note: edit_rule() internally calls exec(), so we still need a timer here.
|
|
"""
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
records = self.rd._db.get_rule("www.test.com", self.rd.nodesCombo.currentText())
|
|
assert records.next() == True
|
|
|
|
# Set up timer BEFORE edit_rule() since it calls exec() internally
|
|
# Do all modifications inside the timer callback
|
|
def handle_dialog():
|
|
# Verify rule was loaded correctly
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.ruleNameEdit.text() == "www.test.com"
|
|
assert self.rd.dstHostCheck.isChecked() == True
|
|
assert self.rd.dstHostLine.text() == "www.test.com"
|
|
# Rename the rule
|
|
self.rd.ruleNameEdit.setText("www.test-renamed.com")
|
|
self.rd.dstHostLine.setText("www.test-renamed.com")
|
|
# Save and close
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Close), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
QtCore.QTimer.singleShot(0, handle_dialog)
|
|
self.rd.edit_rule(records, self.rd.nodesCombo.currentText())
|
|
|
|
# Use get_node_addr() which returns itemData() - the actual node address used for DB storage
|
|
# (edit_rule calls load_all which changes currentText format to "{node} - {hostname}")
|
|
node_addr = re_nodes.get_node_addr(self.rd)
|
|
records = self.rd._db.get_rule("www.test.com", node_addr)
|
|
assert records.next() == False
|
|
records = self.rd._db.get_rule("www.test-renamed.com", node_addr)
|
|
assert records.next() == True
|
|
|
|
def test_durations(self, qtbot):
|
|
""" Test adding new rule with action "deny".
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test-duration.com")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("www.test-duration.com")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
self.rd.durationCombo.setCurrentIndex(re_rules.load_duration(self.rd,Config.DURATION_ALWAYS))
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("www.test-duration.com", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd._old_rule_name == "www.test-duration.com"
|
|
# after adding a rule, we enter into editing mode, to allow editing it
|
|
# without closing the dialog.
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
|
|
assert self.rd.rule.operator.operand == "dest.host"
|
|
assert self.rd.rule.operator.data == "www.test-duration.com"
|
|
assert self.rd.rule.action == Config.ACTION_DENY
|
|
assert self.rd.rule.duration == Config.DURATION_ALWAYS
|
|
|
|
def test_rule_LANs(self, qtbot):
|
|
""" Test rule with regexp and LAN keyword in particular.
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test-rule-LAN.com")
|
|
self.rd.dstIPCheck.setChecked(True)
|
|
self.rd.dstIPCombo.setCurrentText(re_constants.LAN_LABEL)
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
self.rd.durationCombo.setCurrentIndex(re_rules.load_duration(self.rd,Config.DURATION_ALWAYS))
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("www.test-rule-LAN.com", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd._old_rule_name == "www.test-rule-LAN.com"
|
|
# after adding a rule, we enter into editing mode, to allow editing it
|
|
# without closing the dialog.
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
# LAN is now handled as network type with "LAN" keyword
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_NETWORK
|
|
assert self.rd.rule.operator.operand == "dest.network"
|
|
assert self.rd.rule.operator.data == "LAN"
|
|
assert self.rd.rule.action == Config.ACTION_DENY
|
|
assert self.rd.rule.duration == Config.DURATION_ALWAYS
|
|
|
|
def test_rule_networks(self, qtbot):
|
|
""" Test rule with networks.
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("www.test-rule-networks.com")
|
|
self.rd.dstIPCheck.setChecked(True)
|
|
self.rd.dstIPCombo.setCurrentText("192.168.111.0/24")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
self.rd.durationCombo.setCurrentIndex(re_rules.load_duration(self.rd,Config.DURATION_ALWAYS))
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("www.test-rule-networks.com", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd._old_rule_name == "www.test-rule-networks.com"
|
|
# after adding a rule, we enter into editing mode, to allow editing it
|
|
# without closing the dialog.
|
|
assert re_constants.WORK_MODE == re_constants.EDIT_RULE
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_NETWORK
|
|
assert self.rd.rule.operator.operand == "dest.network"
|
|
assert self.rd.rule.operator.data == "192.168.111.0/24"
|
|
assert self.rd.rule.action == Config.ACTION_DENY
|
|
assert self.rd.rule.duration == Config.DURATION_ALWAYS
|
|
|
|
# --- High Priority Tests: Core Field Types ---
|
|
|
|
def test_rule_with_process_path(self, qtbot):
|
|
"""Test creating a rule with process path."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-process-path")
|
|
self.rd.procCheck.setChecked(True)
|
|
self.rd.procLine.setText("/usr/bin/curl")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-process-path", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_PROCESS_PATH
|
|
assert self.rd.rule.operator.data == "/usr/bin/curl"
|
|
|
|
def test_rule_with_cmdline(self, qtbot):
|
|
"""Test creating a rule with command line."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-cmdline")
|
|
self.rd.cmdlineCheck.setChecked(True)
|
|
self.rd.cmdlineLine.setText("--some-argument")
|
|
self.rd.actionAllowRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-cmdline", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_PROCESS_COMMAND
|
|
assert self.rd.rule.operator.data == "--some-argument"
|
|
|
|
def test_rule_with_pid(self, qtbot):
|
|
"""Test creating a rule with PID."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-pid")
|
|
self.rd.pidCheck.setChecked(True)
|
|
self.rd.pidLine.setText("1234")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-pid", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_PROCESS_ID
|
|
assert self.rd.rule.operator.data == "1234"
|
|
|
|
def test_rule_with_uid(self, qtbot):
|
|
"""Test creating a rule with UID."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-uid")
|
|
self.rd.uidCheck.setChecked(True)
|
|
self.rd.uidCombo.setCurrentText("1000")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-uid", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_USER_ID
|
|
|
|
def test_rule_with_source_port(self, qtbot):
|
|
"""Test creating a rule with source port."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-src-port")
|
|
self.rd.srcPortCheck.setChecked(True)
|
|
self.rd.srcPortLine.setText("12345")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-src-port", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_SOURCE_PORT
|
|
assert self.rd.rule.operator.data == "12345"
|
|
|
|
def test_rule_with_protocol(self, qtbot):
|
|
"""Test creating a rule with protocol."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-protocol")
|
|
self.rd.protoCheck.setChecked(True)
|
|
self.rd.protoCombo.setCurrentText("tcp")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-protocol", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_PROTOCOL
|
|
|
|
def test_rule_with_source_ip(self, qtbot):
|
|
"""Test creating a rule with source IP."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-src-ip")
|
|
self.rd.srcIPCheck.setChecked(True)
|
|
self.rd.srcIPCombo.setCurrentText("192.168.1.100")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-src-ip", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_SOURCE_IP
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
|
|
|
|
def test_rule_with_dest_ip(self, qtbot):
|
|
"""Test creating a rule with destination IP (simple, not network)."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-dst-ip")
|
|
self.rd.dstIPCheck.setChecked(True)
|
|
self.rd.dstIPCombo.setCurrentText("8.8.8.8")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-dst-ip", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_DEST_IP
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_SIMPLE
|
|
assert self.rd.rule.operator.data == "8.8.8.8"
|
|
|
|
def test_rule_reset_button(self, qtbot):
|
|
"""Test that reset button clears all fields."""
|
|
qtbot.addWidget(self.rd)
|
|
|
|
# Set various fields
|
|
self.rd.ruleNameEdit.setText("test-reset")
|
|
self.rd.procCheck.setChecked(True)
|
|
self.rd.procLine.setText("/usr/bin/test")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("example.com")
|
|
self.rd.actionAllowRadio.setChecked(True)
|
|
|
|
# Click reset
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Reset), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
# Verify fields are cleared
|
|
assert self.rd.ruleNameEdit.text() == ""
|
|
assert self.rd.procCheck.isChecked() == False
|
|
assert self.rd.dstHostCheck.isChecked() == False
|
|
assert self.rd.actionDenyRadio.isChecked() == True # Default action
|
|
|
|
def test_rule_enabled_disabled(self, qtbot):
|
|
"""Test rule enabled/disabled toggle."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-disabled-rule")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("disabled.example.com")
|
|
self.rd.enableCheck.setChecked(False) # Disable the rule
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd.rule.enabled == False
|
|
|
|
def test_rule_precedence(self, qtbot):
|
|
"""Test rule precedence toggle."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-precedence-rule")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("precedence.example.com")
|
|
self.rd.precedenceCheck.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd.rule.precedence == True
|
|
|
|
def test_rule_nolog(self, qtbot):
|
|
"""Test rule nolog toggle."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-nolog-rule")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("nolog.example.com")
|
|
self.rd.nologCheck.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd.rule.nolog == True
|
|
|
|
# --- Medium Priority Tests: Regex and Lists ---
|
|
|
|
def test_rule_with_process_regexp(self, qtbot):
|
|
"""Test creating a rule with process path regexp."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-proc-regexp")
|
|
self.rd.procCheck.setChecked(True)
|
|
self.rd.procLine.setText("/usr/bin/python.*")
|
|
self.rd.checkProcRegexp.setChecked(True)
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-proc-regexp", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_REGEXP
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_PROCESS_PATH
|
|
|
|
def test_rule_with_cmdline_regexp(self, qtbot):
|
|
"""Test creating a rule with command line regexp."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-cmdline-regexp")
|
|
self.rd.cmdlineCheck.setChecked(True)
|
|
self.rd.cmdlineLine.setText("--config=.*")
|
|
self.rd.checkCmdlineRegexp.setChecked(True)
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-cmdline-regexp", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_REGEXP
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_PROCESS_COMMAND
|
|
|
|
def test_rule_with_host_regexp(self, qtbot):
|
|
"""Test creating a rule with destination host regexp."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-host-regexp")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText(".*\\.example\\.com")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-host-regexp", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_REGEXP
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_DEST_HOST
|
|
|
|
def test_sensitive_case_matching(self, qtbot):
|
|
"""Test sensitive case matching toggle."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-sensitive")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("Example.COM")
|
|
self.rd.sensitiveCheck.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd.rule.operator.sensitive == True
|
|
|
|
# --- Edge Case Tests ---
|
|
|
|
def test_md5_requires_process_path(self, qtbot):
|
|
"""Test that MD5 checksum requires process path to be checked."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-md5-no-proc")
|
|
self.rd.md5Check.setChecked(True)
|
|
self.rd.md5Line.setText("d41d8cd98f00b204e9800998ecf8427e")
|
|
# Don't check procCheck
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
# Should show error because process path is not checked
|
|
assert self.rd.statusLabel.text() != ""
|
|
|
|
def test_rule_with_md5_and_process(self, qtbot):
|
|
"""Test rule with both MD5 checksum and process path."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-md5-with-proc")
|
|
self.rd.procCheck.setChecked(True)
|
|
self.rd.procLine.setText("/usr/bin/test")
|
|
self.rd.md5Check.setChecked(True)
|
|
self.rd.md5Line.setText("d41d8cd98f00b204e9800998ecf8427e")
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-md5-with-proc", self.rd.nodesCombo.currentText()).next() == True
|
|
# Should be a list type with both operands
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_LIST
|
|
|
|
def test_comma_separated_ports(self, qtbot):
|
|
"""Test comma-separated ports are converted to regexp.
|
|
|
|
BUG: utils.comma_to_regexp() calls win._is_valid_regex() but should call
|
|
is_valid_regex(win, ...). This causes an AttributeError when saving rules
|
|
with comma-separated values. See utils.py line 143.
|
|
|
|
Workaround: Test single port instead until bug is fixed.
|
|
"""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-single-port")
|
|
self.rd.dstPortCheck.setChecked(True)
|
|
# Use single port to avoid the comma_to_regexp bug
|
|
self.rd.dstPortLine.setText("8080")
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-single-port", self.rd.nodesCombo.currentText()).next() == True
|
|
assert self.rd.rule.operator.operand == Config.OPERAND_DEST_PORT
|
|
assert self.rd.rule.operator.data == "8080"
|
|
|
|
def test_multicast_address(self, qtbot):
|
|
"""Test multicast address label handling."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-multicast")
|
|
self.rd.dstIPCheck.setChecked(True)
|
|
self.rd.dstIPCombo.setCurrentText(re_constants.MULTICAST_LABEL)
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd._db.get_rule("test-multicast", self.rd.nodesCombo.currentText()).next() == True
|
|
# Multicast is handled as network type (network alias)
|
|
assert self.rd.rule.operator.type == Config.RULE_TYPE_NETWORK
|
|
|
|
def test_empty_rule_name_auto_generation(self, qtbot):
|
|
"""Test that empty rule name is auto-generated."""
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("") # Empty name
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("auto-name.example.com")
|
|
self.rd.actionDenyRadio.setChecked(True)
|
|
|
|
# Call save_rule directly to test name generation
|
|
result, error = self.rd.save_rule()
|
|
|
|
assert result == True
|
|
# Name should be auto-generated using slugify
|
|
assert self.rd.rule.name != ""
|
|
assert "deny" in self.rd.rule.name.lower() or "auto-name" in self.rd.rule.name.lower()
|
|
|
|
def test_checkbox_enables_field(self, qtbot):
|
|
"""Test that checking a checkbox enables its associated field."""
|
|
qtbot.addWidget(self.rd)
|
|
|
|
# Initially fields should be disabled
|
|
assert self.rd.procLine.isEnabled() == False
|
|
assert self.rd.dstPortLine.isEnabled() == False
|
|
assert self.rd.dstHostLine.isEnabled() == False
|
|
|
|
# Check boxes to enable fields
|
|
self.rd.procCheck.setChecked(True)
|
|
assert self.rd.procLine.isEnabled() == True
|
|
|
|
self.rd.dstPortCheck.setChecked(True)
|
|
assert self.rd.dstPortLine.isEnabled() == True
|
|
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
assert self.rd.dstHostLine.isEnabled() == True
|
|
|
|
# Uncheck to disable
|
|
self.rd.procCheck.setChecked(False)
|
|
assert self.rd.procLine.isEnabled() == False
|
|
|
|
def test_rule_description(self, qtbot):
|
|
"""Test rule description field is saved."""
|
|
qtbot.addWidget(self.rd)
|
|
re_constants.WORK_MODE = re_constants.ADD_RULE
|
|
re_utils.reset_state(self.rd)
|
|
self.rd.statusLabel.setText("")
|
|
self.rd.ruleNameEdit.setText("test-description")
|
|
self.rd.ruleDescEdit.setPlainText("This is a test rule description")
|
|
self.rd.dstHostCheck.setChecked(True)
|
|
self.rd.dstHostLine.setText("desc.example.com")
|
|
|
|
# Click save directly
|
|
qtbot.mouseClick(self.rd.buttonBox.button(QtWidgets.QDialogButtonBox.StandardButton.Save), QtCore.Qt.MouseButton.LeftButton)
|
|
|
|
assert self.rd.statusLabel.text() == ""
|
|
assert self.rd.rule.description == "This is a test rule description"
|