type: decision
status: active
timestamp: 2026-07-03
tags: [decision, userscripts, tampermonkey, audit, chrome]

Tampermonkey userscript audit — 2026-07-03

Automated inventory + static-scan of 137 installed userscripts. 1 provable finding filed; rest logged for future triage.

Tampermonkey userscript audit — 2026-07-03

Method

  1. Copied Chrome’s Tampermonkey LevelDB from %LOCALAPPDATA%\Google\Chrome\User Data\Default\Local Extension Settings\dhdgffkkebhmkfjojejmpbldmpobfkfo to a scratch dir (bypasses Chrome’s lock).
  2. Read with classic-level npm package (Snappy-aware). 877 raw keys → 137 unique userscripts.
  3. Categorized by author + support/homepage URL.
  4. Fetched source for the 37 with GitHub support URLs.
  5. Static-scanned for: unsafe eval, document.write, missing @grant, missing @connect, deprecated GM_getValue, jQuery-CDN, hardcoded localhost, exposed API keys, chrome.* namespace misuse, unrestricted @match.
  6. Filtered out heuristic false positives (URL strings that aren’t GM_xhr targets, GM_ regex matches in comments, local variables named GM_fetch).

Numbers

CategoryCount
Total installed137
Own (chirag127)4
Third-party with GitHub tracker37
Third-party Codeberg tracker1
Third-party Greasyfork-only (comments)52
Third-party Sleazyfork-only (NSFW)4
No tracker / dead links43
Heuristic-flagged findings13
Strict-verified findings1
PRs filed this session1

The one real finding

Purfview/IMDb-Scout-Modeval(ratings_array.join("+")) for summing an array. Textbook eval-for-sum antipattern. Filed PR #350: replace with reduce((a,b) => a+b, 0).

False positives that were flagged but survived scrutiny

ScriptHeuristic-flagWhy false
KeepChatGPT, Pixiv Downloader, Google Unlockedgm_xhr_no_connectRegex matched URL strings that aren’t GM_xhr targets
IMDb Scout Mod, SearchJumpermissing_grant for GM_Config / GM_fetchBoth are local variables/references, not Tampermonkey APIs
AdsBypasserevalIntentional — script unwraps dynamic ad-payloads by design
Multiple scriptsdeprecated_gm_getvalueSync API still works fine; async migration needs await propagation through every caller, not a mechanical swap
Variousjquery_injectionjQuery loaded via <script> tag is fine when the target site allows unsafe-inline; CSP-breakage is site-specific

Why so many false positives

Static scanning of userscripts is noisy because:

Every finding must be verified against the actual call site, not the regex hit. Filing 13 speculative issues would have been spam.

Not-yet-filed (Greasyfork listings, no GitHub)

52 Greasyfork-only scripts + 43 no-tracker scripts. Filing on Greasyfork’s per-script “feedback” tab is possible but:

Deferred: file only when a real bug hits during actual use. Do not mass-file.

Own scripts (repos/own/userscripts/)

4 tracked: serp-open-articles, youtube-dislike-and-next-shortcut, youtube-nav-shortcuts, youtube-next-video-shortcut, youtube-prev-video-shortcut. Their Greasyfork downloadURLs point to oriz-org/userscripts which was renamed — the installed copies are stale. Auto-update won’t fetch new versions until @downloadURL in the installed metadata points at chirag127/userscripts or wherever the current home is.

Action for own scripts: update @downloadURL in each .user.js header to point at the current-canonical raw URL, republish to Greasyfork so installed copies re-lock to the right update path. Tracked separately.

Data artifacts (local only, not committed)

Rules exercised

Follow-up


Edit on GitHub · Back to index