Files
dsh_shenxian/scripts/find-ui-scan.py
T
admin 0cdbf52c4a chore(开源脱敏): find-ui 扫描器去掉硬编码服务器路径 + 登记档案 140
- scripts/find-ui-scan.py:PROFILE_GLOB 改由 DSHS_USERS_DIR 注入,缺失即显式失败(不再写死
  /var/lib/dshs)⇒ 补上上一轮涉密外置扫描的唯一漏项(大小写不敏感扫描 1 → 0)
- scripts/find-ui.mjs:用 config/index.cjs#usersDir() 取值并随 ssh 命令注入远端;
  解析不出 POSIX 绝对路径即报错退出(⛔ 不静默回落,避免假阴性)
- dsh-server-docs/INDEX.md:登记 04-140(涉密内容外置到配置目录)

验证:py 语法 OK / node --check OK / 缺 env 显式 exit=2 / 实跑命中 8-8 个 UI 分区(与原行为一致)
2026-09-19 15:42:56 +08:00

105 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""find-ui-scan.py —— 在**服务器上**扫描「哪些包注册了实例 UI 分区」,输出 TSV。
由 `scripts/find-ui.cjs` 通过 `ssh <host> python3 -` 喂进来执行(本文件不在服务器上落地)。
为什么要落到服务器上跑:**权威事实是"活着的 profile 里装了什么"**,不是仓里的快照、
也不是文档库里的历史副本(2026-09-15 就是在这上面绕了很久)。
输出 TSV(每行一个分区):
pkg <TAB> origin <TAB> file <TAB> id <TAB> order <TAB> label_raw
scan 顺序:**自研(@dsh-local)在前**,官方在后 —— 排查时先看自己的。
"""
import glob
import json
import os
import re
import sys
# 用户根目录由调用方(`scripts/find-ui.mjs`)通过 `DSHS_USERS_DIR` 传入 —— 它从 `config/` 读,
# 不在这里写死服务器布局。⛔ 缺失时**显式失败**,绝不回落到某个猜测值(静默 0 命中 = 假阴性)。
USERS_DIR = os.environ.get('DSHS_USERS_DIR', '').rstrip('/')
if not USERS_DIR:
sys.stderr.write('!! 未收到 DSHS_USERS_DIR(用户数据根)—— 由 scripts/find-ui.mjs 注入,勿手动直连\n')
sys.exit(2)
PROFILE_GLOB = USERS_DIR + '/*/home/profiles/web/node_modules/@dsh-local/*/lib/client.js'
OFFICIAL_GLOB = '/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai/*/lib/client.js'
# 注册对象形如: { name: "settings.section", id: "x", order: 100, label: … }
REG = re.compile(r'name:\s*"settings\.section"')
ID = re.compile(r'\bid:\s*"([^"]+)"')
ORDER = re.compile(r'\border:\s*(-?\d+)')
LABEL_LIT = re.compile(r'label:\s*(?:\([^)]*\)\s*=>\s*|function\s*\([^)]*\)\s*\{\s*return\s*)\s*"((?:[^"\\]|\\.)*)"')
LABEL_KEY = re.compile(r'label:\s*(?:\([^)]*\)\s*=>\s*|function\s*\([^)]*\)\s*\{\s*return\s*)\s*t\(\s*"([^"]+)"\s*\)')
def unique_files(pattern):
"""同一 profile 内容一致 ⇒ 只取**第一个** profile,避免同一分区重复几十行。"""
hits = sorted(glob.glob(pattern))
if not hits:
return []
if 'users' in pattern:
# 取第一个用户目录下的全部 @dsh-local 包
first_user = hits[0].split('/home/profiles/')[0]
return [h for h in hits if h.startswith(first_user + '/')]
return hits
def label_of(text, raw):
"""label 是字面量 ⇒ 解码转义;是 t("key") ⇒ 在同文件的词典里找值。"""
m = LABEL_LIT.search(raw)
if m:
lit = m.group(1)
# 关键:`\u7528\u6237\u8bbe\u7f6e` 这类**转义 unicode** 要还原成人能看的字
try:
return json.loads('"' + lit + '"')
except Exception:
return lit
m = LABEL_KEY.search(raw)
if m:
key = m.group(1)
d = re.search(r'"' + re.escape(key) + r'":\s*"((?:[^"\\]|\\.)*)"', text)
if d:
try:
return json.loads('"' + d.group(1) + '"')
except Exception:
return d.group(1)
return 't("%s")' % key
return '(无 label)'
def scan(files, origin):
for f in files:
try:
text = open(f, encoding='utf-8', errors='replace').read()
except Exception:
continue
if 'settings.section' not in text:
continue
# ⚠️ 用 removeprefix 而不是 lstrip:lstrip 是按**字符集**删,会把官方包的 "@deepseek" 也啃掉
k = f.split('/node_modules/')[-1].split('/lib/client.js')[0]
pkg = k.removeprefix('@dsh-local/')
for m in REG.finditer(text):
raw = text[m.start():m.start() + 420]
i = ID.search(raw)
if not i:
continue
o = ORDER.search(raw)
print('\t'.join([
pkg, origin, f, i.group(1), o.group(1) if o else '—', label_of(text, raw),
]))
def main():
mine = unique_files(PROFILE_GLOB)
if not mine:
sys.stderr.write('!! 没找到任何 profile 的 @dsh-local 包(集群模式下用户可能落别的 Worker)\n')
scan(mine, '自研')
scan(unique_files(OFFICIAL_GLOB), '官方')
if __name__ == '__main__':
main()