From 8203c71dc4918dca3c0b4518b25944125f0bf087 Mon Sep 17 00:00:00 2001 From: HisAtri Date: Sun, 31 Mar 2024 19:39:55 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/__import__.py | 2 +- api/__init__.py | 16 +++++- api/file.py | 11 +++- api/source.py | 24 ++------- db/__init__.py | 0 mod/music_tag/__init__.py | 4 -- mod/search.py | 12 +++-- mod/searchx/__init__.py | 33 ++++++++++++ mod/searchx/api.py | 21 ++++++++ mod/searchx/kugou.py | 108 ++++++++++++++++++++++++++++++++++++++ mod/searchx/migu.py | 44 ++++++++++++++++ mod/searchx/netease.py | 0 src/css/mod.css | 9 ++++ 13 files changed, 252 insertions(+), 32 deletions(-) create mode 100644 db/__init__.py create mode 100644 mod/searchx/__init__.py create mode 100644 mod/searchx/api.py create mode 100644 mod/searchx/kugou.py create mode 100644 mod/searchx/migu.py create mode 100644 mod/searchx/netease.py diff --git a/api/__import__.py b/api/__import__.py index 05229ab..1f9cf2e 100644 --- a/api/__import__.py +++ b/api/__import__.py @@ -1,4 +1,4 @@ -from . import cover, login, lyrics, source, tag, time +from . import cover, login, lyrics, source, tag, time, file """ 引入所有子模块 diff --git a/api/__init__.py b/api/__init__.py index e1b6839..19cc294 100644 --- a/api/__init__.py +++ b/api/__init__.py @@ -1,5 +1,7 @@ import shutil import logging +import sys +import os from flask import Flask, Blueprint, request from flask_caching import Cache @@ -31,5 +33,17 @@ def make_cache_key(*args, **kwargs): args = str(hash(frozenset(request.args.items()))) return path + args +def get_base_path(): + """ + 获取程序运行路径 + 如果是打包后的exe文件,则返回打包资源路径 + """ + if getattr(sys, 'frozen', False): + return sys._MEIPASS + else: + return os.getcwd() -__all__ = ['app', 'v1_bp', 'cache', 'make_cache_key', 'logger'] + +src_path = os.path.join(get_base_path(), 'src') # 静态资源路径 + +__all__ = ['app', 'v1_bp', 'cache', 'make_cache_key', 'logger', 'src_path'] diff --git a/api/file.py b/api/file.py index d28f7e5..1cb9ed1 100644 --- a/api/file.py +++ b/api/file.py @@ -7,7 +7,7 @@ import os import requests from urllib.parse import urlparse -from flask import request, render_template_string +from flask import request, render_template_string, send_from_directory from werkzeug.utils import secure_filename from mod.tools import calculate_md5 @@ -65,7 +65,7 @@ def file_api_download(): return {"error": str(e), "code": 500}, 500 -@app.route('/file/upload', methods=['POST']) +@v1_bp.route('/file/upload', methods=['POST']) def upload_file(): match require_auth(request=request, permission="rwd"): case -1: @@ -150,3 +150,10 @@ def list_file(): "files": data["FILES"][row * (page - 1):row * page] } } + + +""" +@app.route('/file', methods=['GET']) +def file(): + return send_from_directory(src_path, 'file.html') +""" diff --git a/api/source.py b/api/source.py index ef8cb36..32913a2 100644 --- a/api/source.py +++ b/api/source.py @@ -1,5 +1,3 @@ -import sys - from . import * import os @@ -10,20 +8,6 @@ from mod.auth.authentication import require_auth -def get_base_path(): - """ - 获取程序运行路径 - 如果是打包后的exe文件,则返回打包资源路径 - """ - if getattr(sys, 'frozen', False): - return sys._MEIPASS - else: - return os.getcwd() - - -path = os.path.join(get_base_path(), 'src') # 静态资源路径 - - @app.route('/') def redirect_to_welcome(): """ @@ -39,7 +23,7 @@ def favicon(): favicon位置,返回图片 :return: """ - return send_from_directory(path, 'img/Logo_Design.svg') + return send_from_directory(src_path, 'img/Logo_Design.svg') @app.route('/src') @@ -48,7 +32,7 @@ def return_index(): 显示主页 :return: index page """ - return send_from_directory(path, 'index.html') + return send_from_directory(src_path, 'index.html') @app.route('/src/') @@ -60,7 +44,7 @@ def serve_file(filename): :return: """ FORBIDDEN_EXTENSIONS = ('.exe', '.bat', '.dll', '.sh', '.so', '.php', '.sql', '.db', '.mdb', '.gz', '.tar', '.bak', - '.tmp', '.key', '.pem', '.crt', '.csr', '.log') + '.tmp', '.key', '.pem', '.crt', '.csr', '.log', '.html', '.htm', '.xml', '.json', '.yml',) _paths = filename.split('/') for _path in _paths: if _path.startswith('.'): @@ -68,7 +52,7 @@ def serve_file(filename): if filename.lower().endswith(FORBIDDEN_EXTENSIONS): abort(404) try: - return send_from_directory(path, filename) + return send_from_directory(src_path, filename) except FileNotFoundError: abort(404) diff --git a/db/__init__.py b/db/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mod/music_tag/__init__.py b/mod/music_tag/__init__.py index 3ad7bf8..0f73e07 100644 --- a/mod/music_tag/__init__.py +++ b/mod/music_tag/__init__.py @@ -75,7 +75,3 @@ def load_file(file_spec, err='raise'): 'AudioFile', 'load_file', ] - -## -## EOF -## diff --git a/mod/search.py b/mod/search.py index 4ddc828..440cb47 100644 --- a/mod/search.py +++ b/mod/search.py @@ -16,7 +16,8 @@ async def kugou(title, artist, album): async with aiohttp.ClientSession() as session: # 第一层Json,要求获得Hash值 async with session.get( - f'http://mobilecdn.kugou.com/api/v3/search/song?format=json&keyword={title} {artist} {album}&page=1&pagesize=2&showtype=1', + f'http://mobilecdn.kugou.com/api/v3/search/song' + f'?format=json&keyword={title} {artist} {album}&page=1&pagesize=2&showtype=1', headers=headers, timeout=10) as response: if response.status == 200: @@ -38,7 +39,8 @@ async def kugou(title, artist, album): ratio_max = max(ratio, ratio_max) if ratio >= ratio_max: async with session.get( - f"https://krcs.kugou.com/search?ver=1&man=yes&client=mobi&keyword=&duration=&hash={song_hash}&album_audio_id=", + f"https://krcs.kugou.com/search" + f"?ver=1&man=yes&client=mobi&keyword=&duration=&hash={song_hash}&album_audio_id=", headers=headers, timeout=10) as response2: lyrics_info = await response2.json() @@ -46,7 +48,8 @@ async def kugou(title, artist, album): lyrics_key = lyrics_info["candidates"][0]["accesskey"] # 第三层Json,要求获得并解码Base64 async with session.get( - f"http://lyrics.kugou.com/download?ver=1&client=pc&id={lyrics_id}&accesskey={lyrics_key}&fmt=lrc&charset=utf8", + f"http://lyrics.kugou.com/download" + f"?ver=1&client=pc&id={lyrics_id}&accesskey={lyrics_key}&fmt=lrc&charset=utf8", headers=headers, timeout=10) as response3: lyrics_data = await response3.json() @@ -60,7 +63,8 @@ async def kugou(title, artist, album): async def api_2(title, artist, album): headers = { - 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 LrcAPI/1.1', + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) ' + 'Chrome/92.0.4515.131 LrcAPI/1.1', } url = f"http://api.lrc.cx/lyrics?title={title}&artist={artist}&album={album}&path=None&limit=1&api=lrcapi" diff --git a/mod/searchx/__init__.py b/mod/searchx/__init__.py new file mode 100644 index 0000000..a4933ea --- /dev/null +++ b/mod/searchx/__init__.py @@ -0,0 +1,33 @@ +from concurrent import futures + +from mod.searchx import api, kugou + + +def search_all(title, artist, album, timeout=30): + funcs = [api, kugou] + results = [] + + def request(task): + res: list = task.search(title, artist, album) + if isinstance(res, list): + results.extend(res) + + with futures.ThreadPoolExecutor() as executor: + _futures = [] + for func in funcs: + _futures.append(executor.submit(request, func)) + + # 等待所有任务完成,或回收超时任务,处理TimeoutError + for future in futures.as_completed(_futures, timeout=timeout): + future.result() + # 回收超时任务 + for future in _futures: + if future.done() and future.exception(): + future.result() + else: + future.cancel() + return results + + +if __name__ == "__main__": + print(search_all("大地", "Beyond", "")) diff --git a/mod/searchx/api.py b/mod/searchx/api.py new file mode 100644 index 0000000..7dbf579 --- /dev/null +++ b/mod/searchx/api.py @@ -0,0 +1,21 @@ +import requests + + +headers = { + "Host": "127.0.0.1", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" +} + + +def search(title='', artist='', album='') -> list: + try: + url = f"http://cn.lrc.cx:28884/jsonapi?title={title}&artist={artist}&album={album}&path=None&limit=1&api=lrcapi" + response = requests.get(url, headers=headers) + return response.json() + except Exception as e: + print(f"Request failed: {e}") + return [] + + +if __name__ == "__main__": + print(search(title="光辉岁月")) diff --git a/mod/searchx/kugou.py b/mod/searchx/kugou.py new file mode 100644 index 0000000..d0821a7 --- /dev/null +++ b/mod/searchx/kugou.py @@ -0,0 +1,108 @@ +import json +import aiohttp +import asyncio +import base64 +import random +import string +import time + +from mod import textcompare +from mod import tools + +headers = {'User-Agent': '{"percent": 21.4, "useragent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' + 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36", "system": "Chrome ' + '116.0 Win10", "browser": "chrome", "version": 116.0, "os": "win10"}', } + + +async def get_cover(session, m_hash, m_id): + def _dfid(num): + random_str = ''.join(random.sample((string.ascii_letters + string.digits), num)) + return random_str + + # 获取a-z 0-9组成的随机23位数列 + def _mid(num): + random_str = ''.join(random.sample((string.ascii_letters[:26] + string.digits), num)) + return random_str + + music_url = 'https://wwwapi.kugou.com/yy/index.php' + parameter = { + 'r': 'play/getdata', + 'hash': m_hash, + 'dfid': _dfid(23), + 'mid': _mid(23), + 'album_id': m_id, + '_': str(round(time.time() * 1000)) # 时间戳 + } + json_data_r = await session.get(music_url, headers=headers, params=parameter) + json_data = json.loads(await json_data_r.text()) + if json_data.get("data"): + return json_data['data'].get("img") + return "" + + +async def a_search(title='', artist='', album=''): + if not any((title, artist, album)): + return None + result_list = [] + limit = 3 + + async with aiohttp.ClientSession() as session: + async with session.get( + f"http://mobilecdn.kugou.com/api/v3/search/song?format=json&keyword={' '.join([item for item in [title, artist, album] if item])}&page=1&pagesize=2&showtype=1", + headers=headers) as response: + if response.status == 200: + song_info_t = await response.text() + song_info = json.loads(song_info_t) + song_info = song_info["data"]["info"] + if len(song_info) >= 1: + for song_item in song_info: + song_name = song_item["songname"] + singer_name = song_item.get("singername", "") + song_hash = song_item["hash"] + album_id = song_item["album_id"] + album_name = song_item.get("album_name", "") + title_conform_ratio = textcompare.association(title, song_name) + artist_conform_ratio = textcompare.assoc_artists(artist, singer_name) + ratio = (title_conform_ratio * (artist_conform_ratio+1)/2) ** 0.5 + if ratio >= 0.2: + async with session.get( + f"https://krcs.kugou.com/search?ver=1&man=yes&client=mobi&keyword=&duration=&hash={song_hash}&album_audio_id=", + headers=headers) as response2: + lyrics_info = await response2.json() + lyrics_id = lyrics_info["candidates"][0]["id"] + lyrics_key = lyrics_info["candidates"][0]["accesskey"] + # 第三层Json,要求获得并解码Base64 + async with session.get( + f"http://lyrics.kugou.com/download?ver=1&client=pc&id={lyrics_id}&accesskey={lyrics_key}&fmt=lrc&charset=utf8", + headers=headers) as response3: + lyrics_data = await response3.json() + lyrics_encode = lyrics_data["content"] # 这里是Base64编码的数据 + lrc_text = tools.standard_lrc(base64.b64decode(lyrics_encode).decode('utf-8')) # 这里解码 + # 结构化JSON数据 + music_json_data = { + "title": song_name, + "album": album_name, + "artists": singer_name, + "lrc": lrc_text, + "cover": await get_cover(session, song_hash, album_id), + "hash": tools.calculate_md5( + f"title:{song_name};artists:{singer_name};album:{album_name}") + } + result_list.append({ + "data": music_json_data, + "ratio": ratio + }) + if len(result_list) > limit: + break + else: + return None + sort_li = sorted(result_list, key=lambda x: x['ratio'], reverse=True) + return sort_li + + +def search(title='', artist='', album=''): + return asyncio.run(a_search(title=title, artist=artist, album=album)) + + +if __name__ == "__main__": + print(search(album="光辉岁月")) diff --git a/mod/searchx/migu.py b/mod/searchx/migu.py new file mode 100644 index 0000000..d5f1571 --- /dev/null +++ b/mod/searchx/migu.py @@ -0,0 +1,44 @@ +import requests + +from mod import tools + + +class MiGuMusicClient: + BASE_URL = "https://m.music.migu.cn/" + header = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:80.0) Gecko/20100101 Firefox/80.0', + 'Referer': 'https://m.music.migu.cn/' + } + + def fetch_lyric(self, song_id): + url = f'https://music.migu.cn/v3/api/music/audioPlayer/getLyric?copyrightId={song_id}' + res = requests.get(url, headers=self.header) + return res.json()["lyric"] + + def fetch_id3_by_title(self, title): + url = self.BASE_URL + f"migu/remoting/scr_search_tag?rows=10&type=2&keyword={title}&pgc=1" + res = requests.get(url, headers=self.header) + songs = res.json()["musics"] + results = [] + for song in songs: + lyrics = self.fetch_lyric(song['copyrightId']) + results.append({ + "title": song['songName'], + "album": song['albumName'], + "artists": song['singerName'], + "lrc": lyrics, + "cover": song['cover'], + "hash": tools.calculate_md5( + f"title:{song['songName']};artists:{song['singerName']};album:{song['albumName']}") + }) + return results + + +def search(title='', artist='', album='') -> list: + migumusic = MiGuMusicClient() + result = migumusic.fetch_id3_by_title(title) + return result + + +if __name__ == "__main__": + print(search(title="光辉岁月")) diff --git a/mod/searchx/netease.py b/mod/searchx/netease.py new file mode 100644 index 0000000..e69de29 diff --git a/src/css/mod.css b/src/css/mod.css index 78ced4b..36d1ee2 100644 --- a/src/css/mod.css +++ b/src/css/mod.css @@ -27,6 +27,15 @@ hr { box-shadow: 0 0 10px rgba(255, 160, 0, 0.8); } +.no-select { + user-select: none; +} + +.filebox { + min-height: 70vh; + margin-top: 20px; +} + .footer { position: fixed; left: 0; From 4dbb6ee398d1bc0782fe5fe561b309ba56bdb3e5 Mon Sep 17 00:00:00 2001 From: HisAtri Date: Sun, 31 Mar 2024 21:45:57 +0800 Subject: [PATCH 2/5] =?UTF-8?q?Feature:=20=E6=9C=AA=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E5=88=B0=E5=9B=BE=E7=89=87=E6=97=B6=E8=BF=94=E5=9B=9E=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E7=A0=81404?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/cover.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/api/cover.py b/api/cover.py index 0f9827d..eca45ec 100644 --- a/api/cover.py +++ b/api/cover.py @@ -24,18 +24,13 @@ def cover_api(): req_args = {key: request.args.get(key) for key in request.args} # 构建目标URL target_url = 'http://api.lrc.cx/cover?' + '&'.join([f"{key}={req_args[key]}" for key in req_args]) - """ - # 跟踪重定向并获取最终URL - final_url = follow_redirects(target_url) - # 获取最终URL的内容或响应 - response = requests.get(final_url) - if response.status_code == 200: - content_type = response.headers.get('Content-Type', 'application/octet-stream') - return Response(response.content, content_type=content_type) - else: + result = requests.get(target_url, headers={"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/"}) + if result.status_code == 200: + return result.content, 200, {"Content-Type": result.headers['Content-Type']} + elif result.status_code == 404: abort(404) - """ - return redirect(target_url, 302) + else: + abort(500) @v1_bp.route('/cover/', methods=['GET']) From 0612859ce3ae06d52c7aea33067d7983d2304cc8 Mon Sep 17 00:00:00 2001 From: HisAtri Date: Sun, 31 Mar 2024 22:06:15 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=96=B0=E7=9A=84API?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E6=96=B9=E5=BC=8F=EF=BC=88=E5=A4=9A=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/lyrics.py | 21 ++++++++------------- mod/searchx/kugou.py | 4 ++-- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/api/lyrics.py b/api/lyrics.py index 8d7abdd..ae71621 100644 --- a/api/lyrics.py +++ b/api/lyrics.py @@ -7,6 +7,7 @@ from urllib.parse import unquote_plus from mod import search, lrc +from mod import searchx from mod import tools from mod import tag from mod.auth import webui @@ -54,11 +55,10 @@ def lyrics(): title = unquote_plus(request.args.get('title')) artist = unquote_plus(request.args.get('artist', '')) album = unquote_plus(request.args.get('album', '')) - executor = concurrent.futures.ThreadPoolExecutor() - # 提交任务到线程池,并设置超时时间 - future = executor.submit(search.main, title, artist, album) - lyrics_text = future.result(timeout=30) - return lrc.standard(lyrics_text) + result: list = searchx.search_all(title=title, artist=artist, album=album, timeout=30) + if not result[0].get('lyrics'): + return "Lyrics not found.", 404 + return result[0].get('lyrics') except: return "Lyrics not found.", 404 @@ -92,18 +92,13 @@ def lrc_json(): "lyrics": file_content }) - lyrics_list = search.allin(title, artist, album) + lyrics_list = searchx.search_all(title, artist, album) if lyrics_list: for i in lyrics_list: if not i: continue - i = lrc.standard(i) - response.append({ - "id": tools.calculate_md5(i), - "title": title, - "artist": artist, - "lyrics": i - }) + i['lyrics'] = lrc.standard(i['lyrics']) + response.append(i) _response = jsonify(response) _response.headers['Content-Type'] = 'application/json; charset=utf-8' return jsonify(response) diff --git a/mod/searchx/kugou.py b/mod/searchx/kugou.py index d0821a7..d9e51c1 100644 --- a/mod/searchx/kugou.py +++ b/mod/searchx/kugou.py @@ -83,7 +83,7 @@ async def a_search(title='', artist='', album=''): "title": song_name, "album": album_name, "artists": singer_name, - "lrc": lrc_text, + "lyrics": lrc_text, "cover": await get_cover(session, song_hash, album_id), "hash": tools.calculate_md5( f"title:{song_name};artists:{singer_name};album:{album_name}") @@ -97,7 +97,7 @@ async def a_search(title='', artist='', album=''): else: return None sort_li = sorted(result_list, key=lambda x: x['ratio'], reverse=True) - return sort_li + return [i.get('data') for i in sort_li] def search(title='', artist='', album=''): From 08209a0bc35034db0dc25b9d8f76ab4b1baf3a59 Mon Sep 17 00:00:00 2001 From: HisAtri Date: Mon, 1 Apr 2024 00:59:24 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/__import__.py | 1 + api/waf.py | 98 ++++++++++++++++++++++++++++++++++++++++++++ mod/args/__init__.py | 2 +- mod/searchx/api.py | 4 +- 4 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 api/waf.py diff --git a/api/__import__.py b/api/__import__.py index 1f9cf2e..cedc095 100644 --- a/api/__import__.py +++ b/api/__import__.py @@ -1,4 +1,5 @@ from . import cover, login, lyrics, source, tag, time, file +from . import waf """ 引入所有子模块 diff --git a/api/waf.py b/api/waf.py new file mode 100644 index 0000000..f950bad --- /dev/null +++ b/api/waf.py @@ -0,0 +1,98 @@ +""" +WAF基本防火墙,承担基本的防火墙功能 +防注入/恶意文件读取 +""" +from api import * + +import re +from flask import request, abort + + +@app.before_request +def check(): + """ + 检查请求是否合法 + :return: + """ + # 获取请求的URL的路径+参数部分,不包括域名 + path = request.path + if waf(path): + logger.warning(f"检测到恶意请求: {path}") + abort(403) + + +def waf(req: str): + """ + :param req: + :return: + """ + NN_RULES = r"""\.\./ +\:\$ +\$\{ +[\\/]proc[\\/]self[\\/](environ|cmdline|maps) +(?i)select.+(from|limit) +(?i)d(?:elete|rop|ump).+table +(?:(union(.*?)select)) +having|rongjitest +sleep\((\s*)(\d*)(\s*)\) +benchmark\((.*)\,(.*)\) +base64_decode\( +(?:from\W+information_schema\W) +(?:(?:current_)user|database|schema|connection_id)\s*\( +(?:etc\/\W*passwd) +into(\s+)+(?:dump|out)file\s* +group\s+by.+\( +xwork.MethodAccessor +xwork\.MethodAccessor +(gopher|doc|php|glob|file|phar|zlib|ftp|ldap|dict|ogg|data)\:\/ +java\.lang +\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[ +\<(iframe|script|body|img|layer|div|meta|style|base|object|input) +(onmouseover|onerror|onload)\= +\.\./\.\./ +/\* +\:\$ +\$\{ +(?:define|eval|file_get_contents|include|require|require_once|shell_exec|phpinfo|system|passthru|char|chr|preg_\w+|execute|echo|print|print_r|var_dump|(fp)open|alert|showmodaldialog)\( +\$_(GET|post|cookie|files|session|env|phplib|GLOBALS|SERVER)\[ +\s+(or|xor|and)\s+.*(=|<|>|'|") +(?i)select.+(from|limit) +(?:(union(.*?)select)) +sleep\((\s*)(\d*)(\s*)\) +benchmark\((.*)\,(.*)\) +(?:from\W+information_schema\W) +(?:(?:current_)user|database|schema|connection_id)\s*\( +into(\s+)+(?:dump|out)file\s* +group\s+by.+\( +\<(iframe|script|body|img|layer|div|meta|style|base|object|input) +@eval.*GET(.*])""" + for re_str in NN_RULES.split("\n"): + if re.search(re_str, req): + # 匹配到恶意请求 + logger.warning(f"匹配规则: {re_str}") + return True + # 测试集均为恶意请求,返回False意味着存在漏报 + return False + + +def test(): + DATAS = [ + "/../../", # 目录穿越 + "/proc/self/maps", # 读取系统信息 + "/etc/passwd", # 读取密码文件 + "/etc/shadow", # 读取密码文件 + "php://input", # PHP流协议 + "SELECT * FROM", # SQL注入 + "DROP TABLE", # SQL注入 + "SeleCt * fRoM", # SQL注入,大小写混合 + "sleep(3)", # SQL注入 + "@@version", # SQL注入 + "S%e%l%e%c%t * F%rom", # SQL注入,百分号编码 + ] + for data in DATAS: + if not waf(data): + print(f"有恶意请求未被拦截: {data}") + + +if __name__ == "__main__": + test() diff --git a/mod/args/__init__.py b/mod/args/__init__.py index cca6d8d..b9e0efe 100644 --- a/mod/args/__init__.py +++ b/mod/args/__init__.py @@ -89,7 +89,7 @@ def __init__(self): self.port = first(env_args.port, kw_args.port, config_args.port, default.port) self.ip = first(config_args.ip, default.ip) self.debug = kw_args.debug - self.version = "1.5.2" + self.version = "1.5.3" def valid(self, key): """ diff --git a/mod/searchx/api.py b/mod/searchx/api.py index 7dbf579..31858d5 100644 --- a/mod/searchx/api.py +++ b/mod/searchx/api.py @@ -9,11 +9,11 @@ def search(title='', artist='', album='') -> list: try: - url = f"http://cn.lrc.cx:28884/jsonapi?title={title}&artist={artist}&album={album}&path=None&limit=1&api=lrcapi" + url = f"http://cn.lrc.cx:880/jsonapi?title={title}&artist={artist}&album={album}&path=None&limit=1&api=lrcapi" response = requests.get(url, headers=headers) return response.json() except Exception as e: - print(f"Request failed: {e}") + print(f"LrcAPI Server - Request failed: {e}") return [] From e3869da9278bad17049639bb8602ec43178df124 Mon Sep 17 00:00:00 2001 From: HisAtri Date: Mon, 1 Apr 2024 01:05:14 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_app.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_app.py b/tests/test_app.py index 182b8c8..8fcd3cf 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -10,5 +10,11 @@ def test_home_route(): def test_source_route(): with app.test_client() as client: - response = client.get('/src/index.html') + response = client.get('/src') assert response.status_code < 300 + + +def test_lyrics_route(): + with app.test_client() as client: + response = client.get('/lyrics?title=使一颗心免于哀伤') + assert response.status_code < 500