修复漏洞

This commit is contained in:
晚风拂柳颜 2024-01-02 19:52:29 +08:00
parent bc34e592ce
commit 173a5d4be3
5 changed files with 168 additions and 80 deletions

View File

@ -19,7 +19,7 @@ from utils.encode import base64Encode, base64Decode, fetch, post, request, getCr
from utils.encode import verifyCode, setDetail, join, urljoin2, parseText, requireCache, forceOrder, base64ToImage, \
encodeStr, decodeStr
from utils.encode import md5 as mmd5
from utils.safePython import safePython
from utils.safePython import safePython, safe_eval
from utils.parser import runPy, runJScode, JsObjectWrapper, PyJsObject, PyJsString
from utils.htmlParser import jsoup
from urllib.parse import urljoin, quote, unquote
@ -828,8 +828,9 @@ class CMS:
# print(url_rep)
# print(cnt_page)
cnt_ctx = {}
exec(f'cnt_pg={cnt_page}', cnt_ctx)
cnt_pg = str(cnt_ctx['cnt_pg']) # 计算表达式的结果
safe_eval(f'cnt_pg={cnt_page}', cnt_ctx)
# exec(f'cnt_pg={cnt_page}', cnt_ctx)
cnt_pg = str(cnt_ctx['cnt_pg']) if cnt_ctx.get('cnt_pg') else 1 # 计算表达式的结果
url = url.replace(url_rep, str(cnt_pg)).replace('(', '').replace(')', '')
# print(url)
else:
@ -1351,8 +1352,9 @@ class CMS:
# print(url_rep)
# print(cnt_page)
cnt_ctx = {}
exec(f'cnt_pg={cnt_page}', cnt_ctx)
cnt_pg = str(cnt_ctx['cnt_pg']) # 计算表达式的结果
# exec(f'cnt_pg={cnt_page}', cnt_ctx)
safe_eval(f'cnt_pg={cnt_page}', cnt_ctx)
cnt_pg = str(cnt_ctx['cnt_pg']) if cnt_ctx.get('cnt_pg') else 1 # 计算表达式的结果
url = url.replace(url_rep, str(cnt_pg)).replace('(', '').replace(')', '')
# print(url)
else:

View File

