Skip to content

[Quality] 安全性检查: 缺少输入验证和 Electron 安全配置 #24

Description

@newtontech

实际问题

1. 分子数据缺少输入验证 (高危)

文件: src/renderer/MolecularRenderer.ts:77
代码:

renderMolecule(molecule: Molecule): void {
    if (this.isDisposed) return;

    // Clear previous molecule
    this.clearMolecule();

    // Create atoms (balls)
    molecule.atoms.forEach(atom => {
      const atomMesh = this.createAtomMesh(atom);
      this.moleculeGroup.add(atomMesh);
    });
    // ...
}

问题: renderMolecule 方法直接接受分子数据并渲染,没有对数据进行任何验证。如果传入恶意构造的数据,可能导致:

  • 原型污染攻击 (Prototype Pollution)
  • 渲染异常导致应用崩溃
  • 潜在的 XSS 如果分子数据包含 HTML/JavaScript

2. 原型污染风险 (中危)

文件: src/utils/cpkColors.ts:58-60
代码:

export function getCPKColor(element: string): number {
  return CPK_COLORS[element] || DEFAULT_ATOM_COLOR;
}

问题: 如果 element 参数来自不可信来源(如用户上传的分子文件),且值为 __proto__constructorprototype,可能触发原型污染。

3. 资源释放不完整 (中危)

文件: src/renderer/MolecularRenderer.ts:156-166
代码:

clearMolecule(): void {
    while (this.moleculeGroup.children.length > 0) {
      const child = this.moleculeGroup.children[0];
      if (child instanceof THREE.Mesh) {
        child.geometry.dispose();
        if (Array.isArray(child.material)) {
          child.material.forEach(m => m.dispose());
        } else {
          child.material.dispose();
        }
      }
      this.moleculeGroup.remove(child);
    }
  }

问题:

  • 只处理了 THREE.Mesh 类型的对象,其他类型(如 THREE.Line, THREE.Points)的资源未被释放
  • userData 中可能包含的引用未被清理,可能导致内存泄漏
  • 没有处理纹理 (Textures) 和渲染目标 (RenderTargets) 的释放

4. CI/CD 配置存在安全风险 (中危)

文件: .github/workflows/deploy.yml:28-29
代码:

    - name: Build
      run: npm run make || npm run build || echo "No build script"

问题: 使用 || 链式回退会掩盖构建失败,可能导致:

  • 安全漏洞的代码被部署到生产环境
  • 无法及时发现构建过程中的安全问题

5. 缺少 Electron 安全配置

问题: 项目计划作为 Electron 应用,但缺少必要的安全配置:

  • 没有配置 contextIsolation
  • 没有配置 sandbox
  • 缺少 Content Security Policy (CSP)
  • 主进程入口文件 src/main/main.js 在 package.json 中被引用但未找到

改进建议

1. 添加输入验证

// src/utils/validation.ts
export function validateMolecule(molecule: unknown): molecule is Molecule {
  if (!molecule || typeof molecule !== 'object') return false;
  
  const m = molecule as Partial<Molecule>;
  if (!m.id || !m.name || !Array.isArray(m.atoms) || !Array.isArray(m.bonds)) {
    return false;
  }
  
  // 验证原子数据
  for (const atom of m.atoms) {
    if (!validateAtom(atom)) return false;
  }
  
  // 验证化学键数据
  for (const bond of m.bonds) {
    if (!validateBond(bond, m.atoms)) return false;
  }
  
  return true;
}

function validateAtom(atom: unknown): atom is Atom {
  if (!atom || typeof atom !== 'object') return false;
  const a = atom as Partial<Atom>;
  
  // 验证元素符号 - 只允许字母
  if (!a.element || !/^[A-Z][a-z]?$/.test(a.element)) return false;
  
  // 验证坐标为有限数字
  if (!isFinite(a.x!) || !isFinite(a.y!) || !isFinite(a.z!)) return false;
  
  return true;
}

2. 修复原型污染风险

// src/utils/cpkColors.ts
export function getCPKColor(element: string): number {
  // 防止原型污染
  if (element === '__proto__' || element === 'constructor' || element === 'prototype') {
    return DEFAULT_ATOM_COLOR;
  }
  return CPK_COLORS[element] || DEFAULT_ATOM_COLOR;
}

export function getAtomRadius(element: string): number {
  // 防止原型污染
  if (element === '__proto__' || element === 'constructor' || element === 'prototype') {
    return DEFAULT_ATOM_RADIUS;
  }
  return ATOM_RADIUS[element] || DEFAULT_ATOM_RADIUS;
}

3. 改进资源释放

clearMolecule(): void {
    const disposeObject = (obj: THREE.Object3D) => {
      // 递归处理子对象
      if (obj.children) {
        obj.children.forEach(child => disposeObject(child));
      }
      
      if (obj instanceof THREE.Mesh) {
        obj.geometry?.dispose();
        
        const materials = Array.isArray(obj.material) ? obj.material : [obj.material];
        materials.forEach(material => {
          material?.dispose();
          // 释放纹理
          Object.values(material).forEach(value => {
            if (value instanceof THREE.Texture) {
              value.dispose();
            }
          });
        });
      }
      
      // 清理 userData
      obj.userData = {};
    };

    while (this.moleculeGroup.children.length > 0) {
      const child = this.moleculeGroup.children[0];
      disposeObject(child);
      this.moleculeGroup.remove(child);
    }
  }

4. 修复 CI/CD 配置

    - name: Build
      run: npm run make
      
    - name: Security audit
      run: npm audit --audit-level=moderate
      continue-on-error: true  # 仅记录,不阻断

5. 添加 Electron 安全配置模板

// src/main/main.js
const { app, BrowserWindow } = require('electron');
const path = require('path');

function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      contextIsolation: true,  // 启用上下文隔离
      sandbox: true,           // 启用沙箱
      preload: path.join(__dirname, 'preload.js'),
      nodeIntegration: false,  // 禁用 Node 集成
      allowRunningInsecureContent: false,
    }
  });

  // 设置 CSP
  mainWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {
    callback({
      responseHeaders: {
        ...details.responseHeaders,
        'Content-Security-Policy': ["default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'"]
      }
    });
  });

  mainWindow.loadFile('index.html');
}

为什么重要

风险 影响
原型污染 攻击者可能修改对象原型,导致应用逻辑被篡改或拒绝服务
内存泄漏 长时间运行后可能导致应用崩溃,影响用户体验
构建失败掩盖 安全漏洞可能被意外部署到生产环境
缺少 Electron 隔离 恶意分子文件可能获得系统级访问权限

优先级

  • 高 - 影响系统稳定性/安全性 (输入验证、原型污染)
  • 中 - 影响代码质量 (资源释放、CI/CD配置)
  • 低 - 改进建议

相关 Issue

参考链接

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions