bool JSLocation::putDelegate(ExecState* exec, PropertyName propertyName, JSValue, PutPropertySlot&, bool& putResult) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); putResult = false; Frame* frame = wrapped().frame(); if (!frame) return true; if (propertyName == exec->propertyNames().toString || propertyName == exec->propertyNames().valueOf) return true; String errorMessage; if (shouldAllowAccessToFrame(exec, frame, errorMessage)) return false; // Cross-domain access to the location is allowed when assigning the whole location, // but not when assigning the individual pieces, since that might inadvertently // disclose other parts of the original location. if (propertyName != exec->propertyNames().href) { throwSecurityError(*exec, scope, errorMessage); return true; } return false; }
bool JSLocation::getOwnPropertySlotDelegate(ExecState* exec, PropertyName propertyName, PropertySlot& slot) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); Frame* frame = wrapped().frame(); if (!frame) { slot.setUndefined(); return true; } // When accessing Location cross-domain, functions are always the native built-in ones. // See JSDOMWindow::getOwnPropertySlotDelegate for additional details. // Our custom code is only needed to implement the Window cross-domain scheme, so if access is // allowed, return false so the normal lookup will take place. String message; if (shouldAllowAccessToFrame(exec, frame, message)) return false; // We only allow access to Location.replace() cross origin. // Make it read-only / non-configurable to prevent writes via defineProperty. if (propertyName == exec->propertyNames().replace) { slot.setCustom(this, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsLocationInstanceFunctionReplace, 1>); return true; } throwSecurityError(*exec, scope, message); slot.setUndefined(); return true; }
bool JSDOMWindow::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { VM& vm = state->vm(); auto scope = DECLARE_THROW_SCOPE(vm); auto* thisObject = jsCast<JSDOMWindow*>(cell); if (!thisObject->wrapped().frame()) return false; String errorMessage; if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) { // We only allow setting "location" attribute cross-origin. if (propertyName == state->propertyNames().location) { bool putResult = false; if (lookupPut(state, propertyName, thisObject, value, *s_info.staticPropHashTable, slot, putResult)) return putResult; return false; } throwSecurityError(*state, scope, errorMessage); return false; } return Base::put(thisObject, state, propertyName, value, slot); }
static bool jsDOMWindowGetOwnPropertySlotRestrictedAccess(JSDOMWindow* thisObject, Frame* frame, ExecState* exec, PropertyName propertyName, PropertySlot& slot, const String& errorMessage) { VM& vm = exec->vm(); auto scope = DECLARE_THROW_SCOPE(vm); // We don't want any properties other than "close" and "closed" on a frameless window // (i.e. one whose page got closed, or whose iframe got removed). // FIXME: This handling for frameless windows duplicates similar behaviour for cross-origin // access below; we should try to find a way to merge the two. if (!frame) { if (propertyName == exec->propertyNames().closed) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, jsDOMWindowClosed); return true; } if (propertyName == exec->propertyNames().close) { slot.setCustom(thisObject, ReadOnly | DontDelete | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionClose, 0>); return true; } // FIXME: We should have a message here that explains why the property access/function call was // not allowed. slot.setUndefined(); return true; } // These are the functions we allow access to cross-origin (DoNotCheckSecurity in IDL). // Always provide the original function, on a fresh uncached function object. if (propertyName == exec->propertyNames().blur) { slot.setCustom(thisObject, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionBlur, 0>); return true; } if (propertyName == exec->propertyNames().close) { slot.setCustom(thisObject, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionClose, 0>); return true; } if (propertyName == exec->propertyNames().focus) { slot.setCustom(thisObject, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionFocus, 0>); return true; } if (propertyName == exec->propertyNames().postMessage) { slot.setCustom(thisObject, ReadOnly | DontEnum, nonCachingStaticFunctionGetter<jsDOMWindowInstanceFunctionPostMessage, 2>); return true; } // When accessing cross-origin known Window properties, we always use the original property getter, // even if the property was removed / redefined. As of early 2016, this matches Firefox and Chrome's // behavior. if (auto* entry = JSDOMWindow::info()->staticPropHashTable->entry(propertyName)) { // Only allow access to these specific properties. if (propertyName == exec->propertyNames().location || propertyName == exec->propertyNames().closed || propertyName == exec->propertyNames().length || propertyName == exec->propertyNames().self || propertyName == exec->propertyNames().window || propertyName == exec->propertyNames().frames || propertyName == exec->propertyNames().opener || propertyName == exec->propertyNames().parent || propertyName == exec->propertyNames().top) { bool shouldExposeSetter = propertyName == exec->propertyNames().location; CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, entry->propertyGetter(), shouldExposeSetter ? entry->propertyPutter() : nullptr); slot.setCustomGetterSetter(thisObject, DontEnum | CustomAccessor, customGetterSetter); return true; } // For any other entries in the static property table, deny access. (Early return also prevents // named getter from returning frames with matching names - this seems a little questionable, see // FIXME comment on prototype search below.) throwSecurityError(*exec, scope, errorMessage); slot.setUndefined(); return true; } // Check for child frames by name before built-in properties to match Mozilla. This does // not match IE, but some sites end up naming frames things that conflict with window // properties that are in Moz but not IE. Since we have some of these, we have to do it // the Moz way. if (auto* scopedChild = frame->tree().scopedChild(propertyNameToAtomicString(propertyName))) { slot.setValue(thisObject, ReadOnly | DontDelete | DontEnum, toJS(exec, scopedChild->document()->domWindow())); return true; } throwSecurityError(*exec, scope, errorMessage); slot.setUndefined(); return true; }