Skip to content

Rule severity bump for dynamic <script> element detection breaks plugins bundling library feature-detection patterns #152

@banisterious

Description

@banisterious

The scan rule that detects "Found N dynamic <script> element creations" appears to have been promoted from warning to error in the Community Plugins automated review pipeline. Plugins that previously passed scans now fail, even with no source code changes.

For Charted Roots, v0.22.39 was scanned with no errors against the release branch on 2026-05-15, then re-scanned after release tag push the same day and failed with 9 sites flagged at error severity. The bundled code is identical between the two scans.

Affected sites - example from Charted Roots v0.22.39

All 9 flagged sites are inside vendored library code, none in plugin-authored source:

Library Sites Pattern
core-js/internals/task.js 2 IE-era setImmediate polyfill: ONREADYSTATECHANGE in createElement("script") feature detection
pdfmake/build/pdfmake.js 2 Bundled core-js with the same pattern
jszip/dist/jszip.min.js 4 UMD module detection: "onreadystatechange" in t.document.createElement("script")
leaflet-distortableimage 1 Webpack chunk loader for code splitting (chunks aren't actually used in plugin context)

Why these are false positives

Each site is a feature-detection / module-loader pattern, not arbitrary code execution:

  1. ONREADYSTATECHANGE in createElement("script") — IE-era feature detection. The created element is checked for a legacy property and discarded; it's never inserted into the DOM and never loads anything. In Obsidian's Electron runtime, the surrounding else if branch is never even entered because modern code paths above it (e.g., setImmediate in globalThis) take precedence.

  2. Webpack chunk loader (__webpack_require__.l) — webpack's lazy-chunk loading infrastructure, bundled by accident when libraries are built with webpack instead of pure-output rollup/esbuild. In an Obsidian plugin context, chunks aren't loaded dynamically (single-bundle plugin output), so this function never executes.

  3. JSZip UMD detection — feature-detection that selects between two implementations based on browser support. The script element is created for property-check purposes only, never injected.

None of these load external scripts. They are dead-branch / feature-detection code that exists in essentially every plugin that transitively depends on any of these libraries.

Why this matters

The scanner failure flips the plugin to a demoted state on the website (Install button disabled) and presumably in Community Plugins app once it syncs. This is a meaningful user-acquisition blocker for affected plugins, and the affected libraries (core-js, jszip, pdfmake, jspdf, canvg, anything bundled with webpack) are extremely common dependency choices.

Suggested directions

I'd love guidance on the intended path forward:

  1. Whitelist these specific patterns - the four mentioned above are reasonably well-known false-positive shapes.
  2. AST-aware analysis - distinguish "script element constructed, property-checked, discarded" from "script element with src attribute or appendChild-inserted".
  3. Vendored-code carve-out - many of these are in node_modules/.../dist/*.js files; a path heuristic could ignore non-plugin-authored code.
  4. Author-side fix path - if the rule is intentional, plugin authors need a documented way to handle this (forks, postbuild patches, or alternative dependencies).

Happy to provide bundle samples, AST excerpts, or reproduce against a minimal test plugin if that would help.

Plugin reference: banisterious/obsidian-charted-roots, v0.22.39.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions