-
Notifications
You must be signed in to change notification settings - Fork 1
/
start.py
339 lines (295 loc) · 10.1 KB
/
start.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import time
import click
import config
import logging
import shutil
import subprocess
import zipfile
import requests
from bs4 import BeautifulSoup
# Т.к. у нас корпоративное общение все отчеты шлем в слакер (при желании)
from slacker import Slacker
os.chdir(config.WORK_DIR)
console = logging.StreamHandler()
console.setFormatter(logging.Formatter(u'%(levelname)-8s [%(asctime)s] %(message)s',))
logging.basicConfig(
level=logging.INFO,
format=u'%(levelname)-8s [%(asctime)s] %(message)s',
filename='ai-bolit.log',
)
logging.getLogger('').addHandler(console)
def get_site_name(path):
"""
Функция берет строку пути и отрезает последннее составляющее и возвращает его
"""
if path[len(path) - 1] == '/':
path = path[:-1]
l = len(str(path).split('/'))
name = str(path).split('/')[l - 1]
return name
def nonsite(name):
"""
Функция убирает лишние найденные на VPS директории из сканирования
:param name: имя директории
:return: возвращает 0 если не найдено и 1 если соответсвие установлено
"""
non_scan = ['tmp', 'vds.ru', 'html', 'httpd-logs', 'vds', 'bitrix']
for n in non_scan:
if name == n:
t = 1
break
else:
t = 0
return t
def set_permission(path):
"""
Устанавливаем правильные права на сайты. 644 на файлы и 755 на дириктории
:param path: Путь до сайта
:return: 1 - если права установлены, 0 - если произошла ошибка
"""
try:
if len(path) > 10:
os.system('find %s -type f -exec chmod 644 {} \;' % path)
print('[644 FILE DONE ] - ' + path)
os.system('find %s -type d -exec chmod 755 {} \;' % path)
print('[755 FOLDER DONE ] - ' + path)
time.sleep(5)
return 1
except Exception as e:
return 0
def get_aiupdate_url():
"""
Открываем страничку с обновлениями, ищем там ссылку на веб версию.
Обязательно нужно прописывать header иначе сайт шлет нафиг!
:return:
"""
import requests
logging.info(u'Get updates URL')
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:45.0) Gecko/20100101 Firefox/45.0'
}
url = "https://revisium.com/ai/"
r = requests.get(url, headers=headers)
soup = BeautifulSoup(r.text, "html.parser")
ai_url = soup.find_all('a')
for link in ai_url:
try:
if link.contents[0] == 'AI-Bolit для сайтов':
return link.get('href').replace('//', 'http://')
except:
pass
def unzip_file(path):
logging.info(u'Extract updates: %s' % path)
zip_ref = zipfile.ZipFile(path, 'r')
zip_ref.extractall(config.WORK_DIR)
zip_ref.close()
def get_site_list():
cmd = 'php %s/tools/vps_docroot.php' % config.WORK_DIR
PIPE = subprocess.PIPE
p = subprocess.Popen(
cmd,
shell=True,
stdin=PIPE,
stdout=PIPE,
stderr=subprocess.STDOUT,
close_fds=True,
cwd='./'
)
sites = p.stdout.read().decode("utf-8").split('\n')
# print(sites)
return sites
def run_ai(SITE_DIR):
logging.info('Scaning: %s' % SITE_DIR)
SITE_NAME = get_site_name(SITE_DIR)
REPORT_NAME = SITE_NAME + '.json'
SERVER_NAME = os.uname()[1]
cmd = 'php %s --path=%s --json_report=%s --report=%s %s %s %s %s %s' % (
config.AI_DIR + config.AI,
SITE_DIR,
config.REPORT_PATH + REPORT_NAME,
config.REPORT_PATH + SITE_NAME + '.html',
config.SKIP,
config.SCAN,
config.MODE,
config.SIZE,
config.PROGRESS
)
PIPE = subprocess.PIPE
p = subprocess.Popen(
cmd,
shell=True,
stdin=PIPE,
stdout=PIPE,
stderr=subprocess.STDOUT,
close_fds=True,
cwd=config.WORK_DIR
)
sites = p.stdout.read().decode("utf-8",'ignore')
logging.info('Scan done! VIRUS STATUS: ' + str(p.returncode))
def remove_report():
pass
def sent_report_to_slack(title, file_path):
"""
Отправляем отчеты в slack
:param title: Название сообщения
:param file_path: путь до файла отчетов
:return: 1 - если отчет отправлен, 0 - отчет не отправлен
"""
try:
logging.info('Send report from slack to: ' + config.slack_channel)
slack = Slacker(config.slack_key)
if os.path.isfile(file_path):
slack.chat.post_message(
channel=config.slack_channel,
text=open(config.logFileName, 'rb').read(),
username='AI-BOLIT'
)
slack.files.upload(
channels=config.slack_channel,
file_=file_path,
title=title,
filetype='zip',
)
return 1
else:
slack.chat.post_message(
channel=config.slack_channel,
text='report [ %s ] not found!!! ' % file_path,
username='AI-BOLIT'
)
return 0
except Exception as e:
print(e)
return 0
def send_report_to_mail(title, file_path):
try:
logging.info('Sending report to ' + str(config.targets))
import smtplib
from email import encoders
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
smtp_ssl_host = config.smtp_ssl_host
smtp_ssl_port = config.smtp_ssl_port
username = config.username
password = config.password
sender = config.sender
targets = config.targets
msg = MIMEMultipart()
msg['Subject'] = 'ANTIVIRUS REPORT [ %s ]' % title
msg['From'] = sender
msg['To'] = ', '.join(targets)
with open('ai-bolit.log', 'r') as log:
txt = MIMEText(log.read())
msg.attach(txt)
filepath = file_path
with open(filepath, 'rb') as f:
img = MIMEBase('application', 'zip')
img.set_payload(f.read())
encoders.encode_base64(img)
img.add_header('Content-Disposition',
'attachment',
filename=os.path.basename(filepath))
msg.attach(img)
server = smtplib.SMTP_SSL(smtp_ssl_host, smtp_ssl_port)
server.login(username, password)
server.sendmail(sender, targets, msg.as_string())
server.quit()
return 1
except Exception as e:
return 0
def clear_log_file():
try:
with open(config.logFileName, 'w'): pass
logging.info('Log file has been erased!')
return 1
except Exception as e:
return 0
def zip_report(server_name):
"""
Архивация отчетов
:param server_name: Все отчеты помещаются в архив, который называется именем сервера, на котором был запущен
:return: Путь до файла отчета, если что-то не получилось - 0
"""
try:
shutil.make_archive('REPORT-'+config.SERVER_NAME, 'zip', config.REPORT_PATH)
return 'REPORT-'+config.SERVER_NAME+'.zip'
except Exception as e:
return 0
@click.command()
@click.option('--channel', default='slack', help='Channel to send report(slack, email)')
def send_report(channel):
if os.path.isdir(config.REPORT_PATH):
if channel=='slack':
sent_report_to_slack(config.SERVER_NAME, zip_report(config.SERVER_NAME))
elif channel=='email':
send_report_to_mail(config.SERVER_NAME, zip_report(config.SERVER_NAME))
pass
else:
logging.info('Report not found :(')
@click.group()
def cli():
os.chdir(config.WORK_DIR)
@click.command()
def update():
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:45.0) Gecko/20100101 Firefox/45.0'
}
url = get_aiupdate_url()
logging.info('Download updates: %s' % url)
r = requests.get(url, headers=headers)
with open(config.WORK_DIR+'/ai.zip', 'wb') as zip:
zip.write(r.content)
logging.info('Extract updates')
unzip_file(config.WORK_DIR+'/ai.zip')
logging.info('Remove temp files')
os.remove(config.WORK_DIR+'/ai.zip')
logging.info('Update done!')
except Exception as e:
print(e)
@click.command()
# @click.argument('path')
def scan():
clear_log_file()
if os.path.isdir(config.REPORT_PATH):
logging.info('Remove old report')
shutil.rmtree(config.REPORT_PATH)
os.mkdir(config.REPORT_PATH)
else:
logging.info('Create report folder')
os.mkdir(config.REPORT_PATH)
sitelist = get_site_list()
for site in sitelist:
if site != '':
sitename = get_site_name(site)
if nonsite(sitename) != 1:
# logging.info(site)
run_ai(site)
# send_report()
@click.command()
@click.argument('path')
def scan_manual(path):
if os.path.isdir(path):
if os.path.isdir(config.REPORT_PATH):
logging.info('Remove old report')
shutil.rmtree(config.REPORT_PATH)
os.mkdir(config.REPORT_PATH)
else:
logging.info('Create report folder')
os.mkdir(config.REPORT_PATH)
run_ai(path)
else:
logging.info('Path %s not found!' % path)
def status():
pass
cli.add_command(send_report)
cli.add_command(update)
cli.add_command(scan)
cli.add_command(scan_manual)
if __name__ == "__main__":
cli()