First of all, I want to clarify, I know that with
is deprecated, and using it is generally a bad practice.
However, my question is about a special case: using a special Proxy
object as the parameter of with
.
I'm working on a project, where I have to limit access of a piece of code to the global scope.
One approach might be to use a loop with eval
, that creates constant variables with the value of undefined
for each property of the global object, but that seems even worse than using with
, and cannot limit access to variables created with let
and const
.
The idea is to use a Proxy
as the argument of with
, whose...
has
trap always returns true
, therefore it doesn't allow any lookups or assignments to go beyond the with
statementget
trap operates normally, except that it throws ReferenceError
s when trying to access a non-existing variable (i.e. property)set
trap operates normally (or maybe contains some custom logic)target
object has no [[Prototype]]
(i.e. it was created with Object.create(null)
)target
object has an @@unscopables
property, with the value of an empty object, to allow scoping of every propertySo, something like this code:
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
}
}
There are several contras listed on the MDN's page about the with
statement, but this usage of it gets rid of each.
The problem:
Looking up identifiers that aren't a member of with
statement's parameter object is less performant.
Avoidance:
No lookups can go beyond the parameter object.
The problem:
It is hard to decide, which identifier gets looked up of those with the same name.
Avoidance:
All lookups and assignments retrieve or modify the property of the parameter object.
The problem:
The properties of the parameter object or its prototype might change in the future.
Avoidance:
The parameter object is initially empty and has no prototype, therefore no properties can change.
The above code works perfectly, and the contras listed on MDN don't seem to apply to this case.
So, my question is:
Is it still a bad practice to use the with
statement, and if so, what are the downsides of using it in this specific case?
Note: I know that this approach in itself is not secure and can be bypassed. However, this question is limited only to whether it's considered bad for some reason to use the abovementioned Proxy
-with
combination. In this question, I'm not concerned about security (that's a related, but different question).
Sounds like the good old lexical vs dynamic scope topic. In general lexical scope is more safe but in some situations dynamic scope makes sense, because it simplifies some solutions very much. I would say your example is one of the cases, where it may be useful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With