@ -8,7 +8,8 @@ import ujson
import os
import re
from flask import Blueprint,abort,render_template,render_template_string,url_for,redirect,make_response,send_from_directory,request
from flask import Blueprint, abort, render_template, render_template_string, url_for, redirect, make_response, \
send_from_directory, request
from controllers.service import storage_service, rules_service, parse_service
from controllers.classes import getClasses, getClassInfo
@ -28,13 +29,14 @@ from utils.web import getParmas,verfy_token
from utils.common_api import js_render
import functools
home = Blueprint("home", __name__, static_folder='/static')
@home.route('/')
def forbidden(): # put application's code here
abort(403)
@home.route('/favicon.ico') # 设置icon
def favicon():
# return home.send_static_file('img/favicon.svg')
@ -42,6 +44,7 @@ def favicon():
# 对于当前文件所在路径,比如这里是static下的favicon.ico
# return send_from_directory(os.path.join(app.root_path, 'static'), 'img/favicon.svg', mimetype='image/vnd.microsoft.icon')
@home.route('/index')
def index():
sup_port = cfg.get('SUP_PORT', 9001)
@ -57,16 +60,26 @@ def index():
manager2 += f':{sup_port}'
# print(manager2)
ver = getLocalVer()
return render_template('index.html',ver=ver,getHost=getHost,manager0=manager0,manager1=manager1,manager2=manager2,is_linux=is_linux())
return render_template('index.html', ver=ver, getHost=getHost, manager0=manager0, manager1=manager1,
manager2=manager2, is_linux=is_linux())
@home.route('/rules/clear')
def rules_to_clear():
if not verfy_token():
# return render_template('login.html')
return R.error('请登录后再试')
return render_template('rules_to_clear.html', rules=getRules(), classes=getClasses())
@home.route('/rules/view')
def rules_to_view():
if not verfy_token():
# return render_template('login.html')
return R.error('请登录后再试')
return render_template('rules_to_view.html', rules=getRules(), classes=getClasses())
@home.route('/pics')
def random_pics():
id = getParmas('id')
@ -92,6 +105,7 @@ def random_pics():
else:
return redirect(new_conf.WALL_PAPER)
@home.route('/clear')
def clear_rule():
rule = getParmas('rule')
@ -103,6 +117,7 @@ def clear_rule():
os.remove(cache_path)
return R.success('成功删除文件:' + cache_path)
@home.route("/plugin/<name>", methods=['GET'])
def plugin(name):
# name=道长影视模板.js
@ -113,6 +128,7 @@ def plugin(name):
except Exception as e:
return R.failed(f'非法猥亵\n{e}')
@home.route('/files/<name>')
def get_files(name):
base_path = 'base/files'
@ -129,18 +145,21 @@ def get_files(name):
response.headers['Content-Disposition'] = f'attachment;filename="{filename}"'
return response
@home.route('/txt/<path:filename>')
def custom_static_txt(filename):
# 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}}
# print(filename)
return send_from_directory('txt', filename)
@home.route('/libs/<path:filename>')
def custom_static_libs(filename):
# 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}}
# print(filename)
return send_from_directory('libs', filename)
# @home.route('/js/<path:filename>')
# def custom_static_js(filename):
# # 自定义静态目录 {{ url_for('custom_static',filename='help.txt')}}
@ -153,10 +172,12 @@ def custom_static_js(name):
# print(name)
return js_render(name)
@home.route('/raw/js/<path:filename>')
def custom_raw_js(filename):
return send_from_directory('js', filename)
# @home.route('/txt/<name>')
# def get_txt_files(name):
# base_path = 'txt'
@ -177,7 +198,8 @@ def get_lives():
# ?path=base/live.txt
path = getParmas('path')
live_path = path or 'base/直播.txt'
if not re.search('(txt|json|conf)$',live_path,re.M|re.S) or not re.search('^(txt|base)',live_path,re.M|re.S):
if not re.search('(txt|json|conf)$', live_path, re.M | re.S) or not re.search('^(txt|base)', live_path,
re.M | re.S):
abort(403)
if not os.path.exists(live_path):
# with open(live_path,mode='w+',encoding='utf-8') as f:
@ -196,6 +218,7 @@ def get_lives():
response.headers['Content-Type'] = 'text/plain; charset=utf-8'
return response
@home.route('/liveslib')
def get_liveslib():
lsg = storage_service()
@ -214,6 +237,7 @@ def get_liveslib():
response.headers['Content-Disposition'] = f'attachment;filename="{filename}"'
return response
@home.route('/hotsugg')
def get_hot_search():
s_from = getParmas('from')
@ -221,6 +245,7 @@ def get_hot_search():
data = getHotSuggest(s_from, size)
return R.success('获取成功', data)
def merged_hide(merged_config):
t1 = time()
store_rule = rules_service()
@ -237,7 +262,9 @@ def merged_hide(merged_config):
return name not in hide_rule_names
merged_config['sites'] = list(filter(filter_show, merged_config['sites']))
logger.info(f'数据库筛选隐藏规则耗时{get_interval(t1)}毫秒,共计{all_cnt}条规则,隐藏后可渲染{len(merged_config["sites"])}条规则')
logger.info(
f'数据库筛选隐藏规则耗时{get_interval(t1)}毫秒,共计{all_cnt}条规则,隐藏后可渲染{len(merged_config["sites"])}条规则')
@home.route('/config/<int:mode>')
def config_render(mode):
@ -300,7 +327,9 @@ def config_render(mode):
else:
new_conf.EXT_FUNC = []
html = render_template('config.txt',js0_password=js0_password,UA=UA,xr_mode=xr_mode,ISTVB=ISTVB,pys=pys,rules=rules,host=host,mode=mode,js_mode=js_mode,jxs=jxs,alists=alists,alists_str=alists_str,live_url=live_url,config=new_conf)
html = render_template('config.txt', js0_password=js0_password, UA=UA, xr_mode=xr_mode, ISTVB=ISTVB, pys=pys,
rules=rules, host=host, mode=mode, js_mode=js_mode, jxs=jxs, alists=alists,
alists_str=alists_str, live_url=live_url, config=new_conf)
merged_config = custom_merge(parseText(html), customConfig)
# print(merged_config['sites'])
merged_hide(merged_config)
@ -333,6 +362,7 @@ def config_render(mode):
logger.info(f'自动生成动态配置共计耗时:{get_interval(tt)}毫秒')
return response
def special_rule(merged_config, lsg):
# print(merged_config['sites'])
special = lsg.getItem('SPECIAL').strip()
@ -350,6 +380,7 @@ def special_rule(merged_config,lsg):
merged_config['sites'] = special_st
merged_config['dr_count'] = len(special_st)
def comp(x, y):
if x['order'] > y['order']:
return 1
@ -363,6 +394,7 @@ def comp(x, y):
else:
return 0
def sort_sites_by_order(sites, js_mode=0):
rules = rules_service()
rule_list = rules.query_all()
@ -399,6 +431,7 @@ def sort_sites_by_order(sites,js_mode=0):
del site['write_date']
return sites
def sort_parses_by_order(parses, host):
t1 = time()
parse = parse_service()
@ -441,6 +474,7 @@ def sort_parses_by_order(parses,host):
logger.info(f'{len(new_parses)}/{len(parses)}条解析解析排序耗时:{get_interval(t1)}毫秒')
return new_parses
@home.route('/configs')
def config_gen():
if not verfy_token():
@ -471,15 +505,21 @@ def config_gen():
rules = get_multi_rules(rules)
host0 = getHost(0)
jxs = getJxs(host=host0)
set_local = render_template('config.txt',js0_password=js0_password,pys=pys,rules=rules,alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,0),mode=0,js_mode=js_mode,host=host0,jxs=jxs,config=new_conf)
set_local = render_template('config.txt', js0_password=js0_password, pys=pys, rules=rules, alists=alists,
alists_str=alists_str, live_url=get_live_url(new_conf, 0), mode=0, js_mode=js_mode,
host=host0, jxs=jxs, config=new_conf)
# print(set_local)
host1 = getHost(1)
jxs = getJxs(host=host1)
set_area = render_template('config.txt',js0_password=js0_password,pys=pys,rules=rules,alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,1),mode=1,js_mode=js_mode,host=host1,jxs=jxs,config=new_conf)
set_area = render_template('config.txt', js0_password=js0_password, pys=pys, rules=rules, alists=alists,
alists_str=alists_str, live_url=get_live_url(new_conf, 1), mode=1, js_mode=js_mode,
host=host1, jxs=jxs, config=new_conf)
host2 = getHost(2) or host1
# print('远程地址:'+host2)
jxs = getJxs(host=host2)
set_online = render_template('config.txt',js0_password=js0_password,pys=pys,rules=rules,alists=alists,alists_str=alists_str,live_url=get_live_url(new_conf,2),mode=1,js_mode=js_mode,host=host2,jxs=jxs,config=new_conf)
set_online = render_template('config.txt', js0_password=js0_password, pys=pys, rules=rules, alists=alists,
alists_str=alists_str, live_url=get_live_url(new_conf, 2), mode=1, js_mode=js_mode,
host=host2, jxs=jxs, config=new_conf)
ali_token = new_conf.ALI_TOKEN
# parses = []
with open('txt/pycms0.json', 'w+', encoding='utf-8') as f:
@ -517,7 +557,11 @@ def config_gen():
except Exception as e:
return R.failed(f'配置文件生成错误:\n{e}')
@home.route("/info", methods=['get'])
def info_all():
if not verfy_token():
# return render_template('login.html')
return R.error('请登录后再试')
data = storage_service.query_all()
return R.ok(data=data)

View File

@ -1,3 +1,6 @@
###### 2024/01/02
- [X] 3.9.49beta11 fixed issues#49
###### 2023/11/25
- [X] 3.9.49beta8 增加直播转换小工具
@ -11,6 +14,7 @@
```shell
git config --global http.proxy http://127.0.0.1:10808
git config --global https.proxy http://127.0.0.1:10808
git config --global --unset http.proxy
# 我的v2vary在10808上系统代理
```

View File

@ -1 +1 @@
3.9.49beta10
3.9.49beta11

View File

@ -20,6 +20,8 @@ import base64
from utils.log import logger
time_out_sec = 8 # 安全执行python代码超时
class my_exception(Exception):
def __init__(self, message):
self.message = message
@ -28,10 +30,12 @@ class my_exception(Exception):
message = f'函数执行超时: "{self.message}"'
return message
@func_set_timeout(time_out_sec)
def excute(*args):
exec(*args)
def check_unsafe_attributes(string):
"""
安全检测需要exec执行的python代码
@ -48,6 +52,7 @@ def check_unsafe_attributes(string):
elif toktype == tokenize.OP:
pre_op = tokval
DEFAULT_PYTHON_CODE = """# 可用内置环境变量:
# - log: log(message): 打印日志功能
# - error: 弹出用户错误的弹窗
@ -57,6 +62,39 @@ zyw_lists = env['hikerule.zyw.list'].with_context(active_test=True).sudo().searc
result = env['hikerule.zyw.list2data.wizard'].sudo().get_publish_value(zyw_lists)
"""
def safe_eval(code: str = '', localdict: dict = None):
code = code.strip()
logger.info('code:' + code)
if not code:
return {}
if localdict is None:
localdict = {}
builtins = __builtins__
builtins = dict(builtins).copy()
for key in ['__import__', 'eval', 'exec', 'globals', 'dir', 'copyright', 'open', 'quit']:
del builtins[key] # 删除不安全的关键字
# print(builtins)
global_dict = {'__builtins__': builtins,
'requests': requests, 'urljoin': urljoin, 'quote': quote, 'unquote': unquote,
'log': logger.info, 'json': json, 'print': print,
're': re, 'etree': etree, 'time': time, 'datetime': datetime, 'base64': base64
} # 禁用内置函数,不允许导入包
try:
check_unsafe_attributes(code)
# 待解决windows下运行超时的问题
try:
# excute(to_run_code, global_dict, localdict)
excute(code, global_dict, localdict)
return localdict
except FunctionTimedOut:
raise my_exception(f'safe_eval运行时间超过{time_out_sec}秒,疑似死循环,已被系统切断')
except Exception as e:
ret = f'执行报错:{e}'
logger.info(ret)
return ret
class safePython:
def __init__(self, name, code):
self.name = name or '未定义'