"""
Unit tests for the malware analyzer
"""
import pytest

from anchore_engine.analyzers import malware
from anchore_engine.analyzers.malware import CLAMAV_CMD, UNKNOWN

test_sig = "Unix.Trojan.MSShellcode-40"
scanned_file = "/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar"

malware_matches = [
    (
        "/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar!POSIX_TAR:bin/busybox!...!(7)POSIX_TAR:elf_payload1: Unix.Trojan.MSShellcode-40 FOUND",
        ("/elf_payload1", test_sig),
    ),
    (
        "/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar!POSIX_TAR:bin/busybox!...!(7)POSIX_TAR:somedir/somepath/elf_payload1: Unix.Trojan.MSShellcode-40 FOUND",
        ("/somedir/somepath/elf_payload1", test_sig),
    ),
    (
        "/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar: Unix.Trojan.MSShellcode-40 FOUND",
        (UNKNOWN, test_sig),
    ),
]

clamscan_full_output_with_duplicates = """/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar: Unix.Trojan.MSShellcode-40 FOUND
/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar: Unix.Malware.Agent-7153843-0 FOUND
/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar: Unix.Trojan.MSShellcode-40 FOUND
/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar!POSIX_TAR:bin/busybox!POSIX_TAR:etc/ssl/ct_log_list.cnf.dist!POSIX_TAR:etc/ssl/openssl.cnf.dist!...!(7)POSIX_TAR:elf_payload1: Unix.Trojan.MSShellcode-40 FOUND

----------- SCAN SUMMARY -----------
Known viruses: 8923276
Engine version: 0.102.4
Scanned directories: 0
Scanned files: 1
Infected files: 1
Data scanned: 12.28 MB
Data read: 6.07 MB (ratio 2.02:1)
Time: 23.835 sec (0 m 23 s)
"""
clamscan_full_output_no_duplicates = """/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar: Unix.Malware.Agent-7153843-0 FOUND
/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar!POSIX_TAR:bin/busybox!POSIX_TAR:etc/ssl/ct_log_list.cnf.dist!POSIX_TAR:etc/ssl/openssl.cnf.dist!...!(7)POSIX_TAR:elf_payload1: Unix.Trojan.MSShellcode-40 FOUND

----------- SCAN SUMMARY -----------
Known viruses: 8923276
Engine version: 0.102.4
Scanned directories: 0
Scanned files: 1
Infected files: 1
Data scanned: 12.28 MB
Data read: 6.07 MB (ratio 2.02:1)
Time: 23.835 sec (0 m 23 s)
"""
clamscan_output = [
    (
        "/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar",
        clamscan_full_output_with_duplicates,
        [
            {"path": "/elf_payload1", "signature": "Unix.Trojan.MSShellcode-40"},
            {"path": "unknown", "signature": "Unix.Malware.Agent-7153843-0"},
        ],
    ),
    (
        "/analysis_scratch/a5346cac-802a-4cb9-a0c5-756b9b51858b/squashed.tar",
        clamscan_full_output_no_duplicates,
        [
            {"path": "/elf_payload1", "signature": "Unix.Trojan.MSShellcode-40"},
            {"path": "unknown", "signature": "Unix.Malware.Agent-7153843-0"},
        ],
    ),
]


@pytest.mark.parametrize(["line", "expected"], malware_matches)
def test_clamav_lineparser(line, expected):
    parsed = malware.parse_clamscan_output_line(scanned_file, line)
    assert parsed == expected


@pytest.mark.parametrize(["file", "output", "expected"], clamscan_output)
def test_parse_clamscan(file, output, expected):
    parsed = malware.parse_clamscan(file, output)
    assert parsed == expected


def test_freshclam_parser():
    did_update = """ClamAV update process started at Fri Jul 28 07:44:12 2020
    WARNING: Your ClamAV installation is OUTDATED!
    WARNING: Local version: 0.102.3 Recommended version: 0.102.4
    DON'T PANIC! Read https://www.clamav.net/documents/upgrading-clamav
    daily database available for download (remote version: 25889)
    Time: 4.0s, ETA: 0.0s [=============================>] 92.69MiB/92.69MiB        
    Testing database: '/var/lib/clamav/tmp.9460b/clamav-0ec6238a545395818add884614c55bd6.tmp-daily.cvd' ...
    Database test passed.
    daily.cvd updated (version: 25889, sigs: 3716328, f-level: 63, builder: raynman)
    main database available for download (remote version: 59)
    Time: 3.9s, ETA: 0.0s [=============================>] 112.40MiB/112.40MiB       
    Testing database: '/var/lib/clamav/tmp.9460b/clamav-97cbae5075c8db063494a9b2e1c04a44.tmp-main.cvd' ...
    Database test passed.
    main.cvd updated (version: 59, sigs: 4564902, f-level: 60, builder: sigmgr)
    bytecode database available for download (remote version: 331)
    Time: 0.4s, ETA: 0.0s [=============================>] 289.44KiB/289.44KiB     
    Testing database: '/var/lib/clamav/tmp.9460b/clamav-f24193a72f209acc0a9c45b79612e8fb.tmp-bytecode.cvd' ...
    Database test passed.
    bytecode.cvd updated (version: 331, sigs: 94, f-level: 63, builder: anvilleg)"""

    result = malware.parse_refresh_output(did_update)
    assert result is not None
    assert result["daily"] == "25889"
    assert result["main"] == "59"
    assert result["bytecode"] == "331"

    no_update = """ClamAV update process started at Fri Jul 28 08:02:11 2020
    WARNING: Your ClamAV installation is OUTDATED!
    WARNING: Local version: 0.102.3 Recommended version: 0.102.4
    DON'T PANIC! Read https://www.clamav.net/documents/upgrading-clamav
    daily.cvd database is up to date (version: 25889, sigs: 3716328, f-level: 63, builder: raynman)
    main.cvd database is up to date (version: 59, sigs: 4564902, f-level: 60, builder: sigmgr)
    bytecode.cvd database is up to date (version: 331, sigs: 94, f-level: 63, builder: anvilleg)"""

    result = malware.parse_refresh_output(no_update)
    assert result is not None
    assert result["daily"] == "25889"
    assert result["main"] == "59"
    assert result["bytecode"] == "331"

    # Use '.cld' instead of '.cvd' as clamav can set both depending on how update is done
    no_update = """ClamAV update process started at Fri Jul 28 08:02:11 2020
    WARNING: Your ClamAV installation is OUTDATED!
    WARNING: Local version: 0.102.3 Recommended version: 0.102.4
    DON'T PANIC! Read https://www.clamav.net/documents/upgrading-clamav
    daily.cld database is up to date (version: 25889, sigs: 3716328, f-level: 63, builder: raynman)
    main.cvd database is up to date (version: 59, sigs: 4564902, f-level: 60, builder: sigmgr)
    bytecode.cvd database is up to date (version: 331, sigs: 94, f-level: 63, builder: anvilleg)"""

    result = malware.parse_refresh_output(no_update)
    assert result is not None
    assert result["daily"] == "25889"
    assert result["main"] == "59"
    assert result["bytecode"] == "331"


def mock_scan():
    return [{"path": "/root/foobar/badfile", "signature": "Some really bad signature"}]


def mock_update():
    return {"daily": "1", "main": "1", "bytecode": "1"}


def test_output_handler():
    test_config = {"clamav": {"enabled": True}}
    fake_path = "/analysis_scratch/123-123-123-123-123/squashed.tar"

    t = malware.ClamAVRunner(
        config=test_config, squashed_tar_path=fake_path, tempdir="/tmp"
    )
    setattr(t, "scan", mock_scan)
    setattr(t, "refresh_db", mock_update)
    output = t.run()
    print(output)
    assert output.name == "clamav"
    assert len(output.findings) > 0


@pytest.mark.parametrize(
    "config",
    [None, {}, {"clamav": None}, {"clamav": {}}, {"clamav": {"enabled": False}}],
)
def test_disabled(config):
    t = malware.ClamAVRunner(
        config=config, squashed_tar_path="squashed.tar", tempdir="/tmp"
    )
    out = t.run()
    assert out is not None
    assert out.findings is None


replace_append_case = {
    "expected": "clamscan --suppress-ok-results --infected --recursive --allmatch --archive-verbose --tempdir={tempdir} --database={database} --max-filesize=3000m --max-scansize=2000m",
    "cmd": CLAMAV_CMD,
    "args": ["max-filesize=3000m", "max-scansize=2000m"],
}


@pytest.mark.parametrize(
    "param", [pytest.param(replace_append_case, id="replace_existing_append")]
)
def test_append_cmdline_args(param):
    assert malware.append_cmdline_args(param["cmd"], param["args"]) == param["expected"]


@pytest.mark.parametrize("param", ["max-filesize", "max-scansize"])
def test_get_bytes_from_cmd_for_key(param):
    assert malware.get_bytes_from_cmd_for_key(CLAMAV_CMD, param) == 4000000000
