Skip to content

check-boundaries.mjs's dynamic-import scanner misses backtick no-substitution template literals #642

Description

@BorisTyshkevich

What

build/check-boundaries.mjs's extractSpecifiers() dynamic-import regex only matches
single- or double-quoted specifiers:

/\bimport\s*\(\s*['"]([^'"]+)['"]/g

A no-substitution backtick template literal escapes it entirely — e.g.:

import(`@preact/signals-core`)

returns [] from the scanner, so every rule built on extractSpecifiers() (the
generic RULES relative-import loop, and the #630 Phase 2 package-boundary rules
added in PR #641: package→root-src ban, root→package-internals ban, zero-bare-
specifiers-in-package-source, bare-specifier-location restriction) can be bypassed by
using this form for a dynamic import instead of a static one.

Where

build/check-boundaries.mjs (extractSpecifiers, the dynamic-import pattern — see
SPECIFIER_PATTERNS). Attributed by git blame to commit f16e4a1a (2026-07-17,
issue #276, Phase 0-1) — predates #630 entirely. The sibling mirror test
tests/unit/dashboard-boundaries.test.js doesn't scan dynamic imports at all either,
so this is a repo-wide characteristic of the hand-rolled-regex convention, not specific
to one rule set.

Why deferred

  • No live exploit path today: a repo-wide git grep for import( under src/** and
    packages/** finds zero real dynamic-import calls (the one hit is inside a comment).
  • The file's own header documents this as a deliberate simplification ("the codebase
    has no exotic import syntax").
  • Fixing it properly means auditing/strengthening every existing rule built on
    extractSpecifiers() (there are more than a dozen), not just the two PRs (Epic: extract the Fetch-native ClickHouse client into a reusable package #630
    phase 1/2) that happened to surface it during review — out of scope for an
    incremental package-boundary PR.

Suggested fix

Make dynamic-import handling fail-closed for guarded directories: reject any
import(...) call in package/rule-guarded source unless the argument is a statically
provable relative string literal (covering both quote styles and no-substitution
template literals), rather than silently skipping anything the regex doesn't parse.
Add sabotage cases for: quoted dynamic import, backtick dynamic import, export * from,
and named/namespace re-exports, mirrored in the corresponding policy test(s).

Surfaced during /ship 630 phase 2 code review (PR #641) — verified independently, not
a regression introduced by that PR.

Metadata

Metadata

Assignees

No one assigned

    Labels

    inboxFiled mid-task; not yet triaged into the roadmap

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions