/** * Safe Child Environment — 子进程环境变量净化(v0.7.3 P3-2 单源收敛) * * 背景:run_command(command.ts buildSafeCommandEnv)与 MCP stdio 启动 * (mcp-manager.service.ts buildSafeEnv)各持有一份几乎相同的"敏感变量黑名单 * + 全量剔除"实现,任何新增敏感 key 都要改两处(历史上已经漂移过一次: * command 版多注入了 Windows 运行时变量)。本模块是唯一实现,两个调用方 * 按需声明运行时差异。 * * 净化策略(黑名单方案,#9 修复 + 审查修复的延续): * - 敏感变量名黑名单(精确匹配)一律剔除; * - 敏感后缀(_API_KEY/_TOKEN/_SECRET/...)一律剔除; * - 其余变量保留(白名单方案会破坏 GIT_* 与 PYTHONPATH、代理变量等子进程必需项, * 见 mcp-manager 历史注释); * - 调用方通过 `runtime` 注入必需的运行时变量(后者覆盖前者,显式可控)。 */ /** 敏感变量后缀黑名单(大小写不敏感后缀匹配) */ const SENSITIVE_SUFFIXES = [ '_API_KEY', '_TOKEN', '_SECRET', '_PASSWORD', '_PASSWD', '_CREDENTIAL', '_CREDENTIALS', '_PRIVATE_KEY', ] as const; /** 敏感变量名黑名单(精确匹配) */ const SENSITIVE_KEYS = new Set([ 'DEEPSEEK_API_KEY', 'AGNES_API_KEY', 'MIMO_API_KEY', 'GITEA_PASSWORD', 'DATABASE_PASSWORD', ]); export interface SafeChildEnvOptions { /** * 调用方必需的运行时变量(如 NODE_ENV / PYTHONIOENCODING / LANG)。 * 在净化后的 process.env 之上覆盖写入。 */ runtime?: Record; /** * 变量来源(默认 process.env;测试可注入受控快照)。 */ source?: Record; } /** * 构建净化后的子进程环境变量。 * @see 模块注释 —— 黑名单方案的设计原因与历史漂移教训 */ export function buildSafeChildEnv(options: SafeChildEnvOptions = {}): Record { const source = options.source ?? process.env; const env: Record = {}; for (const [key, val] of Object.entries(source)) { if (!val) continue; if (SENSITIVE_KEYS.has(key)) continue; if (SENSITIVE_SUFFIXES.some((suffix) => key.toUpperCase().endsWith(suffix))) continue; env[key] = val; } // 运行时必需变量(显式注入,覆盖净化结果) if (options.runtime) { Object.assign(env, options.runtime); } return env; }