New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Figure out how to do read-only Programmable property access #50
Comments
In current browsers, a start would be to isolate the code doing something like: with(newContext){
eval(playerCode)
} (yeah, I know, 3 lines of JS and I use both In |
Unless I misunderstand, I think we can handle that sort of restricting access to the function context already. Here's an example of how the Programmable Component is currently doing it: Relevant part: # ...
inner = aether.createFunction()
outer = (args...) ->
userContext = @createUserContext()
try
result = inner.apply userContext, args
catch error
@handleProgrammingError error, methodName
@replaceMethodCode methodName, null # no-op when this method is called
result = null
@updateFromUserContext userContext # Apply any viable changes they actually made in the context to the real objects
return result
# ...
createUserContext: ->
ctx = {}
thang = @
props = _.union @programmableProperties, @extraProgrammableProperties
for prop in props
value = @[prop]
v2 = value
if _.isFunction value
do (prop, v2, thang) ->
value = =>
result = v2.apply thang, arguments # Functions use original thangs
# But then the context also needs to get updated with any new state that happened as a result of the function call!
# (this.setTarget(obj) gives thang.target == obj, but not ctx.target == obj)
for anotherProp in props
newValue = thang[anotherProp]
unless _.isFunction newValue
ctx[anotherProp] = newValue
result
# Object.defineProperty doesn't work in web worker in my test, no idea why; works in Chrome console
#Object.defineProperty ctx, prop, {value: value, writable: false, enumerable: true}
# It does seem to work in IE 9 and 10 but not 11 and then make it so the property isn't updatable.
# Or something. Man, that was a terrible bug. An endless ocean of blood was spilt here.
ctx[prop] = value # so doing this instbead
for prop in @userCreatedProperties
ctx[prop] = @[prop]
ctx
updateFromUserContext: (ctx) ->
for own prop, value of ctx
if (prop in @programmableProperties) or (prop in @extraProgrammableProperties) then continue
unless prop in @userCreatedProperties
if @[prop]? then continue
@userCreatedProperties.push prop
if prop in @userCreatedProperties
@[prop] = value |
Working on a way to do this over in Aether: https://github.com/codecombat/aether/blob/master/src/protectAPI.coffee It's pretty much the slowest, most brutish thing I could think of that might work: every time user code gets access to an object (through an argument to the method being defined, through the "this" value, or through the return value of some method call), Aether replaces it with a deep clone that has access only to properties listed in the object's "apiProperties" array, but with any methods within bound to the original objects (so that things like setters still work to update the game). Then when user code sends out an object (through the return value or a method call), Aether restores original value from the clone. Still a long way to go on this, but maybe it will work. |
Despite the horrifying complexity of my approach, I think I've pretty much got it. Let the hacking attempts commence. Anyone who can still modify the supposedly read-only properties of other Thangs from within CodeCombat spells wins my grudging admiration! |
In the
programming.Programmable
Component, the player's code runs within a context that is a copy of part of the World, limited to the properties specified inprogrammableProperties
. So for example, if I'm codingchooseAction()
for my Artillery, I might have;All this code should work:
So we need the
getNearestEnemy
,setTarget
,setAction
, andsay
methods to actually interact with the World to get the enemy and set the target and action and such. But this code should not work:I was trying to get around this with
Object.defineProperty
and marking things readonly and such, but I ran into some bugs where it wouldn't work. More testing is needed. Also, more thinking, since I'm a bit confused myself as to how best to do this. I originally thought that making a copy-only context, running the code in that, and them pulling the necessary changes out with a whitelist approach would do it, but then I ran into these problems and made the functions refer to the real functions, and now we have neither full security nor full versatility.The text was updated successfully, but these errors were encountered: