* feat(templating): surface profiles + ceiling + manifest-less migration warning (P1)
Add per-surface sandbox profiles (surface-profiles.ts): a profile is the
ceiling of what a plugin on a surface may be granted, effective grant =
floor ∪ (declared ∩ ceiling).
- TEMPLATE_TAG_PROFILE excludes 'credentials' from its capability ceiling —
cloud-credential access is reserved for first-party bundle plugins; a user
template-tag plugin can't obtain it even by declaring it.
- SCRIPTING_PROFILE (broad, enumerated) defined for the later scripting-engine
reuse; unit-tested, not wired to a live surface yet.
- resolveTemplateTag{Modules,Capabilities} move here from module-registry/
host-bridge and delegate to the profile (capabilities enforce the ceiling;
modules pass declared names through so the require gate gives the precise
'not available' vs 'not permitted' message).
- Migration warning: a manifest-less plugin denied a non-baseline module gets a
one-time (per-plugin-per-session) toast via the existing show-toast channel,
naming the module + the insomnia.permissions field to add.
- Docs: PERMISSIONS.md; demo README + capability ceiling note.
Tests: surface-profiles.test.ts (effectiveGrant matrix, floor⊆ceiling, credentials
ceiling exclusion); migration-warning dedup unit test; C2 shape tests decoupled
from the profile (grant caps directly); e2e P1 test (ceiling + one-time warning).
107 unit + 4 smoke green.
* feat(templating): typed module denial (#10254)
* fix(templating): typed module-denial error instead of message regex (P1)
maybeWarnMissingManifest matched the sandbox's thrown error message
("Module 'X' not permitted by manifest") with a regex to decide whether
to show the migration toast. That couples a UI feature to English error
prose crossing the QuickJS VM boundary — any future wrapping or wording
change breaks the toast silently, with no compiler signal.
__require now throws with `code`/`moduleName` own properties
(SANDBOX_MODULE_NOT_PERMITTED vs SANDBOX_MODULE_NOT_AVAILABLE), which
toError() in plugin-tag-sandbox.ts carries through onto the rebuilt
Error. maybeWarnMissingManifest branches on that discriminant instead of
regexing the message text; the message strings remain unchanged and
still asserted verbatim by the existing tests.
* fix(templating): key migration-warning dedup by (plugin, module), add ceiling tripwire
maybeWarnMissingManifest deduped its one-time toast by plugin name alone,
so a manifest-less plugin that trips on two different ungranted modules in
one session only ever saw a hint for the first one it hit — the second,
equally actionable denial was silently suppressed. Key by (plugin, module)
instead so each distinct missing grant still gets its own toast, while a
repeat denial for the same module still doesn't re-toast.
Also added a regression test pinning that a manifest with a malformed axis
(declared:true, per-card warning) is correctly routed away from this toast
path, and a tripwire test on TEMPLATE_TAG_PROFILE.moduleCeiling: resolveTemplateTagModules
deliberately doesn't enforce the ceiling (an unregistered module must still
reach require() for the precise "not available" message), which is only
safe while the ceiling equals the full module registry — this test fails
first if a future change narrows it, as a signal to revisit that function.
---------
Co-authored-by: kwburns-kong <kyle.burns@konghq.com>
3.3 KiB
insomnia-plugin-sandbox-demo
Manual test fixture for the QuickJS template-tag sandbox (Milestone 2).
The sandboxprobe tag reports where it executed and exercises an async host bridge:
- Sandbox flag off →
hello | ran in: main-process | arch via bridge: <arch> - Sandbox flag on →
hello | ran in: sandbox | arch via bridge: <arch>
ran in flips because the sandbox defines the INSOMNIA_TEMPLATE_SANDBOX marker global, which the
legacy main-process path lacks. (process used to be the signal, but since M2 the sandbox provides
a process stub too, so a dedicated marker is used instead.) arch via bridge proves
context.util.nodeOS() round-tripped through __hostBridge → pluginToMainAPI['nodeOS'] and back.
Install (dev)
- Run the app:
npm run dev(repo root). - Preferences → Plugins → Reveal Plugins Folder.
- Copy this
insomnia-plugin-sandbox-demofolder into that directory. - Click Reload Plugins.
- Preferences → Scripting → toggle Run template tags in sandbox (experimental).
- In a request URL/header, insert the
Sandbox Probetemplate tag (or type{% sandboxprobe 'hi' %}), and watch the preview change as you toggle the flag.
The requireprobe tag demos the manifest-gated module registry (M1): {% requireprobe 'path' %}
renders a/b (baseline grant, curated registry implementation), while {% requireprobe 'fs' %}
or any npm package fails with Module 'X' not permitted by manifest. A granted-but-unshipped
module would fail with Module 'X' not available in sandbox. Registry coverage grows in M2/M3;
relative files (require('./util')) arrive with plugin pre-bundling (M4).
The eventsprobe tag demos a manifest-declared grant (C3): this plugin's package.json
declares insomnia.permissions.modules: ["events"], so {% eventsprobe %} renders events-ok.
A plugin that did not declare events would get Module 'events' not permitted by manifest.
Preferences → Plugins shows each plugin's declared permissions (this one lists modules: events).
The stdlibprobe tag demos the ambient sandbox globals (M2) — Buffer, URL/URLSearchParams,
a frozen process stub, and Web-Crypto crypto.getRandomValues/crypto.subtle. These are always
present (not manifest-gated) as pure-JS or host-backed safe equivalents, and render identically to
the legacy main-process path: {% stdlibprobe 'buffer' %}, {% stdlibprobe 'url' %},
{% stdlibprobe 'platform' %}.
The capabilityprobe tag demos manifest-gated host capabilities (C1): this plugin declares
insomnia.permissions.capabilities: ["storage"], so {% capabilityprobe %} completes a context.store
set/get round-trip and renders storage-ok. A plugin that did not declare storage would get
Capability 'storage' not granted — add it to insomnia.permissions.capabilities. Baseline
capabilities (render, models.read, util, crypto) need no declaration; network / storage /
fs-read / app do. credentials is reserved for first-party bundle plugins and can't be granted to a
community plugin even if declared (it's above the template-tag surface's ceiling — see
PERMISSIONS.md). A plugin that
declares no manifest and reaches for a non-baseline module gets a one-time migration notification
naming the grant to add.