This is the code I'm starting with to prototype goal into the Creep class:
Object.defineProperty(
Creep.prototype,"goal",{
set :function(value){
this.memory.goal= value.id;
},
get :function() {
return Game.getObjectById(this.memory.goal);
},
}
)
Now let's suppose I want Creep.goal not to contain a single value, but multiple values instead, and let every single sub-properties of Creep.goal have the foresaid accessors.
(So I can easily store multiple game objects into the creep's memory)
These properties are meant to be added at runtime, so I do not know how many there will be nor their names, thus I can't simply copy-paste this code once for every property I'd like there to be.
How should I proceed in order to define the accessors of all possible properties-to-be of an object ?
----- SOLUTION -----
So I was suggested to use a Proxy for this. It was a completely new concept to me and I've hit a lot of walls, but I got something to work like I wanted !
// Prototyping goal as a proxy
Object.defineProperty(
Creep.prototype,"goal",{
get :function()
{return new Proxy(this.memory.goal, objectInMemory) }
}
)
// Proxy's Handler (my previous accessors)
const objectInMemory= {
set(goal, property, value){
goal[property] = value.id;
return true;
},
get(goal, property){
return Game.getObjectById(goal[property]);
},
}
Not exactly sure what you're aiming for, but if the properties are truly dynamic and have to be evaluated at runtime there are Proxy objects that are supported by Screeps' runtime.
What this does is it allows you to programmatically intercept all of the messages for an object, including accessing properties.
Related
After setting up eslint-plugin-security, I went on to attempt to address nearly 400 uses of square brackets in our JavaScript codebase (flagged by the rule security/detect-object-injection). Although this plugin could be a lot more intelligent, any uses of square brackets could possibly be an opportunity for a malicious agent to inject their own code.
To understand how, and to understand the whole context of my question, you need to read this documentation: https://github.com/nodesecurity/eslint-plugin-security/blob/master/docs/the-dangers-of-square-bracket-notation.md
I generally tried to use Object.prototype.hasOwnProperty.call(someObject, someProperty) where I could to mitigate the chance that someProperty is maliciously set to constructor. Lot of situations were simply dereferencing an array index in for loops (for (let i=0;i<arr.length;i++) { arr[i] }) If i is always a number, this is obviously always safe.
One situation I don't think I have handled perfectly, are square bracket assignments like this:
someObject[somePropertyPotentiallyDefinedFromBackend] = someStringPotentiallyMaliciouslyDefinedString
I think the easiest way to solve this issue is with a simple util, safeKey defined as such:
// use window.safeKey = for easy tinkering in the console.
const safeKey = (() => {
// Safely allocate plainObject's inside iife
// Since this function may get called very frequently -
// I think it's important to have plainObject's
// statically defined
const obj = {};
const arr = [];
// ...if for some reason you ever use square brackets on these types...
// const fun = function() {}
// const bol = true;
// const num = 0;
// const str = '';
return key => {
// eslint-disable-next-line security/detect-object-injection
if (obj[key] !== undefined || arr[key] !== undefined
// ||
// fun[key] !== undefined ||
// bol[key] !== undefined ||
// num[key] !== undefined ||
// str[key] !== undefined
) {
return 'SAFE_'+key;
} else {
return key;
}
};
})();
You'd then use it like so:
someObject[safeKey(somePropertyPotentiallyDefinedFromBackend)] = someStringPotentiallyMaliciouslyDefinedString
This means if the backend incidentally sends JSON with a key somewhere of constructor we don't choke on it, and instead just use the key SAFE_constructor (lol). Also applies for any other pre-defined method/property, so now the backend doesn't have to worry about JSON keys colliding with natively defined JS properties/methods.
This utility function is nothing without a series of passing unit tests. As I've commented not all the tests are passing. I'm not sure which object(s) natively define toJSON - and this means it may need to be part of a hardcoded list of method/property names that have to be blacklisted. But I'm not sure how to find out every one of these property methods that needs to be blacklisted. So we need to know the best way anyone can generate this list, and keep it updated.
I did find that using Object.freeze(Object.prototype) helps, but I don't think methods like toJSON exist on the prototype.
How can we make sure the property being set is essentially not already defined on vanilla objects? (i.e. constructor)
It is more important to prevent a key from being accessed on the wrong object than to validate/protect object keys themselves. Designating certain object keys as ‘unsafe’ and avoiding accessing just these regardless of the circumstances is just another form of the ‘sanitizing’ anti-pattern. If the object doesn’t contain sensitive data in the first place, there is no risk of it being exfiltrated or modified by untrusted inputs. You don’t need to worry about accessing src or innerHTML if you don’t access it on a DOM node; you don’t need to worry about exposing eval if you don’t perform lookups on the global object. As such:
Only use bracket notation with objects that either are arrays or specifically contain nothing other than a mapping from arbitrary strings to other values (those usually constructed by object literal notation); this kind of object I’m going to call map-like below. When using map-like objects, also ensure the following:
that you never store functions (or, in later versions of ECMAScript, classes or proxies to either) directly in a map-like object. This is to avoid problems when keys like 'toJSON' or 'then' map to functions that the language may then interpret as methods that modify the behaviour of the object. If, for some reason, you need to store functions in a map-like object, put the function in a wrapper like { _: function () { /* ... */ } }.
that you never coerce map-like objects to strings using built-in language mechanisms: the toString method, the String constructor (with or without new), the + operator or Array.prototype.join. This is to avoid triggering problems when the 'toString' key is set on a map-like object, as even a non-function value will prevent the default coercion behaviour from occurring and will instead throw a TypeError.
When accessing arrays, make sure the index is indeed an integer. Consider also using built-in methods like push, forEach, map or filter that avoid explicit indexing altogether; this will reduce the number of places you will need to audit.
If you ever need to associate arbitrary data with an object with a relatively fixed set of keys, e.g. a DOM node, window or an object you defined with class (all of which I’ll call class-like below), and for some reason WeakMap is not available, put the data it on a hardcoded key; if you have more than one such data item, put it in a map-like object a stored on a hardcoded key.
Even when following the above though, you may still fall victim to an injection or exfiltration attack by inadvertently accessing a property of Object.prototype. Particularly worrying are constructor and various built-in methods (which can be leveraged to access the Function object, and ultimately perform arbitrary code execution), and __proto__ (which can be used to modify the prototype chain of an object). To protect against those threats, you may try some of the following strategies. They are not mutually exclusive, but for the sake of consistency it may be preferable to stick with just one.
Mangle all keys: this is probably the (conceptually) simplest option, portable even to engines from the days of ECMAScript 3, and robust even against future additions to Object.prototype (as unlikely as they are). Simply prepend a single non-identifier character to all keys in map-like objects; this will safely namespace away untrusted keys from all reasonably conceivable JavaScript built-ins (which presumably should have names that are valid identifiers). When accessing map-like objects, check for this character and strip it as appropriate. Following this strategy will even make concerns about methods like toJSON or toString mostly irrelevant.
// replacement for (key in maplike)
function maplikeContains(maplike, key) {
return ('.' + key) in maplike;
}
// replacement for (maplike[key])
function maplikeGet(maplike, key) {
return maplike['.' + key];
}
// replacement for (maplike[key] = value)
function maplikeSet(maplike, key, value) {
return maplike['.' + key] = value;
}
// replacement for the for-in loop
function maplikeEach(maplike, visit) {
for (var key in maplike) {
if (key.charAt(0) !== '.')
continue;
if (visit(key.substr(1), maplike[key]))
break;
}
}
Mangling all keys indiscriminately ensures that you will not end up confusing unmangled keys for mangled keys or vice versa. For example if, like in the question, you mangle 'constructor' into 'SAFE_constructor', but leave 'SAFE_constructor' itself as-is, then after mangling both keys will end up referring to the same data, which may be a security vulnerability in itself.
A drawback of this approach is that the prefix character is going to end up in JSON, if you ever serialise such a map-like object.
Enforce direct property access. Read accesses can be guarded with Object.prototype.hasOwnProperty, which will stop exfiltration vulnerabilities, but will not protect you from inadvertently writing to __proto__. If you never mutate such a map-like object, this should not be a problem. You can even enforce immutability using Object.seal. If don’t want that though, you may perform property writes via Object.defineProperty, available since ECMAScript 5, which can create properties directly on the object, bypassing getters and setters.
// replacement for (key in maplike)
function maplikeContains(maplike, key) {
return Object.prototype.hasOwnProperty.call(maplike, key);
}
// replacement for (maplike[key])
function maplikeGet(maplike, key) {
if (Object.prototype.hasOwnProperty.call(maplike, key))
return maplike[key];
}
// replacement for (maplike[key] = value)
function maplikeSet(maplike, key, value) {
Object.defineProperty(maplike, key, {
value: value,
writable: true,
enumerable: true,
configurable: true
});
return value;
}
// replacement for the for-in loop
function maplikeEach(maplike, visit) {
for (var key in maplike) {
if (!Object.prototype.hasOwnProperty.call(maplike, key))
continue;
if (visit(key, maplike[key]))
break;
}
}
Clear the prototype chain: make sure map-like objects have an empty prototype chain. Create them via Object.create(null) (available since ECMAScript 5) instead of {}. If you created them via direct object literals before, you can wrap them in Object.assign(Object.create(null), { /* ... */ }) (Object.assign is available since ECMAScript 6, but easily shimmable to earlier versions). If you follow this approach, you can use the bracket notation as usual; the only code you need to check is where you construct the map-like object.
Objects created by JSON.parse will by default will still inherit from Object.prototype (although modern engines at least add a JSON key like __proto__ directly on the constructed object itself, bypassing the setter from the prototype’s descriptor). You can either treat such objects as read-only, and guard read accesses by hasOwnProperty (as above), or strip away their prototypes by writing a reviver function that calls Object.setPrototypeOf. A reviver function can also use Object.seal to make an object immutable.
function maplikeNew(maplike) {
return Object.assign(Object.create(null), maplike);
}
function jsonParse(json) {
return JSON.parse(json, function (key, value) {
if (typeof value === 'object' && value !== null && !Array.isArray(value))
Object.setPrototypeOf(value, null);
return value;
});
}
Use Maps instead of map-like objects: using Map (available since ECMAScript 6) allows you to use keys other than strings, which isn’t possible with plain objects; but even just with string keys you have the benefit that entries of the map are completely isolated from the prototype chain of the map object itself. Items in Maps are accessed by .get and .set methods instead of the bracket notation and cannot clash with properties at all: the keys exist in a separate namespace.
There is however the problem that a Map cannot be directly serialized into JSON. You can remedy this by writing a replacer function for JSON.stringify that converts Maps into plain, prototype-free map-like objects, and a reviver function for JSON.parse that turns plain objects back into Maps. Then again, naïvely reviving every JSON object into a Map is also going to cover structures I called ‘class-like’ above, which you probably don’t want. To distinguish between them, you might need to add some kind of schema parameter to your JSON-parsing function.
function jsonParse(json) {
return JSON.parse(json, function (key, value) {
if (typeof value === 'object' && value !== null && !Array.isArray(value))
return new Map(Object.entries(value));
return value;
});
}
function jsonStringify(value) {
return JSON.stringify(value, function (key, value) {
if (value instanceof Map)
return Object.fromEntries(value.entries());
return value;
});
}
If you ask me for my preference: use Map if you don’t need to worry about pre-ES6 engines or JSON serialisation; otherwise use Object.create(null); and if you need to work with legacy JS engines where neither is possible, mangle keys (first option) and hope for the best.
Now, can all this discipline be enforced mechanically? Yes, and it’s called static typing. With good enough type definitions, TypeScript should be able to catch cases where class-like objects are accessed in map-like fashion and vice versa. It can even catch some cases where an object with an unwanted prototype appears:
type Maplike<T> = {
[K: string]: T|undefined;
constructor?: T;
propertyIsEnumerable?: T;
hasOwnProperty?: T;
toString?: T;
toLocaleString?: T;
valueOf?: T;
};
const plain: { [K: string]: string } = {};
const naked: Maplike<string> = Object.create(null); // OK
const error: Maplike<string> = {}; // type error
function f(k: string) {
naked[k] = 'yay'; // OK
plain[k] = 'yay'; // OK
document.body[k] = 'yay'; // type error
}
console.info(plain.toString()); // OK
console.info(naked.toString()); // type error
Bear in mind this is no panacea, however. The above type definition may catch the most obvious mistakes, but it’s not too hard to come up with a case which it will not detect.
I'm trying to implement a dictionary much like Python. So, I would like to have a keys() method that returns keys added to the subclass Dict, but not properties such as the Object's method "keys"
EDIT AGAIN
Basically, I'm making a class to pass settings to a function like function(arg1, arg2, myObj) where my object is {map: texMap, alphaMap: aTexMap}. It's for Three.js, and I have to wait on images to download before I can create settings on 3D objects. So, interface like one would expect with d in var d = { a: aData b: bData }, but hide the methods etc that are not added by the user.
ie don't return this.prototype.propertyName when own is passedHere's what I have so far:
function Dict(){
this.prototype = {};
var _keys = this.prototype.keys;
this.keys = function(own){
if(typeof own === 'undefined') { return _keys(); }
var ownKeys = [];
for(var key in _keys()){
if(this.hasOwnProperty(key)) {
ownKeys.push(key);
}
}
return ownKeys;
}
}
Will this work as follows? Is there a better or already existent way to do it?
save the overloaded keys() method to a private var
return everything as usual, unless own is something that resolves to true.
if own == true, get the usual keys and filter out those
belonging to the superclass.
On the subject, I'm likely most concerned about saving back the prototype method as a way to get all of the keys and filter out proto keys.
Also, I've read overloading isn't built into Javascript. But, much of what I've found deals with standalone functions such as this Q&A on best practices. I don't need a built in way, but I'll take advantage of whatever's available (Hence, using Object as a Dict).
Any feedback is appreciated!
EDIT
In Python, we get this:
In[2]: d = {}
In[3]: 'has_key' in d.keys()
Out[3]: False
In[7]: 'has_key' in d.__class__.__dict__.keys()
Out[7]: True
In[8]: d.has_key('has_key')
Out[8]: False
In[9]: d['newKey'] = 5
In[10]: d.newKey # ERROR
Python has a dict attribute contained in its class where the functions are accessed via a dot (see In[8]...). So, those standard {} or dict() functions and operators are hidden (not private) while keys/data are added to the user's dict are accessed via []. d['newKey'] = 5 adds a new key or overwrites the old and sets the data to 5.
I don't need all of that to work, though it would be great. keys() returning Python-like keys would be fine for now.
There seem to be multiple issues here.
You seem to want to pass variable arguments to a function:
I'm making a class to pass settings to a function like function(arg1, arg2, myObj) where my object is {map: texMap, alphaMap: aTexMap}.
JS function arguments are very flexible.
You can either set up names for every one of them:
function foo(arg1, arg2, map, alphaMap)
and pass values directly. This style is preferred for functions that work on a fixed set of arguments.
Or you can set up an "options" object that collects keys and values:
function foo(options)
and pass {arg1: val1, arg2: val2, map: valMap, alphaMap: valAlphaMap}. This style often occurs on constructor functions that initialize objects with a certain set configuration options.
Or you can set up an empty function signature
function foo()
and work with the arguments collection inside the function. This is found in functions that work with a variable number of uniform arguments (imagine add(1, 2, 3, 4, 6)) or strictly positional arguments instead of named ones.
In any case, passing arguments to a function is optional in JavaScript, even when there is an argument list in the function signature. You are free to pass none, less or more arguments. Of course all these approaches can be combined if it suits you.
It's for Three.js, and I have to wait on images to download before I can create settings on 3D objects.
This is a problem caused by the asynchronous nature of the web. The solution is to use event handlers. These are either callbacks or - as an abstraction over callbacks - promises.
So, interface like one would expect with d in var d = { a: aData b: bData }, but hide the methods etc that are not added by the user.
This can be solved by not adding methods etc to data objects, or at least not directly. Add them to the prototype if your data objects must have behavior.
The direct equivalent to a Python Dict is a plain object in JavaScript.
var dict = {};
The direct equivalent of Python's keys() method is the Object.keys() static method in JavaScript.
var keys = Object.keys(dict);
To iterate the keys you can either use an imperative approach:
var i, key;
for (i = 0; i < keys.length; i++) {
key = keys[i];
doSomething(key, dict[key]);
}
or a functional one
keys.forEach(function (key) {
doSomething(key, dict[key]);
});
The direct equivalent of Python's in is .hasOwnProperty() in JavaScript:
if ( dict.hasOwnProperty('foo') ) ...
or, if it is a pure data object with no prototype chain, you can use in as well.
if ('foo' in dict)
Using in in for loops is not recommendable because it iterates the prototype properties as well. The way to guard against this is by using Object.keys() instead or by combining it with .hasOwnProperty(), as you did.
var key;
for (key in dict) {
if ( dict.hasOwnProperty(key) ) ...
}
Your question indicates that you are missing basic puzzle pieces about JS and try to substitute them with more familiar Python constructs. I would recommend not doing that.
I also suspect that you try to shoehorn Python's class-based inhertiance pattern into JS' prototype-based inheritance pattern. I strongly recommend that you don't do that, either.
I've defined an enumerable property in the prototype object and would like it to appear when I convert a prototyped object to JSON.
My first idea was to set it in toJSON but because I don't really want to keep it in the object afterwards I'll have to more or less clone the whole object in the function and set the necessary property.
Redefining the property in the target object and just proxying with the context of the current object doesn't seem to be an option as well, since I can't really use apply or call when getting dynamic properties.
Working solutions I could come up with so far seem to require quite an amount of code and aren't flexible and concise enough, so I'm wondering if there are any best practices of solving this task.
Here is an example which could seem a bit synthetic but still, I believe, conveys the idea:
function ProjectFolder() {
this.files = [];
Object.defineProperty(this, 'size', {enumerable: true, get: function() {
return this.files.length;
}});
}
function GithubProjectFolder() {
this.files = ['.gitignore', 'README.md'];
}
GithubProjectFolder.prototype = new ProjectFolder();
var project1 = new ProjectFolder();
JSON.stringify(project1);
// output: {"files":[],"size":0}
// size is present
var project = new GithubProjectFolder();
JSON.stringify(project);
// output: {"files":[".gitignore","README.md"]}
// size is absent
I'll have to more or less clone the whole object in the function and set the necessary property.
Yes, and there's nothing wrong with that. That's how .toJSON is supposed to work:
ProjectFolder.prototype.toJSON = function toJSON() {
var obj = {};
for (var p in this) // all enumerable properties, including inherited ones
obj[p] = this[p];
return obj;
};
However, there are two other points I'd like to make:
The size of a folder doesn't really need to be stored separately in the JSON when it already is encoded in the length of the files array. This redundant data seems to be superfluous, and can confuse deserialisation. Unless something requires this property to be present, I'd recommend to simply omit it.
In ProjectFolders, the .size is an own property of each instance - in GithubProjectFolders it is not. This suggest that you're doing inheritance wrong. Better:
function GithubProjectFolder() {
ProjectFolder.call(this);
this.files.puhs('.gitignore', 'README.md');
}
GithubProjectFolder.prototype = Object.create(ProjectFolder.prototype);
If you'd fix that alone, the size will appear in the serialisation of your project.
I have been coding in javascript for some time, but am fairly new to Node. I recently undertook a project that involves a complex object structure with multiple levels of prototypical inheritance and sub objects. This structure needs to be periodically saved / loaded. Saving and loading in JSON is desirable.
The Question
Is there a more elegant way of accomplishing the task of saving/loading these complex Javascript objects than my current method (outlined below)? Is it possible to design it in such a way where the constructors can initialize themselves as if they were normal objects without being bound by all of the restoring functionality?
My Solution
The base 'class' (from which, by design, all other objects under consideration inherit protoypically) has a function which processes an 'options' argument, adding all of it's properties to the current object. All deriving objects must include an options argument as the last argument and call the processing function in their constructor.
Each object also must add it's function name to a specific property so that the correct constructor function can be called when the object needs to be rebuilt.
An unpack function takes the saved object JSON, creates a plain object with JSON.parse and then passes that object in as the 'options' argument to the object's constructor.
Each object is given a unique id and stored in a lookup table, so that a function under construction with links to other objects can point to the right ones, or create them if it needs to.
Here is a plunker which demonstrates the idea (obviously in a non-Node way).
If you don't want to load the plunker, here's an excerpt which should hopefully provide the gist of what I'm trying to do:
function BaseClass(name, locale, options){
if(name) this.name = name;
if(locale) this.locale = locale;
// If options are defined, apply them
this.processOptions(options);
// create the classList array which keeps track of
// the object's prototype chain
this._classList = [arguments.callee.name];
// Create a unique id for the object and add it to
// the lookup table
if(!this.id) this.id = numEntities++;
lookupTable[this.id] = this;
if(!this.relations) this.relations = [];
// other initialization stuff
}
BaseClass.prototype = {
processOptions: function(options) {
if(options && !options._processed){
for(var key in options){
if(options.hasOwnProperty(key)){
this[key] = options[key];
}
}
options._processed = true;
}
},
addNewRelation: function(otherObj){
this.relations.push(otherObj.id);
}
// Other functions and such for the base object
}
function DerivedClassA(name, locale, age, options){
if(age) this.age = age;
this.processOptions(options);
if(options && options.connectedObj){
// Get the sub object if it already exists
if(lookupTable[options.subObj.id]){
this.subObj = lookupTable[options.subObj.id];
}
// Otherwise, create it from the options
else {
this.subObj = new OtherDerivedClass(options.subObj);
}
}
else {
// If no options then construct as normal
this.subObj = new OtherDerivedClass();
}
// If something needs to be done before calling the super
// constructor, It's done here.
BaseClass.call(this, name, locale, options);
this._classList.push(arguments.callee.name);
}
DerivedClassA.prototype = Object.create(BaseClass.prototype);
As mentioned, this gets the job done, but I can't help but feeling like this could be much better. It seems to impose a ridiculous amount of restrictions on the inheriting 'classes' and how their constructors must behave. It makes a specific order of execution critical, and requires that each object be deeply involved and aware of the restoration process, which is far from ideal.
Is it possible to reject the changes to a single property of a breeze object without rejecting ALL the changes to that object?
Say I have
// assume manager is an EntityManager containing a number of preexisting entities.
var person = manager.createEntity("Person");
// assume Name and House are valid properties of a Person object
person.Name("Jon Snow");
person.House("Lannister");
But I ONLY want to discard the changes made to the objects House property.
Is this possible, if so, how would I go about doing it?
Note: I would rather not iterate of the originalValues property and just replace them like that. I guess, I'm looking for a more graceful solution like...
person.House.rejectChanges();
where rejectChanges() is called on the property itself or something like that.
For the lack of a better solution I came up with the following code which seems to serve my purposes:
function RevertChangesToProperty(entity, propertyName) {
if (entity.entityAspect.originalValues.hasOwnProperty(propertyName)) {
var origValue = entity.entityAspect.originalValues[propertyName];
entity.setProperty(propertyName, origValue);
delete entity.entityAspect.originalValues[propertyName];
if (Object.getOwnPropertyNames(entity.entityAspect.originalValues).length === 0) {
entity.entityAspect.setUnchanged();
}
}
}
If person.House property has an entityAspect, you can call rejectChanges() on this property's entityAspect. Property has an entityAspect if it is an object, that has other properties. Simple type like string or Int does not have entityAspect, properties of simple type just belong to another object
person.House.entityAspect.rejectChanges()