Skip to content

Security Audit - Torrify

Last Updated: February 2, 2026 (Evening Audit)
Status: ✅ Production-Ready - Most Security Issues Resolved

Executive Summary

Torrify implements comprehensive security best practices for a desktop Electron application. Since the January audit, all critical and most medium-priority security issues have been resolved. The application now includes:

  • ✅ Zod schema validation for all IPC inputs
  • ✅ Path traversal protection with extension whitelisting
  • ✅ Centralized logging with production/dev separation
  • ✅ Content Security Policy (CSP) in index.html
  • ✅ Context URL validation (HTTPS + allowed hosts only)

Remaining Items:

  • ⚠️ Ollama endpoint validation (localhost not enforced)

Current Security Posture

✅ Secure Practices Implemented

  1. Context Isolation: Enabled in Electron (contextIsolation: true)
  2. No Node Integration: Renderer process cannot access Node.js directly (nodeIntegration: false)
  3. IPC Surface Reduced: IPC handlers explicitly defined in preload script
  4. IPC Input Validation: All IPC payloads validated with Zod schemas
  5. Path Sanitization: Path validator rejects traversal, symlinks, invalid extensions
  6. Safe File Operations: User code written to temporary files, not executed directly
  7. Execution Timeouts: 30-second timeout on OpenSCAD, 60-second on build123d
  8. File Size Limits: Output files 50MB, project files 100MB, code 1MB
  9. Project Validation: Project files validated with dedicated validator
  10. Settings File Permissions: Restricted to user-only access (non-Windows)
  11. BYOK Model: No hardcoded API keys, users must configure their own
  12. Electron Hardening: Secure defaults aligned with Electron security guide
  13. Content Security Policy: Comprehensive CSP in index.html
  14. Context URL Validation: Only HTTPS from allowed hosts (github.com, raw.githubusercontent.com)
  15. Centralized Logging: Debug/warn/info gated to dev; errors sanitized in production

⚠️ Remaining Considerations

  1. Ollama Endpoint Validation (Low Priority)
    • Risk: Ollama endpoint can point to any URL (not restricted to localhost).
    • Impact: User-controlled setting; minimal risk since user owns the configuration.
    • Mitigation: Could add localhost/HTTPS enforcement if desired.

Security Issues & Resolutions

1. Hardcoded API Key ✅ RESOLVED

Status: ✅ FIXED - Implemented BYOK (Bring Your Own Key) model

Resolution:

  • Removed hardcoded API key from defaults
  • Default API key is empty string
  • AI is disabled by default until user configures their own key
  • Added validation to require API key when enabling AI
  • Settings modal shows warning when AI is enabled without API key
  • Chat panel shows helpful status message when API key is missing

Implementation:

typescript
const DEFAULT_SETTINGS: Settings = {
  llm: {
    apiKey: '', // User must configure their own API key (BYOK)
    enabled: false, // Disabled by default until API key is configured
    // ...
  }
}

Best Practices Applied:

  • ✅ No hardcoded keys in source code
  • ✅ User-configured API keys stored in settings file
  • ✅ Password-masked input field in UI
  • ✅ Validation prevents enabling AI without key
  • ✅ Clear error messages guide users to configure key

2. OpenSCAD CLI Input Sanitization ✅ RESOLVED

Status: ✅ FIXED - Added timeouts, size validation, and proper error handling

Issues Addressed:

  • No execution timeout (could hang indefinitely)
  • No output file size validation (DoS risk)
  • No resource limits

Resolution:

  1. Execution Timeout: 30-second timeout on all OpenSCAD processes

    • Processes are killed if they exceed the timeout
    • Clear error messages inform users of timeout
  2. File Size Validation: Maximum 50MB for output files (PNG/STL)

    • Validates file size before reading into memory
    • Prevents memory exhaustion from large files
    • Clear error messages with file size information
  3. Proper Cleanup: Timeout handlers and process cleanup on errors

    • Ensures processes are killed even on errors
    • Prevents resource leaks

Implementation:

typescript
const OPENSCAD_TIMEOUT_MS = 30000 // 30 seconds
const MAX_OUTPUT_FILE_SIZE = 50 * 1024 * 1024 // 50MB

// Timeout implementation
timeoutId = setTimeout(() => {
  if (openscadProcess && !openscadProcess.killed) {
    openscadProcess.kill()
    reject(new Error('OpenSCAD execution timed out after 30 seconds'))
  }
}, OPENSCAD_TIMEOUT_MS)

// File size validation
const stats = fs.statSync(OUTPUT_FILE)
if (stats.size > MAX_OUTPUT_FILE_SIZE) {
  reject(new Error(`Output file too large: ${(stats.size / 1024 / 1024).toFixed(2)}MB`))
}

Remaining Considerations:

  • Process resource limits (CPU/memory) would require platform-specific code
  • Sandboxing OpenSCAD execution is complex and may not be necessary for a desktop app
  • These are low-priority enhancements for future consideration

3. Settings File Security ✅ RESOLVED

Status: ✅ FIXED - File permissions enforced on non-Windows platforms

Implementation:

  • Settings file stored in ~/.torrify/settings.json
  • File permissions set to 0o600 (user read/write only) on Unix-like systems
  • Windows relies on OS-level file permissions (user directory is protected)
  • Plain text storage is acceptable for local desktop applications

Security Model:

  • Settings file is in user's home directory (protected by OS)
  • API keys are stored in plain text (acceptable for local desktop app)
  • File permissions prevent other users from reading settings
  • If system is compromised, API keys could be stolen (same risk as any local app)

Future Enhancements (Optional):

  • Encrypt API keys using OS keychain (Windows Credential Manager, macOS Keychain, Linux Secret Service)
  • This would be a nice-to-have but not critical for a local desktop app

4. Project File Security ✅ RESOLVED

Status: ✅ FIXED - Added validation, size limits, and error handling

Issues Addressed:

  • No validation of project file structure
  • No file size limits
  • No error handling for malformed files

Resolution:

  1. Schema Validation: Validates project file structure before loading

    • Checks required fields (version, code, stlBase64, chat)
    • Validates chat message structure
    • Rejects malformed files with clear error messages
  2. File Size Limits: Maximum 100MB for project files

    • Validates file size before reading
    • Prevents memory exhaustion
    • Clear error messages
  3. Error Handling: Comprehensive error handling

    • Catches JSON parse errors
    • Validates structure after parsing
    • User-friendly error messages

Implementation:

typescript
const MAX_PROJECT_FILE_SIZE = 100 * 1024 * 1024 // 100MB

function validateProject(project: any): boolean {
  // Validates structure, types, and required fields
  // Returns false if invalid
}

// File size check before reading
const stats = fs.statSync(filePath)
if (stats.size > MAX_PROJECT_FILE_SIZE) {
  return { canceled: true, error: 'File too large' }
}

// Structure validation after parsing
if (!validateProject(project)) {
  return { canceled: true, error: 'Invalid project format' }
}

Security Considerations:

  • Project files may contain chat history (potentially sensitive)
  • Users control where files are saved
  • No automatic sharing or cloud sync
  • Users should be aware when sharing project files

5. Console Logging ✅ RESOLVED

Status: ✅ FIXED (February 2026) - Centralized loggers gate debug/warn/info to dev; error logs sanitized in production

Implementation:

  • Main process: electron/utils/logger.tsisDev = !!process.env.VITE_DEV_SERVER_URL; logger.debug, logger.warn, logger.info only when isDev; logger.error(msg, error) logs full details in dev, message-only in production.
  • Renderer: src/utils/logger.tsisDev = import.meta.env.DEV; same API.
  • All console.log/console.warn/console.error in electron/main.ts, electron/cad/Build123dService.ts, src/App.tsx, ChatPanel, SettingsModal, ErrorBoundary, WelcomeModal, HelpBot, DemoDialog, and LLM services (utils, GatewayService, OpenRouterService, OllamaService, GeminiService) replaced with logger calls.
  • Test files unchanged (test environment only).

6. Pro Model Proxy Service ✅ IMPLEMENTED

Status: ✅ CLOSED - Proxy service for Pro model requests is live.

Implementation:

  • Pro model requests are routed through a managed proxy service.
  • Server-side key management.
  • Rate limits and quota management are enforced.

Priority: Resolved


7. IPC Input Validation ✅ RESOLVED

Status: ✅ FIXED (February 2026) - Zod schemas validate all IPC payloads; size limits enforced

Implementation:

  • electron/validation/schemas.ts: CodeSchema (1MB), SettingsSchema, FilePathSchema (1KB), GatewayRequestSchema, CADBackendSchema, WindowTitleSchema
  • All affected handlers in electron/main.ts use safeParse() and return structured errors on validation failure
  • Handlers validated: render-scad, render-stl, save-settings, open-recent-file, save-scad-file, export-scad, gateway-request, set-window-title, validate-cad-backend, check-openscad-path, check-python-path, remove-recent-file, get-context, update-context-from-cloud, reset-context-to-factory

Previous State (for reference):

  • IPC handlers accepted unvalidated objects and strings
  • No enforced size or type checks on renderer-provided inputs

Affected Handlers:

typescript
// electron/main.ts
ipcMain.handle('render-scad', async (event, code: string) => {
  // No validation - code could be 100MB+ string
  fs.writeFileSync(TEMP_SCAD_FILE, code, 'utf-8')
})

ipcMain.handle('render-stl', async (event, code: string) => {
  // Same issue
})

ipcMain.handle('save-settings', (event, settings: Settings) => {
  // Settings object not validated
  currentSettings = settings
  saveSettings(settings)
})

Specific Vulnerabilities:

  1. Code injection via render handlers - While not direct code execution, malformed code could cause resource exhaustion
  2. Settings tampering - Malicious renderer could send invalid settings objects
  3. Memory exhaustion - Large strings can consume excessive memory before processing
  4. Type confusion - TypeScript types only exist at compile time, runtime validation missing

Recommended Actions:

  1. Install and use Zod for schema validation: npm install zod
  2. Define schemas for all IPC payloads
  3. Enforce maximum sizes (code: 1MB, settings: 10KB, file paths: 1KB)
  4. Validate and sanitize all inputs before use

Example Implementation:

typescript
import { z } from 'zod'

// Define schemas
const CodeSchema = z.string().max(1024 * 1024) // Max 1MB
const SettingsSchema = z.object({
  cadBackend: z.enum(['openscad', 'build123d']),
  openscadPath: z.string().max(512),
  build123dPythonPath: z.string().max(512),
  llm: z.object({
    provider: z.enum(['gemini', 'openai', 'anthropic', 'custom', 'openrouter', 'ollama']),
    model: z.string().max(100),
    apiKey: z.string().max(200),
    enabled: z.boolean(),
    customEndpoint: z.string().max(512).optional(),
    temperature: z.number().min(0).max(2).optional(),
    maxTokens: z.number().min(1).max(100000).optional()
  }),
  recentFiles: z.array(z.object({
    filePath: z.string().max(1024),
    lastOpened: z.string()
  })).max(10).optional()
})

// Use in handlers
ipcMain.handle('render-scad', async (event, codeInput: unknown) => {
  try {
    const code = CodeSchema.parse(codeInput)
    // ... rest of handler
  } catch (error) {
    return { success: false, error: 'Invalid code input' }
  }
})

ipcMain.handle('save-settings', (event, settingsInput: unknown) => {
  try {
    const settings = SettingsSchema.parse(settingsInput)
    currentSettings = settings
    saveSettings(settings)
    return { success: true }
  } catch (error) {
    return { success: false, error: 'Invalid settings format' }
  }
})

Priority: High (Critical for production)


8. File Path Sanitization ✅ RESOLVED

Status: ✅ FIXED (February 2026) - Path validation utility enforces normalization, traversal checks, extension whitelist, and symbolic link rejection

Implementation:

  • electron/validation/pathValidator.ts: validatePath(filePath, allowedExtensions) normalizes with path.resolve(path.normalize()), rejects input containing .., enforces extension whitelist (.scad, .py, .torrify, .opencursor, .json), rejects symbolic links when file exists
  • Applied in open-recent-file, save-scad-file, remove-recent-file (paths validated before file operations)

Previous State (for reference):

  • Recent and open-file flows accepted arbitrary paths from renderer
  • Path normalization not enforced

Affected Handlers:

typescript
// electron/main.ts
ipcMain.handle('open-recent-file', async (event, filePath: string) => {
  // filePath could be '../../../etc/passwd' or 'C:\\Windows\\System32\\config\\SAM'
  if (!fs.existsSync(filePath)) {
    return { canceled: true, error: 'File no longer exists' }
  }
  const content = fs.readFileSync(filePath, 'utf-8')
  // ...
})

ipcMain.handle('save-scad-file', async (event, code: string, filePath?: string) => {
  if (filePath && fs.existsSync(filePath)) {
    // No validation - could overwrite system files if user has permission
    fs.writeFileSync(filePath, code, 'utf-8')
  }
})

Specific Vulnerabilities:

  1. Path traversal - Malicious renderer could read/write files outside intended directories
  2. Symbolic link attacks - Paths not resolved to canonical form
  3. File extension bypass - Could open/save non-CAD files
  4. System file access - Could potentially access sensitive system files

Recommended Actions:

  1. Normalize all paths using path.resolve() and path.normalize()
  2. Validate that resolved paths are within allowed directories
  3. Enforce file extension whitelist (.scad, .py, .torrify, .opencursor)
  4. Reject paths containing suspicious patterns (.., symbolic links)

Example Implementation:

typescript
import * as path from 'path'
import * as fs from 'fs'

// Utility function
function validateAndNormalizePath(filePath: string, allowedExtensions: string[]): { valid: boolean; path?: string; error?: string } {
  try {
    // Normalize and resolve to absolute path
    const normalized = path.resolve(path.normalize(filePath))
    
    // Check for path traversal attempts
    if (normalized.includes('..')) {
      return { valid: false, error: 'Path traversal detected' }
    }
    
    // Validate file extension
    const ext = path.extname(normalized).toLowerCase()
    if (!allowedExtensions.includes(ext)) {
      return { valid: false, error: `Invalid file extension: ${ext}` }
    }
    
    // Check if it's a symbolic link (optional, platform-specific)
    if (fs.existsSync(normalized)) {
      const stats = fs.lstatSync(normalized)
      if (stats.isSymbolicLink()) {
        return { valid: false, error: 'Symbolic links not allowed' }
      }
    }
    
    return { valid: true, path: normalized }
  } catch (error) {
    return { valid: false, error: 'Invalid path format' }
  }
}

// Use in handlers
ipcMain.handle('open-recent-file', async (event, filePath: string) => {
  const validation = validateAndNormalizePath(filePath, ['.scad', '.py', '.torrify', '.opencursor', '.json'])
  if (!validation.valid) {
    return { canceled: true, error: validation.error }
  }
  
  const normalizedPath = validation.path!
  if (!fs.existsSync(normalizedPath)) {
    return { canceled: true, error: 'File no longer exists' }
  }
  
  // ... rest of handler using normalizedPath
})

Priority: High (Critical for production)


9. Content Security Policy ✅ IMPLEMENTED

Status: ✅ FIXED (February 2026) - Comprehensive CSP in index.html

Implementation (index.html):

html
<meta http-equiv="Content-Security-Policy"
  content="default-src 'self'; 
           script-src 'self' https://cdn.jsdelivr.net; 
           style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; 
           font-src 'self' data: https://cdn.jsdelivr.net; 
           img-src 'self' data: blob:; 
           connect-src 'self' [whitelisted API endpoints]; 
           worker-src 'self' blob:;" />

CSP Analysis:

  • default-src 'self' - Restrictive default
  • script-src - Only self and jsDelivr CDN (Monaco Editor)
  • ⚠️ style-src 'unsafe-inline' - Required for Monaco Editor styling
  • connect-src - Whitelisted API endpoints only
  • worker-src - Self and blob for Monaco web workers

Priority: ✅ Resolved


10. Endpoint Validation ⚠️ IMPROVED

Status: ⚠️ IMPROVED - Context URLs validated; Ollama endpoints user-controlled

Current State:

  • ✅ Context updates require HTTPS from allowed hosts (github.com, raw.githubusercontent.com)
  • validateContextUrl() function enforces URL validation in electron/main.ts
  • ⚠️ Ollama endpoint is user-configurable (not a vulnerability, user-controlled)

Remaining Gap (Low Priority):

  • Ollama endpoint can be set to any URL
  • This is an advanced user setting, not a security vulnerability
  • Could add warning when endpoint is not localhost/HTTPS

Priority: Low (user-controlled configuration)

Security Best Practices Checklist

✅ Completed (All Critical Items)

  • [x] Remove hardcoded API key from source code
  • [x] Add execution timeout for OpenSCAD (30 seconds)
  • [x] Add output file size validation (50MB limit)
  • [x] Add project file validation (schema check)
  • [x] Add project file size limits (100MB limit)
  • [x] Add settings file permission checks (non-Windows)
  • [x] Gate all production logging (centralized loggers)
  • [x] Update README with security notes
  • [x] Add IPC payload validation and size limits ✅ DONE (February 2026)
  • [x] Sanitize and validate file paths ✅ DONE (February 2026)
  • [x] Add CSP headers to HTML ✅ DONE (February 2026)
  • [x] Validate context download URLs ✅ DONE (HTTPS + allowed hosts only)
  • [x] Implement proxy service for Pro model requests ✅ DONE (February 2026)

🔄 Low Priority (Nice to Have)

  • [ ] Restrict Ollama endpoint to localhost/HTTPS
    • Currently user-configurable to any URL
    • Low risk since this is a user-controlled advanced setting
    • Could add warning or validation
    • Estimated effort: 1 hour

🔄 Medium Priority (Future Enhancements)

  • [ ] Use proper logging library with levels (electron-log)
  • [ ] Add error boundaries in React components
  • [ ] Implement retry logic for API calls

📋 Low Priority (Nice to Have)

  • [ ] Encrypt API keys using OS keychain (optional)
  • [ ] Add process resource limits (CPU/memory) - platform-specific
  • [ ] Implement sandboxing for OpenSCAD (advanced, may not be necessary)
  • [ ] Add security audit to CI/CD pipeline
  • [ ] Add automated security scanning

Threat Model

Assumptions

  • Application runs on user's local machine
  • User has control over their system
  • OpenSCAD executable is trusted (installed by user)
  • Network access required for AI features
  • User is responsible for their API keys

Potential Attack Vectors & Mitigations

  1. Malicious OpenSCAD Code

    • Risk: Resource exhaustion, large output files
    • Mitigation: ✅ 30-second timeout, 50MB file size limit, process isolation
  2. Compromised Settings File

    • Risk: API keys could be stolen
    • Mitigation: ✅ File permissions (0o600), user directory protection, BYOK model
  3. Malicious Project Files

    • Risk: Malformed files causing crashes or issues
    • Mitigation: ✅ Schema validation, size limits, error handling
  4. API Key Exposure

    • Risk: Hardcoded keys in source code
    • Mitigation: ✅ BYOK model, no hardcoded keys, password masking in UI
  5. Resource Exhaustion

    • Risk: Large files consuming memory
    • Mitigation: ✅ File size limits, timeouts, proper cleanup

Security Recommendations Summary

✅ All Critical Items Implemented

  1. BYOK Model - Users configure their own API keys
  2. Execution Timeouts - 30s OpenSCAD, 60s build123d
  3. File Size Validation - 50MB outputs, 100MB projects, 1MB code
  4. IPC Input Validation ✅ - Zod schemas for all handlers
  5. Path Sanitization ✅ - Traversal, symlink, extension validation
  6. File Permissions - Settings file restricted to user-only
  7. Centralized Logging ✅ - Dev/prod separation, error sanitization
  8. Content Security Policy ✅ - Comprehensive CSP in index.html
  9. Context URL Validation ✅ - HTTPS + allowed hosts only

⚠️ Business Priority (Not Security-Critical)

  1. Pro Model Proxy Service - Route Pro requests through server-side proxy
    • Affects monetization, not security for BYOK users
    • Gateway service exists; infrastructure not deployed

🔄 Optional Future Enhancements

  1. Ollama Endpoint Restriction - Add localhost/HTTPS validation (low priority)
  2. Proper Logging Library - Consider electron-log for advanced logging
  3. OS Keychain Integration - Optional encryption for API keys
  4. CI/CD Security Scanning - Automated security checks in pipeline

Git History Hygiene (Pre-GitHub Release)

If this repository will be public on GitHub, consider sanitizing git history to ensure no secrets, keys, or sensitive artifacts exist in any commit. This section is advisory only.

  1. Scan history for secrets:

    • Use a secret scanner (e.g., gitleaks or trufflehog) to identify sensitive content in history.
  2. Rewrite history if needed:

    • Preferred tool: git filter-repo (replaces filter-branch).
    • Example (remove a leaked file across history):
      • git filter-repo --path path/to/leaked.file --invert-paths
    • Example (remove sensitive strings via replace rules):
      • Create a replacements file and run git filter-repo --replace-text replacements.txt
  3. Invalidate old history:

    • Force-push rewritten history to the remote (only if acceptable for the team).
    • Rotate any exposed credentials immediately.

Caution

History rewrites are disruptive for collaborators. Coordinate before running and provide instructions for re-cloning or resetting local branches.


Security Testing

Manual Testing Checklist

  • [x] Verify API key is not hardcoded
  • [x] Test OpenSCAD timeout (30 seconds)
  • [x] Test file size limits (50MB output, 100MB project)
  • [x] Test project file validation (malformed files rejected)
  • [x] Verify settings file permissions (non-Windows)
  • [x] Verify logging only in development mode

Automated Testing (Future)

  • [x] Add security-focused unit tests (IPC validation and path validator tests in electron/__tests__/)
  • [ ] Add integration tests for file operations
  • [ ] Add fuzzing tests for project file loading
  • [ ] Add performance tests for large files

Compliance & Standards

OWASP Top 10 Alignment

  • A01:2021 – Broken Access Control: File permissions enforced, path validation
  • A02:2021 – Cryptographic Failures: API keys properly handled (BYOK model)
  • A03:2021 – Injection: Comprehensive input validation (Zod schemas, path sanitization)
  • A04:2021 – Insecure Design: Secure by default (AI disabled, timeouts, size limits)
  • A05:2021 – Security Misconfiguration: Proper Electron security settings, CSP
  • A06:2021 – Vulnerable Components: Dependencies kept up to date
  • A07:2021 – Authentication Failures: N/A (local desktop app)
  • A08:2021 – Software and Data Integrity: Project file validation, context URL validation
  • A09:2021 – Security Logging: Centralized logging with production sanitization
  • A10:2021 – Server-Side Request Forgery: Context URLs restricted to HTTPS + allowed hosts

Electron Security Best Practices

  • ✅ Context isolation enabled
  • ✅ Node integration disabled
  • ✅ Preload script for IPC
  • ✅ No remote module usage
  • ✅ Secure default settings

References


Conclusion

Torrify is production-ready from a security perspective. All critical and medium-priority security issues identified in previous audits have been resolved as of February 2026:

Security Improvements Completed

  • ✅ IPC input validation with Zod schemas
  • ✅ File path sanitization with traversal protection
  • ✅ Centralized logging with production/dev separation
  • ✅ Content Security Policy implementation
  • ✅ Context URL validation (HTTPS + allowed hosts)
  • ✅ Pro model proxy service implemented

Security Best Practices Implemented

  • ✅ BYOK model for API keys (no hardcoded secrets)
  • ✅ Execution timeouts and resource limits
  • ✅ Comprehensive input validation and sanitization
  • ✅ Proper file permissions
  • ✅ Secure Electron configuration (context isolation, no node integration)
  • ✅ Extension whitelisting for file operations
  • ✅ Symbolic link rejection

Remaining Items (Not Security-Critical)

  • ⚠️ Ollama endpoint restriction (user-controlled setting, low risk)

The application is ready for public release on GitHub from a security standpoint.

Released under the GPL-3.0 License.