Usar a instrução `with` com Proxies é uma má prática?

8

Antes de tudo, quero esclarecer, sei que isso withfoi preterido e usá-lo geralmente é uma prática ruim .

No entanto, minha pergunta é sobre um caso especial: usar um Proxyobjeto especial como parâmetro de with.


fundo

Estou trabalhando em um projeto, no qual tenho que limitar o acesso de um pedaço de código ao escopo global.

Uma abordagem pode ser usar um loop com eval, que cria variáveis ​​constantes com o valor de undefinedpara cada propriedade do objeto global, mas isso parece ainda pior do que usar withe não pode limitar o acesso a variáveis ​​criadas com lete const.

A ideia

A idéia é usar a Proxycomo argumento de with, cuja ...

  • hasA armadilha sempre retorna true, portanto, não permite que nenhuma pesquisa ou atribuição vá além da withinstrução
  • getA armadilha opera normalmente, exceto que eles lançam ReferenceErrors ao tentar acessar uma variável inexistente (ou seja, propriedade)
  • set a armadilha opera normalmente (ou talvez contenha alguma lógica personalizada)
  • targetO objeto não possui [[Prototype]](ou seja, foi criado com Object.create(null))
  • targetO objeto tem uma @@unscopablespropriedade, com o valor de um objeto vazio, para permitir o escopo de cada propriedade

Então, algo como este código:

const scope = Object.create(null)
Object.assign(scope, {
  undefined,
  console,
  String,
  Number,
  Boolean,
  Array,
  Object,
  /* etc. */
  [Symbol.unscopables]: Object.create(null)
})

const scopeProxy = new Proxy(scope, {
  get: (obj, prop) => {
    if (prop in obj)
      return obj[prop]
    else
      throw new ReferenceError(`${prop} is not defined`)
  },
  set: Reflect.set,
  has: () => true
})

with(scopeProxy) {
  //Sandboxed code
  
  foo = Number('42')
  console.log(foo) //42
  
  try{
    console.log(scopeProxy) //Inaccessible
  }catch(e){
    console.error(e) //ReferenceError: scopeProxy is not defined
  }
}

Evitando contras

Existem vários contras listados na página do MDN sobre a withdeclaração , mas esse uso se livra de cada uma.

1. Desempenho

  • O problema:

    A pesquisa de identificadores que não são membros do withobjeto de parâmetro da instrução tem menos desempenho.

  • Prevenção:

    Nenhuma pesquisa pode ir além do objeto de parâmetro.

2. Ambiguidade

  • O problema:

    É difícil decidir qual identificador é procurado por pessoas com o mesmo nome.

  • Prevenção:

    Todas as pesquisas e atribuições recuperam ou modificam a propriedade do objeto de parâmetro.

3. Compatibilidade futura

  • O problema:

    As propriedades dos objetos de parâmetro ou seus protótipos podem mudar no futuro.

  • Prevenção:

    O objeto de parâmetro está inicialmente vazio e não possui protótipo; portanto, nenhuma propriedade pode ser alterada.

Questão

O código acima funciona perfeitamente e os contras listados no MDN não se aplicam a isso.

Então, minha pergunta é:

Ainda é uma má prática usar a withdeclaração e, em caso afirmativo, quais são as desvantagens de usá-la nesse caso?

FZs
fonte
Parece que o que você realmente deseja é executar o código dentro de uma sandbox. Sim, uma sandbox pode usar um proxy em sua implementação, mas você provavelmente não deve escrever isso sozinho.
Bergi 11/03
@ Bergi eu sei, que um mecanismo de sandbox deve ser mais complicado que esse, mas minha pergunta não é realmente sobre se esse sandbox é seguro e bom, mas se o uso withdessa maneira é ruim ou não.
FZs 12/03
Bem, ele ainda impede que você use o modo estrito e o acesso às propriedades através de withum proxy ainda é bastante lento. Eu tentaria procurar uma solução diferente para o problema subjacente, mas fora isso, você está apenas usando withcomo uma ferramenta que parece fazer o que você precisa.
Bergi 12/03

Respostas:

1

Parece o bom e velho tópico lexical vs dynamic scope . Em geral, o escopo lexical é mais seguro, mas em algumas situações o escopo dinâmico faz sentido, porque simplifica muito algumas soluções. Eu diria que seu exemplo é um dos casos em que pode ser útil.

ceving
fonte