Skip to content

Security: dwekat/nodeswarm

SECURITY.md

Security Policy

Overview

NodeSwarm uses function serialization to execute code in worker threads. While this provides flexibility, it requires careful consideration of security implications.

Security Considerations

Function Serialization and eval()

NodeSwarm internally uses new Function() (similar to eval()) to deserialize and execute functions in worker threads. This is a deliberate design choice to enable dynamic function execution but comes with important security implications.

⚠️ CRITICAL: Never use NodeSwarm with untrusted or user-provided code.

Safe Usage Guidelines

✅ SAFE - Your Own Code

const pool = new ThreadPool();

// Safe: Your own, reviewed function
await pool.thread((x, y) => x + y, 5, 10);

// Safe: Your own CPU-intensive logic
await pool.thread((n) => {
  let result = 0;
  for (let i = 0; i < n; i++) {
    result += Math.sqrt(i);
  }
  return result;
}, 1000000);

❌ UNSAFE - User Input or Untrusted Code

// DANGEROUS: Never do this!
const userFunction = request.body.code; // User-provided code
await pool.thread(eval(userFunction)); // SEVERE SECURITY RISK

// DANGEROUS: Dynamic function from external source
const externalCode = await fetch("untrusted-source.com/code.js");
await pool.thread(externalCode); // SEVERE SECURITY RISK

// DANGEROUS: Unvalidated user input as arguments
await pool.thread(
  (code) => eval(code), // Allows arbitrary code execution
  userInput
);

Strict Mode (Recommended)

NodeSwarm enables strict mode by default, which performs validation checks on functions before execution:

const pool = new ThreadPool({ strictMode: true }); // Default

// This will throw an error due to security validation
try {
  await pool.thread(() => {
    require('fs').readFileSync('/etc/passwd'); // Blocked!
  });
} catch (error) {
  console.error('Security validation failed:', error);
}

Strict mode detects and blocks:

  • require() and import statements
  • eval() and Function() constructor calls
  • Access to process, global, __dirname, __filename
  • File system and child process operations

Important: Strict mode is defense-in-depth, not a security sandbox. It uses string pattern matching and can be bypassed via unicode escapes, template literals, or indirect evaluation. Code review remains the primary defense for untrusted function inputs

Disabling Strict Mode

Only disable strict mode if you have a specific need and understand the risks:

const pool = new ThreadPool({ strictMode: false });

Shared Memory (ref)

ref() creates values backed by SharedArrayBuffer, enabling true shared memory between the main thread and worker threads.

Security considerations:

  • Race conditions: Multiple threads can read and write the same memory concurrently. Number operations use Float64Array (not atomic for compound operations like +=). String operations use Atomics for length tracking but not for content writes. Design accordingly.
  • Stack trace file reading: ref() reads source files from disk at call time to auto-detect variable names. This is a one-time cost per ref() call and only reads files that appear in the call stack. No user input influences which files are read.
  • Scope injection: pool.create() wraps the user's function in a new Function() that injects ref variables into scope. This uses the same eval-like mechanism as pool.thread() — the same security considerations apply. The injected variables are strictly the ref instances registered by the caller.

Important: ref() does not bypass strict mode validation. Functions passed to pool.create() are not currently validated by strict mode — only pool.thread() applies strict mode checks. If you rely on strict mode, be aware of this gap.

Best Practices

  1. Code Review: Always review functions passed to pool.thread()
  2. Input Validation: Validate all arguments passed to worker functions
  3. Principle of Least Privilege: Only pass necessary data to workers
  4. Audit Dependencies: Ensure your codebase doesn't pass untrusted code
  5. Environment Isolation: Run NodeSwarm in isolated environments for additional security

Limitations

What NodeSwarm Cannot Protect Against

  • Supply Chain Attacks: Compromised dependencies in your codebase
  • Code Injection: If your application logic allows untrusted code execution
  • Privilege Escalation: Running NodeSwarm with elevated privileges

Serialization Constraints

Worker threads can only receive serializable data:

// ✅ Serializable: primitives, plain objects, arrays
await pool.thread((data) => data.length, [1, 2, 3]);
await pool.thread((obj) => obj.value, { value: 42 });

// ❌ Not serializable: functions, symbols, class instances
await pool.thread((fn) => fn(), () => {}); // Will fail
await pool.thread((map) => map.size, new Map()); // Will fail

Exception: ref() values bypass serialization constraints because they use SharedArrayBuffer (transferred by reference, not copied). This is by design — the shared buffer is the same memory in both threads.

Reporting Security Issues

If you discover a security vulnerability in NodeSwarm, please report it responsibly:

  1. DO NOT open a public GitHub issue
  2. Email the maintainer at: mudwekat@gmail.com
  3. Include:
    • Description of the vulnerability
    • Steps to reproduce
    • Potential impact
    • Suggested fix (if any)

We will respond within 48 hours and work on a fix as quickly as possible.

Security Updates

  • Security patches are released as soon as possible
  • Check the CHANGELOG.md for security-related updates
  • Subscribe to release notifications on GitHub

Additional Resources

Version Support

Version Supported
2.x.x
1.x.x
0.x.x

License

This security policy is part of the NodeSwarm project and is licensed under the MIT License.

There aren’t any published security advisories