铸渊专线全面排查修复: Shell注入防护+SMTP持久化+loadConfig通用化+ecosystem环境变量补全

Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/f9b09d73-b949-4f02-b3b9-2e69f90cd8b6

Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-04-04 09:04:51 +00:00 committed by GitHub
parent 2967fce24f
commit faf2f28c7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 53 additions and 17 deletions

View File

@ -91,6 +91,8 @@ jobs:
"cd /opt/zhuyuan/proxy-deploy && \ "cd /opt/zhuyuan/proxy-deploy && \
export ZY_SERVER_HOST='${{ secrets.ZY_SERVER_HOST }}' && \ export ZY_SERVER_HOST='${{ secrets.ZY_SERVER_HOST }}' && \
export ZY_CN_RELAY_HOST='${{ secrets.ZY_CN_SERVER_HOST }}' && \ export ZY_CN_RELAY_HOST='${{ secrets.ZY_CN_SERVER_HOST }}' && \
export ZY_SMTP_USER='${{ secrets.ZY_SMTP_USER }}' && \
export ZY_SMTP_PASS='${{ secrets.ZY_SMTP_PASS }}' && \
bash deploy-proxy.sh ${{ github.event.inputs.action }}" bash deploy-proxy.sh ${{ github.event.inputs.action }}"
- name: '🧹 清理SSH密钥' - name: '🧹 清理SSH密钥'

View File

@ -51,7 +51,7 @@ ensure_log_permissions() {
chmod 755 "$PROXY_DIR/logs" chmod 755 "$PROXY_DIR/logs"
} }
# ── 共用: 保存ZY_SERVER_HOST到.env.keys ── # ── 共用: 保存环境变量到.env.keys ──
save_server_host() { save_server_host() {
KEYS_FILE="$PROXY_DIR/.env.keys" KEYS_FILE="$PROXY_DIR/.env.keys"
if [ -n "${ZY_SERVER_HOST:-}" ] && [ -f "$KEYS_FILE" ]; then if [ -n "${ZY_SERVER_HOST:-}" ] && [ -f "$KEYS_FILE" ]; then
@ -84,6 +84,26 @@ save_server_host() {
fi fi
echo " ✅ ZY_CN_RELAY_HOST 已保存到 .env.keys" echo " ✅ ZY_CN_RELAY_HOST 已保存到 .env.keys"
fi fi
# 保存SMTP凭据 (如果有) — 使守护Agent和流量监控可发送告警邮件
if [ -n "${ZY_SMTP_USER:-}" ] && [ -f "$KEYS_FILE" ]; then
if grep -q "^ZY_SMTP_USER=" "$KEYS_FILE" 2>/dev/null; then
sed -i "s|^ZY_SMTP_USER=.*|ZY_SMTP_USER=${ZY_SMTP_USER}|" "$KEYS_FILE"
else
echo "" >> "$KEYS_FILE"
echo "# SMTP凭据 (部署时自动写入·守护Agent告警用)" >> "$KEYS_FILE"
echo "ZY_SMTP_USER=${ZY_SMTP_USER}" >> "$KEYS_FILE"
fi
echo " ✅ ZY_SMTP_USER 已保存到 .env.keys"
fi
if [ -n "${ZY_SMTP_PASS:-}" ] && [ -f "$KEYS_FILE" ]; then
if grep -q "^ZY_SMTP_PASS=" "$KEYS_FILE" 2>/dev/null; then
sed -i "s|^ZY_SMTP_PASS=.*|ZY_SMTP_PASS=${ZY_SMTP_PASS}|" "$KEYS_FILE"
else
echo "ZY_SMTP_PASS=${ZY_SMTP_PASS}" >> "$KEYS_FILE"
fi
echo " ✅ ZY_SMTP_PASS 已保存到 .env.keys"
fi
} }
# ── install: 首次完整安装 ───────────────────── # ── install: 首次完整安装 ─────────────────────

View File

@ -28,7 +28,8 @@ module.exports = {
instances: 1, instances: 1,
env: { env: {
NODE_ENV: 'production', NODE_ENV: 'production',
ZY_PROXY_DATA_DIR: '/opt/zhuyuan/proxy/data' ZY_PROXY_DATA_DIR: '/opt/zhuyuan/proxy/data',
ZY_PROXY_KEYS_FILE: '/opt/zhuyuan/proxy/.env.keys'
}, },
max_memory_restart: '64M', max_memory_restart: '64M',
log_file: '/opt/zhuyuan/proxy/logs/monitor.log', log_file: '/opt/zhuyuan/proxy/logs/monitor.log',
@ -43,7 +44,8 @@ module.exports = {
env: { env: {
NODE_ENV: 'production', NODE_ENV: 'production',
ZY_PROXY_DATA_DIR: '/opt/zhuyuan/proxy/data', ZY_PROXY_DATA_DIR: '/opt/zhuyuan/proxy/data',
ZY_PROXY_LOG_DIR: '/opt/zhuyuan/proxy/logs' ZY_PROXY_LOG_DIR: '/opt/zhuyuan/proxy/logs',
ZY_PROXY_KEYS_FILE: '/opt/zhuyuan/proxy/.env.keys'
}, },
max_memory_restart: '128M', max_memory_restart: '128M',
log_file: '/opt/zhuyuan/proxy/logs/guardian.log', log_file: '/opt/zhuyuan/proxy/logs/guardian.log',

View File

@ -24,7 +24,7 @@
'use strict'; 'use strict';
const { execSync, exec } = require('child_process'); const { execSync, execFileSync } = require('child_process');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const https = require('https'); const https = require('https');
@ -275,10 +275,11 @@ async function consultLLM(issue) {
function sendAlertEmail(subject, body) { function sendAlertEmail(subject, body) {
try { try {
const sendScript = path.join(__dirname, 'send-subscription.js'); const sendScript = path.join(__dirname, 'send-subscription.js');
execSync( // 使用execFileSync避免Shell命令注入 (不经过shell解释器)
`node "${sendScript}" alert "${subject}\n\n${body}"`, execFileSync('node', [sendScript, 'alert', `${subject}\n\n${body}`], {
{ encoding: 'utf8', timeout: 30000 } encoding: 'utf8',
); timeout: 30000
});
console.log('[守护Agent] 告警邮件已发送'); console.log('[守护Agent] 告警邮件已发送');
} catch (err) { } catch (err) {
console.error('[守护Agent] 邮件发送失败:', err.message); console.error('[守护Agent] 邮件发送失败:', err.message);

View File

@ -29,6 +29,8 @@ const path = require('path');
const KEYS_FILE = process.env.ZY_PROXY_KEYS_FILE || '/opt/zhuyuan/proxy/.env.keys'; const KEYS_FILE = process.env.ZY_PROXY_KEYS_FILE || '/opt/zhuyuan/proxy/.env.keys';
// ── 加载配置 ───────────────────────────────── // ── 加载配置 ─────────────────────────────────
// 优先级: 环境变量 > .env.keys文件
// 通用读取所有键值对确保guardian/monitor等服务也能获取完整配置
function loadConfig() { function loadConfig() {
const config = { const config = {
smtp_user: process.env.ZY_SMTP_USER || '', smtp_user: process.env.ZY_SMTP_USER || '',
@ -37,13 +39,21 @@ function loadConfig() {
sub_token: process.env.ZY_PROXY_SUB_TOKEN || '' sub_token: process.env.ZY_PROXY_SUB_TOKEN || ''
}; };
// 尝试从本地密钥文件读取Token // 从.env.keys文件读取所有键值对环境变量未设置时回退
try { try {
const content = fs.readFileSync(KEYS_FILE, 'utf8'); const content = fs.readFileSync(KEYS_FILE, 'utf8');
for (const line of content.split('\n')) { for (const line of content.split('\n')) {
if (line.startsWith('ZY_PROXY_SUB_TOKEN=')) { if (line.startsWith('#') || !line.includes('=')) continue;
config.sub_token = line.split('=')[1].trim(); const [key, ...vals] = line.split('=');
} const k = key.trim();
const v = vals.join('=').trim();
if (!v) continue;
// 仅在环境变量未设置时使用文件中的值
if (k === 'ZY_PROXY_SUB_TOKEN' && !config.sub_token) config.sub_token = v;
if (k === 'ZY_SERVER_HOST' && !config.server_host) config.server_host = v;
if (k === 'ZY_SMTP_USER' && !config.smtp_user) config.smtp_user = v;
if (k === 'ZY_SMTP_PASS' && !config.smtp_pass) config.smtp_pass = v;
} }
} catch { /* ignore */ } } catch { /* ignore */ }

View File

@ -15,7 +15,7 @@
'use strict'; 'use strict';
const { execSync } = require('child_process'); const { execSync, execFileSync } = require('child_process');
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
@ -177,10 +177,11 @@ async function sendAlert(alert, quota) {
const usedGB = ((quota.upload_bytes + quota.download_bytes) / (1024 ** 3)).toFixed(2); const usedGB = ((quota.upload_bytes + quota.download_bytes) / (1024 ** 3)).toFixed(2);
const totalGB = (quota.total_bytes / (1024 ** 3)).toFixed(0); const totalGB = (quota.total_bytes / (1024 ** 3)).toFixed(0);
execSync( // 使用execFileSync避免Shell命令注入 (不经过shell解释器)
`node "${sendScript}" alert "${alert.message} 已用 ${usedGB}GB / ${totalGB}GB"`, execFileSync('node', [sendScript, 'alert', `${alert.message} 已用 ${usedGB}GB / ${totalGB}GB`], {
{ encoding: 'utf8', timeout: 30000 } encoding: 'utf8',
); timeout: 30000
});
console.log(`[流量监控] 告警邮件已发送: ${alert.level}`); console.log(`[流量监控] 告警邮件已发送: ${alert.level}`);
} catch (err) { } catch (err) {
console.error('[流量监控] 告警邮件发送失败:', err.message); console.error('[流量监控] 告警邮件发送失败:', err.message);