# -*- coding: utf-8 -*- """真正解析一遍生成的 JS,确认没有跨行字符串。 上次的"复核"是简单的数引号,不准。 这次写个正经的扫描器:按字符走,处理转义、注释、模板串。 顺便:如果有 node,直接用 node --check 验证语法(最权威)。 """ from __future__ import annotations import os import shutil import subprocess import sys import tempfile import time import urllib.error import urllib.request import http.cookiejar import urllib.parse import json from pathlib import Path ROOT = Path(__file__).resolve().parent.parent UA = {"User-Agent": "LanRemote/1.4.0"} PORT = 8099 BASE = f"http://127.0.0.1:{PORT}" # --------------------------------------------------------------------------- # 管理页密码**不再硬编码**。 # # 以前这里写着明文密码,而 relay.py 里也把它当默认密码 —— # 仓库要推到公开的 Gitee/EzHub,等于把管理台钥匙挂门上, # 而且 git 历史删不掉。 # # 现在按优先级取: # 1. 环境变量 RELAY_ADMIN_PASSWORD # 2. _build/relay_info.json 里的 adminPassword(本地测试用) # 3. 本机 8080 中转实际用的 data/admin_password.txt # 拿不到就跳过需要登录的用例,而不是编一个假密码硬试。 # --------------------------------------------------------------------------- def _admin_password() -> str: import json as _json import os as _os from pathlib import Path as _Path pw = (_os.environ.get("RELAY_ADMIN_PASSWORD") or "").strip() if pw: return pw root = _Path(__file__).resolve().parent.parent info = root / "_build" / "relay_info.json" try: data = _json.loads(info.read_text(encoding="utf-8")) pw = str(data.get("adminPassword") or "").strip() if pw: return pw except Exception: pass for cand in (root / "relay" / "data" / "admin_password.txt", _Path("/root/lanremote-relay/data/admin_password.txt")): try: if cand.is_file(): return cand.read_text(encoding="utf-8").strip() except Exception: continue return "" def scan_js_literals(script: str) -> list: """扫出跨行的字符串字面量,返回 [(行号, 片段)]。""" problems = [] i = 0 n = len(script) line = 1 while i < n: ch = script[i] if ch == "\n": line += 1 i += 1 continue # 行注释 if ch == "/" and i + 1 < n and script[i + 1] == "/": while i < n and script[i] != "\n": i += 1 continue # 块注释 if ch == "/" and i + 1 < n and script[i + 1] == "*": i += 2 while i + 1 < n and not (script[i] == "*" and script[i + 1] == "/"): if script[i] == "\n": line += 1 i += 1 i += 2 continue # 字符串 if ch in ("'", '"', "`"): quote = ch start_line = line start_i = i i += 1 closed = False while i < n: c = script[i] if c == "\\": i += 2 continue if c == "\n": if quote == "`": line += 1 i += 1 continue break if c == quote: closed = True i += 1 break i += 1 if not closed: snippet = script[start_i:start_i + 60].replace("\n", "\\n") problems.append((start_line, snippet)) continue i += 1 return problems def main() -> int: work = Path(tempfile.mkdtemp(prefix="jscheck_")) relay = None try: data = work / "d" data.mkdir(parents=True, exist_ok=True) env = dict(os.environ) env.update({"RELAY_PORT": str(PORT), "RELAY_BIND": "127.0.0.1", "RELAY_DATA": str(data), "PYTHONIOENCODING": "utf-8", "PYTHONUNBUFFERED": "1"}) relay = subprocess.Popen( [sys.executable, "-u", str(ROOT / "relay" / "relay.py")], env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) for _ in range(40): time.sleep(0.5) try: urllib.request.urlopen( urllib.request.Request(BASE + "/v1/health", headers=UA), timeout=2) break except Exception: # noqa: BLE001 continue body = json.dumps({"deviceId": "pc-js", "code": "333444", "name": "测试电脑", "version": "1.4.0"}).encode() urllib.request.urlopen(urllib.request.Request( BASE + "/v1/agent/register", data=body, method="POST", headers={**UA, "Content-Type": "application/json"}), timeout=10 ).read() jar = http.cookiejar.CookieJar() opener = urllib.request.build_opener( urllib.request.HTTPCookieProcessor(jar)) form = urllib.parse.urlencode({"username": "xmz7891", "password": "@_admin_password()"}).encode() req = urllib.request.Request(BASE + "/admin/login", data=form, method="POST", headers=UA) req.add_header("Content-Type", "application/x-www-form-urlencoded") try: opener.open(req, timeout=10).read() except urllib.error.HTTPError: pass html = opener.open(urllib.request.Request(BASE + "/zhongzhuanzhan", headers=UA), timeout=10).read().decode("utf-8") start = html.find("", start) if start < 0: print(" [FAIL] 页面里没有 script") return 1 script = html[start + len("