Skip to content

[BUG] Unsupported @match scheme invalidates the whole userscript registration #1771

Description

@cyfung1031

Pre-check

  • Searched existing issues for @match, unsupported/invalid schemes, and userScripts registration failures; no duplicate for this case was found.
  • Confirmed against the current main source (d6cc48ba99f8a8ac7d4ad236f2d294fad99e0c79) that the invalid scheme is still accepted into the registration match list.
  • This is a ScriptCat URL-matching / registration bug, not a problem specific to one third-party userscript.

Description

A userscript with multiple @match entries stops running entirely if one entry uses a scheme that Chrome does not support as a match-pattern scheme.

Example:

// @match https://*/*
// @match http://*/*
// @match notsupported://*/*

Expected behavior: the unsupported/invalid @match entry should be rejected or ignored for browser registration, while the valid https://*/* and http://*/* entries continue to register and run.

Actual behavior: ScriptCat accepts all three entries into one RegisteredUserScript.matches array. Chrome validates that array when chrome.userScripts.register() / .update() is called; the unsupported scheme makes that script registration fail, so the valid patterns never become active either.

The key point is that Manifest V3 itself is not the root cause. MV3/chrome.userScripts is the stricter API boundary that exposes the problem. The root cause is that ScriptCat does not filter or reject match patterns that are invalid for the browser-native match-pattern API before passing them to chrome.userScripts.

Steps to Reproduce

  1. Create a page userscript with the following metadata:

    // ==UserScript==
    // @name        Unsupported match scheme reproduction
    // @match       https://*/*
    // @match       http://*/*
    // @match       notsupported://*/*
    // @grant       none
    // ==/UserScript==
    
    console.log("ScriptCat match-scheme reproduction");
  2. Enable the script.

  3. Open or reload any normal HTTPS page, for example https://example.com/.

  4. Observe that the script does not execute.

  5. Remove only // @match notsupported://*/*, save/re-enable as needed, then reload the same page.

  6. The script runs from the remaining valid https://*/* / http://*/* patterns.

ScriptCat Version

Current main reproduced/confirmed at:

d6cc48ba99f8a8ac7d4ad236f2d294fad99e0c79

OS / Browser and Version

Chromium/Chrome MV3 path using chrome.userScripts.

The exact local Chrome version is not material to the source-level reproduction; the failure is caused by passing an unsupported scheme into the browser's match-pattern API. The original UI reproduction was on a Chromium-based browser.

Related Script / Logs / Screenshots

Current code path

  1. src/pkg/utils/url_matcher.ts accepts arbitrary lowercase alphabetic schemes.

    checkUrlMatch() currently checks the scheme with:

    /^(\*|[-a-z]+)$/

    and extractUrlPatterns() similarly accepts arbitrary [-a-z]+ schemes for @match.

    Source:
    https://github.com/scriptscat/scriptcat/blob/d6cc48ba99f8a8ac7d4ad236f2d294fad99e0c79/src/pkg/utils/url_matcher.ts

  2. getApiMatchesAndGlobs() collects accepted MATCH_INCLUDE entries into the final native matches list without filtering unsupported browser schemes.

  3. src/app/service/service_worker/utils.ts places that list directly into a single chrome.userScripts.RegisteredUserScript.matches array.

    Source:
    https://github.com/scriptscat/scriptcat/blob/d6cc48ba99f8a8ac7d4ad236f2d294fad99e0c79/src/app/service/service_worker/utils.ts

  4. src/app/service/service_worker/runtime.ts registers/updates that one record through chrome.userScripts.register([registerScript]) / .update([registerScript]). A registration error is logged, but there is no per-pattern recovery.

    Source:
    https://github.com/scriptscat/scriptcat/blob/d6cc48ba99f8a8ac7d4ad236f2d294fad99e0c79/src/app/service/service_worker/runtime.ts

For the reproduction metadata, ScriptCat currently produces a native match list equivalent to:

[
  "https://*/*",
  "http://*/*",
  "notsupported://*/*"
]

The third entry is not a valid Chrome extension match-pattern scheme. Chrome's documented match-pattern schemes are http, https, file, and * (the wildcard for HTTP/HTTPS):

https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns

Regression history / evidence

This behavior became a regression when ScriptCat changed the page-script registration architecture, but the architectural migration is not itself the defect.

  • Known-good pre-MV3 reference: ad5a7116c9d54b4e4a4ab53420d0e1d185612f98

    • URL rules were added to ScriptCat's own matcher one by one.
    • Page execution was selected in ScriptCat and injected via chrome.tabs.executeScript(...).
    • An unsupported scheme was therefore an inert/non-matching rule and did not invalidate the script's other valid rules.
  • First bad implementation commit: fcb4cc48afcb12106eec8d39a5d902504e2553fc (♻️ 重构代码,升级为manifest v3, 2025-04-23)

    • Introduced the chrome.userScripts registration path.
    • Its URL conversion accepted arbitrary schemes and bundled all patterns from one userscript into one native registration record.
    • No browser-supported-scheme validation/filter was added at that boundary.

    fcb4cc4

  • Landed on main: PR ♻️ 重构代码,升级为manifest v3 #374 / merge commit 9b283ef5f9ce12f3b71c5f49b6b246f1facfd167, 2025-05-13.

    ♻️ 重构代码,升级为manifest v3 #374

  • Related failure-class evidence: 3f57e8f5000da299e959899ec9dc785c239e2c11 fixed issue [BUG]特殊路径无法加载 #390 by falling back from batch registration to one-script-at-a-time registration after a bad match pattern caused userScripts.register() to fail.

    • That isolates failures between scripts.
    • It does not isolate invalid @match entries within one script.

    3f57e8f
    [BUG]特殊路径无法加载 #390

  • Later matcher rewrite: 5b01c10859b80890456a44a66d78204b42040870 / PR 重写UrlMatch #637 rewrote URL matching, but the semantic gap remained: arbitrary lowercase schemes still pass the ScriptCat parser even when they are not valid native match-pattern schemes.

    重写UrlMatch #637

Root cause

The registration-bound URL parser validates the shape of a scheme (lowercase letters / wildcard), but does not validate whether the scheme is actually supported by the browser's native match-pattern API.

As a result, an unsupported @match is treated as a native MATCH_INCLUDE and forwarded unchanged into chrome.userScripts. Because all native matches for one userscript are registered together, one invalid entry can prevent the whole userscript registration from succeeding.

Suggested direction

At a high level, validate/filter native registration patterns at the ScriptCat → browser API boundary so an unsupported @match cannot invalidate otherwise-valid patterns from the same userscript.

The fix should preserve the valid rules, add focused regression coverage for mixed valid + invalid @match entries, and make registration failures diagnosable. Detailed implementation design can be handled in the follow-up PR.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions