From 16722743ceb59a5e60a05121773c6599a02cab20 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 14 Apr 2021 15:14:33 +0200 Subject: Check thisObject when calling various methods on URL We should not crash when you try to call them on the wrong object. Rather, throw a type error. Change-Id: I1b146d9c77d838e013408988e02a65a623641f1f Reviewed-by: Fabian Kosmale (cherry picked from commit f32436122f6ac16bfd6f23228b85a6f7c12502b6) Reviewed-by: Qt Cherry-pick Bot --- src/qml/jsruntime/qv4urlobject.cpp | 160 ++++++++++++++++++++++------- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 64 ++++++++++++ 2 files changed, 186 insertions(+), 38 deletions(-) diff --git a/src/qml/jsruntime/qv4urlobject.cpp b/src/qml/jsruntime/qv4urlobject.cpp index 937cd6e3a2..1e2f940c5d 100644 --- a/src/qml/jsruntime/qv4urlobject.cpp +++ b/src/qml/jsruntime/qv4urlobject.cpp @@ -309,13 +309,24 @@ void UrlObject::updateHost() d()->host.set(engine(), engine()->newString(host)); } +static bool checkUrlObjectType(ExecutionEngine *v4, const Scoped &r) +{ + if (r) + return true; + + v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URL")); + return false; +} + ReturnedValue UrlPrototype::method_getHash(const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->hash())); } @@ -332,7 +343,9 @@ ReturnedValue UrlPrototype::method_setHash(const FunctionObject *b, const Value if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); r->setHash(stringValue->toQString()); @@ -345,7 +358,9 @@ ReturnedValue UrlPrototype::method_getHost(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->host())); } @@ -362,7 +377,9 @@ ReturnedValue UrlPrototype::method_setHost(const FunctionObject *b, const Value if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); QString host = stringValue->toQString(); if (!r->setHost(host)) @@ -377,7 +394,9 @@ ReturnedValue UrlPrototype::method_getHostname(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->hostname())); } @@ -394,7 +413,9 @@ ReturnedValue UrlPrototype::method_setHostname(const FunctionObject *b, const Va if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); QString hostname = stringValue->toQString(); if (!r->setHostname(hostname)) @@ -409,7 +430,9 @@ ReturnedValue UrlPrototype::method_getHref(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->href())); } @@ -426,7 +449,9 @@ ReturnedValue UrlPrototype::method_setHref(const FunctionObject *b, const Value if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); QString href = stringValue->toQString(); if (!r->setHref(href)) @@ -441,7 +466,9 @@ ReturnedValue UrlPrototype::method_getOrigin(const FunctionObject *b, const Valu ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->origin())); } @@ -452,7 +479,9 @@ ReturnedValue UrlPrototype::method_getPassword(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->password())); } @@ -469,7 +498,9 @@ ReturnedValue UrlPrototype::method_setPassword(const FunctionObject *b, const Va if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); r->setPassword(stringValue->toQString()); @@ -482,7 +513,9 @@ ReturnedValue UrlPrototype::method_getPathname(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->pathname())); } @@ -499,7 +532,9 @@ ReturnedValue UrlPrototype::method_setPathname(const FunctionObject *b, const Va if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); r->setPathname(stringValue->toQString()); @@ -512,7 +547,9 @@ ReturnedValue UrlPrototype::method_getPort(const FunctionObject *b, const Value ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->port())); } @@ -535,7 +572,9 @@ ReturnedValue UrlPrototype::method_setPort(const FunctionObject *b, const Value else return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); if (!r->setPort(port)) return v4->throwTypeError(QLatin1String("Invalid port: %1").arg(port)); @@ -549,7 +588,9 @@ ReturnedValue UrlPrototype::method_getProtocol(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->protocol())); } @@ -566,7 +607,9 @@ ReturnedValue UrlPrototype::method_setProtocol(const FunctionObject *b, const Va if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); r->setProtocol(stringValue->toQString()); @@ -579,7 +622,9 @@ ReturnedValue UrlPrototype::method_getSearch(const FunctionObject *b, const Valu ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->search())); } @@ -596,7 +641,9 @@ ReturnedValue UrlPrototype::method_setSearch(const FunctionObject *b, const Valu if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); r->setSearch(stringValue->toQString()); @@ -609,7 +656,9 @@ ReturnedValue UrlPrototype::method_getUsername(const FunctionObject *b, const Va ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); return Encode(v4->newString(r->username())); } @@ -626,7 +675,9 @@ ReturnedValue UrlPrototype::method_setUsername(const FunctionObject *b, const Va if (stringValue == nullptr) return v4->throwTypeError(QLatin1String("Invalid parameter provided")); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); r->setUsername(stringValue->toQString()); @@ -639,7 +690,9 @@ ReturnedValue UrlPrototype::method_getSearchParams(const FunctionObject *b, cons ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped r(scope, thisObject->as()); + Scoped r(scope, thisObject); + if (!checkUrlObjectType(v4, r)) + return Encode::undefined(); Scoped usp(scope, v4->newUrlSearchParamsObject()); @@ -1122,13 +1175,24 @@ PropertyAttributes UrlSearchParamsObject::virtualGetOwnProperty(const Managed *m return Object::virtualGetOwnProperty(m, id, p); } -ReturnedValue UrlSearchParamsPrototype::method_toString(const FunctionObject *b, const Value *thisObject, - const Value *, int) +static bool checkSearchParamsType(ExecutionEngine *v4, const Scoped &o) +{ + if (o) + return true; + + v4->throwTypeError(QStringLiteral("Value of \"this\" must be of type URLSearchParams")); + return false; +} + +ReturnedValue UrlSearchParamsPrototype::method_toString( + const FunctionObject *b, const Value *thisObject, const Value *, int) { ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); auto params = o->params(); @@ -1149,7 +1213,9 @@ ReturnedValue UrlSearchParamsPrototype::method_sort(const FunctionObject *b, con ExecutionEngine *v4 = b->engine(); Scope scope(v4); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); QList params = o->params(); std::stable_sort(params.begin(), params.end(), [](QStringList a, QStringList b) { return a[0] < b[0]; }); @@ -1179,9 +1245,9 @@ ReturnedValue UrlSearchParamsPrototype::method_append(const FunctionObject *b, c ScopedString name(scope, argName->as()); ScopedString value(scope, argValue->toString(v4)); - - - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); o->append(name->d(), value->d()); @@ -1206,7 +1272,9 @@ ReturnedValue UrlSearchParamsPrototype::method_delete(const FunctionObject *b, c QString name = argNameString->toQString(); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); QList params = o->params(); @@ -1237,7 +1305,9 @@ ReturnedValue UrlSearchParamsPrototype::method_has(const FunctionObject *b, cons if (argNameString == nullptr) return v4->throwTypeError(QLatin1String("Invalid argument provided")); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); QString name = argNameString->toQString(); @@ -1261,7 +1331,9 @@ ReturnedValue UrlSearchParamsPrototype::method_set(const FunctionObject *b, cons if (argNameString == nullptr) return v4->throwTypeError(QLatin1String("Invalid argument provided")); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); QString name = argNameString->toQString(); QString value = argValue->toQString(); @@ -1308,7 +1380,9 @@ ReturnedValue UrlSearchParamsPrototype::method_get(const FunctionObject *b, cons if (argNameString == nullptr) return v4->throwTypeError(QLatin1String("Invalid argument provided")); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); QString name = argNameString->toQString(); @@ -1337,7 +1411,9 @@ ReturnedValue UrlSearchParamsPrototype::method_getAll(const FunctionObject *b, if (argNameString == nullptr) return v4->throwTypeError(QLatin1String("Invalid argument provided")); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); QString name = argNameString->toQString(); @@ -1370,7 +1446,9 @@ ReturnedValue UrlSearchParamsPrototype::method_forEach(const FunctionObject *b, if (func == nullptr) return v4->throwTypeError(QLatin1String("Invalid argument: must be a function")); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); for (int i = 0; i < o->length(); i++) { Scoped name(scope, o->nameAtRaw(i)); @@ -1397,7 +1475,9 @@ ReturnedValue UrlSearchParamsPrototype::method_entries(const FunctionObject *b, if (argc != 0) return v4->throwError(QLatin1String("Bad amount of arguments")); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); ScopedObject params(scope, o->d()->params.get()); @@ -1416,7 +1496,9 @@ ReturnedValue UrlSearchParamsPrototype::method_keys(const FunctionObject *b, if (argc != 0) return v4->throwError(QLatin1String("Bad amount of arguments")); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); ScopedObject keys(scope, o->d()->keys.get()); @@ -1435,7 +1517,9 @@ ReturnedValue UrlSearchParamsPrototype::method_values(const FunctionObject *b, if (argc != 0) return v4->throwError(QLatin1String("Bad amount of arguments")); - Scoped o(scope, thisObject->as()); + Scoped o(scope, thisObject); + if (!checkSearchParamsType(v4, o)) + return Encode::undefined(); ScopedObject values(scope, o->d()->values.get()); diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index 85d61a3537..e892d2dfcf 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -273,6 +273,7 @@ private slots: void dataViewCtor(); void uiLanguage(); + void urlObject(); public: Q_INVOKABLE QJSValue throwingCppMethod1(); @@ -5270,6 +5271,69 @@ void tst_QJSEngine::uiLanguage() } } +void tst_QJSEngine::urlObject() +{ + QJSEngine engine; + + const QString href = QStringLiteral( + "http://uuu:ppp@example.com:777/foo/bar?search=stuff&other=where#hhh"); + const QUrl url(href); + + QJSManagedValue v(engine.evaluate(QStringLiteral("new URL('%1')").arg(href)), &engine); + QVERIFY(v.isObject()); + QJSManagedValue proto(v.prototype()); + + auto check = [&](const QString &prop, const QString &expected) { + QCOMPARE(v.property(prop).toString(), expected); + QVERIFY(proto.property(prop).isUndefined()); + QVERIFY(engine.hasError()); + QCOMPARE(engine.catchError().toString(), + QStringLiteral("TypeError: Value of \"this\" must be of type URL")); + }; + + check(QStringLiteral("href"), url.toString()); + check(QStringLiteral("origin"), QStringLiteral("http://example.com:777")); + check(QStringLiteral("protocol"), url.scheme()); + check(QStringLiteral("username"), url.userName()); + check(QStringLiteral("password"), url.password()); + check(QStringLiteral("host"), url.host() + u':' + QString::number(url.port())); + check(QStringLiteral("hostname"), url.host()); + check(QStringLiteral("port"), QString::number(url.port())); + check(QStringLiteral("pathname"), url.path()); + check(QStringLiteral("search"), QStringLiteral("?search=stuff&other=where")); + check(QStringLiteral("hash"), u'#' + url.fragment()); + + QJSManagedValue s(v.property("searchParams"), &engine); + QVERIFY(s.isObject()); + + const QStringList searchParamsMethods = { + QStringLiteral("append"), + QStringLiteral("delete"), + QStringLiteral("get"), + QStringLiteral("getAll"), + QStringLiteral("has"), + QStringLiteral("set"), + QStringLiteral("sort"), + QStringLiteral("entries"), + QStringLiteral("forEach"), + QStringLiteral("keys"), + QStringLiteral("values"), + QStringLiteral("toString") + }; + + for (const QString &method : searchParamsMethods) { + QJSManagedValue get(s.property(method), &engine); + + // Shoudn't crash. + // We get different error messages depending on parameters, though. + QJSValue undef = get.call({}); + QVERIFY(undef.isUndefined()); + QVERIFY(engine.hasError()); + engine.catchError(); + } + +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" -- cgit v1.2.3