
vm2 v3.11.6
Sandbox JavaScript isolado para Node.js que executa código não confiável com acesso restrito a módulos integrados e recursos do host por meio de interceptação baseada em Proxy.
vm2 [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![License][license-image]][license-url]
[![Known Vulnerabilities][snyk-image]][snyk-url]
O vm2 é um sandbox que pode executar código não confiável com os módulos integrados do Node na lista de permissões.
Aviso de Segurança Importante
Antes de usar o vm2, você deve entender como ele funciona e quais são suas limitações.
O vm2 tenta isolar código JavaScript não confiável dentro do mesmo processo Node.js da sua aplicação. Ele faz isso por meio de uma rede complexa de Proxies que interceptam e mediam toda interação entre o sandbox e o ambiente host.
O Desafio Fundamental
JavaScript é uma linguagem extraordinariamente dinâmica. Objetos podem ser acessados por meio de cadeias de protótipos, construtores podem ser alcançados via objetos de erro, símbolos fornecem ganchos de protocolo e a execução assíncrona cria janelas de temporização. A enorme quantidade de maneiras de percorrer de um objeto a outro em JavaScript torna extremamente difícil construir um sandbox intraprocesso hermético.
Somos honestos quanto a essa realidade: Apesar de nossos melhores esforços, pesquisadores e profissionais de segurança continuamente descobrem novas maneiras de escapar do sandbox do vm2. Corrigimos ativamente essas vulnerabilidades à medida que são relatadas, mas a natureza de gato e rato do isolamento intraprocesso significa que:
- Novas formas de contorno provavelmente serão descobertas no futuro. Consulte nossos avisos de segurança para vulnerabilidades conhecidas.
- Você deve manter o vm2 atualizado para se beneficiar das correções de segurança mais recentes. Assine os avisos de segurança e atualize imediatamente.
- O vm2 não deve ser sua única linha de defesa. A defesa em profundidade é essencial ao executar código não confiável.
Alternativas Mais Robustas
Se você precisar de garantias de isolamento mais fortes, considere estas alternativas que oferecem isolamento real em nível de processo ou hardware:
| Solução | Abordagem | Desempenho | Contrapartidas |
|---|---|---|---|
| isolated-vm | Isolados V8 separados (heap V8 diferente) | Rápido | Em modo de manutenção; exige atualizações manuais do V8 |
| Processo separado / Worker | child_process ou threads Worker com permissões limitadas | Médio | Maior sobrecarga de IPC; os dados precisam ser serializados |
| Contêineres / VMs | Docker, gVisor, Firecracker | Lento | Sobrecarga de inicialização; uso intenso de recursos |
| Soluções gerenciadas | Execução de código em nuvem (ex.: AWS Lambda, Cloudflare Workers) | Variável | Latência de rede; dependência externa |
Quando o vm2 Ainda Pode Ser Apropriado
O vm2 pode ser adequado quando:
- Você precisa de integração estreita com objetos do host e comunicação síncrona rápida
- O código não confiável vem de uma fonte relativamente confiável (ex.: ferramentas internas, sistemas de plugins com autores revisados)
- Você combina o vm2 com outras camadas de segurança (isolamento de rede, restrições de sistema de arquivos, limites de recursos)
- Você aceita o risco e monitora ativamente as atualizações de segurança
Se você estiver executando código de fontes completamente não confiáveis (ex.: envios arbitrários de usuários), recomendamos fortemente usar uma solução com garantias de isolamento mais fortes.
Recursos
- Executa código não confiável com segurança em um único processo, lado a lado com seu código
- Controle total sobre a saída do console do sandbox
- O sandbox tem acesso limitado aos métodos do processo
- É possível fazer require de módulos (integrados e externos) a partir do sandbox
- Você pode limitar o acesso a determinados (ou todos) os módulos integrados
- Você pode chamar métodos com segurança e trocar dados e callbacks entre sandboxes
- Mantido ativamente com correções para métodos de escape conhecidos (veja Aviso de Segurança)
- Suporte a transpiladores
Como funciona
- Ele usa o módulo VM interno para criar um contexto seguro.
- Ele usa Proxies para impedir o escape do sandbox.
- Ele substitui o require integrado para controlar o acesso aos módulos.
Para uma análise aprofundada do funcionamento interno do vm2, consulte o arquivo CONTRIBUTING.md.
Qual é a diferença entre o vm do Node e o vm2?
Experimente você mesmo:```js import { runInNewContext } from "node:vm";
runInNewContext('this.constructor.constructor("return process")().exit()'); console.log('Never gets executed.');
No input content was provided for translation. Please supply the chunk text to translate.```js
import { VM } from 'vm2';
new VM().run('this.constructor.constructor("return process")().exit()');
// Throws ReferenceError: process is not defined
Instalação```sh
npm install vm2
## Exemplos rápidos```js
import { VM } from 'vm2';
const vm = new VM();
vm.run(`process.exit()`); // TypeError: process.exit is not a function
🔥 Funcionalidades
- Gerenciamento de Perfis: Alterna entre vários perfis da AWS sem esforço.
- Enumeração Rápida: Enumera vários serviços em paralelo com uma barra de progresso em tempo real.
- Descoberta Detalhada de Recursos:
- Buckets do S3 (nome, região, versionamento, criptografia, ACLs, políticas, bloqueio de acesso público, capacidade de URL pré‑assinada)
- Instâncias EC2 (ID, tipo, estado, endereço IP, VPC, sub‑rede, grupos de segurança, horário de lançamento, tags)
- Usuários, perfis e políticas do IAM (pode ajudar a identificar contas desatualizadas ou com privilégios excessivos)
- Funções Lambda (runtime, última modificação, tamanho do código)
- Bancos de dados RDS (engine, versão, endpoint, porta, armazenamento, multi‑AZ, criptografia, acessibilidade pública)
- Tabelas do DynamoDB (modo de cobrança, contagem de itens, tamanho, criptografia)
- VPCs, sub‑redes, tabelas de rotas e regras de grupos de segurança (pode revelar exposições públicas não intencionais)
- Análise de Segurança do S3:
- Relata buckets com acesso público, ACLs abertas ou políticas irrestritas
- Destaca buckets sem criptografia
- Lista buckets com versionamento e exclusão por MFA desabilitados
- Detecta buckets com hospedagem de site habilitada
- Suporta a verificação de capacidades de URL pré‑assinada
- Recursos Não Utilizados e Consciência de Custos: Encontra volumes EBS desanexados, instâncias RDS ociosas, chaves IAM não rotacionadas, buckets S3 vazios, funções Lambda inativas e tabelas DynamoDB não utilizadas.
- Geração de Relatório HTML: Cria um relatório HTML profissional e pesquisável, com painel de resumo, gráficos, abas de navegação e tabelas detalhadas de recursos.```js import { NodeVM } from 'vm2';
const vm = new NodeVM({ require: { external: true, root: './', }, });
vm.run(
var request = require('request'); request('http://www.google.com', function (error, response, body) { console.error(error); if (!error && response.statusCode == 200) { console.log(body); // Show the HTML for the Google homepage. } });,
'vm.js',
);
## Documentação
- [VM](#vm)
- [NodeVM](#nodevm)
- [VMScript](#vmscript)
- [Tratamento de erros](#error-handling)
- [Depuração de código em sandbox](#debugging-a-sandboxed-code)
- [Objetos somente leitura](#read-only-objects-experimental)
- [Objetos protegidos](#protected-objects-experimental)
- [Relações entre sandboxes](#cross-sandbox-relationships)
- [CLI](#cli)
- [Mudanças da 2.x para 3.x](https://github.com/patriksimek/vm2/wiki/2.x-to-3.x-changes)
- [Documentação da 1.x e 2.x](https://github.com/patriksimek/vm2/wiki/1.x-and-2.x-docs)
- [Contribuindo](https://github.com/patriksimek/vm2/wiki/Contributing)
## VM
VM é um sandbox simples para executar código não confiável de forma síncrona sem o recurso `require`. Apenas objetos internos do JavaScript e `Buffer` do Node estão disponíveis. Funções de agendamento (`setInterval`, `setTimeout` e `setImmediate`) não estão disponíveis por padrão.
**Opções:**
- `timeout` - Tempo limite do script em milissegundos. **ATENÇÃO**: Talvez você queira usar esta opção junto com `allowAsync=false`. Além disso, operar em objetos retornados do sandbox pode executar código arbitrário e contornar o tempo limite. Deve-se testar se o objeto retornado é um primitivo com `typeof` e descartá-lo completamente (fazer log ou criar mensagens de erro com esse objeto também pode executar código arbitrário novamente) caso contrário.
- `sandbox` - Objeto global da VM.
- `compiler` - `javascript` (padrão), `typescript`, `coffeescript` ou função de compilador personalizada. A biblioteca espera que você tenha o compilador pré-instalado se o valor for definido como `typescript` ou `coffeescript`.
- `eval` - Se definido como `false`, qualquer chamada a `eval` ou construtores de função (`Function`, `GeneratorFunction`, etc.) lançará um `EvalError` (padrão: `true`).
- `wasm` - Se definido como `false`, qualquer tentativa de compilar um módulo WebAssembly lançará um `WebAssembly.CompileError` (padrão: `true`). Observação: `WebAssembly.JSTag` é removido dentro do sandbox por motivos de segurança, portanto o código wasm não pode capturar exceções JavaScript.
- `allowAsync` - Se definido como `false`, qualquer tentativa de executar código usando `async` lançará um `VMError` (padrão: `true`).
- `bufferAllocLimit` - Tamanho máximo em bytes para uma única solicitação `Buffer.alloc` / `Buffer.allocUnsafe` / `Buffer.allocUnsafeSlow` / `Buffer(N)` / `new Buffer(N)` de dentro do sandbox. Solicitações que excedem esse limite lançam um `RangeError` de forma síncrona sem realizar a alocação no host. Padrão: `Infinity` (sem limite, totalmente compatível com versões anteriores). Incorporadores que executam código não confiável em ambientes com memória limitada (Docker / Kubernetes / Lambda / serverless) devem optar por um limite finito (ex.: `32 * 1024 * 1024`) como parte da defesa em camadas contra DoS, da mesma forma que optam por `timeout`. Consulte [Recomendações de endurecimento](#hardening-recommendations) abaixo.
**IMPORTANTE**: O tempo limite só é eficaz em código síncrono executado por meio de `run`. O tempo limite **NÃO** funciona em nenhum método retornado pela VM. Existem algumas situações em que o tempo limite não funciona - consulte [#244](https://github.com/patriksimek/vm2/pull/244).```js
import { VM } from 'vm2';
const vm = new VM({
timeout: 1000,
allowAsync: false,
sandbox: {},
});
vm.run('process.exit()'); // throws ReferenceError: process is not defined
Você também pode recuperar valores da VM.```js let number = vm.run('1337'); // returns 1337
**DICA**: Consulte os testes para mais exemplos de uso.
## NodeVM
Ao contrário de `VM`, o `NodeVM` permite que você utilize módulos da mesma forma que faria no contexto normal do Node.
**Opções:**
- `console` - `inherit` para habilitar o console, `redirect` para redirecionar para eventos, `off` para desabilitar o console (padrão: `inherit`).
- `sandbox` - Objeto global da VM.
- `compiler` - `javascript` (padrão), `typescript`, `coffeescript` ou uma função de compilador personalizada (que recebe o código e o caminho do arquivo dele). A biblioteca espera que você tenha o compilador pré-instalado se o valor for definido como `typescript` ou `coffeescript`.
- `eval` - Se definido como `false`, qualquer chamada a `eval` ou construtores de função (`Function`, `GeneratorFunction`, etc.) lançará um `EvalError` (padrão: `true`).
- `wasm` - Se definido como `false`, qualquer tentativa de compilar um módulo WebAssembly lançará um `WebAssembly.CompileError` (padrão: `true`). Observação: `WebAssembly.JSTag` é removido dentro da sandbox por motivos de segurança, portanto, o código wasm não pode capturar exceções JavaScript.
- `bufferAllocLimit` - Mesma semântica do `VM` — tamanho máximo em bytes para uma única solicitação da família `Buffer.alloc` de dentro da sandbox. Padrão: `Infinity`. Consulte [Recomendações de endurecimento](#hardening-recommendations).
- `sourceExtensions` - Matriz de extensões de arquivo a serem tratadas como código-fonte (padrão: `['js']`).
- `require` - `true`, um objeto ou um Resolver para habilitar o método `require` (padrão: `false`).
- `require.external` - Os valores podem ser `true`, uma matriz de módulos externos permitidos ou um objeto (padrão: `false`). Todos os caminhos que correspondem a `/node_modules/${any_allowed_external_module}/(?!/node_modules/)` são permitidos para require.
- `require.external.modules` - Matriz de módulos externos permitidos. Também suporta curingas, então especificar `['@scope/*-ver-??]`, por exemplo, permitirá usar todos os módulos com um nome da forma `@scope/something-ver-aa`, `@scope/other-ver-11`, etc. O curinga `*` não corresponde a separadores de caminho.
- `require.external.transitive` - Booleano que indica se dependências transitivas de módulos externos são permitidas (padrão: `false`). **ATENÇÃO**: Quando um módulo é exigido transitivamente, qualquer módulo pode então exigi-lo normalmente, mesmo que isso não fosse possível antes de ser carregado.
- `require.builtin` - Matriz de módulos integrados permitidos, aceita ["\*"] para todos (padrão: nenhum). **ATENÇÃO**: "\*" pode ser perigoso, pois novos módulos integrados podem ser adicionados.
- `require.root` - Caminho(s) restrito(s) onde módulos locais podem ser exigidos (padrão: todos os caminhos).
- `require.mock` - Coleção de módulos simulados (tanto externos quanto integrados).
- `require.context` - `host` (padrão) para exigir módulos no host e fazer proxy deles para a sandbox. `sandbox` para carregar, compilar e exigir módulos na sandbox. `callback(moduleFilename, ext)` para escolher dinamicamente um contexto por módulo. O padrão será sandbox se nada for especificado. Exceto para `events`, os módulos integrados são sempre exigidos no host e encapsulados por proxy na sandbox.
- `require.import` - Uma matriz de módulos a serem carregados no NodeVM na inicialização.
- `require.resolve` - Uma função de consulta adicional caso um módulo não seja encontrado em um dos caminhos tradicionais de consulta do node.
- `require.customRequire` - Use em vez da função `require` para carregar módulos do host.
- `require.strict` - `false` para não forçar o modo estrito nos módulos carregados por require (padrão: `true`).
- `require.fs` - Implementação personalizada do sistema de arquivos.
- `nesting` - **ATENÇÃO**: Permitir isso é um risco de segurança, pois scripts podem criar uma NodeVM que pode exigir qualquer módulo do host. `true` para habilitar aninhamento de VMs (padrão: `false`).
- `wrapper` - `commonjs` (padrão) para envolver o script no wrapper CommonJS, `none` para recuperar o valor retornado pelo script.
- `argv` - Matriz a ser passada para `process.argv`.
- `env` - Objeto a ser passado para `process.env`.
- `strict` - `true` para módulos carregados em modo estrito (padrão: `false`).
**IMPORTANTE**: O timeout não é eficaz para o NodeVM, portanto ele não é imune a `while (true) {}` ou a outros códigos maliciosos semelhantes.
**LEMBRE-SE**: Quanto mais módulos você permitir, mais frágil sua sandbox se torna.```js
import { NodeVM } from 'vm2';
const vm = new NodeVM({
console: 'inherit',
sandbox: {},
require: {
external: true,
builtin: ['fs', 'path'],
root: './',
mock: {
fs: {
readFileSync: () => 'Nice try!',
},
},
},
});
// Sync
let functionInSandbox = vm.run('module.exports = function(who) { console.log("hello "+ who); }');
functionInSandbox('world');
// Async
let functionWithCallbackInSandbox = vm.run('module.exports = function(who, callback) { callback("hello "+ who); }');
functionWithCallbackInSandbox('world', greeting => {
console.log(greeting);
});
Quando wrapper é definido como none, NodeVM se comporta mais como VM para código síncrono.```js
assert.ok(vm.run('return true') === true);
**DICA**: Veja os testes para mais exemplos de uso.
### Carregando módulos por caminho relativo
Para carregar módulos por caminho relativo, você deve passar o caminho completo do script que está executando como segundo argumento para o método `run` do vm se o script for uma string. O nome do arquivo é então exibido em quaisquer stack traces geradas pelo script.```js
vm.run('require("foobar")', '/data/myvmscript.js');
Se o script que você está executando for um VMScript, o caminho é fornecido no construtor do VMScript.```js const script = new VMScript('require("foobar")', { filename: '/data/myvmscript.js' }); vm.run(script);
### Resolver
Um resolver pode ser criado via `makeResolverFromLegacyOptions` e usado para múltiplas instâncias de `NodeVM`, permitindo compartilhar código de módulo compilado e potencialmente acelerando os tempos de carregamento. O primeiro exemplo de `NodeVM` pode ser reescrito usando `makeResolverFromLegacyOptions` da seguinte forma.```js
const resolver = makeResolverFromLegacyOptions({
external: true,
builtin: ['fs', 'path'],
root: './',
mock: {
fs: {
readFileSync: () => 'Nice try!',
},
},
});
const vm = new NodeVM({
console: 'inherit',
sandbox: {},
require: resolver,
});
VMScript
Você pode aumentar o desempenho usando scripts pré-compilados. O VMScript pré-compilado pode ser executado várias vezes. É importante observar que o código não está vinculado a nenhuma VM (contexto); em vez disso, ele é vinculado antes de cada execução, apenas para essa execução.```js import { VM, VMScript } from 'vm2';
const vm = new VM(); const script = new VMScript('Math.random()'); console.log(vm.run(script)); console.log(vm.run(script));
Funciona para ambos `VM` e `NodeVM`.```js
import { NodeVM, VMScript } from 'vm2';
const vm = new NodeVM();
const script = new VMScript('module.exports = Math.random()');
console.log(vm.run(script));
console.log(vm.run(script));
O código é compilado automaticamente na primeira vez que é executado. Pode-se compilar o código a qualquer momento com script.compile(). Uma vez que o código é compilado, o método não tem efeito.
Tratamento de erros
Erros na compilação do código e na execução síncrona do código podem ser tratados por try-catch. Erros na execução assíncrona do código podem ser tratados anexando o manipulador de eventos uncaughtException ao process do Node.```js
try {
var script = new VMScript('Math.random()').compile();
} catch (err) {
console.error('Failed to compile script.', err);
}
try { vm.run(script); } catch (err) { console.error('Failed to execute script.', err); }
process.on('uncaughtException', err => { console.error('Asynchronous error caught.', err); });
## Depurando um código em sandbox
Você pode depurar ou inspecionar código em execução na sandbox como se estivesse em um processo normal.
- Você pode usar pontos de interrupção (o que exige especificar um nome de arquivo de script)
- Você pode usar a palavra-chave `debugger`.
- Você pode usar step-in para entrar no código em execução na sandbox.
### Exemplo
/tmp/main.js:```js
import { VM, VMScript } from 'vm2';
import { readFileSync } from 'node:fs';
const file = `${__dirname}/sandbox.js`;
// By providing a file name as second argument you enable breakpoints
const script = new VMScript(readFileSync(file), file);
new VM().run(script);
/tmp/sandbox.js```js const foo = 'ahoj';
// The debugger keyword works just fine everywhere. // Even without specifying a file name to the VMScript object. debugger;
## Objetos somente leitura (experimental)
Para impedir que scripts em sandbox adicionem, alterem ou excluam propriedades dos objetos com proxy, você pode usar métodos `freeze` para tornar o objeto somente leitura. Isso só é eficaz dentro da VM. Objetos congelados são afetados profundamente. Tipos primitivos não podem ser congelados.
**Exemplo sem usar `freeze`:**```js
const util = {
add: (a, b) => a + b,
};
const vm = new VM({
sandbox: { util },
});
vm.run('util.add = (a, b) => a - b');
console.log(util.add(1, 1)); // returns 0
Exemplo usando freeze:```js
const vm = new VM(); // Objects specified in the sandbox cannot be frozen.
vm.freeze(util, 'util'); // Second argument adds object to global.
vm.run('util.add = (a, b) => a - b'); // Fails silently when not in strict mode. console.log(util.add(1, 1)); // returns 2
**IMPORTANTE:** Não é possível congelar objetos que já foram passados por proxy para a VM.
## Objetos protegidos (experimental)
Ao contrário de `freeze`, este método permite que scripts em sandbox adicionem, alterem ou excluam propriedades em objetos, com uma exceção – não é possível anexar funções. Portanto, scripts em sandbox não conseguem modificar métodos como `toJSON`, `toString` ou `inspect`.
**IMPORTANTE:** Não é possível proteger objetos que já foram passados por proxy para a VM.
## Relações entre sandboxes```js
const assert = require('assert');
const { VM } = require('vm2');
const sandbox = {
object: new Object(),
func: new Function(),
buffer: new Buffer([0x01, 0x05]),
};
const vm = new VM({ sandbox });
assert.ok(vm.run(`object`) === sandbox.object);
assert.ok(vm.run(`object instanceof Object`));
assert.ok(vm.run(`object`) instanceof Object);
assert.ok(vm.run(`object.__proto__ === Object.prototype`));
assert.ok(vm.run(`object`).__proto__ === Object.prototype);
assert.ok(vm.run(`func`) === sandbox.func);
assert.ok(vm.run(`func instanceof Function`));
assert.ok(vm.run(`func`) instanceof Function);
assert.ok(vm.run(`func.__proto__ === Function.prototype`));
assert.ok(vm.run(`func`).__proto__ === Function.prototype);
assert.ok(vm.run(`new func() instanceof func`));
assert.ok(vm.run(`new func()`) instanceof sandbox.func);
assert.ok(vm.run(`new func().__proto__ === func.prototype`));
assert.ok(vm.run(`new func()`).__proto__ === sandbox.func.prototype);
assert.ok(vm.run(`buffer`) === sandbox.buffer);
assert.ok(vm.run(`buffer instanceof Buffer`));
assert.ok(vm.run(`buffer`) instanceof Buffer);
assert.ok(vm.run(`buffer.__proto__ === Buffer.prototype`));
assert.ok(vm.run(`buffer`).__proto__ === Buffer.prototype);
assert.ok(vm.run(`buffer.slice(0, 1) instanceof Buffer`));
assert.ok(vm.run(`buffer.slice(0, 1)`) instanceof Buffer);
CLI
Antes de poder usar o vm2 na linha de comando, instale-o globalmente com npm install vm2 -g.```sh
vm2 ./script.js
## Recomendações de endurecimento
O vm2 evita escapes de sandbox (código não confiável obtendo acesso ao realm do host). Ele **não** impede, por si só, todas as formas de esgotamento de recursos ou negação de serviço. Integradores que executam código não confiável devem adicionar as seguintes defesas em camadas ao redor da sandbox.
### 1. Limite a alocação de memória com `bufferAllocLimit`
Uma única chamada `Buffer.alloc(N)` com `N` controlado pelo atacante é executada como uma alocação síncrona em C++ do host que o `timeout` do V8 não pode interromper. Em ambientes com memória restrita, uma carga útil de sandbox de ~100 bytes pode causar um salto de RSS do host de 100 MB+ e derrubar o processo do host via OOM. Defina `bufferAllocLimit` (ex.: `32 * 1024 * 1024`) para limitar alocações individuais:```js
const vm = new VM({
timeout: 1000,
bufferAllocLimit: 32 * 1024 * 1024,
allowAsync: false,
});
O limite também se aplica aos caminhos obsoletos Buffer(N) e new Buffer(N). Observe que a exaustão agregada (muitas alocações pequenas, Buffer.concat, Uint8Array, String.repeat, Array(n).fill(), etc.) não é coberta por esse limite — combine com um limite de memória do lado do host (--max-old-space-size, limite do contêiner, cgroup) para cobertura completa.
2. Instale um manipulador unhandledRejection no lado do host
Existe uma classe de DoS por aborto do processo host em que o código da sandbox cria uma async function, async function* ou await using cujo corpo lança um valor que dispara um erro do reino do host durante a formatação da pilha (por exemplo, e.name = Symbol(); e.stack). O V8 cria a promise de rejeição por meio da Promise intrínseca do reino, o que contorna o encapsulamento da subclasse Promise do vm2, fazendo com que a rejeição escape para o host como unhandledRejection. No Node 15+, o comportamento padrão é encerrar o processo.
Fechar isso exige alterar o comportamento observável do host, portanto o vm2 não envia uma correção por padrão. Os incorporadores devem instalar um manipulador no nível do processo que engula (ou registre) rejeições originadas da sandbox:```js // Recommended: filter rejections that originated inside vm2 and swallow them, // while letting your own host-side rejections propagate. process.on('unhandledRejection', (reason, promise) => { // Heuristic: rejections from the sandbox frequently surface as values // without proper Error semantics, or with stacks pointing at vm.js. // Adjust the predicate to match your application. if (looksLikeSandboxOrigin(reason)) { return; // swallow — don't terminate the process } // Otherwise: handle (or rethrow) as normal for your host code. yourLogger.error('unhandled rejection', reason); });
Se a sua aplicação não tiver nenhuma outra fonte de rejeições não tratadas, uma captura geral + registro é aceitável:```js
process.on('unhandledRejection', reason => {
yourLogger.warn('swallowed sandbox rejection', reason);
});
Uma correção restrita pode ser lançada atrás de um sinalizador opt-in swallowSandboxUnhandledRejections em uma futura versão menor; até lá, o manipulador do lado do host é a mitigação recomendada.
3. Execute com um limite de memória no nível do processo
Mesmo com bufferAllocLimit definido, execute o processo host com --max-old-space-size (ou um limite de memória de contêiner equivalente) dimensionado para a carga de trabalho. O limite protege contra a primitiva de alocação única; o limite no nível do sistema operacional protege contra esgotamento agregado e contra qualquer primitiva de alocação futura que o vm2 ainda não tenha limitado.
4. Trate require.builtin: ['*'] como uma configuração que não é de sandbox
O curinga '*' expande para a maioria dos built-ins do Node, incluindo child_process, fs, dgram, net, http e dns. Essas são primitivas completas de capacidade do host — require('child_process').execSync('id') é alcançável a partir do sandbox sob '*'. A semântica de '*' do vm2 é intencional (alguns incorporadores executam código confiável, mas isolado), mas ela não deve ser usada como padrão para código não confiável. Prefira uma allowlist explícita do menor conjunto de módulos que seu sandbox realmente precisa.
5. nesting: true é uma válvula de escape
nesting: true permite que o código do sandbox faça require('vm2') e construa NodeVMs aninhadas. A configuração de require da VM aninhada é escolhida pelo código do sandbox que a constrói, não restringida pela VM externa. Concretamente:```js
const vm = new NodeVM({ nesting: true, require: { builtin: [] } });
vm.run( const { NodeVM: NVM } = require('vm2'); // Inner VM's config is whatever the sandbox writes here: const inner = new NVM({ require: { builtin: ['child_process'] } }); inner.run('require("child_process").execSync("id")'); // RCE);
Se você definir `nesting: true`, você efetivamente concedeu à sandbox o mesmo nível de confiança que você tem. **Não habilite `nesting: true` para código não confiável.** Use-o apenas quando você confia no código da sandbox em si, mas deseja semântica de execução estilo VM (global novo, timeouts controlados) por motivos não relacionados à segurança.
`nesting: true` **exige um objeto de configuração `require` explícito** (ex.: `require: { builtin: [] }` ou `require: {}`). Qualquer outra forma — `require: false`, `require: undefined`, `require: null`, ou omitir `require` completamente — lança `VMError` na construção (GHSA-m4wx-m65x-ghrr, substitui GHSA-8hg8-63c5-gwmx). Todas essas formas produzem um resolvedor somente-NESTING_OVERRIDE: a sandbox pode usar `require('vm2')` mas nada mais, o que é uma primitiva de escape pura sem uso legítimo. Para negar todos os requires, remova `nesting: true`. Para permitir VMs aninhadas, forneça um objeto de configuração `require` explícito para que o trade-off fique visível no local da chamada.
## Problemas Conhecidos
- Não é possível definir uma classe que estenda uma classe com proxy. Isso inclui usar uma classe com proxy em `Object.create`.
- O eval direto não funciona.
- Registrar arrays da sandbox fará com que a parte do array se repita nas propriedades.
- As transformações de código-fonte podem resultar em uma string de origem diferente para uma função.
- Existem maneiras de derrubar o processo node de dentro da sandbox. Consulte [Recomendações de endurecimento](#hardening-recommendations).
[npm-image]: https://img.shields.io/npm/v/vm2.svg
[npm-url]: https://www.npmjs.com/package/vm2
[license-image]: https://img.shields.io/npm/l/vm2.svg
[license-url]: https://raw.githubusercontent.com/patriksimek/vm2/resurrection/LICENSE.md
[downloads-image]: https://img.shields.io/npm/dm/vm2.svg
[downloads-url]: https://www.npmjs.com/package/vm2
[snyk-image]: https://snyk.io/test/github/patriksimek/vm2/badge.svg
[snyk-url]: https://snyk.io/test/github/patriksimek/vm2