Universal XSS in JSObject::putInlineSlow and JSValue::putToPrimitive
authorysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Aug 2019 01:20:22 +0000 (01:20 +0000)
committerysuzuki@apple.com <ysuzuki@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 10 Aug 2019 01:20:22 +0000 (01:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=199864

Reviewed by Saam Barati.

Source/JavaScriptCore:

Our JSObject::put implementation is not correct in term of the spec. Our [[Put]] implementation is something like this.

    JSObject::put(object):
        if (can-do-fast-path(object))
            return fast-path(object);
        // slow-path
        do {
            object-put-check-and-setter-calls(object); // (1)
            object = object->prototype;
        } while (is-object(object));
        return do-put(object);

Since JSObject::put is registered in the methodTable, the derived classes can override it. Some of classes are adding
extra checks to this put.

    Derived::put(object):
        if (do-extra-check(object))
            fail
        return JSObject::put(object)

The problem is that Derived::put is only called when the |this| object is the Derived class. When traversing [[Prototype]] in
JSObject::put, at (1), we do not perform the extra checks added in Derived::put even if `object` is Derived one. This means that
we skip the check.

Currently, JSObject::put and WebCore checking mechanism are broken. JSObject::put should call getOwnPropertySlot at (1) to
perform the additional checks. This behavior is matching against the spec. However, currently, our JSObject::getOwnPropertySlot
does not propagate setter information. This is required to cache cacheable [[Put]] at (1) for CustomValue, CustomAccessor, and
Accessors. We also need to reconsider how to integrate static property setters to this mechanism. So, basically, this involves
large refactoring to renew our JSObject::put and JSObject::getOwnPropertySlot.

To work-around for now, we add a new TypeInfo flag, HasPutPropertySecurityCheck . And adding this flag to DOM objects
that implements the addition checks. We also add doPutPropertySecurityCheck method hook to perform the check in JSObject.
When we found this flag at (1), we perform doPutPropertySecurityCheck to properly perform the checks.

Since our JSObject::put code is old and it does not match against the spec now, we should refactor it largely. This is tracked separately in [1].

[1]: https://bugs.webkit.org/show_bug.cgi?id=200562

* runtime/ClassInfo.h:
* runtime/JSCJSValue.cpp:
(JSC::JSValue::putToPrimitive):
* runtime/JSCell.cpp:
(JSC::JSCell::doPutPropertySecurityCheck):
* runtime/JSCell.h:
* runtime/JSObject.cpp:
(JSC::JSObject::putInlineSlow):
(JSC::JSObject::getOwnPropertyDescriptor):
* runtime/JSObject.h:
(JSC::JSObject::doPutPropertySecurityCheck):
* runtime/JSTypeInfo.h:
(JSC::TypeInfo::hasPutPropertySecurityCheck const):

Source/WebCore:

Test: http/tests/security/cross-frame-access-object-put-optimization.html

* bindings/js/JSDOMWindowCustom.cpp:
(WebCore::JSDOMWindow::doPutPropertySecurityCheck):
* bindings/js/JSLocationCustom.cpp:
(WebCore::JSLocation::doPutPropertySecurityCheck):
* bindings/scripts/CodeGeneratorJS.pm:
(GenerateHeader):
* bindings/scripts/test/JS/JSTestActiveDOMObject.h:

LayoutTests:

* http/tests/security/cross-frame-access-object-put-optimization-expected.txt: Added.
* http/tests/security/cross-frame-access-object-put-optimization.html: Added.
* http/tests/security/resources/cross-frame-iframe-for-object-put-optimization-test.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@248494 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/security/cross-frame-access-object-put-optimization-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/security/cross-frame-access-object-put-optimization.html [new file with mode: 0644]
LayoutTests/http/tests/security/resources/cross-frame-iframe-for-object-put-optimization-test.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/ClassInfo.h
Source/JavaScriptCore/runtime/JSCJSValue.cpp
Source/JavaScriptCore/runtime/JSCell.cpp
Source/JavaScriptCore/runtime/JSCell.h
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/JSTypeInfo.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSDOMWindowCustom.cpp
Source/WebCore/bindings/js/JSLocationCustom.cpp
Source/WebCore/bindings/scripts/CodeGeneratorJS.pm
Source/WebCore/bindings/scripts/test/JS/JSTestActiveDOMObject.h

index b5efd58..fa87531 100644 (file)
@@ -1,3 +1,14 @@
+2019-08-09  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        Universal XSS in JSObject::putInlineSlow and JSValue::putToPrimitive
+        https://bugs.webkit.org/show_bug.cgi?id=199864
+
+        Reviewed by Saam Barati.
+
+        * http/tests/security/cross-frame-access-object-put-optimization-expected.txt: Added.
+        * http/tests/security/cross-frame-access-object-put-optimization.html: Added.
+        * http/tests/security/resources/cross-frame-iframe-for-object-put-optimization-test.html: Added.
+
 2019-08-09  Ali Juma  <ajuma@chromium.org>
 
         Don't allow cross-origin iframes to autofocus
diff --git a/LayoutTests/http/tests/security/cross-frame-access-object-put-optimization-expected.txt b/LayoutTests/http/tests/security/cross-frame-access-object-put-optimization-expected.txt
new file mode 100644 (file)
index 0000000..8790e03
--- /dev/null
@@ -0,0 +1,10 @@
+This tests that you can't get cross-origin object during [[Put]] operation.
+
+PASS data is not "null"
+PASS data is not "Cocoa"
+PASS data is not "null"
+PASS data is not "Cocoa"
+PASS: successfullyParsed should be 'true' and is.
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/security/cross-frame-access-object-put-optimization.html b/LayoutTests/http/tests/security/cross-frame-access-object-put-optimization.html
new file mode 100644 (file)
index 0000000..af7d0e7
--- /dev/null
@@ -0,0 +1,54 @@
+<html>
+<head>
+    <script src="/js-test-resources/js-test-pre.js"></script>
+    <script src="resources/cross-frame-access.js"></script>
+    <script>
+        jsTestIsAsync = true;
+
+        data = null;
+
+        // Set up listener for message from iframe
+        addEventListener('message', function(event) {
+            if (event.data == "finishedLoad")
+                doTest();
+        }, false);
+
+        function turnLeakedException(object) {
+            try {
+                object.setter = {toString: function() { return {} } };
+            } catch (e) {
+                let crossOriginFunctionConstructor = e.constructor.constructor;
+                data = crossOriginFunctionConstructor(`
+                    var element = document.getElementById("password");
+                    if (!element)
+                        return null;
+                    return element.textContent;`)();
+            }
+            shouldNotBeEqualToString(`data`, `null`);
+            shouldNotBeEqualToString(`data`, "Cocoa");
+            data = null;
+        }
+
+        doTest = function()
+        {
+            let targetWindow = document.getElementById("target").contentWindow;
+
+            // putInlineSlow
+            turnLeakedException({__proto__: targetWindow.location});
+
+            // putToPrimitive
+            num = 1337;
+            num.__proto__.__proto__ = targetWindow.location;
+            turnLeakedException(num);
+
+            finishJSTest();
+        }
+    </script>
+</head>
+<body>
+    <div>This tests that you can't get cross-origin object during [[Put]] operation.</div>
+    <iframe id="target" src="http://localhost:8000/security/resources/cross-frame-iframe-for-object-put-optimization-test.html"></iframe>
+    <pre id="console"></pre>
+    <script src="/js-test-resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/http/tests/security/resources/cross-frame-iframe-for-object-put-optimization-test.html b/LayoutTests/http/tests/security/resources/cross-frame-iframe-for-object-put-optimization-test.html
new file mode 100644 (file)
index 0000000..abd2f6a
--- /dev/null
@@ -0,0 +1,9 @@
+<h1 id="password">Cocoa</h1>
+<script>
+onload = function() {
+    location.__defineSetter__('setter', function(value) {
+        throw new Error();
+    });
+    parent.postMessage("finishedLoad", "*");
+}
+</script>
index 757c219..edca836 100644 (file)
@@ -1,3 +1,62 @@
+2019-08-09  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        Universal XSS in JSObject::putInlineSlow and JSValue::putToPrimitive
+        https://bugs.webkit.org/show_bug.cgi?id=199864
+
+        Reviewed by Saam Barati.
+
+        Our JSObject::put implementation is not correct in term of the spec. Our [[Put]] implementation is something like this.
+
+            JSObject::put(object):
+                if (can-do-fast-path(object))
+                    return fast-path(object);
+                // slow-path
+                do {
+                    object-put-check-and-setter-calls(object); // (1)
+                    object = object->prototype;
+                } while (is-object(object));
+                return do-put(object);
+
+        Since JSObject::put is registered in the methodTable, the derived classes can override it. Some of classes are adding
+        extra checks to this put.
+
+            Derived::put(object):
+                if (do-extra-check(object))
+                    fail
+                return JSObject::put(object)
+
+        The problem is that Derived::put is only called when the |this| object is the Derived class. When traversing [[Prototype]] in
+        JSObject::put, at (1), we do not perform the extra checks added in Derived::put even if `object` is Derived one. This means that
+        we skip the check.
+
+        Currently, JSObject::put and WebCore checking mechanism are broken. JSObject::put should call getOwnPropertySlot at (1) to
+        perform the additional checks. This behavior is matching against the spec. However, currently, our JSObject::getOwnPropertySlot
+        does not propagate setter information. This is required to cache cacheable [[Put]] at (1) for CustomValue, CustomAccessor, and
+        Accessors. We also need to reconsider how to integrate static property setters to this mechanism. So, basically, this involves
+        large refactoring to renew our JSObject::put and JSObject::getOwnPropertySlot.
+
+        To work-around for now, we add a new TypeInfo flag, HasPutPropertySecurityCheck . And adding this flag to DOM objects
+        that implements the addition checks. We also add doPutPropertySecurityCheck method hook to perform the check in JSObject.
+        When we found this flag at (1), we perform doPutPropertySecurityCheck to properly perform the checks.
+
+        Since our JSObject::put code is old and it does not match against the spec now, we should refactor it largely. This is tracked separately in [1].
+
+        [1]: https://bugs.webkit.org/show_bug.cgi?id=200562
+
+        * runtime/ClassInfo.h:
+        * runtime/JSCJSValue.cpp:
+        (JSC::JSValue::putToPrimitive):
+        * runtime/JSCell.cpp:
+        (JSC::JSCell::doPutPropertySecurityCheck):
+        * runtime/JSCell.h:
+        * runtime/JSObject.cpp:
+        (JSC::JSObject::putInlineSlow):
+        (JSC::JSObject::getOwnPropertyDescriptor):
+        * runtime/JSObject.h:
+        (JSC::JSObject::doPutPropertySecurityCheck):
+        * runtime/JSTypeInfo.h:
+        (JSC::TypeInfo::hasPutPropertySecurityCheck const):
+
 2019-08-08  Per Arne Vollan  <pvollan@apple.com>
 
         [Win] Fix internal build
index 42525a0..f26985b 100644 (file)
@@ -72,6 +72,9 @@ struct MethodTable {
     using GetOwnPropertySlotByIndexFunctionPtr = bool (*)(JSObject*, ExecState*, unsigned, PropertySlot&);
     GetOwnPropertySlotByIndexFunctionPtr METHOD_TABLE_ENTRY(getOwnPropertySlotByIndex);
 
+    using DoPutPropertySecurityCheckFunctionPtr = void (*)(JSObject*, ExecState*, PropertyName, PutPropertySlot&);
+    DoPutPropertySecurityCheckFunctionPtr METHOD_TABLE_ENTRY(doPutPropertySecurityCheck);
+
     using ToThisFunctionPtr = JSValue (*)(JSCell*, ExecState*, ECMAMode);
     ToThisFunctionPtr METHOD_TABLE_ENTRY(toThis);
 
@@ -160,6 +163,7 @@ struct MethodTable {
         &ClassName::deletePropertyByIndex, \
         &ClassName::getOwnPropertySlot, \
         &ClassName::getOwnPropertySlotByIndex, \
+        &ClassName::doPutPropertySecurityCheck, \
         &ClassName::toThis, \
         &ClassName::defaultValue, \
         &ClassName::getOwnPropertyNames, \
index 4e5e6a0..9288e82 100644 (file)
@@ -162,18 +162,27 @@ bool JSValue::putToPrimitive(ExecState* exec, PropertyName propertyName, JSValue
         return false;
     JSValue prototype;
     if (propertyName != vm.propertyNames->underscoreProto) {
-        for (; !obj->structure(vm)->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
+        while (true) {
+            Structure* structure = obj->structure(vm);
+            if (structure->hasReadOnlyOrGetterSetterPropertiesExcludingProto() || structure->typeInfo().hasPutPropertySecurityCheck())
+                break;
             prototype = obj->getPrototype(vm, exec);
             RETURN_IF_EXCEPTION(scope, false);
 
             if (prototype.isNull())
                 return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
+            obj = asObject(prototype);
         }
     }
 
     for (; ; obj = asObject(prototype)) {
+        Structure* structure = obj->structure(vm);
+        if (UNLIKELY(structure->typeInfo().hasPutPropertySecurityCheck())) {
+            obj->methodTable(vm)->doPutPropertySecurityCheck(obj, exec, propertyName, slot);
+            RETURN_IF_EXCEPTION(scope, false);
+        }
         unsigned attributes;
-        PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
+        PropertyOffset offset = structure->get(vm, propertyName, attributes);
         if (offset != invalidOffset) {
             if (attributes & PropertyAttribute::ReadOnly)
                 return typeError(exec, scope, slot.isStrictMode(), ReadonlyPropertyWriteError);
index 0731827..96273ba 100644 (file)
@@ -212,6 +212,11 @@ bool JSCell::getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned, Property
     return false;
 }
 
+void JSCell::doPutPropertySecurityCheck(JSObject*, ExecState*, PropertyName, PutPropertySlot&)
+{
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
 void JSCell::getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode)
 {
     RELEASE_ASSERT_NOT_REACHED();
index 0f38604..69d7670 100644 (file)
@@ -265,6 +265,7 @@ protected:
     static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
     static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
     static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
+    static NO_RETURN_DUE_TO_CRASH void doPutPropertySecurityCheck(JSObject*, ExecState*, PropertyName, PutPropertySlot&);
 
 private:
     friend class LLIntOffsetsExtractor;
index 8c2c1c5..ba3a78d 100644 (file)
@@ -790,8 +790,13 @@ bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue
 
     JSObject* obj = this;
     for (;;) {
+        Structure* structure = obj->structure(vm);
+        if (UNLIKELY(structure->typeInfo().hasPutPropertySecurityCheck())) {
+            obj->methodTable(vm)->doPutPropertySecurityCheck(obj, exec, propertyName, slot);
+            RETURN_IF_EXCEPTION(scope, false);
+        }
         unsigned attributes;
-        PropertyOffset offset = obj->structure(vm)->get(vm, propertyName, attributes);
+        PropertyOffset offset = structure->get(vm, propertyName, attributes);
         if (isValidOffset(offset)) {
             if (attributes & PropertyAttribute::ReadOnly) {
                 ASSERT(this->prototypeChainMayInterceptStoreTo(vm, propertyName) || obj == this);
@@ -801,7 +806,7 @@ bool JSObject::putInlineSlow(ExecState* exec, PropertyName propertyName, JSValue
             JSValue gs = obj->getDirect(offset);
             if (gs.isGetterSetter()) {
                 // We need to make sure that we decide to cache this property before we potentially execute aribitrary JS.
-                if (!structure(vm)->isDictionary())
+                if (!this->structure(vm)->isDictionary())
                     slot.setCacheableSetter(obj, offset);
 
                 bool result = callSetter(exec, slot.thisValue(), gs, value, slot.isStrictMode() ? StrictMode : NotStrictMode);
@@ -3468,6 +3473,11 @@ bool JSObject::getOwnPropertyDescriptor(ExecState* exec, PropertyName propertyNa
     if (!result)
         return false;
 
+
+    // FIXME: https://bugs.webkit.org/show_bug.cgi?id=200560
+    // This breaks the assumption that getOwnPropertySlot should return "own" property.
+    // We should fix DebuggerScope, ProxyObject etc. to remove this.
+    //
     // DebuggerScope::getOwnPropertySlot() (and possibly others) may return attributes from the prototype chain
     // but getOwnPropertyDescriptor() should only work for 'own' properties so we exit early if we detect that
     // the property is not an own property.
index 41f51fe..b071f77 100644 (file)
@@ -168,6 +168,7 @@ public:
     static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
     JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSObject*, ExecState*, unsigned propertyName, PropertySlot&);
     bool getOwnPropertySlotInline(ExecState*, PropertyName, PropertySlot&);
+    static void doPutPropertySecurityCheck(JSObject*, ExecState*, PropertyName, PutPropertySlot&);
 
     // The key difference between this and getOwnPropertySlot is that getOwnPropertySlot
     // currently returns incorrect results for the DOM window (with non-own properties)
@@ -1404,6 +1405,10 @@ ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSObject* object, ExecState* exe
     return false;
 }
 
+ALWAYS_INLINE void JSObject::doPutPropertySecurityCheck(JSObject*, ExecState*, PropertyName, PutPropertySlot&)
+{
+}
+
 // It may seem crazy to inline a function this large but it makes a big difference
 // since this is function very hot in variable lookup
 template<bool checkNullStructure>
index d67b441..4b8d535 100644 (file)
@@ -56,6 +56,7 @@ static const unsigned IsImmutablePrototypeExoticObject = 1 << 13;
 static const unsigned GetOwnPropertySlotIsImpureForPropertyAbsence = 1 << 14;
 static const unsigned InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero = 1 << 15;
 static const unsigned StructureIsImmortal = 1 << 16;
+static const unsigned HasPutPropertySecurityCheck = 1 << 17;
 
 class TypeInfo {
 public:
@@ -96,6 +97,7 @@ public:
     bool prohibitsPropertyCaching() const { return isSetOnFlags2(ProhibitsPropertyCaching); }
     bool getOwnPropertySlotIsImpure() const { return isSetOnFlags2(GetOwnPropertySlotIsImpure); }
     bool getOwnPropertySlotIsImpureForPropertyAbsence() const { return isSetOnFlags2(GetOwnPropertySlotIsImpureForPropertyAbsence); }
+    bool hasPutPropertySecurityCheck() const { return isSetOnFlags2(HasPutPropertySecurityCheck); }
     bool newImpurePropertyFiresWatchpoints() const { return isSetOnFlags2(NewImpurePropertyFiresWatchpoints); }
     bool isImmutablePrototypeExoticObject() const { return isSetOnFlags2(IsImmutablePrototypeExoticObject); }
     bool interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero() const { return isSetOnFlags2(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero); }
index e4cdf8c..7a514c9 100644 (file)
@@ -1,3 +1,20 @@
+2019-08-09  Yusuke Suzuki  <ysuzuki@apple.com>
+
+        Universal XSS in JSObject::putInlineSlow and JSValue::putToPrimitive
+        https://bugs.webkit.org/show_bug.cgi?id=199864
+
+        Reviewed by Saam Barati.
+
+        Test: http/tests/security/cross-frame-access-object-put-optimization.html
+
+        * bindings/js/JSDOMWindowCustom.cpp:
+        (WebCore::JSDOMWindow::doPutPropertySecurityCheck):
+        * bindings/js/JSLocationCustom.cpp:
+        (WebCore::JSLocation::doPutPropertySecurityCheck):
+        * bindings/scripts/CodeGeneratorJS.pm:
+        (GenerateHeader):
+        * bindings/scripts/test/JS/JSTestActiveDOMObject.h:
+
 2019-08-09  Saam Barati  <sbarati@apple.com>
 
         When I did the devirtualization of the AST in r248488, I needed to
index 311e292..7a51ac4 100644 (file)
@@ -266,6 +266,25 @@ bool JSDOMWindow::getOwnPropertySlotByIndex(JSObject* object, ExecState* state,
     return Base::getOwnPropertySlotByIndex(thisObject, state, index, slot);
 }
 
+void JSDOMWindow::doPutPropertySecurityCheck(JSObject* cell, ExecState* state, PropertyName propertyName, PutPropertySlot&)
+{
+    VM& vm = state->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+
+    auto* thisObject = jsCast<JSDOMWindow*>(cell);
+    if (!thisObject->wrapped().frame())
+        return;
+
+    String errorMessage;
+    if (!BindingSecurity::shouldAllowAccessToDOMWindow(*state, thisObject->wrapped(), errorMessage)) {
+        // We only allow setting "location" attribute cross-origin.
+        if (propertyName == static_cast<JSVMClientData*>(vm.clientData)->builtinNames().locationPublicName())
+            return;
+        throwSecurityError(*state, scope, errorMessage);
+        return;
+    }
+}
+
 bool JSDOMWindow::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
 {
     VM& vm = state->vm();
index 13c2f65..3e662bd 100644 (file)
@@ -126,6 +126,23 @@ static bool putCommon(JSLocation& thisObject, ExecState& state, PropertyName pro
     return false;
 }
 
+void JSLocation::doPutPropertySecurityCheck(JSObject* object, ExecState* state, PropertyName propertyName, PutPropertySlot&)
+{
+    auto* thisObject = jsCast<JSLocation*>(object);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+
+    VM& vm = state->vm();
+
+    // Always allow assigning to the whole location.
+    // However, alllowing assigning of pieces might inadvertently disclose parts of the original location.
+    // So fall through to the access check for those.
+    if (propertyName == static_cast<JSVMClientData*>(vm.clientData)->builtinNames().hrefPublicName())
+        return;
+
+    // Block access and throw if there is a security error.
+    BindingSecurity::shouldAllowAccessToDOMWindow(state, thisObject->wrapped().window(), ThrowSecurityError);
+}
+
 bool JSLocation::put(JSCell* cell, ExecState* state, PropertyName propertyName, JSValue value, PutPropertySlot& putPropertySlot)
 {
     auto* thisObject = jsCast<JSLocation*>(cell);
index d5dd6ef..f1c58d0 100644 (file)
@@ -2655,6 +2655,11 @@ sub GenerateHeader
         push(@headerContent, "    static bool getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned propertyName, JSC::PropertySlot&);\n");
         $structureFlags{"JSC::InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero"} = 1;
     }
+
+    if ($interface->extendedAttributes->{CheckSecurity}) {
+        push(@headerContent, "    static void doPutPropertySecurityCheck(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PutPropertySlot&);\n");
+        $structureFlags{"JSC::HasPutPropertySecurityCheck"} = 1;
+    }
     
     if (InstanceOverridesGetOwnPropertyNames($interface)) {
         push(@headerContent, "    static void getOwnPropertyNames(JSC::JSObject*, JSC::ExecState*, JSC::PropertyNameArray&, JSC::EnumerationMode = JSC::EnumerationMode());\n");
index 0e3854f..5a5a037 100644 (file)
@@ -39,6 +39,7 @@ public:
     static JSC::JSObject* createPrototype(JSC::VM&, JSDOMGlobalObject&);
     static JSC::JSObject* prototype(JSC::VM&, JSDOMGlobalObject&);
     static TestActiveDOMObject* toWrapped(JSC::VM&, JSC::JSValue);
+    static void doPutPropertySecurityCheck(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, JSC::PutPropertySlot&);
     static void destroy(JSC::JSCell*);
 
     DECLARE_INFO;
@@ -51,7 +52,7 @@ public:
     static JSC::JSValue getConstructor(JSC::VM&, const JSC::JSGlobalObject*);
     static void heapSnapshot(JSCell*, JSC::HeapSnapshotBuilder&);
 public:
-    static const unsigned StructureFlags = Base::StructureFlags | JSC::HasStaticPropertyTable;
+    static const unsigned StructureFlags = Base::StructureFlags | JSC::HasPutPropertySecurityCheck | JSC::HasStaticPropertyTable;
 protected:
     JSTestActiveDOMObject(JSC::Structure*, JSDOMGlobalObject&, Ref<TestActiveDOMObject>&&);