sourcemonkey 0.0.0-stage → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +87 -2
- package/dist/bytes.js +37 -0
- package/dist/channel-placeholder.js +5 -0
- package/dist/collection-scan.js +17 -0
- package/dist/collections.html +353 -0
- package/dist/compare.html +358 -0
- package/dist/composed-source.js +205 -0
- package/dist/connect-prompt.html +47 -0
- package/dist/debug/icons.html +211 -0
- package/dist/debug/storage.html +163 -0
- package/dist/debug-log.js +75 -0
- package/dist/diagnostic-location.js +64 -0
- package/dist/eval-hook.js +308 -0
- package/dist/extension/background.js +5263 -0
- package/dist/extension/channel.js +133 -0
- package/dist/extension/code-links.js +187 -0
- package/dist/extension/code-tabs.js +460 -0
- package/dist/extension/collection-dialog.js +619 -0
- package/dist/extension/collection-display.js +322 -0
- package/dist/extension/collections.js +2905 -0
- package/dist/extension/compare.js +867 -0
- package/dist/extension/concurrency.js +186 -0
- package/dist/extension/connect-grants.js +109 -0
- package/dist/extension/connect-prompt-page.js +70 -0
- package/dist/extension/connect-prompt.js +266 -0
- package/dist/extension/copy-button.js +63 -0
- package/dist/extension/copy-dialog.js +502 -0
- package/dist/extension/copy-preference.js +66 -0
- package/dist/extension/debug/icons.js +203 -0
- package/dist/extension/debug/storage.js +365 -0
- package/dist/extension/deep-link.js +77 -0
- package/dist/extension/delete-dialog.js +432 -0
- package/dist/extension/delete-icon.js +40 -0
- package/dist/extension/details-tooltips.js +66 -0
- package/dist/extension/directories.js +679 -0
- package/dist/extension/export-backup.js +407 -0
- package/dist/extension/export-dialog.js +142 -0
- package/dist/extension/file-access.js +74 -0
- package/dist/extension/file-changes.js +346 -0
- package/dist/extension/frame-info.js +85 -0
- package/dist/extension/github-provider.js +336 -0
- package/dist/extension/glyph.js +84 -0
- package/dist/extension/gm-bindings.js +601 -0
- package/dist/extension/gm-download.js +419 -0
- package/dist/extension/gm-menu.js +228 -0
- package/dist/extension/gm-notification.js +141 -0
- package/dist/extension/gm-originator-map.js +23 -0
- package/dist/extension/gm-tab-data.js +121 -0
- package/dist/extension/gm-tab.js +160 -0
- package/dist/extension/gm-url-change.js +111 -0
- package/dist/extension/gm-url-scheme.js +27 -0
- package/dist/extension/gm-values.js +176 -0
- package/dist/extension/gm-window-control.js +104 -0
- package/dist/extension/gm-xhr-headers.js +333 -0
- package/dist/extension/gm-xhr.js +584 -0
- package/dist/extension/injector.js +3145 -0
- package/dist/extension/install-capture.js +277 -0
- package/dist/extension/line-diff.js +233 -0
- package/dist/extension/local-copy.js +560 -0
- package/dist/extension/local-delete.js +276 -0
- package/dist/extension/location-kinds.js +108 -0
- package/dist/extension/log-format.js +158 -0
- package/dist/extension/managed-changes.js +361 -0
- package/dist/extension/managed-items.js +57 -0
- package/dist/extension/match-index.js +375 -0
- package/dist/extension/missing-scripts.js +66 -0
- package/dist/extension/more-menu.js +109 -0
- package/dist/extension/move-dialog.js +197 -0
- package/dist/extension/offscreen.js +37 -0
- package/dist/extension/options.js +504 -0
- package/dist/extension/page-heading.js +170 -0
- package/dist/extension/page-links.js +199 -0
- package/dist/extension/parse-check-client.js +70 -0
- package/dist/extension/parse-check.js +161 -0
- package/dist/extension/parse-sandbox-host.js +172 -0
- package/dist/extension/parse-sandbox.js +175 -0
- package/dist/extension/popup-commands-open.js +41 -0
- package/dist/extension/popup-view.js +387 -0
- package/dist/extension/popup.js +703 -0
- package/dist/extension/prism-userscript.js +53 -0
- package/dist/extension/refresh-error.js +136 -0
- package/dist/extension/refresh-targets.js +160 -0
- package/dist/extension/related-versions.js +1144 -0
- package/dist/extension/resize-observer-loop.js +19 -0
- package/dist/extension/row-cache.js +387 -0
- package/dist/extension/script-blobs.js +413 -0
- package/dist/extension/script-details.js +658 -0
- package/dist/extension/script-enablement.js +285 -0
- package/dist/extension/script-identity.js +310 -0
- package/dist/extension/script-keys.js +54 -0
- package/dist/extension/script-log-id.js +155 -0
- package/dist/extension/script-log.js +290 -0
- package/dist/extension/script-rejection.js +104 -0
- package/dist/extension/script-sites.js +158 -0
- package/dist/extension/script-store.js +3507 -0
- package/dist/extension/script-updater.js +991 -0
- package/dist/extension/script-url-hash.js +61 -0
- package/dist/extension/select-all.js +37 -0
- package/dist/extension/select-open-guard.js +90 -0
- package/dist/extension/site-icon.js +1261 -0
- package/dist/extension/sm-dev-link.js +302 -0
- package/dist/extension/solo-click.js +58 -0
- package/dist/extension/sticky-numbers.js +188 -0
- package/dist/extension/storage-breakdown.js +253 -0
- package/dist/extension/storage-scan.js +86 -0
- package/dist/extension/storage-usage.js +194 -0
- package/dist/extension/structural-keys.js +29 -0
- package/dist/extension/sw-teardown.js +36 -0
- package/dist/extension/tab-status.js +403 -0
- package/dist/extension/toolbar-summary.js +105 -0
- package/dist/extension/user-scripts-access.js +43 -0
- package/dist/extension/userscript.js +3672 -0
- package/dist/extension/value-store.js +204 -0
- package/dist/extension/version-probe-fetch.js +68 -0
- package/dist/extension/worker-client.js +109 -0
- package/dist/extension/worker-ops.js +171 -0
- package/dist/extension/zip-writer.js +24 -0
- package/dist/external-files.js +878 -0
- package/dist/file-loader.js +436 -0
- package/dist/file-locations.js +439 -0
- package/dist/generated-wrappers.js +5 -0
- package/dist/github-auth.js +148 -0
- package/dist/github-urls.js +46 -0
- package/dist/gm-api-catalog.js +217 -0
- package/dist/gm-connect.js +273 -0
- package/dist/gm-xhr-types.js +5 -0
- package/dist/http-read.js +142 -0
- package/dist/icons/icon-128.png +0 -0
- package/dist/icons/icon-16.png +0 -0
- package/dist/icons/icon-48.png +0 -0
- package/dist/local-file-overrides.js +558 -0
- package/dist/manifest.json +38 -0
- package/dist/markup-response.js +126 -0
- package/dist/match-targets.js +305 -0
- package/dist/menu-captions.js +22 -0
- package/dist/offscreen.html +39 -0
- package/dist/options.html +197 -0
- package/dist/parse-check-protocol.js +51 -0
- package/dist/parse-location.js +171 -0
- package/dist/parse-sandbox.html +56 -0
- package/dist/phase-timing.js +155 -0
- package/dist/popup.html +197 -0
- package/dist/public-suffixes.js +6957 -0
- package/dist/run-context.js +31 -0
- package/dist/runtime-messages.js +34 -0
- package/dist/sandboxed-page.js +35 -0
- package/dist/script-build.js +787 -0
- package/dist/script-display.js +383 -0
- package/dist/script-icon.js +43 -0
- package/dist/script-links.js +204 -0
- package/dist/script-source-url.js +214 -0
- package/dist/script-world.js +65 -0
- package/dist/sm-dev-protocol.js +84 -0
- package/dist/source-map.js +177 -0
- package/dist/stack-link.js +138 -0
- package/dist/styles.css +825 -0
- package/dist/unsafe-window-use.js +229 -0
- package/dist/url-match.js +734 -0
- package/dist/userscript-header.js +248 -0
- package/dist/userscript.html +1165 -0
- package/dist/vendor/fflate.LICENSE +21 -0
- package/dist/vendor/fflate.js +2692 -0
- package/dist/vendor/prism/prism-line-numbers.css +1 -0
- package/dist/vendor/prism/prism-line-numbers.js +1 -0
- package/dist/vendor/prism/prism.css +1 -0
- package/dist/vendor/prism/prism.js +1946 -0
- package/dist/version-compare.js +141 -0
- package/dist/version-probe.js +120 -0
- package/lib/build-info.json +1 -0
- package/lib/bytes.d.ts +3 -0
- package/lib/bytes.js +37 -0
- package/lib/channel-placeholder.d.ts +1 -0
- package/lib/channel-placeholder.js +5 -0
- package/lib/collection-scan.d.ts +8 -0
- package/lib/collection-scan.js +17 -0
- package/lib/composed-source.d.ts +28 -0
- package/lib/composed-source.js +205 -0
- package/lib/debug-log.d.ts +9 -0
- package/lib/debug-log.js +75 -0
- package/lib/diagnostic-location.d.ts +8 -0
- package/lib/diagnostic-location.js +64 -0
- package/lib/eval-hook.d.ts +34 -0
- package/lib/eval-hook.js +308 -0
- package/lib/external-files.d.ts +111 -0
- package/lib/external-files.js +878 -0
- package/lib/file-loader.d.ts +48 -0
- package/lib/file-loader.js +436 -0
- package/lib/file-locations.d.ts +44 -0
- package/lib/file-locations.js +439 -0
- package/lib/generated-wrappers.d.ts +11 -0
- package/lib/generated-wrappers.js +5 -0
- package/lib/github-auth.d.ts +12 -0
- package/lib/github-auth.js +148 -0
- package/lib/github-urls.d.ts +1 -0
- package/lib/github-urls.js +46 -0
- package/lib/gm-api-catalog.d.ts +24 -0
- package/lib/gm-api-catalog.js +217 -0
- package/lib/gm-connect.d.ts +34 -0
- package/lib/gm-connect.js +273 -0
- package/lib/gm-xhr-types.d.ts +68 -0
- package/lib/gm-xhr-types.js +5 -0
- package/lib/harness/browser-extensions.d.ts +73 -0
- package/lib/harness/browser-extensions.js +285 -0
- package/lib/harness/browser-lang.d.ts +20 -0
- package/lib/harness/browser-lang.js +86 -0
- package/lib/harness/browser-launch.d.ts +14 -0
- package/lib/harness/browser-launch.js +184 -0
- package/lib/harness/checks.d.ts +110 -0
- package/lib/harness/checks.js +321 -0
- package/lib/harness/cli.d.ts +2 -0
- package/lib/harness/cli.js +2259 -0
- package/lib/harness/command-args.d.ts +91 -0
- package/lib/harness/command-args.js +369 -0
- package/lib/harness/command-lines.d.ts +80 -0
- package/lib/harness/command-lines.js +180 -0
- package/lib/harness/command-list.d.ts +206 -0
- package/lib/harness/command-list.js +1475 -0
- package/lib/harness/console-commands.d.ts +463 -0
- package/lib/harness/console-commands.js +3216 -0
- package/lib/harness/download-watch.d.ts +21 -0
- package/lib/harness/download-watch.js +156 -0
- package/lib/harness/extension-backend.d.ts +41 -0
- package/lib/harness/extension-backend.js +100 -0
- package/lib/harness/extension-host.d.ts +112 -0
- package/lib/harness/extension-host.js +552 -0
- package/lib/harness/extension-launch.d.ts +67 -0
- package/lib/harness/extension-launch.js +285 -0
- package/lib/harness/extension-link.d.ts +315 -0
- package/lib/harness/extension-link.js +607 -0
- package/lib/harness/external-manager.d.ts +146 -0
- package/lib/harness/external-manager.js +414 -0
- package/lib/harness/fixture.d.ts +165 -0
- package/lib/harness/fixture.js +461 -0
- package/lib/harness/frame-sessions.d.ts +71 -0
- package/lib/harness/frame-sessions.js +248 -0
- package/lib/harness/frames.d.ts +68 -0
- package/lib/harness/frames.js +216 -0
- package/lib/harness/help-format.d.ts +17 -0
- package/lib/harness/help-format.js +127 -0
- package/lib/harness/host.d.ts +352 -0
- package/lib/harness/host.js +423 -0
- package/lib/harness/index.d.ts +8 -0
- package/lib/harness/index.js +10 -0
- package/lib/harness/input-file.d.ts +18 -0
- package/lib/harness/input-file.js +61 -0
- package/lib/harness/json-output.d.ts +565 -0
- package/lib/harness/json-output.js +71 -0
- package/lib/harness/manager-actions.d.ts +21 -0
- package/lib/harness/manager-actions.js +129 -0
- package/lib/harness/manager-commands.d.ts +142 -0
- package/lib/harness/manager-commands.js +1196 -0
- package/lib/harness/manager-fetch.d.ts +33 -0
- package/lib/harness/manager-fetch.js +239 -0
- package/lib/harness/managers.d.ts +24 -0
- package/lib/harness/managers.js +27 -0
- package/lib/harness/output-style.d.ts +125 -0
- package/lib/harness/output-style.js +322 -0
- package/lib/harness/page-commands.d.ts +134 -0
- package/lib/harness/page-commands.js +1141 -0
- package/lib/harness/page-output.d.ts +67 -0
- package/lib/harness/page-output.js +139 -0
- package/lib/harness/page-watch.d.ts +171 -0
- package/lib/harness/page-watch.js +607 -0
- package/lib/harness/parse-check-node.d.ts +3 -0
- package/lib/harness/parse-check-node.js +132 -0
- package/lib/harness/path-args.d.ts +22 -0
- package/lib/harness/path-args.js +148 -0
- package/lib/harness/playwright-chromium.d.ts +16 -0
- package/lib/harness/playwright-chromium.js +86 -0
- package/lib/harness/prepare.d.ts +168 -0
- package/lib/harness/prepare.js +364 -0
- package/lib/harness/prompt-editor.d.ts +17 -0
- package/lib/harness/prompt-editor.js +354 -0
- package/lib/harness/prompt-history.d.ts +13 -0
- package/lib/harness/prompt-history.js +89 -0
- package/lib/harness/rebuild-advice.d.ts +6 -0
- package/lib/harness/rebuild-advice.js +39 -0
- package/lib/harness/run-log.d.ts +47 -0
- package/lib/harness/run-log.js +140 -0
- package/lib/harness/run-logs.d.ts +132 -0
- package/lib/harness/run-logs.js +241 -0
- package/lib/harness/run.d.ts +194 -0
- package/lib/harness/run.js +1973 -0
- package/lib/harness/runs.d.ts +229 -0
- package/lib/harness/runs.js +726 -0
- package/lib/harness/save-html.d.ts +39 -0
- package/lib/harness/save-html.js +89 -0
- package/lib/harness/saved-pages.d.ts +120 -0
- package/lib/harness/saved-pages.js +275 -0
- package/lib/harness/script-mark.d.ts +17 -0
- package/lib/harness/script-mark.js +56 -0
- package/lib/harness/snapshot.d.ts +143 -0
- package/lib/harness/snapshot.js +474 -0
- package/lib/harness/stuck-tabs.d.ts +19 -0
- package/lib/harness/stuck-tabs.js +131 -0
- package/lib/harness/tab-refs.d.ts +20 -0
- package/lib/harness/tab-refs.js +80 -0
- package/lib/harness/tampermonkey.d.ts +71 -0
- package/lib/harness/tampermonkey.js +356 -0
- package/lib/harness/tm-ids.d.ts +18 -0
- package/lib/harness/tm-ids.js +88 -0
- package/lib/harness/violentmonkey.d.ts +34 -0
- package/lib/harness/violentmonkey.js +162 -0
- package/lib/harness/wait.d.ts +77 -0
- package/lib/harness/wait.js +458 -0
- package/lib/http-read.d.ts +30 -0
- package/lib/http-read.js +142 -0
- package/lib/local-file-overrides.d.ts +35 -0
- package/lib/local-file-overrides.js +558 -0
- package/lib/manifest.json +38 -0
- package/lib/markup-response.d.ts +11 -0
- package/lib/markup-response.js +126 -0
- package/lib/match-targets.d.ts +18 -0
- package/lib/match-targets.js +305 -0
- package/lib/menu-captions.d.ts +1 -0
- package/lib/menu-captions.js +22 -0
- package/lib/parse-check-protocol.d.ts +27 -0
- package/lib/parse-check-protocol.js +51 -0
- package/lib/parse-location.d.ts +12 -0
- package/lib/parse-location.js +171 -0
- package/lib/phase-timing.d.ts +12 -0
- package/lib/phase-timing.js +155 -0
- package/lib/public-suffixes.d.ts +1 -0
- package/lib/public-suffixes.js +6957 -0
- package/lib/run-context.d.ts +2 -0
- package/lib/run-context.js +31 -0
- package/lib/runtime-messages.d.ts +10 -0
- package/lib/runtime-messages.js +34 -0
- package/lib/sandboxed-page.d.ts +5 -0
- package/lib/sandboxed-page.js +35 -0
- package/lib/script-build.d.ts +111 -0
- package/lib/script-build.js +787 -0
- package/lib/script-display.d.ts +51 -0
- package/lib/script-display.js +383 -0
- package/lib/script-icon.d.ts +16 -0
- package/lib/script-icon.js +43 -0
- package/lib/script-links.d.ts +24 -0
- package/lib/script-links.js +204 -0
- package/lib/script-source-url.d.ts +8 -0
- package/lib/script-source-url.js +214 -0
- package/lib/script-world.d.ts +6 -0
- package/lib/script-world.js +65 -0
- package/lib/sm-dev-protocol.d.ts +251 -0
- package/lib/sm-dev-protocol.js +84 -0
- package/lib/source-map.d.ts +32 -0
- package/lib/source-map.js +177 -0
- package/lib/stack-link.d.ts +15 -0
- package/lib/stack-link.js +138 -0
- package/lib/unsafe-window-use.d.ts +1 -0
- package/lib/unsafe-window-use.js +229 -0
- package/lib/url-match.d.ts +40 -0
- package/lib/url-match.js +734 -0
- package/lib/userscript-header.d.ts +33 -0
- package/lib/userscript-header.js +248 -0
- package/lib/version-compare.d.ts +6 -0
- package/lib/version-compare.js +141 -0
- package/lib/version-probe.d.ts +17 -0
- package/lib/version-probe.js +120 -0
- package/package.json +55 -3
- package/skills/SourceMonkey-dev/SKILL.md +868 -0
- package/skills/SourceMonkey-dev/references/cdp.md +293 -0
- package/skills/SourceMonkey-dev/references/commands.md +297 -0
- package/skills/SourceMonkey-dev/references/other-managers.md +63 -0
- package/skills/SourceMonkey-dev/references/specs.md +46 -0
- package/skills/SourceMonkey-friction/SKILL.md +103 -0
- package/skills/SourceMonkey-friction/references/template.md +59 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Pure helpers used by the userscript viewer to turn stack-trace
|
|
2
|
+
// references into clickable, readable links: `parseStackRefs` finds
|
|
3
|
+
// them, `displayRefText` decides how they read.
|
|
4
|
+
//
|
|
5
|
+
// References look like
|
|
6
|
+
// sourcemonkey://<scriptId>/<file>:<line>:<col>
|
|
7
|
+
// https://cdn.example.com/jquery.js?v=3:97:11
|
|
8
|
+
// inside the Log tab's entries into clickable links that jump to
|
|
9
|
+
// the matching code tab.
|
|
10
|
+
//
|
|
11
|
+
// We extract any URL with a recognized scheme followed by `:L:C`.
|
|
12
|
+
// The caller filters by matching the URL against tabs it has open;
|
|
13
|
+
// refs whose URL doesn't match render as plain text. That keeps the
|
|
14
|
+
// parser ignorant of script identity — the same code linkifies both
|
|
15
|
+
// main-body refs (`sourcemonkey://<id>/<name>.user.js`) and
|
|
16
|
+
// `@require` refs (their real CDN URLs, query string and all).
|
|
17
|
+
import { decodeForDisplay } from './script-display.js';
|
|
18
|
+
import { ROOT } from './script-source-url.js';
|
|
19
|
+
import { remapStackRef } from './source-map.js';
|
|
20
|
+
// Find every `<scheme>://…:<line>:<col>` ref in `text` and return
|
|
21
|
+
// runs the caller can paint as text or anchors. Recognized schemes:
|
|
22
|
+
// `sourcemonkey`, `http`, `https`, `file`. The URL portion may
|
|
23
|
+
// contain anything except whitespace and parens (V8's named-frame
|
|
24
|
+
// shape is `at fn (URL:L:C)` — without excluding parens the trailing
|
|
25
|
+
// `)` would greedily attach to the URL).
|
|
26
|
+
//
|
|
27
|
+
// Query strings and fragments work fine — they live in the URL part
|
|
28
|
+
// and only the final `:digits:digits` is split off. Port numbers in
|
|
29
|
+
// the authority are also fine for the same reason: the path comes
|
|
30
|
+
// after the port, so the final `:L:C` is always the line:col.
|
|
31
|
+
//
|
|
32
|
+
// IPv6 hosts in `[…]` brackets would parse OK because the brackets
|
|
33
|
+
// don't contain `:digits:digits` at the end — not currently exercised
|
|
34
|
+
// but worth noting if it ever comes up.
|
|
35
|
+
export function parseStackRefs(text) {
|
|
36
|
+
if (!text)
|
|
37
|
+
return [];
|
|
38
|
+
// Greedy `[^\s()]+` plus `:(\d+):(\d+)` at the end — the engine
|
|
39
|
+
// backtracks so the FINAL `:digits:digits` becomes line:col even
|
|
40
|
+
// when the URL contains its own `:digit` runs (port numbers in the
|
|
41
|
+
// authority, etc.).
|
|
42
|
+
const re = /(?:sourcemonkey|https?|file):\/\/[^\s()]+:(\d+):(\d+)/g;
|
|
43
|
+
const runs = [];
|
|
44
|
+
let lastIndex = 0;
|
|
45
|
+
let m;
|
|
46
|
+
while ((m = re.exec(text)) !== null) {
|
|
47
|
+
if (m.index > lastIndex) {
|
|
48
|
+
runs.push({ kind: 'text', text: text.slice(lastIndex, m.index) });
|
|
49
|
+
}
|
|
50
|
+
// Split off the trailing `:line:col` to recover the bare URL.
|
|
51
|
+
const full = m[0];
|
|
52
|
+
const line = parseInt(m[1], 10);
|
|
53
|
+
const col = parseInt(m[2], 10);
|
|
54
|
+
const url = full.slice(0, full.length - (m[1].length + m[2].length + 2));
|
|
55
|
+
runs.push({ kind: 'ref', text: full, url, line, col });
|
|
56
|
+
lastIndex = re.lastIndex;
|
|
57
|
+
}
|
|
58
|
+
if (lastIndex < text.length) {
|
|
59
|
+
runs.push({ kind: 'text', text: text.slice(lastIndex) });
|
|
60
|
+
}
|
|
61
|
+
return runs;
|
|
62
|
+
}
|
|
63
|
+
// Text to paint for a ref, as opposed to the URL used to find its tab.
|
|
64
|
+
//
|
|
65
|
+
// `script-source-url.ts` percent-encodes the script's log label into
|
|
66
|
+
// `//# sourceURL=` — a bare `#` would start a fragment and a space or
|
|
67
|
+
// paren would end the run `parseStackRefs` matches — so script 4's
|
|
68
|
+
// `foo.user.js` arrives as `foo%23E4`. Right on the wire, unreadable in
|
|
69
|
+
// the log. Decoding here is the viewer's usual display-decodes /
|
|
70
|
+
// functional-stays-encoded split (docs/names-and-paths.md).
|
|
71
|
+
//
|
|
72
|
+
// **Only our own URLs decode**, and the reason is not that decoding
|
|
73
|
+
// them is unsafe elsewhere — `displayLocation` decodes any absolute
|
|
74
|
+
// URL and accepts the same `%2F`-reads-as-`/` ambiguity. It is that a
|
|
75
|
+
// stack ref is a *coordinate*, not a name: the user compares it
|
|
76
|
+
// against devtools, pastes it into a Sources-panel search, or matches
|
|
77
|
+
// it to a `@require` line, and all of those want the bytes that were
|
|
78
|
+
// actually in the URL. Our own synthetic URLs are the exception
|
|
79
|
+
// because their encoding is an artifact we introduced, not part of any
|
|
80
|
+
// address the user could go look up.
|
|
81
|
+
//
|
|
82
|
+
// Matched against the full `sourcemonkey://SourceMonkey` prefix rather
|
|
83
|
+
// than the scheme: the scheme is unregistered and a hostile script can
|
|
84
|
+
// put `sourcemonkey://elsewhere/…` in its own log message.
|
|
85
|
+
export function displayRefText(text, url) {
|
|
86
|
+
return url.startsWith(`${ROOT}/`) ? decodeForDisplay(text) : text;
|
|
87
|
+
}
|
|
88
|
+
// Rewrite every ref in `text` (an `Error.stack`, or a console line that
|
|
89
|
+
// carries one) that points into a script's concatenated entry, so it
|
|
90
|
+
// names the `@require` or the script at its own line. For plain-text
|
|
91
|
+
// readers of stacks, such as sm-dev's output; the Log tab maps refs one
|
|
92
|
+
// at a time as it paints them (`resolveLogRef` in userscript.ts).
|
|
93
|
+
//
|
|
94
|
+
// `layouts` are the scripts that may have logged it. A ref stays as it
|
|
95
|
+
// was when it matches none of their entry URLs, or an entry URL that
|
|
96
|
+
// scripts with different layouts share: two `foo.user.js` files run by
|
|
97
|
+
// sm-dev both get `.../foo/foo.user.js`, and mapping with the wrong
|
|
98
|
+
// one's layout would name a confidently wrong file and line.
|
|
99
|
+
//
|
|
100
|
+
// A ref on a line of our own wrap (the head, which holds the GM
|
|
101
|
+
// bindings, a `// @require` marker, a seam, the closing brace) has no
|
|
102
|
+
// original line. Left alone it would read as that line of the user's
|
|
103
|
+
// file, so it is renamed to the script's `__wrapper.js` instead, at
|
|
104
|
+
// the generated line.
|
|
105
|
+
export function remapStackText(text, layouts) {
|
|
106
|
+
if (!text.includes(`${ROOT}/`))
|
|
107
|
+
return text;
|
|
108
|
+
const byUrl = new Map();
|
|
109
|
+
for (const layout of layouts) {
|
|
110
|
+
const seen = byUrl.get(layout.entryUrl);
|
|
111
|
+
if (seen === undefined)
|
|
112
|
+
byUrl.set(layout.entryUrl, layout);
|
|
113
|
+
else if (seen && !sameSegments(seen, layout))
|
|
114
|
+
byUrl.set(layout.entryUrl, null);
|
|
115
|
+
}
|
|
116
|
+
return parseStackRefs(text).map((run) => {
|
|
117
|
+
if (run.kind === 'text')
|
|
118
|
+
return run.text;
|
|
119
|
+
const layout = byUrl.get(run.url);
|
|
120
|
+
if (!layout || layout.segments.length === 0)
|
|
121
|
+
return run.text;
|
|
122
|
+
const ref = remapStackRef(run, layout.entryUrl, layout.segments);
|
|
123
|
+
if (ref)
|
|
124
|
+
return `${ref.url}:${ref.line}:${ref.col}`;
|
|
125
|
+
const folder = layout.entryUrl.slice(0, layout.entryUrl.lastIndexOf('/'));
|
|
126
|
+
return `${folder}/${WRAPPER_FILE}:${run.line}:${run.col}`;
|
|
127
|
+
}).join('');
|
|
128
|
+
}
|
|
129
|
+
// The name `remapStackText` gives frames on our own wrap's lines.
|
|
130
|
+
export const WRAPPER_FILE = '__wrapper.js';
|
|
131
|
+
// Two copies of one script (sm-dev's `copy`) share a URL and a layout,
|
|
132
|
+
// so they don't make the URL ambiguous.
|
|
133
|
+
function sameSegments(a, b) {
|
|
134
|
+
return a.segments.length === b.segments.length && a.segments.every((s, i) => {
|
|
135
|
+
const t = b.segments[i];
|
|
136
|
+
return s.url === t.url && s.generatedLine === t.generatedLine && s.lineCount === t.lineCount;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function sourceUsesUnsafeWindow(bodyText: string | null | undefined): boolean;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// Does a script's own source reference `unsafeWindow` without granting
|
|
2
|
+
// it?
|
|
3
|
+
//
|
|
4
|
+
// `@grant unsafeWindow` is how a script is *supposed* to ask for the
|
|
5
|
+
// page's window, and it is what gets the script the binding. Plenty of
|
|
6
|
+
// real scripts never declare it: `unsafeWindow` is implicitly available under Tampermonkey and
|
|
7
|
+
// Violentmonkey — neither keys it off a grant line, measured under TM
|
|
8
|
+
// 5.5.0 and VM 2.49.0 by `tests/examples/unsafe-window/ungranted.user.js`
|
|
9
|
+
// — so an author who only ever tested there has no reason to add one.
|
|
10
|
+
//
|
|
11
|
+
// Measured over the GreasyFork top 2000: 112 scripts use `unsafeWindow`
|
|
12
|
+
// with no grant for it. Without the binding, the identifier is a
|
|
13
|
+
// `ReferenceError`, so honoring the reference is what makes those
|
|
14
|
+
// scripts run at all.
|
|
15
|
+
//
|
|
16
|
+
// This is a text scan, not a parse. We have no JS parser in the
|
|
17
|
+
// extension (the install-time check compiles with `new Function` in a
|
|
18
|
+
// sandboxed page and reports only a verdict — see docs/parse-check.md),
|
|
19
|
+
// and the answer is needed on the storage row. So:
|
|
20
|
+
//
|
|
21
|
+
// - Comments and string / template literals are stripped first, so a
|
|
22
|
+
// mention in a comment or a log message gets no binding. That was
|
|
23
|
+
// 13 of the 126 ungranted mentions in the corpus.
|
|
24
|
+
// - A property key, a `#private` member and a `foo.unsafeWindow`
|
|
25
|
+
// access don't count. A false positive only adds an unused
|
|
26
|
+
// `window` alias.
|
|
27
|
+
// - A regex literal is NOT recognized. `/['"]/` therefore opens a
|
|
28
|
+
// string state that runs to the next quote, and a literal ending in
|
|
29
|
+
// `\/\/` reads as a line comment — either can swallow real code.
|
|
30
|
+
// Normally that costs a missed reference, so the script gets no
|
|
31
|
+
// binding. If it swallows a DECLARATION while leaving a use
|
|
32
|
+
// visible, the script gets a binding it shadows anyway, which is
|
|
33
|
+
// harmless. No corpus script hits it.
|
|
34
|
+
// - A script that DECLARES the name — `const unsafeWindow =
|
|
35
|
+
// window.wrappedJSObject || window;`, a real cross-browser idiom —
|
|
36
|
+
// answers false, including in a declarator list or a destructuring
|
|
37
|
+
// pattern. Its references read its own binding, never ours. (Our binding would be
|
|
38
|
+
// harmless beside it: the user program runs in a nested scope, so
|
|
39
|
+
// the script's declaration shadows it. See wrapCombinedEntry in
|
|
40
|
+
// injector.ts.) The check is scope-blind, so a declaration inside a
|
|
41
|
+
// function suppresses the whole script: that direction only costs
|
|
42
|
+
// the feature.
|
|
43
|
+
//
|
|
44
|
+
// Only the script's own body is scanned, not its `@require` bodies: the
|
|
45
|
+
// requires arrive on a later pipeline step (`attachFiles`). See
|
|
46
|
+
// TODO.md.
|
|
47
|
+
const IDENT = /[$\w]/;
|
|
48
|
+
// Strip comments and quoted text, leaving a space in their place so
|
|
49
|
+
// adjacent tokens can't merge into a new identifier.
|
|
50
|
+
//
|
|
51
|
+
// Written to jump between interesting positions rather than walk every
|
|
52
|
+
// character: this runs on every script body that mentions the name, at
|
|
53
|
+
// load time, beside the row's SHA-256. On the corpus's largest body
|
|
54
|
+
// (1.8 MB) the per-character form cost ~200 ms, more than 20x that hash.
|
|
55
|
+
function stripQuotedAndComments(src) {
|
|
56
|
+
// A fresh regex per call — `lastIndex` is state, and this function
|
|
57
|
+
// recurses into template substitutions.
|
|
58
|
+
const delim = /\/\/|\/\*|["'`]/g;
|
|
59
|
+
const parts = [];
|
|
60
|
+
const n = src.length;
|
|
61
|
+
let kept = 0;
|
|
62
|
+
for (let m = delim.exec(src); m !== null; m = delim.exec(src)) {
|
|
63
|
+
const from = m.index;
|
|
64
|
+
let after;
|
|
65
|
+
let replacement = ' ';
|
|
66
|
+
if (m[0] === '//') {
|
|
67
|
+
const nl = src.indexOf('\n', from);
|
|
68
|
+
after = nl === -1 ? n : nl;
|
|
69
|
+
}
|
|
70
|
+
else if (m[0] === '/*') {
|
|
71
|
+
const close = src.indexOf('*/', from + 2);
|
|
72
|
+
after = close === -1 ? n : close + 2;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const quote = m[0];
|
|
76
|
+
// The code found inside this template's `${ ... }` substitutions.
|
|
77
|
+
const inner = [];
|
|
78
|
+
let i = from + 1;
|
|
79
|
+
while (i < n) {
|
|
80
|
+
if (src[i] === '\\') {
|
|
81
|
+
i += 2;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (src[i] === quote) {
|
|
85
|
+
i += 1;
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
// A substitution holds real code, so scan it as code rather
|
|
89
|
+
// than as text.
|
|
90
|
+
if (quote === '`' && src[i] === '$' && src[i + 1] === '{') {
|
|
91
|
+
let depth = 1;
|
|
92
|
+
i += 2;
|
|
93
|
+
const innerStart = i;
|
|
94
|
+
while (i < n && depth > 0) {
|
|
95
|
+
if (src[i] === '{')
|
|
96
|
+
depth += 1;
|
|
97
|
+
else if (src[i] === '}')
|
|
98
|
+
depth -= 1;
|
|
99
|
+
i += 1;
|
|
100
|
+
}
|
|
101
|
+
inner.push(stripQuotedAndComments(src.slice(innerStart, i - 1)));
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
i += 1;
|
|
105
|
+
}
|
|
106
|
+
after = i;
|
|
107
|
+
if (inner.length > 0)
|
|
108
|
+
replacement = ` ${inner.join(' ')} `;
|
|
109
|
+
}
|
|
110
|
+
if (from > kept)
|
|
111
|
+
parts.push(src.slice(kept, from));
|
|
112
|
+
parts.push(replacement);
|
|
113
|
+
kept = after;
|
|
114
|
+
delim.lastIndex = after;
|
|
115
|
+
}
|
|
116
|
+
if (n > kept)
|
|
117
|
+
parts.push(src.slice(kept, n));
|
|
118
|
+
return parts.join('');
|
|
119
|
+
}
|
|
120
|
+
// A free `unsafeWindow` — not `foo.unsafeWindow`, not `unsafeWindows`,
|
|
121
|
+
// not a member the source happens to have named that.
|
|
122
|
+
function hasFreeReference(code) {
|
|
123
|
+
let from = 0;
|
|
124
|
+
for (;;) {
|
|
125
|
+
const at = code.indexOf('unsafeWindow', from);
|
|
126
|
+
if (at === -1)
|
|
127
|
+
return false;
|
|
128
|
+
from = at + 'unsafeWindow'.length;
|
|
129
|
+
if (IDENT.test(code[from] ?? ''))
|
|
130
|
+
continue;
|
|
131
|
+
// Immediately preceded by an identifier character → the tail of a
|
|
132
|
+
// longer name (`myunsafeWindow`). Whitespace in between means a
|
|
133
|
+
// separate token, which `typeof unsafeWindow` needs to pass.
|
|
134
|
+
if (IDENT.test(code[at - 1] ?? ''))
|
|
135
|
+
continue;
|
|
136
|
+
// Walk back over whitespace: `foo\n .unsafeWindow` is a property,
|
|
137
|
+
// and `#unsafeWindow` is a private class member.
|
|
138
|
+
let before = at - 1;
|
|
139
|
+
while (before >= 0 && /\s/.test(code[before]))
|
|
140
|
+
before -= 1;
|
|
141
|
+
if (code[before] === '.' || code[before] === '#')
|
|
142
|
+
continue;
|
|
143
|
+
// Followed by `:` → a property key (`{ unsafeWindow: window }`) or a
|
|
144
|
+
// label, which declares a name rather than reading the page's. This
|
|
145
|
+
// also skips `cond ? unsafeWindow : other`, and that is the trade we
|
|
146
|
+
// want: a script using the name in a ternary almost always uses it
|
|
147
|
+
// somewhere else too, while a key is the whole mention.
|
|
148
|
+
let next = from;
|
|
149
|
+
while (next < code.length && /\s/.test(code[next]))
|
|
150
|
+
next += 1;
|
|
151
|
+
if (code[next] === ':')
|
|
152
|
+
continue;
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// `function unsafeWindow(` / `class unsafeWindow`.
|
|
157
|
+
const DECLARES_AS_CALLABLE = /(?:^|[^$\w.])(?:function|class)\s+unsafeWindow(?![$\w])/;
|
|
158
|
+
const KEYWORD = /(?:^|[^$\w.])(?:var|let|const)(?![$\w])/g;
|
|
159
|
+
// How far past a `var` / `let` / `const` to keep looking for the end of
|
|
160
|
+
// its declarator list. A statement without a `;` (ASI) would otherwise
|
|
161
|
+
// run the scan into the rest of the file; over-running only ever
|
|
162
|
+
// over-suppresses, but there is no reason to pay for it.
|
|
163
|
+
const DECLARATOR_SPAN = 2000;
|
|
164
|
+
// Is `unsafeWindow` a NAME BEING BOUND anywhere in this source, rather
|
|
165
|
+
// than a value being read? A script that binds the name reads its own
|
|
166
|
+
// value, so it gets neither our binding nor the move to MAIN world.
|
|
167
|
+
//
|
|
168
|
+
// Walks each `var` / `let` / `const` declarator list, tracking bracket
|
|
169
|
+
// depth and whether the current declarator has passed its `=`:
|
|
170
|
+
//
|
|
171
|
+
// const unsafeWindow = window → bound (before any `=`)
|
|
172
|
+
// let uw = window, unsafeWindow=uw → bound (the `,` starts a declarator)
|
|
173
|
+
// const { unsafeWindow } = window → bound (pattern, before the `=`)
|
|
174
|
+
// const app = unsafeWindow.thing → read (after the `=`) — by far the
|
|
175
|
+
// commonest real use, and the one
|
|
176
|
+
// thing this must not claim
|
|
177
|
+
// const xs = [1, unsafeWindow] → read (bracketed, after the `=`)
|
|
178
|
+
//
|
|
179
|
+
// Scope-blind on purpose: a declaration inside a function suppresses the
|
|
180
|
+
// whole script. That direction only costs the feature.
|
|
181
|
+
function declaresBinding(code) {
|
|
182
|
+
if (DECLARES_AS_CALLABLE.test(code))
|
|
183
|
+
return true;
|
|
184
|
+
KEYWORD.lastIndex = 0;
|
|
185
|
+
for (let m = KEYWORD.exec(code); m !== null; m = KEYWORD.exec(code)) {
|
|
186
|
+
const limit = Math.min(code.length, m.index + DECLARATOR_SPAN);
|
|
187
|
+
let depth = 0;
|
|
188
|
+
let afterEquals = false;
|
|
189
|
+
let i = KEYWORD.lastIndex;
|
|
190
|
+
while (i < limit) {
|
|
191
|
+
const c = code[i];
|
|
192
|
+
if (c === '(' || c === '[' || c === '{')
|
|
193
|
+
depth += 1;
|
|
194
|
+
else if (c === ')' || c === ']' || c === '}') {
|
|
195
|
+
if (depth === 0)
|
|
196
|
+
break;
|
|
197
|
+
depth -= 1;
|
|
198
|
+
}
|
|
199
|
+
else if (c === ';' && depth === 0)
|
|
200
|
+
break;
|
|
201
|
+
else if (c === ',' && depth === 0)
|
|
202
|
+
afterEquals = false;
|
|
203
|
+
else if (c === '=' && code[i + 1] !== '=' && !'=!<>'.includes(code[i - 1] ?? '')) {
|
|
204
|
+
afterEquals = true;
|
|
205
|
+
}
|
|
206
|
+
else if (c === 'u' && code.startsWith('unsafeWindow', i)) {
|
|
207
|
+
const after = code[i + 'unsafeWindow'.length] ?? '';
|
|
208
|
+
const prev = code[i - 1] ?? '';
|
|
209
|
+
if (!IDENT.test(after) && !IDENT.test(prev) && prev !== '.') {
|
|
210
|
+
if (!afterEquals)
|
|
211
|
+
return true;
|
|
212
|
+
i += 'unsafeWindow'.length;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
i += 1;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
export function sourceUsesUnsafeWindow(bodyText) {
|
|
222
|
+
// The overwhelmingly common answer, without paying for the scan.
|
|
223
|
+
if (!bodyText || bodyText.indexOf('unsafeWindow') === -1)
|
|
224
|
+
return false;
|
|
225
|
+
const code = stripQuotedAndComments(bodyText);
|
|
226
|
+
if (declaresBinding(code))
|
|
227
|
+
return false;
|
|
228
|
+
return hasFreeReference(code);
|
|
229
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface UrlMatching {
|
|
2
|
+
matches: string[];
|
|
3
|
+
includes: string[];
|
|
4
|
+
excludes: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function isPublicSuffix(name: string): boolean;
|
|
7
|
+
export declare function hostEndsInTld(pattern: string): boolean;
|
|
8
|
+
export declare function tldCoarseGlob(pattern: string, isMatch: boolean): string;
|
|
9
|
+
export declare function canonicalMatchPatterns(pattern: string): string[];
|
|
10
|
+
export declare function matchPattern(pattern: string, url: string): boolean;
|
|
11
|
+
export declare const MATCH_SCHEMES: string[];
|
|
12
|
+
export interface MatchPatternProblem {
|
|
13
|
+
pattern: string;
|
|
14
|
+
what: string;
|
|
15
|
+
fix?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function matchPatternProblem(pattern: string): MatchPatternProblem | null;
|
|
18
|
+
export declare function ignoredMatchMessage(problem: MatchPatternProblem): string;
|
|
19
|
+
export declare function unusableMatchMessage(problem: MatchPatternProblem): string;
|
|
20
|
+
export declare function unusableMatchGroupKey(message: string): string | null;
|
|
21
|
+
export declare function includePattern(pattern: string, url: string): boolean;
|
|
22
|
+
export declare function chromeIncludeGlob(glob: string): string;
|
|
23
|
+
export type PageRegex = [source: string, flags: string];
|
|
24
|
+
export interface PageMatcher {
|
|
25
|
+
include: PageRegex[];
|
|
26
|
+
exclude: PageRegex[];
|
|
27
|
+
tld?: true;
|
|
28
|
+
}
|
|
29
|
+
export declare function pageMatcher(matching: UrlMatching): PageMatcher;
|
|
30
|
+
export declare function urlMatchesScript(url: string, matching: UrlMatching): boolean;
|
|
31
|
+
export interface UrlMatchExplanation {
|
|
32
|
+
matches: boolean;
|
|
33
|
+
/** Which header line decided, e.g. `@exclude *://example.com/admin*`. */
|
|
34
|
+
rule: string | null;
|
|
35
|
+
/** Every `@match` / `@include` line that covers the URL, excluded or not. */
|
|
36
|
+
matchedBy: string[];
|
|
37
|
+
/** Every `@exclude` line that covers it. Any one keeps the script off. */
|
|
38
|
+
excludedBy: string[];
|
|
39
|
+
}
|
|
40
|
+
export declare function explainUrlMatch(url: string, matching: UrlMatching): UrlMatchExplanation;
|