NodeSwarm uses function serialization to execute code in worker threads. While this provides flexibility, it requires careful consideration of security implications.
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.
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);// 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
);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()andimportstatementseval()andFunction()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
Only disable strict mode if you have a specific need and understand the risks:
const pool = new ThreadPool({ strictMode: false });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 useAtomicsfor 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 perref()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 anew Function()that injectsrefvariables into scope. This uses the same eval-like mechanism aspool.thread()— the same security considerations apply. The injected variables are strictly therefinstances 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.
- Code Review: Always review functions passed to
pool.thread() - Input Validation: Validate all arguments passed to worker functions
- Principle of Least Privilege: Only pass necessary data to workers
- Audit Dependencies: Ensure your codebase doesn't pass untrusted code
- Environment Isolation: Run NodeSwarm in isolated environments for additional security
- 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
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 failException: 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.
If you discover a security vulnerability in NodeSwarm, please report it responsibly:
- DO NOT open a public GitHub issue
- Email the maintainer at: mudwekat@gmail.com
- 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 patches are released as soon as possible
- Check the CHANGELOG.md for security-related updates
- Subscribe to release notifications on GitHub
| Version | Supported |
|---|---|
| 2.x.x | ✅ |
| 1.x.x | ✅ |
| 0.x.x | ❌ |
This security policy is part of the NodeSwarm project and is licensed under the MIT License.