File indexing completed on 2024-04-28 15:23:09

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 2010 Maksim Orlovich <maksim@kde.org>
0004  *
0005  *  This library is free software; you can redistribute it and/or
0006  *  modify it under the terms of the GNU Lesser General Public
0007  *  License as published by the Free Software Foundation; either
0008  *  version 2 of the License, or (at your option) any later version.
0009  *
0010  *  This library is distributed in the hope that it will be useful,
0011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013  *  Lesser General Public License for more details.
0014  *
0015  *  You should have received a copy of the GNU Lesser General Public
0016  *  License along with this library; if not, write to the Free Software
0017  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0018  */
0019 #include "khtml_part.h"
0020 #include "kjs_data.h"
0021 #include <dom/dom_exception.h>
0022 #include <kjs/array_instance.h>
0023 
0024 #include <QSet>
0025 
0026 using namespace DOM;
0027 using namespace khtml;
0028 
0029 namespace KJS
0030 {
0031 
0032 // HTML5 deep copy algorithm, as described in "2.7.5 Safe passing of structured data"
0033 static JSValue *cloneInternal(ExecState *exec, Interpreter *ctx, JSValue *in, QSet<JSObject *> &path)
0034 {
0035     if (exec->hadException()) { // e.g. OOM or DATA_CLONE_ERR
0036         return jsUndefined();
0037     }
0038 
0039     switch (in->type()) {
0040     case NumberType:
0041     case BooleanType:
0042     case UndefinedType:
0043     case NullType:
0044     case StringType:
0045         // Values -> can pass straight through.
0046         return in;
0047 
0048     case ObjectType: {
0049         JSObject *obj = in->getObject();
0050 
0051         // Some things are handled by creating a new wrapper for their value;
0052         // this includes both JS-builtin types like autoboxing wrappers, and
0053         // those of HTML5 types we support that have deep cloning specified.
0054         // This goes through valueClone.
0055         if (JSObject *copy = obj->valueClone(ctx)) {
0056             return copy;
0057         }
0058 
0059         // Otherwise, we can only clone if it it's an array or plain object
0060         // that isn't already on our path from the root
0061         if (path.contains(obj)) {
0062             setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR);
0063             break;
0064         }
0065 
0066         path.insert(obj);
0067 
0068         JSObject *clone = nullptr;
0069         if (obj->inherits(&ArrayInstance::info)) {
0070             clone = new ArrayInstance(ctx->builtinArrayPrototype(), 0);
0071         } else if (!obj->classInfo()) { // plain object
0072             clone = new JSObject(ctx->builtinObjectPrototype());
0073         } else {
0074             // Something complicated and native -> error out
0075             setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR);
0076             break;
0077         }
0078 
0079         // Copy over clones of properties
0080         PropertyNameArray props;
0081         obj->getOwnPropertyNames(exec, props, PropertyMap::ExcludeDontEnumProperties);
0082         for (PropertyNameArrayIterator i = props.begin(); i != props.end(); ++i) {
0083             JSValue *propVal = obj->get(exec, *i);
0084             clone->put(exec, *i, cloneInternal(exec, ctx, propVal, path)); // ### flags?
0085         }
0086 
0087         path.remove(obj);
0088 
0089         return clone;
0090     }
0091 
0092     default: // shouldn't happen!
0093         setDOMException(exec, DOM::DOMException::DATA_CLONE_ERR);
0094     }
0095 
0096     return jsUndefined();
0097 }
0098 
0099 JSValue *cloneData(ExecState *exec, JSValue *data)
0100 {
0101     QSet<JSObject *> visited;
0102     return cloneInternal(exec, exec->dynamicInterpreter(), data, visited);
0103 }
0104 
0105 class JSMessageData : public DOM::MessageEventImpl::Data
0106 {
0107 public:
0108     DOM::MessageEventImpl::DataType messageDataType() const override
0109     {
0110         return DOM::MessageEventImpl::JS_VALUE;
0111     }
0112 
0113     JSMessageData(JSValue *val): m_value(val) {}
0114 
0115     ProtectedPtr<JSValue> m_value;
0116 };
0117 
0118 DOM::MessageEventImpl::Data *encapsulateMessageEventData(ExecState *exec, Interpreter *ctx,
0119         JSValue *data)
0120 {
0121     QSet<JSObject *> visited;
0122     JSValue *copy = cloneInternal(exec, ctx, data, visited);
0123     if (exec->hadException()) {
0124         return nullptr;
0125     } else {
0126         return new JSMessageData(copy);
0127     }
0128 }
0129 
0130 JSValue *getMessageEventData(ExecState * /*exec*/, DOM::MessageEventImpl::Data *data)
0131 {
0132     if (data && data->messageDataType() == DOM::MessageEventImpl::JS_VALUE) {
0133         return static_cast<JSMessageData *>(data)->m_value.get();
0134     } else {
0135         return jsUndefined();
0136     }
0137 }
0138 
0139 //------------------------------------------------------------------------------
0140 DelayedPostMessage::DelayedPostMessage(KHTMLPart *_source,
0141                                        const QString &_sourceOrigin,
0142                                        const QString &_targetOrigin,
0143                                        JSValue *_payload):
0144     sourceOrigin(_sourceOrigin), targetOrigin(_targetOrigin), payload(_payload), source(_source)
0145 {}
0146 
0147 void DelayedPostMessage::mark()
0148 {
0149     if (!payload->marked()) {
0150         payload->mark();
0151     }
0152 }
0153 
0154 bool DelayedPostMessage::execute(Window *w)
0155 {
0156     KHTMLPart *part = qobject_cast<KHTMLPart *>(w->part());
0157     DOM::DocumentImpl *doc = part ? static_cast<DOM::DocumentImpl *>(part->document().handle()) : nullptr;
0158     KJSProxy *js = part ? KJSProxy::proxy(part) : nullptr;
0159 
0160     // qCDebug(KHTML_LOG) << doc << js << sourceOrigin << targetOrigin;
0161     if (doc && js) {
0162         // Verify destination.
0163         bool safe = false;
0164         if (targetOrigin == QLatin1String("*")) {
0165             safe = true;
0166         } else {
0167             RefPtr<SecurityOrigin> targetCtx =
0168                 SecurityOrigin::createFromString(targetOrigin);
0169             safe = doc->origin()->isSameSchemeHostPort(targetCtx.get());
0170         }
0171 
0172         if (safe) {
0173             RefPtr<MessageEventImpl> msg = new MessageEventImpl();
0174 
0175             DOM::MessageEventImpl::Data *data =
0176                 encapsulateMessageEventData(js->interpreter()->globalExec(),
0177                                             js->interpreter(), payload);
0178 
0179             msg->initMessageEvent("message",
0180                                   false, false, // doesn't bubble or cancel
0181                                   data,
0182                                   sourceOrigin,
0183                                   DOMString(), // lastEventId -- not here
0184                                   source.data());
0185             doc->dispatchWindowEvent(msg.get());
0186         } else {
0187             qCWarning(KHTML_LOG) << "PostMessage XSS check failed;"
0188                        << "target mask:" << targetOrigin
0189                        << "actual:" << doc->origin()->toString()
0190                        << "source:" << sourceOrigin;
0191         }
0192     }
0193 
0194     return true;
0195 }
0196 
0197 } // namespace KJS
0198