140 lines
3.8 KiB
Python
140 lines
3.8 KiB
Python
"""Tests for auto-patcher."""
|
|
import pytest
|
|
import os
|
|
import stat
|
|
from ayn_antivirus.remediation.patcher import AutoPatcher, RemediationAction
|
|
|
|
|
|
def test_patcher_init():
|
|
p = AutoPatcher(dry_run=True)
|
|
assert p.dry_run is True
|
|
assert p.actions == []
|
|
|
|
|
|
def test_patcher_init_live():
|
|
p = AutoPatcher(dry_run=False)
|
|
assert p.dry_run is False
|
|
|
|
|
|
def test_fix_permissions_dry_run(tmp_path):
|
|
f = tmp_path / "test.sh"
|
|
f.write_text("#!/bin/bash")
|
|
f.chmod(0o4755) # SUID
|
|
p = AutoPatcher(dry_run=True)
|
|
action = p.fix_permissions(str(f))
|
|
assert action is not None
|
|
assert action.success is True
|
|
assert action.dry_run is True
|
|
# In dry run, file should still have SUID
|
|
assert f.stat().st_mode & stat.S_ISUID
|
|
|
|
|
|
def test_fix_permissions_real(tmp_path):
|
|
f = tmp_path / "test.sh"
|
|
f.write_text("#!/bin/bash")
|
|
f.chmod(0o4755) # SUID
|
|
p = AutoPatcher(dry_run=False)
|
|
action = p.fix_permissions(str(f))
|
|
assert action.success is True
|
|
# SUID should be stripped
|
|
assert not (f.stat().st_mode & stat.S_ISUID)
|
|
|
|
|
|
def test_fix_permissions_already_safe(tmp_path):
|
|
f = tmp_path / "safe.txt"
|
|
f.write_text("hello")
|
|
f.chmod(0o644)
|
|
p = AutoPatcher(dry_run=False)
|
|
action = p.fix_permissions(str(f))
|
|
assert action.success is True
|
|
assert "already safe" in action.details
|
|
|
|
|
|
def test_fix_permissions_sgid(tmp_path):
|
|
f = tmp_path / "sgid.sh"
|
|
f.write_text("#!/bin/bash")
|
|
f.chmod(0o2755) # SGID
|
|
p = AutoPatcher(dry_run=False)
|
|
action = p.fix_permissions(str(f))
|
|
assert action.success is True
|
|
assert not (f.stat().st_mode & stat.S_ISGID)
|
|
|
|
|
|
def test_fix_permissions_world_writable(tmp_path):
|
|
f = tmp_path / "ww.txt"
|
|
f.write_text("data")
|
|
f.chmod(0o777) # World-writable
|
|
p = AutoPatcher(dry_run=False)
|
|
action = p.fix_permissions(str(f))
|
|
assert action.success is True
|
|
assert not (f.stat().st_mode & stat.S_IWOTH)
|
|
|
|
|
|
def test_block_domain_dry_run():
|
|
p = AutoPatcher(dry_run=True)
|
|
action = p.block_domain("evil.example.com")
|
|
assert action is not None
|
|
assert action.success is True
|
|
assert action.dry_run is True
|
|
assert "evil.example.com" in action.target
|
|
|
|
|
|
def test_block_ip_dry_run():
|
|
p = AutoPatcher(dry_run=True)
|
|
action = p.block_ip("1.2.3.4")
|
|
assert action.success is True
|
|
assert action.dry_run is True
|
|
assert "1.2.3.4" in action.target
|
|
|
|
|
|
def test_remediate_threat_dry_run(tmp_path):
|
|
# Create a dummy file
|
|
f = tmp_path / "malware.bin"
|
|
f.write_text("evil_payload")
|
|
f.chmod(0o4755)
|
|
|
|
p = AutoPatcher(dry_run=True)
|
|
threat = {
|
|
"path": str(f),
|
|
"threat_name": "Test.Malware",
|
|
"threat_type": "MALWARE",
|
|
"severity": "HIGH",
|
|
}
|
|
actions = p.remediate_threat(threat)
|
|
assert isinstance(actions, list)
|
|
assert len(actions) >= 1
|
|
# Should have at least a fix_permissions action
|
|
action_names = [a.action for a in actions]
|
|
assert "fix_permissions" in action_names
|
|
|
|
|
|
def test_remediate_threat_miner_with_domain():
|
|
p = AutoPatcher(dry_run=True)
|
|
threat = {
|
|
"threat_type": "MINER",
|
|
"domain": "pool.evil.com",
|
|
"ip": "1.2.3.4",
|
|
}
|
|
actions = p.remediate_threat(threat)
|
|
action_names = [a.action for a in actions]
|
|
assert "block_domain" in action_names
|
|
assert "block_ip" in action_names
|
|
|
|
|
|
def test_remediation_action_dataclass():
|
|
a = RemediationAction(
|
|
action="test_action", target="/tmp/test", details="testing",
|
|
success=True, dry_run=True,
|
|
)
|
|
assert a.action == "test_action"
|
|
assert a.target == "/tmp/test"
|
|
assert a.success is True
|
|
assert a.dry_run is True
|
|
|
|
|
|
def test_fix_ld_preload_missing():
|
|
"""ld.so.preload doesn't exist — should succeed gracefully."""
|
|
p = AutoPatcher(dry_run=True)
|
|
action = p.fix_ld_preload()
|
|
assert action.success is True
|