Problem
The SYN bypass series covers globals that make real effects without declaring them in the fn header: `fetch()` (SYN007), `WebSocket` (SYN008), `XMLHttpRequest` (SYN009), `setTimeout/setInterval/queueMicrotask` (SYN010). Dynamic `import()` is the same class of bypass but for module loading.
`import(specifier)` at runtime:
- Loads a module whose capabilities are not statically declared
- Is invisible to CAP001 (which only checks stdlib namespace calls)
- Can load code with arbitrary capabilities (`net`, `fs`, etc.) that the calling fn does not declare
- Creates an implicit dependency on the entire capability surface of the dynamically loaded module
- Cannot be checked by the compiler's transitive closure since the specifier may be determined at runtime
A fn that calls `import(specifier)` has an undeclared capability surface proportional to everything the dynamically loaded module might do. The manifest hash proves the fn body hasn't changed; it says nothing about what the dynamically loaded module does at runtime.
Proposed diagnostic
SYN011 fires when a fn body calls `import(...)` — the dynamic import form — at `?bs 0.7+`.
Detection: the token `import` followed by `(` (not preceded by `.`/`?.`). This excludes static `import { ... } from` and `import * as ns from` declarations which are top-level and resolved at compile time.
Cases to detect:
- `import(specifier)` — bare dynamic import
- `import(`./module`)` — template literal specifier
- `const mod = await import(path)` — awaited dynamic import
Suppression: `unsafe "loads plugin dynamically" { import(specifier) }`
Relation to existing diagnostics
- Same bypass class as SYN007/SYN008/SYN009: a global that acquires a real capability without declaring it
- More severe: dynamic import loads arbitrary code; the capability surface is unbounded
- CAP001 will not fire even if the dynamically loaded module uses `http.`, `fs.`, etc.
Draft rewrite
```
// before — undeclared module load
async fn loadPlugin(name: string) uses { fs } -> Plugin {
const mod = await import(`./plugins/${name}`)
return mod.default
}
// after — explicit escape hatch
async fn loadPlugin(name: string) uses { fs } -> Plugin {
const mod = await unsafe "loads plugin by name from trusted plugin dir" { import(`./plugins/${name}`) }
return mod.default
}
```
Notes
Problem
The SYN bypass series covers globals that make real effects without declaring them in the fn header: `fetch()` (SYN007), `WebSocket` (SYN008), `XMLHttpRequest` (SYN009), `setTimeout/setInterval/queueMicrotask` (SYN010). Dynamic `import()` is the same class of bypass but for module loading.
`import(specifier)` at runtime:
A fn that calls `import(specifier)` has an undeclared capability surface proportional to everything the dynamically loaded module might do. The manifest hash proves the fn body hasn't changed; it says nothing about what the dynamically loaded module does at runtime.
Proposed diagnostic
SYN011 fires when a fn body calls `import(...)` — the dynamic import form — at `?bs 0.7+`.
Detection: the token `import` followed by `(` (not preceded by `.`/`?.`). This excludes static `import { ... } from` and `import * as ns from` declarations which are top-level and resolved at compile time.
Cases to detect:
Suppression: `unsafe "loads plugin dynamically" { import(specifier) }`
Relation to existing diagnostics
Draft rewrite
```
// before — undeclared module load
async fn loadPlugin(name: string) uses { fs } -> Plugin {
const mod = await import(`./plugins/${name}`)
return mod.default
}
// after — explicit escape hatch
async fn loadPlugin(name: string) uses { fs } -> Plugin {
const mod = await unsafe "loads plugin by name from trusted plugin dir" { import(`./plugins/${name}`) }
return mod.default
}
```
Notes