Commit Graph
96 Commits
Author SHA1 Message Date
dotCooCoo 2212739c54 MCP: plug-in, recovery and script report replies leave out paths outside the read roots
A client confined by --automation-read-root saw paths outside its read
roots that the person or an earlier request had put in the session:

- plugin.list: the plug-in folder last loaded and its failed files;
  plugin.list and plugin.info: a plug-in's source file; plugin.list,
  plugin.safeMode and plugin.setEnabled: the file of a plug-in turned
  off for the next launch.
- file.recovery.list and file.recovery.restore: a document's original
  file; file.recovery.list and file.recovery.save: the recovery folder.
- ui.inspect's scriptReport: the files a run wrote and the included
  file a failed run stopped in.

A folder outside the read roots is now null, and a file outside them is
named by its file name. plugin.setEnabled {file} takes that name. The
restored documents keep their files, and unconfined clients and the
app's own UI see the same replies as before.
2026-11-12 06:03:01 -08:00
dotCooCoo 1eb02f2f7a Merge main into fix/plugin-ignored-grants-shared 2026-11-11 22:03:31 -08:00
BFlat 485d4e3fff Merge pull request #6747 from dotCooCoo/fix/plugin-grant-sealed-api
Plug-ins: only an approval records a grant, and installs need one
2026-11-12 12:41:15 +07:00
dotCooCoo be4fd1c23e Merge main into fix/plugin-put-back-result 2026-11-11 18:55:34 -08:00
dotCooCoo 822efb5db9 Merge main into fix/plugin-grant-sealed-api
# Conflicts:
#	docs/target-app-parity.md
2026-11-11 18:49:27 -08:00
dotCooCoo cde367b889 Plug-ins: only an approval records a grant, and installs need one
registry::grant takes a GrantToken, which the engine makes after a
person's approval and when it loads grants.json. registry::install,
install_bytes and folder loads refuse a module that lists permissions no
recorded grant gives this very module (Error::NeedsApproval), and leave
the plug-in under its id and the revision as they were. A folder load's
approve callback only narrows what installs. A module without
permissions, or with an empty list, installs as before.

A folder load in a session with a plug-in store records the store's
grant for each module it approves, so those modules still load.

docs/plugins.md says what the approval does not protect against: native
code linked into the app, and another program running as the same user,
which can edit the Plug-ins folder and its unsigned grants.json.
2026-11-11 17:35:06 -08:00
dotCooCoo 45987bc948 Plug-ins: a grant one session stops counting stops counting for every session on its folder
The grants that should be gone from grants.json but could not be removed
were kept per Session, so a second Session attached to the same Plug-ins
folder in the same process still counted them for plugin.inspect,
plugin.install and loading plug-ins. They are now kept for the process,
by the folder's canonical path and the plug-in id, and a successful
write of grants.json for the id in any session makes the grant count
again. Attaching another folder no longer clears them.

A failed removal does not override a write another session made for
the id in the meantime. The process keeps the latest 256 grant writes
for that check. Another process on the folder still reads grants.json
as it is written.
2026-11-11 17:25:19 -08:00
dotCooCoo ef7fbaac31 Plug-ins: a failed rename back of a removed plug-in's settings is reported and undone later
When plugin.remove could not remove the grant and then could not rename
the plug-in's settings folder back from <folder>.removed, the failure was
only logged: the plug-in got empty settings, and the next removal deleted
the .removed folder with the settings in it.

- The error now names the .removed folder, the plug-in folder and the
  reason, and the plug-in gets no storage until the settings are back.
- The next load, install or plugin.remove of the plug-in renames a kept
  folder back while the plug-in has no folder of its own. When it has one,
  that folder is used and the kept one stays until plugin.remove deletes
  both. A removal while a kept .removed folder exists renames the plug-in
  folder to <folder>.removing.
- Before deleting a renamed folder, the store writes an empty
  <folder>.removed.delete file and removes it once the folder is gone, so
  what a failed deletion leaves is not moved back. When that file can't
  be written, nothing is deleted.
- A .removed folder without a .delete file, including one an earlier
  version left, counts as kept settings: a new install of the id needs
  replace: true, and plugin.remove of the id deletes it.
2026-11-11 15:51:11 -08:00
dotCooCoo 08d4a5b486 Plug-ins: a put-back that fails after writing the old module keeps a disabled plug-in off
When plugin.install can't write or remove a grant and the write that puts
the old module back reports a failure, install now reads the stored module
again and turns the plug-in on only when the store holds the new module. A
put-back that wrote the old bytes and then failed (for example at its last
flush) leaves a disabled plug-in disabled. This applies to modules that
list permissions and to modules that don't.
2026-11-11 14:03:36 -08:00
dotCooCoo fd7ff70941 Plug-ins: an update left in the store by a failed put-back turns a disabled plug-in on
When plugin.install writes a module that lists permissions, its grant
can't be written, and the old module can't be put back (or, on a first
install, the new module can't be removed), the store holds the new
module. install now takes put_back_plugin's result, so the plug-in's
disabled entries (by id and by stored file) are cleared as for any
install the store holds. The old grant, or none, still doesn't cover the
new module, so the next start skips it until it is approved.

Docs: docs/plugins.md, docs/mcp.md, docs/target-app-parity.md.
2026-11-11 13:03:38 -08:00
dotCooCoo 8eb3788eba Plug-ins: setting the plug-ins folder again after clearing it installs its plug-ins
Clearing Preferences > Plug-ins & Scratch Disks > Additional Plug-ins Folder
left the folder recorded as the one last loaded. Setting the same folder
again then matched that record, and nothing in it was installed, so a
plug-in removed in the meantime stayed removed. Clearing the preference now
forgets the folder when it is the one last loaded.
2026-11-11 08:07:27 -08:00
dotCooCoo 811c21e7c2 Plug-ins: calls need doc.read, and an edit in progress is kept before a calling run
A command's result describes the document, so a plug-in without doc.read
gets -6 for every call, whether or not the id names a command. A run that
calls commands commits an edit still in progress first and prepares its
input again when that changed the document or the selection.
2026-11-10 11:26:10 -08:00
dotCooCoo f921a6923d Plug-ins: an API level 2 plug-in calls the commands its manifest lists
A filter at plug-in API level 2 with the new `commands` permission names
the commands it may run in its manifest's `commandAllowlist` and runs
them with `env.vc_call`, reading each result with `env.vc_call_result`.
Each call runs through Session::execute on the run's document and sees
what earlier calls changed; the calls and the filter's output are one
undo step, and a run that fails, is cancelled or makes a failed call
leaves the documents, their history and the session state the commands
change as they were.

Commands that touch files, the clipboard, preferences, the view,
scripts, actions, plug-ins, open documents or the undo history are never
callable, and every other command must be listed as allowed in
cmd/plugin_call/reviewed.rs, so a new command is refused until placed.
Calls are capped at 1,000 a run, 8 MiB a call and 64 MiB a run, and each
costs instructions from the run's budget. plugin.install refuses a
manifest that lists a refused command and reports unknown ids;
plugin.inspect lists both, and the approval dialog lists the commands.

Refs #2186
2026-11-10 10:16:28 -08:00
dotCooCoo f688129ef2 Merge main into feat/command-param-schemas 2026-11-09 12:34:02 -08:00
dotCooCoo eebbe496d4 Merge main into fix/plugin-grant-app-approval 2026-11-08 23:53:57 -08:00
BFlat 0f2857e1a1 Merge pull request #6286 from dotCooCoo/fix/plugin-stale-grant
Plug-ins: replacing a granted plug-in with one that lists no permissions removes the grant
2026-11-09 15:41:04 +08:00
dotCooCoo 0efd9a9ccc Merge main into fix/plugin-grant-app-approval 2026-11-08 23:26:53 -08:00
dotCooCoo 65ff9df849 Plug-ins: a grant left behind stops counting when its module can't go back
plugin.install and plugin.remove count an entry of grants.json that this
version can't use when it can't be removed. plugin.install stops counting
a grant it can't remove when the old module can't be written back to the
Plug-ins folder.
2026-11-08 22:00:26 -08:00
dotCooCoo 7177438feb Plug-ins: plugin.install's params text and docs/mcp.md state when a grant is kept
A refused install keeps the grant, a failed write of the module to the
plug-in store leaves the grant in the store, and a stored module that is
disabled or not read in Safe Mode keeps the grant for its id. This
matches docs/plugins.md.
2026-11-08 21:26:13 -08:00
dotCooCoo 9ae57b03c0 Plug-ins: the texts say a grant accepted under the flag stays recorded
A grant accepted under --allow-plugin-grants is written to grants.json like a
person's approval and counts as approved in later sessions and in the desktop
app. The CLI usage text, the plugin.install params text and docs/plugins.md and
docs/mcp.md said only a person's approval in the desktop app records one.
2026-11-08 21:10:26 -08:00
dotCooCoo 0e1aa5cc2e Plug-ins: a filter's read-only objects stay in its input and failed decodes count
A filter run kept a second copy of every read-only object as JSON values.
The run now keeps where each object is in the input, and checks an output
entry against it by parsing that one object, unless the entry is `{"id": …}`
alone.

An image that reaches the decoder now counts toward the 16 MiB pixel total
even when decoding fails, so broken images use up the same total as images
that decode.

The plugin.run text, docs/mcp.md and the progress line state the paint
order and that rule, and docs/plugins.md states that a CMYK image's pixels
are sRGB. New tests cover the per-image 1 MiB, the 16 MiB pixel total and
the 16 MiB text total at and one past their values, memory-budget failures
before the plug-in starts, images that are not decoded, CMYK images
included, and leading per run.
2026-11-08 20:54:02 -08:00
dotCooCoo 9e655ae2e6 Merge main into fix/plugin-stale-grant 2026-11-08 20:38:46 -08:00
dotCooCoo 6cce39dd7f Plug-ins: a grant that can't be removed stops counting for the session
When loading the plug-in store can't remove the grant of an id whose stored
module lists no permissions, or can't read grants.json, the entry stays on
disk and still approved the bytes it covers. plugin.install has the same case
when the grant can't be removed and no earlier module can be put back.

The session now keeps those ids and does not count their grants in
plugin.inspect, plugin.install, plugin.reload or a later load of the store. A
successful update of the grant for the id ends that, and so does attaching a
different store.
2026-11-08 20:38:03 -08:00
dotCooCoo bff7f0ad54 Merge main into fix/plugin-grant-app-approval 2026-11-08 20:16:56 -08:00
dotCooCoo 529dcb5c64 Merge main into feat/plugin-filter-inputs 2026-11-08 19:06:11 -08:00
dotCooCoo 4941b377f9 Merge main into fix/plugin-grant-app-approval 2026-11-08 19:01:49 -08:00
dotCooCoo e1281c0266 Merge main into fix/plugin-stale-grant 2026-11-08 18:59:23 -08:00
dotCooCoo 63a483492b Plug-ins: a grants file that can't be read keeps the grant it may hold
A grants file that can't be opened or read may name the old module once
it reads again. plugin.remove and an install of a module listing no
permissions now fail on it and keep the module. A file over 1 MiB or one
that doesn't hold a list still approves nothing and doesn't stop them.
2026-11-08 18:59:22 -08:00
dotCooCoo bd5ea2a1bf Merge main into feat/plugin-effect-paint 2026-11-08 17:59:33 -08:00
dotCooCoo e82ed4642e Merge main into feat/plugin-filter-inputs 2026-11-08 17:50:28 -08:00
dotCooCoo 969d1cb725 Merge main into fix/plugin-stale-grant 2026-11-08 17:45:48 -08:00
dotCooCoo d6a91b6142 Merge main into feat/plugin-effect-paint 2026-11-08 17:26:35 -08:00
dotCooCoo b79e89de1d Plug-ins: a grant that can't be removed keeps the module it covers
plugin.install of a module that lists no permissions ignored the result
of removing the id's grant. When grants.json could not be updated, the
formerly approved bytes could still be installed without approval. The
store now gets the old module back, so the grant and the module it
covers stay together. The new module stays when the old one can't be
read back and when grants.json can't be read, since such a file approves
nothing. Deleting the last grant now reports a failure.

plugin.remove had the same fault. It now moves the settings and cache
aside and removes the grant before anything is deleted. When the grant
can't be removed, the folder is moved back, the command returns an
error and the plug-in stays installed.
2026-11-08 17:25:09 -08:00
BFlat b652599548 Merge pull request #6264 from dotCooCoo/feat/plugin-importers
Plug-ins: importer plug-ins open file types VectorCraft does not read
2026-11-09 09:24:07 +08:00
dotCooCoo 80558fa273 Merge main into feat/plugin-filter-inputs 2026-11-08 17:09:58 -08:00
dotCooCoo 918ef940cb Merge main into feat/plugin-importers 2026-11-08 16:45:20 -08:00
dotCooCoo 727a611ef7 Merge fix/plugin-grant-module-hash into fix/plugin-grant-app-approval 2026-11-08 16:35:45 -08:00
dotCooCoo efe204bb55 Merge main into feat/plugin-filter-inputs 2026-11-08 16:20:15 -08:00
dotCooCoo 627a40ec36 Merge fix/plugin-grant-module-hash into fix/plugin-grant-app-approval 2026-11-08 16:19:43 -08:00
dotCooCoo 9b0ab19e3a Merge main into fix/plugin-stale-grant 2026-11-08 16:05:50 -08:00
dotCooCoo 9511d24ff6 Merge fix/plugin-grant-module-hash into fix/plugin-stale-grant 2026-11-08 15:56:42 -08:00
dotCooCoo 988da4538a Merge main into fix/plugin-grant-module-hash 2026-11-08 15:55:53 -08:00
dotCooCoo 027dc8894d Plug-ins: a filter run given more read-only objects than an output lists fails up front
An output lists at most 100,000 objects and must return every read-only object, so an input with
more than that many is now an object-budget limit before the plug-in starts.
2026-11-08 15:37:36 -08:00
dotCooCoo 5540a5e797 Plug-ins: an oversized path fails a filter run, and objects that clip stay out of its input
A path or compound path that doesn't fit what is left of the input now fails the run with the
runtime's own "selection is too large to hand over" limit before the rest of the selection is
written, instead of later read-only objects reporting a size error. Objects that clip a clip
group are left out for every filter: compound paths and paths inside a group that clips one
reached filters before, as type, images and symbol instances did.
2026-11-08 15:37:36 -08:00
dotCooCoo 6326d1c4ff Plug-ins: the install comment states what a failed module restore leaves 2026-11-08 15:07:20 -08:00
dotCooCoo 64c7c2703e Merge main into feat/plugin-menu-commands 2026-11-08 14:41:13 -08:00
dotCooCoo 5366b9e323 Merge main into feat/plugin-filter-inputs 2026-11-08 14:19:35 -08:00
dotCooCoo 11703bd350 Merge main into feat/plugin-effect-paint 2026-11-08 12:56:42 -08:00
dotCooCoo a717e34f02 Merge main into fix/plugin-grant-module-hash 2026-11-08 12:55:59 -08:00
dotCooCoo 9518dc0e3a Merge main into feat/plugin-importers 2026-11-08 12:33:17 -08:00