Allow property access to arbitrary objects #2746
Replies: 29 comments
-
Thanks for reporting. That's indeed a common use case. These restrictions are there for security reasons, we've created them quite tight initially, let's look into whether we can relax them. |
Beta Was this translation helpful? Give feedback.
-
I believe this limitation is from the isPlainObject check, which is currently required for both getting and setting any properties. (class/constructor Instances are not plain objects but We could look into the consequences of relaxing this condition: Allowing the manipulation of properties on non-plain objects would potentially affect any MathJS class instances and function properties, we need to determine if that is a security concern. I think we should exclude set/get properties of functions unless anyone has any objections, which leaves object class instances, specifically:
The second question may have more to do with application specific security (e.g defeating some logic defined on the custom class via the parser), I have no examples this is pure speculation. Note: calling of ghosted methods is dissalowed separately in the isSafeMethod check so we only need to consider non-method properties. |
Beta Was this translation helpful? Give feedback.
-
Maybe it is just fine for class instances (objects which are not plain objects) to allow accessing properties of any type as long as they are defined on the object instance itself and do not exist on the the prototype? |
Beta Was this translation helpful? Give feedback.
-
Accessing inherited properties is currently allowed on plain objects, I think it would be a shame to lose that but I'm a bit biased because that is the use case that got me involved in this part in the first place 😛 If dissalowing inherited properties is the way to allow class instance property access then different rules could be used for different object types, (we already use different rules for different property types). This might actually make it a bit more clear by grouping the conditions more explicitly (prop method rules also become unified through an access mode parameter rather than using a separate function), e.g:
Note this is just a rough outline and may have holes. Also I've blurred the line between "safe" and "valid" by requiring the access type to be valid for the property type (specifically mode=call is only allowed for method props, currently this is handled by a separate validation function), but I suppose there could be a third return mode to differentiate invalid from unsafe if needed. I prefer this more holistic organisation and have already noticed a hole in the existing implementation after looking at it from this new perspective. I think this organisation is also more accommodating to future additions and changes beyond this issue. We could make this internal to customs.js if you wanted to retain existing interfaces it exports. What do you think? [EDITED] (I was asking too many silly questions) [EDITED]... I'll try to simplify above into how this could move forward:
Then (assuming above goes ok) It would be worth having a poke around in various mathjs built ins to see if anything can be broken via an expression due to the new instance rules before merging. |
Beta Was this translation helpful? Give feedback.
-
Thanks for your interest in this topic. I am not sure I understand the issue with ghosted properties. I have a use case that may or may not by related where setters/getters are defined in the class and reference 'shadow' properties in the instance. This is to provide reactive behavior at the property level, e.g. redisplaying a view when a model changes. (I am using mobx, and this is basically how it works under the hood).
I would like this to work when Maybe one approach to allow full access to instance objects while limiting the risks would be for the application to explicitly whitelist constructors whose instances are legit, e.g. by adding them to a Set or WeakSe (to make the test efficient). |
Beta Was this translation helpful? Give feedback.
-
Just to clarify: I use "ghosted" for lack of a better word to describe when an own-property is overriding an inherited property of the same name. It originates from a similar scenario with variable scopes in linting.
The ghosting check is only used for methods (more specifically, properties where JavaScript property setters and getters are not only transparent to assignment like I think it might be possible to unintentionally override setters with On a side note: It's been a while since I've used JavaScript setters and getters, but I learned one thing from writing some small physics engines once which is: setters and getters are not very performant. So if your example is anything like your use case (it looks like a cartesian point of some kind), if you are accessing x and y many times a second (i.e doing some numerical integration in some simulation or something) you may want to consider that aspect. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the clarification. I now understand why you don't want to redefine a function from within a mathjs expression. Any thoughts about my suggestion to use a whitelist of constructor functions? The implementation is trivial:
Side note: I am fully aware of the performance hit of setters/getters, although it varies greatly across browsers (see, e.g., this performance test). |
Beta Was this translation helpful? Give feedback.
-
I don't see the need for it yet, unless i'm missing something? The pseudo code in my earlier post allows any instances of classes without the need for an explicit white list. Is there a particular behaviour i've left out that you need? [edit] Admittedly I haven't specified how I'm going to determine object = {}
object.constructor === Object // true
object.constructor === Function // false
object = function () {}
object.constructor === Object // false
object.constructor === Function // true
object = () => {}
object.constructor === Object // false
object.constructor === Function // true
const MyConstructor = function () {}
object = new MyConstructor()
object.constructor === Object // false
object.constructor === Function // false
class MyClass {}
object = new MyClass()
object.constructor === Object // false
object.constructor === Function // false So an Instance could be defined as an object whom's constructor is not one of Also I realise technically both |
Beta Was this translation helpful? Give feedback.
-
If you are OK with giving access to properties of any instance object (except ghosted function properties), that's great! However in your pseudo code, accessing (non-method) inherited properties of instance objects is listed as unsafe. I believe this would not work for my "reactive property" use case since the setter/getter is defined in the class (i.e., the prototype), not the instance. So checking access to |
Beta Was this translation helpful? Give feedback.
-
Ah, you are correct, since the get/set type is determined by it's return value it would fall into the non-method block. The inherited property restriction was suggested by Jos.
@josdejong could you tell us what the thinking behind that idea was? would it be possible to remove that condition safely? |
Beta Was this translation helpful? Give feedback.
-
For example, if you can replace the Coming weekend I hope to dive deeper in this discussion. |
Beta Was this translation helpful? Give feedback.
-
It the target property is a method I think that should be restricted by the
In other words, on instances the only allowed access mode for method property types is 'call' (disallowing set and get). To be honest I find the security test cases difficult to follow and will need to study them with a bit more effort. |
Beta Was this translation helpful? Give feedback.
-
If it's easier I could just go ahead and implement this and then we can poke around with some examples afterwards to try and break it. It's probably more intuitive to just see the code in action... I realise this isn't exactly the most exciting type of feature for mathjs 😛 I'm hoping to be able to do this next weekend (shouldn't take much time). |
Beta Was this translation helpful? Give feedback.
-
Sorry for the late reply
ha ha, yes, it's far from my favorite but very important. @ThomasBrierley it would be great if you could play around with it. The code Just to try, I have changed So what we aim for now is the following right?
|
Beta Was this translation helpful? Give feedback.
-
That sounds about right. I'd like to do this by going down the re-factor route based on my pseudo code from earlier if that's ok. This should make it much clearer to reason about the various rules as well as modifying them. I'll have a go over the weekend. |
Beta Was this translation helpful? Give feedback.
-
Great, curious to see what you will come up with! |
Beta Was this translation helpful? Give feedback.
-
I've done some experimentation, didn't quite turn out like I expected, I might have a few questions to ask later to save me some time. My first attempt didn't break as many test as I expected once I removed checking for specific assertion messages... I have about 11 broken, a handful are to be expected because they test exactly what we are trying to allow (I will list them once i'm happy with the implementation)... others I need to work on because they are due to differences in the implementation. RE #1019 Moving away from native eval sounds nice. Do you think that would completely obviate any property access checking for security? or would it just turn it into something more specific to mathjs expressions? |
Beta Was this translation helpful? Give feedback.
-
Property access checking remains essential (see #1019 (comment)), basically we must prevent from getting access to |
Beta Was this translation helpful? Give feedback.
-
@ThomasBrierley did you manage to make any progress in refactoring the accessor functions? What is the current state? |
Beta Was this translation helpful? Give feedback.
-
Sorry, things got critical at work and then I was also sick so haven't made any progress. I'll try to fit some more in this week. |
Beta Was this translation helpful? Give feedback.
-
I'll carry on with this idea for now and if mike's idea in #1019 becomes the way to go then we can just take the rules and put them in a class instead. Maybe as a separate PR to keep things simple. |
Beta Was this translation helpful? Give feedback.
-
Take it easy, no need to hurry. I have these weeks too, I guess everyone has. :) |
Beta Was this translation helpful? Give feedback.
-
Did this ever find a resolution? I'm trying to do something like this, which is failing because the property is inaccessible:
The "easy" fix is to use an |
Beta Was this translation helpful? Give feedback.
-
No progress on this issue yet. If anyone is interested in picking this up please let me know. As for the problem at hand: using a |
Beta Was this translation helpful? Give feedback.
-
My colleagues are also stuck on an old version of MathJS due to this issue but i haven't had the time, I'll see if I can get some allotted time to spend on it. However i'm happy to surrender this if someone gets to it first 😃 [EDIT] This finally blocked something important enough to get me some time to spend on it 😃 I'll be picking it up this month to completion. |
Beta Was this translation helpful? Give feedback.
-
😬 several years later... hoping for an update on this. I may be up for the challenge of taking this on. @josdejong any newer ideas/thoughts come to mind? |
Beta Was this translation helpful? Give feedback.
-
Thanks @WesleyKapow it's indeed quite some time ago. I don't remember all details by heart, but the most important is that we need to carefully think through security. In the mean time, we started implementing support for |
Beta Was this translation helpful? Give feedback.
-
Yikes, it's certainly been a while! Believe it or not my project is still stuck on an old version of MathJS and is still blocked by this, and it's the opposite of unimportant... it's just been a crazy few years. I can't promise anything, considering my last comment 😛 but I'm pretty sure I have an experimental branch kicking around somewhere. I'll post it if I find it, otherwise happy to step aside. |
Beta Was this translation helpful? Give feedback.
-
Good to hear back from your @ThomasBrierley 😁 |
Beta Was this translation helpful? Give feedback.
-
It is currently only possible to access properties of direct instances of Object.
This is highly constraining. For example I cannot use a class Point like so:
Why is this? I assume there is some security issue?
Could this constraint be somehow relaxed?
For now I have patched my version by removing the test
object.constructor === Object
in functionisPlainObject
, but this is not a satisfying solution...Beta Was this translation helpful? Give feedback.
All reactions