Your Name
cdd0be328a
fix: 六轮深度扫描 — 47项Bug修复、安全加固、死代码清理
...
Critical runtime bugs:
- terminal.html WebSSH完全不可用(URL前缀/JSON解析/Content-Type三处错误)
- servers.py路由遮蔽:/logs被/{id}拦截,3个前端页面同步日志查询失败
- scripts.html startExecPoll()→startExecPolling(),长任务快速执行崩溃
- agent.py {value!r!s:.50}格式串非法,agent发非数值时ValueError
- alerts.html d.daily.reduce()无null检查,API返回空数据时TypeError
Resource leak / stability:
- websocket.py僵尸连接未关闭TCP,文件描述符泄漏
- websocket.py _last_alert_time字典无限增长(加1小时过期清理)
- asyncssh_pool.py全忙时超过MAX_CONNECTIONS无限增长
- self_monitor.py Telegram告警无冷却,宕机时每30秒刷屏
- schedule_runner.py一次性调度执行超60秒会重复触发
- 限速脚本EXPIRE每次重置窗口可绕过(改用Lua原子脚本)
Security:
- JWT access token加token_version声明,改密码后旧token立失效(零宽限)
- INSTALL_MODE导入时常量→动态函数,安装后JWT认证不再残留禁用
- install.py /lock端点加管理员存在性验证,防止阻断安装
- ServerUpdate schema移除connectivity只读字段,防止伪造连接状态
Frontend fixes:
- doExec()缺r.ok检查、commands.html null检查
- _server_to_dict()补last_checked_at+ssh_key_public
- _field_match()逗号cron表达式修复
- alerts类型显示、SSH会话名称、搜索高亮定位
- 一次性/循环定时任务(run_mode+fire_at+自动禁用)
Dead code removed (400+ lines):
- SyncService batch_push/_push_single等5个方法(零调用者)
- 5个未使用schema(SyncCommands/SyncConfig/SyncSftp/FileDeploy/PaginatedResponse)
- 6个零调用service方法、3个无前端API端点
- 4个未使用import
Schema migrations:
- push_schedules: run_mode + fire_at列,cron_expr改NULL
- servers: 7个新列 + ssh_key_private/public VARCHAR(500)→TEXT
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com >
2026-05-24 16:26:40 +08:00
Your Name
fbf5eadcf2
fix(security): R2+R3 全面代码审计修复 — 3 CRITICAL / 9 HIGH / 15 MEDIUM / 10 LOW
...
CRITICAL:
- C-1: sync_service.py rsync命令shlex.quote防Shell注入回归
- C-2: agent.py exec端点危险命令正则拦截+compare_digest+僵尸进程修复
- C-3: WebSSH JWT token绑定server_id防IDOR
HIGH:
- H-1: 所有PUT/POST端点setattr改为字段白名单防任意字段注入
- H-2: settings/assets/scripts端点添加JWT认证
- H-3: refresh_token invalidate-then-generate防并发重放
- H-4: servers.py Redis pipeline解决N+1查询
- H-7: settings API Key/Secret掩码+禁止修改
- H-8: TOTP暴力破解速率限制(5次/5分钟)
- H-9: get_optional_admin改用request-scoped session
MEDIUM:
- M-2: X-Real-IP优先+X-Forwarded-For取末值防IP伪造
- M-4: asyncssh异常信息脱敏(只含server_id)
- M-5: 分页limit上限(500/200/500/500)
- M-9: API层Redis依赖抽象至dependencies.py
- M-10: WebSocket ConnectionManager多worker Redis聚合
- M-13: login_attempts复合索引
- M-14: ServerRepository.get_by_ids批量查询
- M-15: API错误消息统一中文
R3追加:
- sysctl命令注入防护+regex验证
- $DB_PASS明文存储→masked_command双命令方案
- MYSQL_PWD环境变量替代-p flag
- health端点拆分(公开/需认证)
- repo层**kwargs→字段白名单
- 全局str(e)信息泄露修复(API/Telegram/WebSocket)
- SSRF私有IP拦截
- Nginx HTTPS配置+DB备份脚本
- 前端CDN SRI哈希+XSS修复
- __import__替换为正常import
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-24 10:58:33 +08:00
Your Name
9bba58a529
feat: Telegram test+ChatID, Agent IP allowlist/upgrade, alert history page
...
Telegram:
- POST /api/settings/telegram/test: send test message (checks token+chat_id)
- GET /api/settings/telegram/chats: call getUpdates, return up to 5 chats
- settings.html: test button + detect chat IDs with click-to-fill
Agent IP allowlist (web/agent/agent.py):
- allowed_ips config: list of trusted IPs/CIDRs from config.json
- _ip_allowed(): checks exact IP, CIDR (ipaddress module), hostname
- verify_api_key(): now checks IP allowlist before API key
- /health: also checks IP allowlist
- install.sh: auto-extracts central server IP from --url, adds to allowed_ips
Agent auto-upgrade (server/api/servers.py):
- POST /api/servers/{id}/upgrade-agent: SSH → curl new agent.py → systemctl restart
- servers.html: 升级 Agent button in Agent tab (only when online)
Alert history (new page):
- domain/models: AlertLog table (server_id, type, value, is_recovery, created_at)
- migrations.py: CREATE TABLE IF NOT EXISTS alert_logs
- websocket.py: _save_alert_log() called from broadcast_alert/recovery
- settings.py: GET /api/alert-history/ (paginated, filters), GET /stats
- main.py: register alert_history_router
- alerts.html: new page with stats cards, top-servers bar chart, alert list
- layout.js: 🔔 告警中心 added to sidebar nav
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 18:27:12 +08:00
Your Name
488838c989
feat: separate subscription/manual IPs + master enable/disable toggle
...
Data model (separate storage):
- login_allowlist_enabled: 'true'/'false' master switch
- login_subscription_url: URL to fetch every 2h
- login_subscription_ips: last fetch result (replaced entirely each refresh)
- login_manual_ips: manually added (never overwritten by refresh)
- login_allowed_ips: combined (used by auth for legacy compat)
ip_allowlist_refresh.py:
- Saves to login_subscription_ips (replace) + rebuilds login_allowed_ips
auth_service.py:
- Login check: only runs when LOGIN_ALLOWLIST_ENABLED is 'true'
- Combines subscription_ips + manual_ips at auth time (no stale combined key needed)
settings.py:
- POST /ip-allowlist/toggle: quick enable/disable endpoint
- GET /ip-allowlist: returns subscription_ips, manual_ips separately
- POST /ip-allowlist: saves manual_ips + optional subscription_url + optional enabled
settings.html:
- Toggle switch: enable/disable with warning if list is empty
- Green notice when enabled (warns about self-lockout risk)
- Grey notice when disabled
- Subscription IPs section: read-only, auto-refresh badge
- Manual IPs section: deletable per-IP, clear all button
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 18:19:47 +08:00
Your Name
da43960f38
feat: subscription URL stored in settings, auto-refresh allowlist every 2h
...
config.py:
- LOGIN_SUBSCRIPTION_URL: stored in DB, configurable from UI
- LOGIN_MANUAL_IPS: manually added IPs (preserved across auto-refresh)
background/ip_allowlist_refresh.py (new):
- ip_allowlist_refresh_loop(): primary-worker task, every 2h
- _do_refresh(): fetch subscription, parse IPs, merge with manual IPs,
write combined to LOGIN_ALLOWED_IPS + DB setting; runs once at startup (5s delay)
- get_last_refresh_time(): returns last successful refresh timestamp
main.py:
- Register ip_allowlist_refresh_loop as background task
settings.py:
- GET /ip-allowlist: now returns subscription_url, manual_ips, last_refresh
- POST /ip-allowlist: saves manual_ips + subscription_url, triggers refresh
- POST /ip-allowlist/manual: append to manual list only, trigger rebuild
- DELETE /ip-allowlist/ip: remove single manual IP, trigger rebuild
settings.html:
- Subscription URL field with 保存 + 🔄 instant-refresh button
- Last refresh timestamp display
- Status badge shows '订阅自动刷新' when URL configured
- IP badges: blue=manual (deletable), grey=from subscription (auto)
- 预览 button to inspect subscription IPs without saving
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 18:15:29 +08:00
Your Name
5fcc1e22a6
feat: login IP allowlist with proxy subscription parser
...
server/infrastructure/subscription_parser.py (new):
- parse_subscription_content(): fetch and decode subscription, extract host/IP
- Supports SS, VMess, VLESS, Trojan, Hysteria2 formats
- Base64 decode with padding fix; IPv4/CIDR/hostname detection
server/config.py:
- LOGIN_ALLOWED_IPS setting (empty = allow all, comma-separated)
server/api/settings.py:
- POST /settings/ip-allowlist/parse-subscription: fetch URL, decode, return hosts
- POST /settings/ip-allowlist: save allowlist to settings table + reload
- GET /settings/ip-allowlist: return current list
server/application/services/auth_service.py:
- _ip_in_allowlist(): supports exact IP, CIDR (ipaddress module), hostname match
- login(): check allowlist before brute-force check; if IP blocked return
reason='ip_blocked' with clear message; audit log 'login_ip_blocked'
web/app/settings.html:
- New '登录 IP 白名单' card with:
- Subscription URL input + parse button (calls backend parser)
- Parsed IPs with checkboxes (select/deselect before adding)
- Manual IP/CIDR/hostname textarea input
- Current allowlist with per-IP remove buttons
- Clear all button (removes restriction)
- Status badge: 启用 N条 / 未启用(不限制)
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 18:11:23 +08:00
Your Name
37ceaef6d9
feat: auto-poll Agent online status after install with live badge update
...
- remoteInstallAgent(): after success, start _startAgentOnlinePoller() instead
of one-shot setTimeout(loadServers, 2000)
- _startAgentOnlinePoller(serverId): poll GET /api/servers/{id} every 5s up to
2 minutes (24 attempts); when is_online=true, update status badge live,
disable remote-install button, toast 'Agent 已上线', reload server list
- _updateAgentStatusBadge(isOnline): in-place badge update without full reload
- Install log shows: '✓ 安装成功!等待首次心跳...' then '✅ Agent 心跳已收到'
- Hint text explains: install ~30-60s, first heartbeat another ~60s
- Manual install: 'click to wait for Agent online' button triggers same poller
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 17:47:22 +08:00
Your Name
d414b945e1
feat: audit log time-range filter + script execution schedules
...
Audit log time-range filter:
- audit_log_repo.py: unified query() with action/admin_username/date_from/date_to
filters + total count; _build_filters() helper for date parsing
- settings.py GET /audit/: add date_from, date_to, admin_username query params
- audit.html: date range inputs (YYYY-MM-DD), admin filter input, action filter
expanded with optgroups (服务器/推送/脚本/认证/设置); clear filter button
Script execution schedules:
- domain/models: PushSchedule extended with schedule_type('push'|'script'),
script_id FK, script_content Text, exec_timeout int, long_task bool;
source_path made nullable
- migrations.py: 6 new ALTER TABLE statements (idempotent)
- schedule_runner.py: detect schedule_type; for 'script' type use ScriptService;
for 'push' type keep original SyncEngineV2 sync_files behaviour
- schemas.py: ScheduleCreate/Update add all new fields
- schedules.html: full UI rewrite
- type selector radio: 📁 文件推送 / ⚡ 脚本执行
- script fields: script library dropdown + textarea + timeout + long_task
- schedule list shows type badge + detail preview
- saveSched() validates required fields per type
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 17:29:16 +08:00
Your Name
4ca07fa252
feat: quick execute panel in scripts.html (no script save required)
...
Add collapsible '直接执行命令' card at top of scripts page:
- Textarea for ad-hoc command (no script_id, not saved to library)
- Multi-select server list (loaded on first expand, same batch size)
- Options: timeout, DB credential (* substitution), long_task nohup
- Ctrl+Enter shortcut to submit
- Uses same batch tracking + status panel + polling as saved scripts
- Clear button to reset command
- Batch hint shows server count and batch size
- 全选 button for quick server selection
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 17:23:25 +08:00
Your Name
d2832567c7
feat: push logs view in commands.html + paginated sync log API
...
Nexus CI/CD / test (push) Waiting to run
Nexus CI/CD / deploy (push) Blocked by required conditions
Nexus Pre-commit Checks / quick-check (push) Waiting to run
sync_log_repo.py:
- get_all_paginated(): server_id/status/sync_mode/trigger_type filters +
server name JOIN + total count for pagination
server/api/servers.py GET /servers/logs:
- Add server_id/status/sync_mode/trigger_type/offset/limit query params
- Return {items, total, offset, limit} instead of plain list
web/app/commands.html (renamed title to '操作日志'):
- Third view: '推送日志' with paginated table
- Columns: 状态(dot badge) / 服务器 / 模式 / 触发方式 / 源路径 /
目标路径 / 文件数 / 耗时 / 操作人 / 开始时间
- Filters: 服务器 + 状态(success/failed/running) + 模式 (incremental/full/...)
- Pagination controls with prev/next page (50 per page)
- Error message in row title attribute (hover for details)
web/app/push.html:
- Update loadHistory() to handle new {items, total} response format
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 17:20:53 +08:00
Your Name
14dc29f241
feat: Agent install tab with copy command + one-click remote install via SSH
...
server/api/servers.py:
- POST /{id}/install-agent: SSH-execute install.sh on managed server using
stored credentials; requires API_BASE_URL + agent_api_key; 120s timeout
- GET /{id}/agent-install-cmd: return pre-filled curl command for copy-paste
web/app/servers.html:
- New 'Agent 安装' tab in server detail panel
- Shows Agent online/offline status badge
- Warns if NEXUS_API_BASE_URL or API Key is missing
- Displays ready-to-copy curl install command with port auto-filled
- '⚡ 一键远程安装' button (SSH exec): disabled if already online
- Scrollable install log output panel
- Tab uses shared showDetailTab() + loadAgentInstall() / remoteInstallAgent()
- Explains: script auto-installs Python, downloads agent.py, writes config,
sets up systemd, opens firewall port
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 17:16:14 +08:00
Your Name
b9e8db7a3f
feat: per-alert notification toggles with switch UI
...
config.py:
- Add NOTIFY_ALERT_CPU/MEM/DISK, NOTIFY_RECOVERY, NOTIFY_TIME_DRIFT,
NOTIFY_SYSTEM_REDIS, NOTIFY_SYSTEM_MYSQL, NOTIFY_RESTART settings
(string 'true'/'false', loaded from MySQL settings table at startup)
telegram/__init__.py:
- _notify_enabled(): check setting string ('false'/'0'/'no'/'off' = disabled)
- send_telegram_alert(): checks NOTIFY_ALERT_{CPU|MEM|DISK} per alert_type
- send_telegram_recovery(): checks NOTIFY_RECOVERY
- send_telegram_system_alert(): new notify_key param, checks matching setting
- send_telegram_restart_*(): check NOTIFY_RESTART
self_monitor.py:
- Pass notify_key='NOTIFY_SYSTEM_REDIS/MYSQL' to send_telegram_system_alert
server/api/agent.py:
- Pass notify_key='NOTIFY_TIME_DRIFT' to time-drift alert
settings.html:
- New '告警通知项目' section with 8 toggle switches (pill style)
- Grouped by category: 服务器资源 / 服务器 / 系统
- Optimistic toggle with revert on API failure
- renderNotifyToggles() reads _allSettings and toggleNotify() saves instantly
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 17:12:07 +08:00
Your Name
08d0baf762
feat: TOTP re-bind support + improved settings UI
...
settings.html TOTP section overhaul:
- Status cards: green (enabled) / amber (disabled) with context-aware buttons
- Enabled state: add '重新绑定' button to change Authenticator app/device
without disabling first (same API, just new QR + verify flow)
- Disabled state: prominent 'Enable TOTP' CTA with warning about security
- Bind panel: shared between first-time setup and re-bind, auto-scrolls into view
- Auto-focus verify input after QR renders; Enter key submits
- QR generation: 180px, transparent background, fallback message if QRious fails
- Re-bind label distinguishes 'rebind success' from 'first enable' toast
- Disable flow: clearer confirm dialog mentioning security implication
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 17:07:40 +08:00
Your Name
9c063a788f
feat: time drift detection between agent and central server
...
Agent (web/agent/agent.py):
- Heartbeat now includes system_info.agent_time (UTC ISO string)
Central (server/api/agent.py):
- On each heartbeat, compare agent_time vs central datetime.now(utc)
- Thresholds: warn >= 30s, crit >= 60s (affects TOTP/cron accuracy)
- Stores time_drift_seconds + drift_level in Redis heartbeat key
- Sends Telegram alert (5-min cooldown) when drift >= 60s critical
API (server/api/servers.py):
- List and detail endpoints expose time_drift_seconds + drift_level from Redis
Frontend (web/app/servers.html):
- driftBadge(): shows clock emoji badge next to server name
- amber badge: >=30s (warn)
- red badge: >=60s (crit)
- tooltip shows exact drift and NTP check hint
- no badge when drift < 30s or no data
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 17:04:34 +08:00
Your Name
5a42345bc0
docs: Nexus 6.0 功能说明书 HTML + roadmap stale fix
...
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:55:24 +08:00
Your Name
6c5e6a2daa
feat: file operations (delete/rename/mkdir) + fix roadmap Step3 status
...
Backend:
- schemas.py: FileOperation model (server_id, operation, path, new_path)
- sync_v2.py: POST /api/sync/file-ops endpoint
- delete: rm -rf with shlex.quote
- rename: mv src -> dst with shlex.quote on both paths
- mkdir: mkdir -p with shlex.quote
- all operations audit-logged, 30s timeout
Frontend (files.html):
- Header: add 📁 + button (doMkdir via prompt)
- File rows: action buttons (rename + delete) appear on hover
- rename: prompt for new name, inline mv
- delete: confirm dialog with warning for dirs (rm -rf)
- all ops refresh current directory after success
- fileOp() shared helper for POST /api/sync/file-ops
Docs:
- roadmap.md: Step 3 Web SSH updated from 70% to completed
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:46:00 +08:00
Your Name
ac0eb79707
feat: rsync dry-run preview (方案D 智能提示型)
...
Backend:
- schemas.py: SyncPreview model (server_id, source/target_path, sync_mode, verbose)
- sync_engine_v2.py: preview_sync() + _parse_rsync_stats() helpers
- runs rsync --dry-run --stats; verbose=True adds -v (capped 200 lines)
- returns stats dict: files_transferred/created/deleted + byte counts
- sync_v2.py: POST /api/sync/preview endpoint with audit log
Frontend (push.html - 方案D):
- Sync mode radio change listener: onSyncModeChange()
- full/overwrite mode: orange warning card + prominent preview button
- incremental/checksum mode: subtle "预览首台变动 →" link
- Inline preview result section (no modal): stats cards + optional file list
- files_deleted shown in red card for destructive modes
- verbose toggle: "显示详细文件列表 ▼" triggers second request with verbose=true
- fmtBytes() helper for human-readable file sizes
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:41:47 +08:00
Your Name
e37bc6cea9
docs: drop file editor (Monaco/ACE) - officially rejected
...
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:31:33 +08:00
Your Name
2c6f3abbd0
docs: add standards/ folder with all reusable dev & audit standards
...
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:23:50 +08:00
Your Name
d5c498549b
docs: system development standard v1.0 - full coding rules for Python/FastAPI/Frontend
...
Nexus CI/CD / test (push) Waiting to run
Nexus CI/CD / deploy (push) Blocked by required conditions
Nexus Pre-commit Checks / quick-check (push) Waiting to run
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:22:36 +08:00
Your Name
07eab6020c
docs: extract audit core principles as standalone reference card
...
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:19:49 +08:00
Your Name
938e700fb3
docs: line-walk audit standard v2.0 - full spec integrating mdc rules + Phase 1-4 lessons
...
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:17:27 +08:00
Your Name
424c4efd64
docs: general-purpose line-walk audit standard (reusable for any project/AI)
...
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:09:28 +08:00
Your Name
c08e2a2242
fix: Phase 1 audit - ssh_key columns String(500) -> Text for RSA key support
...
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:07:24 +08:00
Your Name
6108d69b24
fix: Phase 4 audit fixes
...
F4a-01 script_callback_rate: INCR+EXPIRE were non-atomic; a crash between
the two could leave the rate-limit key without a TTL, permanently
blocking future callbacks for that job/IP. Fix: use Redis pipeline
so INCR+EXPIRE are sent in one roundtrip and EXPIRE always executes.
F4a-02 sync_service: add comment documenting that StrictHostKeyChecking=no
is a legacy issue in the dead-code SyncService shim; main sync path
(SyncEngineV2) honours SSH_STRICT_HOST_CHECKING from settings.
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 16:00:28 +08:00
Your Name
680577fc97
docs: Phase 3 walk-closure report + index update
...
- docs/reports/audit-phase-3-walk-closure.md: closure declaration
covering 3a-3i (12 new FINDINGs, all fixed, code backlog = 0)
- docs/reports/README.md: add Phase 3 closure entry
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 15:56:11 +08:00
Your Name
86bb32f87e
fix: Phase 3h script_service + server_service
...
F3h-01 script_service._build_kill_command: shlex.quote(log_path) wraps the
path in single-quotes; embedding that inside "..." produced a filename
with literal single-quote chars that cat/kill could not find.
Fix: use the shlex-quoted string directly without extra double-quotes.
F3h-02 script_service._substitute_db_vars: non-atomic string replacement
allowed nested substitution when a credential value contained another
$DB_* pattern. Fix: replace with null-byte sentinels first, then
substitute actual values in a second pass.
server_service.push_to_servers: add comment documenting that audit_repo and
retry_repo are None (internal compat shim — not used by routes).
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 15:50:44 +08:00
Your Name
c3c3bae370
fix: Phase 3g sync_engine_v2 bugs
...
F3g-01 sync_commands line 195: server_results is a list; unpacking it
with ** raises TypeError, silently caught by gather(return_exceptions=True),
so S2 command sync and S3 config sync returned empty results for all
servers. Fixed by using a proper dict: {"server_name": ..., "results": ...}.
F3g-02 sync_config: config value containing newline chars was not stripped
before shlex.quote; echo inside single-quotes would write multiple lines
to the sysctl config file, injecting arbitrary keys. Strip \n\r\x00 from
value before quoting (requires admin auth, defense-in-depth fix).
scripts.py, settings.py: CLEAN (no changes needed).
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 15:46:52 +08:00
Your Name
33b7ed93bf
fix: Phase 3e auth_service.py bugs
...
F3e-01 auth_service.py:180 - jwt_token_expires (MySQL tz-naive) vs
datetime.now(timezone.utc) caused TypeError on token refresh;
strip tzinfo before comparison when expires is tz-naive.
F3e-02 auth_service.py:271 - TOTP provisioning URI was not URL-encoding
issuer or username; special chars (spaces etc.) broke QR scan.
Use urllib.parse.quote() on both fields.
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 15:41:50 +08:00
Your Name
61b891db73
fix: Phase 3 line-walk fixes (3a-3d)
...
F3a-01 agent.py: _safe_float() prevents ValueError from non-numeric metric values
F3b-01 schedule_runner.py: fix tz-aware minus naive DateTime TypeError
F3b-02 schedule_runner.py: cron field divided by 0 returns False instead of crash
F3c-01 redis/client.py: mask credentials in REDIS_URL log output
F3d-01 main.py: cancel background tasks when primary worker lock is lost
F3d-02 main.py: strip whitespace from CORS origins after split
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 15:37:15 +08:00
Your Name
aa65f10c52
docs: Phase 2 audit records, project memory, WSL guide
...
Audit docs:
- docs/reports/audit-phase-2{a-j}-line-walk.md: per-file line walk findings
- docs/reports/audit-phase-2-findings-matrix.md: 63 findings (52 fixed, 3 partial, 7 accepted)
- docs/reports/2026-05-23-full-audit-report.md: full audit consolidation
- docs/reports/audit-full-vs-phase2-reconciliation.md: cross-reference
- docs/reports/audit-phase-2-walk-closure.md: closure sign-off
Project docs:
- docs/project/phase-2-audit-remediation.md: SSOT for all fixes
- docs/project/alert-push-policy.md: dashboard alert design decisions
- docs/project/production-verification-checklist.md: first-deploy checklist
- docs/project/script-execution.md: script platform design
- docs/project/wsl-integration-test.md: WSL code-only verification guide
- docs/project/line-walk-audit-standard.md: audit methodology
- docs/project/mysql-mcp-setup.md: MCP config guide
Changelogs: 18 entries (2026-05-22 to 2026-05-23)
Project memory: CLAUDE.md, AGENTS.md updated to reflect current state
Deploy: nginx_https.conf template updated
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 15:27:40 +08:00
Your Name
80d73d1b74
fix: WSL startup compatibility + dependency updates
...
- redis/client.py: use BlockingConnectionPool.from_url() (fixes 'url' kwarg error)
- database/engine_compat.py: patch aiomysql do_ping to satisfy pool_pre_ping
- database/session.py: apply engine_compat patch before create_async_engine
- requirements.txt: SQLAlchemy 2.0.49 (fixes aiomysql ping() reconnect signature)
- .env.example: document NEXUS_REDIS_URL format
- scripts/wsl_ensure_venv.sh: create project .venv (PEP 668 safe)
- scripts/wsl_start_dev.sh: use .venv python, validate Redis before start
- scripts/wsl_integration_smoke.sh: code-only verification mode
- scripts/wsl_check_mysql.py / bootstrap_database.py: MySQL setup helpers
- scripts/sync_frontend_vendor.py: vendor asset sync utility
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 15:27:18 +08:00
Your Name
752a24497c
feat: Phase 2 security audit fixes + script execution platform
...
Security (P0/P1/P2):
- WebSSH: dedicated short-lived token, server_id binding, 4003 close code
- Remove /api/agent/exec from control plane (RCE surface eliminated)
- Global JWT middleware (JwtAuthMiddleware) with install-mode bypass
- decrypt_value: failure returns None, never leaks ciphertext
- Telegram: sanitize_external_message strips sensitive fields
- Agent auth: startup enforces non-empty API key, compare_digest
- Credentials: password_set bool flag, no plaintext in API responses
- audit_log: writes admin_username + ip_address on all CUD ops
- Install lock: all /api/install/* except GET /status return 403 post-install
- WebSocket dedup: publish once, subscribers deliver locally
Script execution platform:
- script_jobs.py / script_job_callback.py: async batching and callback
- script_execution_store.py / script_callback_rate.py: Redis-backed state
- script_execution_flush.py: background flusher to MySQL
- scripts.html / script-executions.html: full execution UI
- agent_url.py: centralised URL builder
Frontend:
- All 13 pages migrated from CDN to /app/vendor/ (no external deps)
- vendor/: alpinejs, tailwindcss-browser, xterm, xterm-addon-*, qrious
- Dashboard WebSocket real-time alerts; 8h JWT session timeout
Tests:
- test_security_unit.py: 15 unit tests (JWT, sanitize, vendor, install 403)
- test_api.py: env-configurable admin credentials
Co-authored-by: Cursor <cursoragent@cursor.com >
2026-05-23 15:26:56 +08:00
Your Name
341d16fd6d
P2迭代: 危险命令检测 + DB会话合并 + API密钥修复 + 安全审计 (6项)
...
P2-14: agent.py _verify_api_key() 重写文档并明确两步验证逻辑,
全局密钥匹配直接通过,非匹配密钥放行至handler做per-server验证
P2-15: websocket.py 删除已废弃的 connected_clients=[] 别名,
health.py 已使用 manager.client_count
P2-16: webssh.py 合并4个AsyncSessionLocal()为1个共享session(预接受操作),
命令日志保留独立session(长连接WebSocket不能持有单session)
P2-18: 新增危险命令检测 (check_dangerous_command),识别 rm -rf /、
fork bomb、dd写裸设备、mkfs等模式,scripts.py + webssh.py集成
P2-19: install.py 状态文件不再存储db_pass明文(步骤5已删除文件),
审计确认所有logger调用无敏感数据泄露
P2-17: httpOnly cookie迁移标记为延期(需前后端+WebSocket全面重构,
当前JWT+updated_at+CORS限制方案已足够安全)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 23:18:08 +08:00
Your Name
d744d7df8e
安全与质量修复: P0-4/P0-5 + P1-6~P1-13 (8项)
...
P0-4: install.py _write_env() 值加引号转义,防止含#$\等字符的密码破坏.env格式
P0-5: install.py _build_redis_url() URL编码redis密码,防止@:等字符破坏URL解析
P1-6: auth_service.py _decode_access_token() 验证exp/sub声明存在,拒绝畸形JWT
P1-7: websocket.py + webssh.py WebSocket JWT验证增加updated_at检查,密码修改后令牌失效
P1-8: auth.py 无refresh_token的logout返回更明确的提示信息
P1-9: install.py create_admin增加_is_installed()检查,防止.env存在后重复创建
P1-10: servers.py server_stats() 改用SQL聚合查询,避免加载全部服务器对象
P1-11: heartbeat_flush.py 移除get_redis()永不返回None的死代码
P1-13: sync_engine_v2.py completed/failed计数器加asyncio.Lock防止并发竞态
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 23:04:40 +08:00
Your Name
4af9acf564
项目文档与验证脚本: AGENTS.md + 交接文档 + 审计/测试脚本
...
- AGENTS.md: 项目记忆(与CLAUDE.md同步)
- PROJECT_HANDOVER.md: 项目交接文档
- TEAM_ROLES.md: 团队分工与职责
- audit_scan.py: 全量代码审计扫描
- test_p1_p2.py: P1+P2功能快速验证
- verify.py: WSL模块/路由/安全验证
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-22 22:29:54 +08:00
Your Name
6354d507df
修复 sync_v2.py 缺少 AuditLog import (NameError)
...
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-22 22:29:35 +08:00
Your Name
7e29397235
配置与文档更新: .gitignore + CLAUDE.md + API微调
...
- .gitignore: 添加.env/supervisor/日志等忽略规则
- CLAUDE.md: 更新项目记忆与完成状态
- assets.py: Platform/Node API端点
- settings.py: DB_OVERRIDE_MAP键名修复
- schemas.py: 模型schema补充
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-22 22:29:20 +08:00
Your Name
4969876ce8
新页面: 资产管理 + 命令日志 + 数据库备份脚本
...
- assets.html: Platform/Node资产管理页面
- commands.html: 命令/会话双视图 + 危险命令高亮
- deploy/db_backup.sh: mysqldump + 30天保留 + cron集成
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-22 22:29:13 +08:00
Your Name
b676e320de
前端增强: XSS防护 + 共享布局 + 移动适配 + 设置页
...
Nexus CI/CD / test (push) Waiting to run
Nexus CI/CD / deploy (push) Blocked by required conditions
Nexus Pre-commit Checks / quick-check (push) Waiting to run
- index.html: WebSocket告警文本esc()转义
- files.html: escAttr()函数 + 路径拼接转义
- layout.js: 移动端侧边栏overlay + 全局搜索 + 用户信息
- servers.html: 点击展开详情面板(系统信息/同步/SSH)
- settings.html: TOTP QR码设置 + 修改密码 + API Key复制
- api.js: JWT refresh token自动刷新 + Toast通知
- terminal.html: WebSSH xterm.js集成
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-22 22:29:06 +08:00
Your Name
938d26927f
P1/P2: 后端安全与稳定性修复
...
- Agent per-server API Key认证 (agent.py)
- JWT updated_at校验与会话超时 (auth_jwt.py)
- 服务器凭据Fernet加密存储+密码脱敏 (servers.py)
- WebSSH服务器级授权检查 (webssh.py)
- Config同步去重+回滚机制 (sync_engine_v2.py)
- DB层分页offset/limit (server_repo.py)
- session.py logger修复 + @property死代码清理
- 心跳刷入rollback误用修复 (heartbeat_flush.py)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-22 22:28:57 +08:00
Your Name
8530f0e0d5
安全补强: 6项P0/P1漏洞修复
...
P0-1: PHP配置注入防护 — install.py添加_escape_php_string()转义单引号和反斜杠
P0-2: WebSocket JWT校验 — _verify_ws_token()要求exp+sub字段
P1-1: 删除SHA256密码fallback — auth_service.py和auth.py直接import bcrypt
P1-3: LIKE通配符转义 — search.py添加_escape_like()并对所有ilike()加escape参数
P1-2: 安全响应头中间件 — main.py添加SecurityHeadersMiddleware注入4个安全头
P0-3: Refresh Token重用检测 — Admin模型添加token_version字段,token格式改为token:admin_id:version
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-05-22 22:16:50 +08:00
Your Name
cea5730804
P0: Add StaticFiles mount + root redirect to install wizard
...
Critical fix: FastAPI was not serving any static files, causing all
frontend pages (install.html, login.html, etc.) to return 404.
Changes:
- Add StaticFiles mount at /app/ for web/app/ directory (html=True)
- Redirect root path / to /app/install.html in install mode
- Remove redundant "/" path bypass in InstallModeMiddleware
- Production Nginx still serves static files directly (no conflict)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 12:24:32 +08:00
Your Name
dfc37f6e90
修复设置页面键名与后端DB_OVERRIDE_MAP不匹配
...
settings.html引用pool_size/max_overflow/agent_heartbeat_interval
但config.py的DB_OVERRIDE_MAP期望db_pool_size/db_max_overflow/
heartbeat_timeout,导致这些设置无法从MySQL正确加载。
对齐前端键名与后端映射表。
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 12:02:08 +08:00
Your Name
0bcfeaa6d6
修复心跳刷入rollback误用 + API参数规范化
...
- heartbeat_flush: 移除循环内session.rollback(),防止一个服务器
刷入失败导致整个批次session状态损坏
- assets.py: 为Query参数添加Query()类型+描述+校验约束,
统一FastAPI参数风格 (server_id/session_id/limit/parent_id)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 11:58:15 +08:00
Your Name
cb5b4c42de
多Worker守护 + 告警服务器名 + 清理PHP残留
...
- main.py: Redis主Worker选举,防止多Worker重复执行后台任务
- agent.py: 告警/恢复广播使用真实服务器名(查MySQL)替代"server-{id}"
- sync_v2.py: 修复browse目录解析死代码,正确取parts[8]
- install.html: 移除过时install.php引用,更新为"删除.env重装"
- nginx配置: 移除PHP-FPM路由(install.html纯静态无需PHP)
- schedule_runner: 清理未使用的get_redis_sync导入
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 11:54:11 +08:00
Your Name
b36e1d8010
服务器详情: 兼容多种Agent心跳字段名(cpu_percent/cpu_usage)
...
- normalize: cpu_percent || cpu_usage, memory_percent || mem_usage, disk_percent || disk_usage
- 防止Agent使用不同字段名时显示空白
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 11:23:19 +08:00
Your Name
8a5d4159a4
文件管理: 添加缺失的路径输入框 + Enter键触发浏览
...
- 添加dirPath输入框(之前只有JS引用没有DOM元素)
- Enter键支持快速跳转到指定路径
- 输入框预填默认值'/'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 11:13:23 +08:00
Your Name
c03fe97d18
UX优化: 同步日志显示服务器名 + redis_url可编辑
...
- 同步日志API: JOIN Server表返回server_name字段
- 推送历史: 显示服务器名称替代操作人
- 仪表盘最近同步: 显示服务器名称替代server_id
- redis_url从SENSITIVE_KEYS移除(非密码,用户需可编辑)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 11:10:20 +08:00
Your Name
e9feaa6cdc
前后端一致性审计 + 批量操作 + 调度同步模式
...
- 服务器列表: 添加批量选择(checkbox)+批量推送/删除操作栏
- 推送页面: 支持从服务器列表页预选服务器跳转(sessionStorage)
- 调度页面: 添加同步模式选择(增量/全量/校验和)
- 后端: PushSchedule模型+Schema+API+ScheduleRunner添加sync_mode字段
- 仪表盘: 移除重复的函数定义和初始化调用
- 数据库迁移: 启动时自动执行schema migration(添加sync_mode列)
- 安装向导: 添加schema migration步骤
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com >
2026-05-22 11:01:17 +08:00