Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-67726

Userscripts with bad @match directives run on all pages.

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P2: Important
    • 5.11.1
    • 5.11
    • WebEngine
    • None
    • I8172184e79fe3e515f391bc6cc8274a624e67a19

    Description

      In src/core/user_script.cpp when the greasemonkey metadata is parsed from a user script, url pattern strings from @match directives are not added to the script data struct if they fail to parse correctly. If all such match patterns in the script are invalid, and there are no @include directives in the metadata, then the script defaults to being included on all pages. I think in this case it would make sense to include the script on no pages. Here is my proposed change to address this:

      • don't test the patterns for validity at parse time
      • check them at match time and don't bother trying to match if pattern parsing fails

      For example:

      diff --git i/src/core/renderer/user_resource_controller.cpp w/src/core/renderer/user_resource_controller.cpp
      index f530b7680803..e9878832c8f3 100644
      --- i/src/core/renderer/user_resource_controller.cpp
      +++ w/src/core/renderer/user_resource_controller.cpp
      @@ -95,8 +95,8 @@ static bool scriptMatchesURL(const UserScriptData &scriptData, const GURL &url)
           if (!scriptData.urlPatterns.empty()) {
               matchFound = false;
               for (auto it = scriptData.urlPatterns.begin(), end = scriptData.urlPatterns.end(); it != end; ++it) {
      -            URLPattern urlPattern(QtWebEngineCore::UserScript::validUserScriptSchemes(), *it);
      -            if (urlPattern.MatchesURL(url))
      +            URLPattern urlPattern(QtWebEngineCore::UserScript::validUserScriptSchemes());
      +            if (urlPattern.Parse(*it)==URLPattern::PARSE_SUCCESS && urlPattern.MatchesURL(url))
                       matchFound = true;
               }
               if (!matchFound)
      diff --git i/src/core/user_script.cpp w/src/core/user_script.cpp
      index 9b9d66d55678..622deb911918 100644
      --- i/src/core/user_script.cpp
      +++ w/src/core/user_script.cpp
      @@ -260,8 +260,7 @@ void UserScript::parseMetadataHeader()
                       }
                       scriptData->excludeGlobs.push_back(value);
                   } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) {
      -                if (URLPattern::PARSE_SUCCESS == urlPatternParser.Parse(value))
      -                    scriptData->urlPatterns.push_back(value);
      +                scriptData->urlPatterns.push_back(value);
                   } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) {
                       if (value == kRunAtDocumentStartValue)
                           scriptData->injectionPoint = DocumentElementCreation;
      
      

      There will be no additional runtime cost because the patterns were already being fully parsed at the point where they were being matched against URLs.

      An alternative is what chromium appears to do for extensions (in extensions/common/user_script.cc) which is just to exit out of the metadata parsing method if a pattern fails to parse, so the include = "*" fallback would never be executed. This isn't ideal because the caller wouldn't know the state of the script; like if it had correct match patterns proceeding the broken one and other things like run-at afterwards, the script would still be injected into some pages but not at the expected time.

      I don't think this is a problem for chrome because extensions with bad patterns are rejected at install time.

      Here is a patch for a test that reproduces:

      --- tests/auto/quick/qmltests/data/tst_userScripts.qml
      +++ tests/auto/quick/qmltests/data/tst_userScripts.qml
      @@ -50,6 +50,11 @@
           }
       
           WebEngineScript {
      +        id: scriptWithBadMatchMetadata
      +        sourceUrl: Qt.resolvedUrl("script-with-bad-match-metadata.js")
      +    }
      +
      +    WebEngineScript {
               id: scriptWithMetadata
               sourceUrl: Qt.resolvedUrl("script-with-metadata.js")
           }
      @@ -164,13 +169,25 @@
                   tryCompare(webEngineView , "title", "Big user script changed title");
               }
       
      +        function test_dontInjectBadUrlPatternsEverywhere() {
      +            compare(scriptWithBadMatchMetadata.name, "Test bad match script");
      +            compare(scriptWithBadMatchMetadata.injectionPoint, WebEngineScript.DocumentReady);
      +
      +            webEngineView.userScripts = [ scriptWithBadMatchMetadata ];
      +
      +            // @match some:junk
      +            webEngineView.url = Qt.resolvedUrl("test2.html");
      +            webEngineView.waitForLoadSucceeded();
      +            tryCompare(webEngineView, "title", "Test page with huge link area");
      +        }
      +
               function test_parseMetadataHeader() {
                   compare(scriptWithMetadata.name, "Test script");
                   compare(scriptWithMetadata.injectionPoint, WebEngineScript.DocumentReady);
      

      And script-with-bad-match-metadata.js is just:

      // ==UserScript==
      // @name           Test bad match script
      // @homepageURL    http://www.qt.io/
      // @description    Test script with metadata block with an invalid match directive
      // @match          some:junk
      // @run-at         document-end
      // ==/UserScript==
      
      document.title = "New title";
      

      Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

        Activity

          People

            davidsz Szabolcs David
            toofar toofar
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes