Edit File by line
/home/barbar84/public_h.../wp-conte.../plugins/sujqvwi/AnonR/anonr.TX.../opt/imh-scan
File: clamlib.py
"""Clamscan and freshclam classes"""
[0] Fix | Delete
from contextlib import ExitStack
[1] Fix | Delete
import getpass
[2] Fix | Delete
import pwd
[3] Fix | Delete
import logging
[4] Fix | Delete
from pathlib import Path
[5] Fix | Delete
import shlex
[6] Fix | Delete
import shutil
[7] Fix | Delete
import sys
[8] Fix | Delete
import os
[9] Fix | Delete
import threading
[10] Fix | Delete
from dataclasses import dataclass
[11] Fix | Delete
import time
[12] Fix | Delete
import re
[13] Fix | Delete
from typing import IO, Union
[14] Fix | Delete
from subprocess import Popen, CalledProcessError, TimeoutExpired
[15] Fix | Delete
from subprocess import run, DEVNULL, PIPE
[16] Fix | Delete
from datetime import timedelta
[17] Fix | Delete
import tempfile
[18] Fix | Delete
import psutil
[19] Fix | Delete
import requests
[20] Fix | Delete
import rads
[21] Fix | Delete
import rads.color as c
[22] Fix | Delete
from cproc import Proc
[23] Fix | Delete
[24] Fix | Delete
IS_ROOT = os.getuid() == 0
[25] Fix | Delete
DEFS_SERVER = 'https://repo.imhadmin.net/open/shellscan/v3/'
[26] Fix | Delete
DEF_FILES = ('imh.yara',)
[27] Fix | Delete
HEUR_FILES = ('heuristic.yara',)
[28] Fix | Delete
HEUR_DIR = '/opt/imh-scan/sigs/heuri/'
[29] Fix | Delete
DEFS_DIR = '/opt/imh-scan/sigs/yara/'
[30] Fix | Delete
FRESH_CACHE = '/opt/imh-scan/sigs/last_freshclam'
[31] Fix | Delete
DUMMY = '/opt/imh-scan/sigs/new/new.yara'
[32] Fix | Delete
COMMAND_TIMEOUT = int(timedelta(weeks=1).total_seconds())
[33] Fix | Delete
FOUND_RE = re.compile(r'(.*)\: (.*) FOUND$', re.DOTALL)
[34] Fix | Delete
HEURISTIC_RE = re.compile(r'(?:YARA.)?[Hh]euristic')
[35] Fix | Delete
CUR_USER_HOME = Path(pwd.getpwnam(getpass.getuser()).pw_dir)
[36] Fix | Delete
HOME_RE = re.compile(r'(/home[0-9]?/[a-zA-Z0-9]{1,16})/')
[37] Fix | Delete
[38] Fix | Delete
[39] Fix | Delete
@dataclass
[40] Fix | Delete
class ScanResult:
[41] Fix | Delete
"""Clamscan result"""
[42] Fix | Delete
[43] Fix | Delete
rcode: int
[44] Fix | Delete
command: str
[45] Fix | Delete
hits_found: dict[Path, str]
[46] Fix | Delete
heur_found: dict[Path, str]
[47] Fix | Delete
summary: str
[48] Fix | Delete
[49] Fix | Delete
@property
[50] Fix | Delete
def __dict__(self) -> dict:
[51] Fix | Delete
return {
[52] Fix | Delete
'rcode': self.rcode,
[53] Fix | Delete
'command': self.command,
[54] Fix | Delete
'hits_found': {str(k): v for k, v in self.hits_found.items()},
[55] Fix | Delete
'heur_found': {str(k): v for k, v in self.heur_found.items()},
[56] Fix | Delete
'summary': self.summary,
[57] Fix | Delete
}
[58] Fix | Delete
[59] Fix | Delete
@property
[60] Fix | Delete
def all_found(self) -> dict[Path, str]:
[61] Fix | Delete
return self.heur_found | self.hits_found
[62] Fix | Delete
[63] Fix | Delete
[64] Fix | Delete
class ClamParser(threading.Thread):
[65] Fix | Delete
"""Thread for parsing clamscan stdout"""
[66] Fix | Delete
[67] Fix | Delete
def __init__(
[68] Fix | Delete
self,
[69] Fix | Delete
proc: Popen[str],
[70] Fix | Delete
scanner: 'Scanner',
[71] Fix | Delete
open_logfiles: list[IO],
[72] Fix | Delete
print_items: bool,
[73] Fix | Delete
):
[74] Fix | Delete
super().__init__(target=self.parse, daemon=True)
[75] Fix | Delete
self.proc = proc
[76] Fix | Delete
self.scanner = scanner
[77] Fix | Delete
self._scanning_summary = False
[78] Fix | Delete
self._print_items = print_items
[79] Fix | Delete
self.heur_found: dict[Path, str] = {}
[80] Fix | Delete
self.hits_found: dict[Path, str] = {}
[81] Fix | Delete
self.summary_lines: list[str] = []
[82] Fix | Delete
self.open_logfiles = open_logfiles
[83] Fix | Delete
self.start()
[84] Fix | Delete
[85] Fix | Delete
def parse(self):
[86] Fix | Delete
for line in self._iter_found():
[87] Fix | Delete
if self._scanning_summary:
[88] Fix | Delete
if self._print_items:
[89] Fix | Delete
print(line, end='')
[90] Fix | Delete
self.summary_lines.append(line)
[91] Fix | Delete
else:
[92] Fix | Delete
self._handle_found(line)
[93] Fix | Delete
[94] Fix | Delete
def _iter_found(self):
[95] Fix | Delete
prev = ''
[96] Fix | Delete
for line in self.proc.stdout:
[97] Fix | Delete
for log_file in self.open_logfiles:
[98] Fix | Delete
log_file.write(line)
[99] Fix | Delete
if self._scanning_summary:
[100] Fix | Delete
yield line
[101] Fix | Delete
continue
[102] Fix | Delete
if line.endswith('SCAN SUMMARY -----------\n'):
[103] Fix | Delete
self._scanning_summary = True
[104] Fix | Delete
prev = ''
[105] Fix | Delete
yield line
[106] Fix | Delete
continue
[107] Fix | Delete
if line.endswith('FOUND\n'):
[108] Fix | Delete
yield f"{prev}{line}".rstrip()
[109] Fix | Delete
prev = ''
[110] Fix | Delete
else:
[111] Fix | Delete
prev += line
[112] Fix | Delete
[113] Fix | Delete
def _handle_found(self, data: str):
[114] Fix | Delete
match = FOUND_RE.match(data)
[115] Fix | Delete
if not match:
[116] Fix | Delete
self.scanner.log.error("imh-scan bug: Regex failed on %r", data)
[117] Fix | Delete
return
[118] Fix | Delete
path_str, rule = match.groups()
[119] Fix | Delete
path = Path(path_str)
[120] Fix | Delete
if not path.is_file():
[121] Fix | Delete
self.scanner.log.error('imh-scan bug? %r is not a file', path)
[122] Fix | Delete
return
[123] Fix | Delete
if is_heur := bool(HEURISTIC_RE.search(rule)):
[124] Fix | Delete
self.heur_found[path] = rule
[125] Fix | Delete
else:
[126] Fix | Delete
self.hits_found[path] = rule
[127] Fix | Delete
self._print_found(path, rule, is_heur)
[128] Fix | Delete
[129] Fix | Delete
def _print_found(self, path: str, rule: str, is_heur: bool) -> None:
[130] Fix | Delete
if not self._print_items:
[131] Fix | Delete
return
[132] Fix | Delete
color = c.yellow if is_heur else c.red
[133] Fix | Delete
print(
[134] Fix | Delete
shlex.quote(str(path)), color(f"{rule} FOUND"), sep=': ', flush=True
[135] Fix | Delete
)
[136] Fix | Delete
[137] Fix | Delete
[138] Fix | Delete
class Scanner:
[139] Fix | Delete
"""Handles executing clamscan and freshclam"""
[140] Fix | Delete
[141] Fix | Delete
def __init__(
[142] Fix | Delete
self,
[143] Fix | Delete
*,
[144] Fix | Delete
exclude: list[str],
[145] Fix | Delete
verbose: bool,
[146] Fix | Delete
extra_heuri: bool,
[147] Fix | Delete
install: bool,
[148] Fix | Delete
update: bool,
[149] Fix | Delete
heuristic: bool,
[150] Fix | Delete
phishing: bool,
[151] Fix | Delete
disable_media: bool,
[152] Fix | Delete
disable_excludes: bool,
[153] Fix | Delete
disable_default: bool,
[154] Fix | Delete
disable_freshclam: bool,
[155] Fix | Delete
disable_maldetect: bool,
[156] Fix | Delete
disable_new_yara: bool,
[157] Fix | Delete
):
[158] Fix | Delete
"""Initializes variables"""
[159] Fix | Delete
self.verbose = verbose
[160] Fix | Delete
self.install = install
[161] Fix | Delete
self.update = update
[162] Fix | Delete
self.log = rads.setup_logging(
[163] Fix | Delete
path=None,
[164] Fix | Delete
name='imh_scan.Scanner',
[165] Fix | Delete
loglevel=logging.DEBUG if verbose else logging.INFO,
[166] Fix | Delete
print_out=sys.stdout,
[167] Fix | Delete
fmt='%(message)s',
[168] Fix | Delete
)
[169] Fix | Delete
self.cmd_paths = self._check_deps(
[170] Fix | Delete
disable_maldetect=disable_maldetect,
[171] Fix | Delete
disable_freshclam=disable_freshclam,
[172] Fix | Delete
disable_default=disable_default,
[173] Fix | Delete
)
[174] Fix | Delete
self.command = self._make_command(
[175] Fix | Delete
disable_media=disable_media,
[176] Fix | Delete
phishing=phishing,
[177] Fix | Delete
heuristic=heuristic,
[178] Fix | Delete
exclude=exclude,
[179] Fix | Delete
disable_new_yara=disable_new_yara,
[180] Fix | Delete
disable_excludes=disable_excludes,
[181] Fix | Delete
extra_heuri=extra_heuri,
[182] Fix | Delete
disable_maldetect=disable_maldetect,
[183] Fix | Delete
disable_default=disable_default,
[184] Fix | Delete
)
[185] Fix | Delete
[186] Fix | Delete
def _check_deps(
[187] Fix | Delete
self,
[188] Fix | Delete
disable_maldetect: bool,
[189] Fix | Delete
disable_freshclam: bool,
[190] Fix | Delete
disable_default: bool,
[191] Fix | Delete
):
[192] Fix | Delete
"""error handling and installing of dependencies vps can use any clamav
[193] Fix | Delete
packages installing only supports imh centos repo"""
[194] Fix | Delete
shared_permit = os.getuid() == 0 and not rads.IMH_ROLE == 'shared'
[195] Fix | Delete
clamdb_need = not disable_default
[196] Fix | Delete
freshclam_need = not disable_default and not disable_freshclam
[197] Fix | Delete
maldet_need = not disable_maldetect
[198] Fix | Delete
deps_d = {
[199] Fix | Delete
'clamscan': {
[200] Fix | Delete
'need': True,
[201] Fix | Delete
'permit': shared_permit,
[202] Fix | Delete
'pkg': 'imh-clamav',
[203] Fix | Delete
'paths': [
[204] Fix | Delete
'/opt/imh-clamav/usr/bin/clamscan',
[205] Fix | Delete
'/usr/bin/clamscan',
[206] Fix | Delete
'/bin/clamscan',
[207] Fix | Delete
'/usr/local/cpanel/3rdparty/bin/clamscan',
[208] Fix | Delete
],
[209] Fix | Delete
},
[210] Fix | Delete
'clamav-db': {
[211] Fix | Delete
'need': clamdb_need,
[212] Fix | Delete
'permit': shared_permit,
[213] Fix | Delete
'pkg': 'imh-clamav',
[214] Fix | Delete
'paths': ['/opt/imh-clamav/var/clamav', '/var/clamav'],
[215] Fix | Delete
},
[216] Fix | Delete
'freshclam': {
[217] Fix | Delete
'need': freshclam_need,
[218] Fix | Delete
'permit': shared_permit,
[219] Fix | Delete
'pkg': 'imh-clamav',
[220] Fix | Delete
'paths': [
[221] Fix | Delete
'/opt/imh-clamav/usr/bin/freshclam',
[222] Fix | Delete
'/usr/bin/freshclam',
[223] Fix | Delete
'/usr/local/cpanel/3rdparty/bin/freshclam',
[224] Fix | Delete
'/bin/freshclam',
[225] Fix | Delete
],
[226] Fix | Delete
},
[227] Fix | Delete
'freshconf': {
[228] Fix | Delete
'need': freshclam_need,
[229] Fix | Delete
'permit': shared_permit,
[230] Fix | Delete
'pkg': 'imh-clamav',
[231] Fix | Delete
'paths': [
[232] Fix | Delete
'/opt/imh-clamav/etc/freshclam.conf',
[233] Fix | Delete
'/etc/freshclam.conf',
[234] Fix | Delete
],
[235] Fix | Delete
},
[236] Fix | Delete
'maldet': {
[237] Fix | Delete
'need': maldet_need,
[238] Fix | Delete
'permit': shared_permit,
[239] Fix | Delete
'pkg': 'maldet-imh',
[240] Fix | Delete
'paths': ['/opt/maldetect/sigs'],
[241] Fix | Delete
},
[242] Fix | Delete
}
[243] Fix | Delete
install_list = []
[244] Fix | Delete
failed_list = []
[245] Fix | Delete
paths = {}
[246] Fix | Delete
for dep, opts in deps_d.items():
[247] Fix | Delete
# skips if not needed
[248] Fix | Delete
if not opts['need']:
[249] Fix | Delete
continue
[250] Fix | Delete
# checks if missing
[251] Fix | Delete
found_path = _find_binary(opts['paths'])
[252] Fix | Delete
if found_path:
[253] Fix | Delete
self.log.debug('found path %s', found_path)
[254] Fix | Delete
paths[dep] = found_path
[255] Fix | Delete
continue
[256] Fix | Delete
self.log.error('missing dependancy %s', dep)
[257] Fix | Delete
# checks if allowed to install
[258] Fix | Delete
if not opts['permit']:
[259] Fix | Delete
self.log.error('not permitted to install missing %s', dep)
[260] Fix | Delete
failed_list.append(dep)
[261] Fix | Delete
continue
[262] Fix | Delete
if opts['pkg'] not in install_list:
[263] Fix | Delete
install_list.append(opts['pkg'])
[264] Fix | Delete
# adds imh's path expecting the install to work
[265] Fix | Delete
paths[dep] = opts['paths'][0]
[266] Fix | Delete
# fails if not allowed to install
[267] Fix | Delete
if failed_list:
[268] Fix | Delete
self.log.error('missing pkgs: %s', failed_list)
[269] Fix | Delete
self.log.error('allowed to install: %s', install_list)
[270] Fix | Delete
sys.exit(1)
[271] Fix | Delete
# already installed
[272] Fix | Delete
if not install_list:
[273] Fix | Delete
return paths
[274] Fix | Delete
# install missing deps
[275] Fix | Delete
req = self._install_deps(install_list)
[276] Fix | Delete
if not req:
[277] Fix | Delete
self.log.error("Failed to install dependancies %s", install_list)
[278] Fix | Delete
sys.exit(0)
[279] Fix | Delete
return paths
[280] Fix | Delete
[281] Fix | Delete
def _install_deps(self, dep):
[282] Fix | Delete
if self.install:
[283] Fix | Delete
ret = 'y'
[284] Fix | Delete
else:
[285] Fix | Delete
ret = ask_prompt(
[286] Fix | Delete
f'Would you like to install {dep}? (y|n)',
[287] Fix | Delete
chars=('y', 'n'),
[288] Fix | Delete
)
[289] Fix | Delete
if ret == 'n':
[290] Fix | Delete
self.log.warning('exiting')
[291] Fix | Delete
sys.exit(0)
[292] Fix | Delete
try:
[293] Fix | Delete
run(
[294] Fix | Delete
['yum', '-y', 'install'] + dep,
[295] Fix | Delete
stdout=None if self.verbose else DEVNULL,
[296] Fix | Delete
check=True,
[297] Fix | Delete
)
[298] Fix | Delete
except FileNotFoundError as exc:
[299] Fix | Delete
self.log.error(exc)
[300] Fix | Delete
sys.exit(1)
[301] Fix | Delete
except CalledProcessError:
[302] Fix | Delete
self.log.fatal('error running yum, exiting')
[303] Fix | Delete
sys.exit(1)
[304] Fix | Delete
return True
[305] Fix | Delete
[306] Fix | Delete
def cpu_wait(self):
[307] Fix | Delete
cpu_limit = psutil.cpu_count() - 1
[308] Fix | Delete
while True:
[309] Fix | Delete
loadavg = os.getloadavg()[0]
[310] Fix | Delete
if loadavg > cpu_limit:
[311] Fix | Delete
self.log.warning(
[312] Fix | Delete
'Load too high to start clamscan (%s/%s), sleeping 30s',
[313] Fix | Delete
loadavg,
[314] Fix | Delete
cpu_limit,
[315] Fix | Delete
)
[316] Fix | Delete
time.sleep(30)
[317] Fix | Delete
else:
[318] Fix | Delete
return
[319] Fix | Delete
[320] Fix | Delete
def update_defs(self, disable_freshclam: bool, disable_default: bool):
[321] Fix | Delete
"""Updates the custom definitions"""
[322] Fix | Delete
for dir_name in DEFS_DIR, HEUR_DIR:
[323] Fix | Delete
Path(dir_name).mkdir(mode=0o755, parents=True, exist_ok=True)
[324] Fix | Delete
self.log.debug('Definitions to get: %s', DEF_FILES + HEUR_FILES)
[325] Fix | Delete
for def_file in DEF_FILES:
[326] Fix | Delete
url = f'{DEFS_SERVER}{def_file}'
[327] Fix | Delete
path = f'{DEFS_DIR}{def_file}'
[328] Fix | Delete
self._download_file(url, path)
[329] Fix | Delete
for def_file in HEUR_FILES:
[330] Fix | Delete
url = f'{DEFS_SERVER}{def_file}'
[331] Fix | Delete
path = f'{HEUR_DIR}{def_file}'
[332] Fix | Delete
self._download_file(url, path)
[333] Fix | Delete
if self.update:
[334] Fix | Delete
self.log.warning('Just updating defintions')
[335] Fix | Delete
self._freshclam()
[336] Fix | Delete
sys.exit(0)
[337] Fix | Delete
if disable_default or disable_freshclam:
[338] Fix | Delete
return
[339] Fix | Delete
self._freshclam()
[340] Fix | Delete
[341] Fix | Delete
def _download_file(self, url: str, dest: str):
[342] Fix | Delete
self.log.debug('Downloading %s to %s', url, dest)
[343] Fix | Delete
try:
[344] Fix | Delete
with requests.get(url, stream=True, timeout=30) as req:
[345] Fix | Delete
req.raise_for_status()
[346] Fix | Delete
with open(str(dest) + '.tmp', 'wb') as file:
[347] Fix | Delete
for chunk in req.iter_content(chunk_size=8192):
[348] Fix | Delete
file.write(chunk)
[349] Fix | Delete
except requests.RequestException as exc:
[350] Fix | Delete
self.log.error("Unable to retrieve %s, skipping\n%s", url, exc)
[351] Fix | Delete
os.rename(dest + '.tmp', dest)
[352] Fix | Delete
[353] Fix | Delete
def _freshclam(self):
[354] Fix | Delete
"""Runs freshclam for system"""
[355] Fix | Delete
now = int(time.time())
[356] Fix | Delete
if os.path.exists(FRESH_CACHE) and not self.update:
[357] Fix | Delete
with open(FRESH_CACHE, encoding='ascii') as file:
[358] Fix | Delete
try:
[359] Fix | Delete
last_run = int(file.read().strip())
[360] Fix | Delete
if last_run + 86400 > now:
[361] Fix | Delete
self.log.warning(
[362] Fix | Delete
'freshclam ran less than a day ago, skipping'
[363] Fix | Delete
)
[364] Fix | Delete
return
[365] Fix | Delete
except Exception as exc:
[366] Fix | Delete
self.log.error('error reading %s\n%s', FRESH_CACHE, exc)
[367] Fix | Delete
[368] Fix | Delete
freshclam_conf = f"--config-file={self.cmd_paths['freshconf']}"
[369] Fix | Delete
fresh_cmd = [self.cmd_paths['freshclam'], freshclam_conf]
[370] Fix | Delete
self.log.debug('freshclam command: %s', fresh_cmd)
[371] Fix | Delete
try:
[372] Fix | Delete
with Popen(fresh_cmd, stdout=PIPE, encoding='utf-8') as proc:
[373] Fix | Delete
for line in proc.stdout:
[374] Fix | Delete
if not self.verbose:
[375] Fix | Delete
continue
[376] Fix | Delete
self._freshclam_print(line)
[377] Fix | Delete
except OSError as exc:
[378] Fix | Delete
self.log.error("ERROR: freshclam failed: %s", exc)
[379] Fix | Delete
return
[380] Fix | Delete
else:
[381] Fix | Delete
if proc.returncode:
[382] Fix | Delete
self.log.error(
[383] Fix | Delete
'ERROR: freshclam failed, database.clamav.net '
[384] Fix | Delete
'probably offline'
[385] Fix | Delete
)
[386] Fix | Delete
return
[387] Fix | Delete
with open(FRESH_CACHE, 'w', encoding='ascii') as file:
[388] Fix | Delete
file.write(str(now))
[389] Fix | Delete
proc.stdout.close()
[390] Fix | Delete
[391] Fix | Delete
def write_ok(self, path):
[392] Fix | Delete
try:
[393] Fix | Delete
testfile = tempfile.TemporaryFile(dir=path)
[394] Fix | Delete
testfile.close()
[395] Fix | Delete
except OSError:
[396] Fix | Delete
self.log.debug('%s is not writeable', path)
[397] Fix | Delete
return False
[398] Fix | Delete
self.log.debug('%s is writeable', path)
[399] Fix | Delete
return True
[400] Fix | Delete
[401] Fix | Delete
def _init_logs(
[402] Fix | Delete
self,
[403] Fix | Delete
stack: ExitStack,
[404] Fix | Delete
log_tuples: list[tuple[Path, Union[pwd.struct_passwd, None]]],
[405] Fix | Delete
) -> list[IO]:
[406] Fix | Delete
files = []
[407] Fix | Delete
for log_path, owner in log_tuples:
[408] Fix | Delete
try:
[409] Fix | Delete
log_path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
[410] Fix | Delete
if owner is not None:
[411] Fix | Delete
os.chown(log_path.parent, owner.pw_uid, owner.pw_gid)
[412] Fix | Delete
except OSError as exc:
[413] Fix | Delete
self.log.fatal('%s\nerror in _init_logs', exc)
[414] Fix | Delete
sys.exit(2)
[415] Fix | Delete
files.append(
[416] Fix | Delete
stack.enter_context(log_path.open('a', encoding='utf-8'))
[417] Fix | Delete
)
[418] Fix | Delete
if owner is not None:
[419] Fix | Delete
os.chown(log_path, owner.pw_uid, owner.pw_gid)
[420] Fix | Delete
log_path.chmod(mode=0o600)
[421] Fix | Delete
return files
[422] Fix | Delete
[423] Fix | Delete
def _make_command(
[424] Fix | Delete
self,
[425] Fix | Delete
disable_media: bool,
[426] Fix | Delete
phishing: bool,
[427] Fix | Delete
heuristic: bool,
[428] Fix | Delete
disable_new_yara: bool,
[429] Fix | Delete
disable_excludes: bool,
[430] Fix | Delete
extra_heuri: bool,
[431] Fix | Delete
disable_maldetect: bool,
[432] Fix | Delete
disable_default: bool,
[433] Fix | Delete
exclude: bool,
[434] Fix | Delete
):
[435] Fix | Delete
cmd = [
[436] Fix | Delete
self.cmd_paths['clamscan'],
[437] Fix | Delete
'-r',
[438] Fix | Delete
'--normalize=no',
[439] Fix | Delete
'--cross-fs=yes',
[440] Fix | Delete
'-i',
[441] Fix | Delete
]
[442] Fix | Delete
if extra_heuri:
[443] Fix | Delete
cmd.append('--heuristic-alerts=yes')
[444] Fix | Delete
if phishing:
[445] Fix | Delete
cmd.append('--phishing-sigs=yes')
[446] Fix | Delete
if not disable_default:
[447] Fix | Delete
cmd.extend(['-d', self.cmd_paths['clamav-db']])
[448] Fix | Delete
cmd.extend(['-d', DEFS_DIR]) # custom imh dbs and maldetect
[449] Fix | Delete
if not disable_maldetect:
[450] Fix | Delete
cmd.extend(['-d', self.cmd_paths['maldet']])
[451] Fix | Delete
if not disable_new_yara and os.path.exists(DUMMY):
[452] Fix | Delete
cmd.extend(['-d', f'{DUMMY}'])
[453] Fix | Delete
if heuristic:
[454] Fix | Delete
cmd.extend(['-d', HEUR_DIR])
[455] Fix | Delete
# excludes common false positive/time wasting dirs for cPanel
[456] Fix | Delete
if disable_media:
[457] Fix | Delete
cmd.append(
[458] Fix | Delete
r'--exclude=\.(jpe?g|png|gif|mp(eg|4|g)|mov|avi|wmv|flv)$'
[459] Fix | Delete
)
[460] Fix | Delete
if not disable_excludes:
[461] Fix | Delete
cmd.append(
[462] Fix | Delete
r'--exclude=/home[0-9]?/[^/]*/'
[463] Fix | Delete
r'(quarantine*|mail/|etc/|logs/.*(\.tar)?\.gz'
[464] Fix | Delete
r'|tmp/awstats/.*.txt|tmp/webalizer/'
[465] Fix | Delete
r'(.*usage_.*.html|webalizer\.current))'
[466] Fix | Delete
)
[467] Fix | Delete
if exclude:
[468] Fix | Delete
for path in exclude:
[469] Fix | Delete
cmd.extend(['--exclude', path])
[470] Fix | Delete
cmd.append('--')
[471] Fix | Delete
return cmd
[472] Fix | Delete
[473] Fix | Delete
def scan(
[474] Fix | Delete
self,
[475] Fix | Delete
scan_paths: list[str],
[476] Fix | Delete
log_tuples: list[tuple[Path, Union[pwd.struct_passwd, None]]],
[477] Fix | Delete
print_items: bool = False,
[478] Fix | Delete
) -> ScanResult:
[479] Fix | Delete
"""Runs clamscan"""
[480] Fix | Delete
scan_path_strs = list(map(str, scan_paths))
[481] Fix | Delete
assert scan_paths
[482] Fix | Delete
cmd = self.command.copy()
[483] Fix | Delete
cmd.extend(scan_path_strs)
[484] Fix | Delete
self.log.warning('Scan command: %s', c.cyan(shlex.join(cmd)))
[485] Fix | Delete
try:
[486] Fix | Delete
with ExitStack() as stack:
[487] Fix | Delete
open_logfiles = self._init_logs(stack, log_tuples)
[488] Fix | Delete
try:
[489] Fix | Delete
with Proc(
[490] Fix | Delete
cmd,
[491] Fix | Delete
lim=os.cpu_count(),
[492] Fix | Delete
stdout=PIPE,
[493] Fix | Delete
stderr=None,
[494] Fix | Delete
encoding='utf-8',
[495] Fix | Delete
errors='surrogateescape',
[496] Fix | Delete
) as proc:
[497] Fix | Delete
parser = ClamParser(
[498] Fix | Delete
proc=proc,
[499] Fix | Delete
12
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function