fix: publishing a directory takes ownership, not write (PUT-1619) (#3654)
Maintain Release Merge PR / update-release-pr (push) Canceled after 0s
Notify HeyPuter / notify (push) Canceled after 0s
release-please / release-please (push) Canceled after 0s

Creating a hosted subdomain gated `root_dir` on `write`, and hosting serves
everything under that directory with the ACL deliberately bypassed. So a
recipient of a `write` share could point a `*.puter.site` subdomain at the
owner's folder and make the subtree world-readable — continuously, covering
files the owner added later, with the row under the recipient's account where
nothing the owner can list would show it. `update` had the same gate for a
changed `root_dir`.

`#checkPublishAccess` now decides both: the actor's own tree still takes
`write`, anyone else's takes `manage` — "Can edit & share", the level that
delegates the decision.

Keyed on who owns the entry rather than asking for `manage` outright, which is
what the ticket proposed. `manage`'s is-owner implicator declines to answer for
app actors, so a flat `manage` would refuse every app publishing a directory
its user handed it, with no way for the app to obtain the grant. The write
check still runs first — it is what masks a directory the caller cannot see as
a 404 — and `manage` satisfies every lower mode, so the order costs a
manage-holder nothing.

The GUI's Publish As Website item reuses the own-it-or-`manage` answer it
already computes for sharing, so it is not offered where this would refuse.
Docs state the rule on `hosting.create()` and in `share()`'s level list.

Regression tests fail without the driver change: a write-share recipient is
refused on create and on repointing an existing subdomain, while `manage` and
the actor's own directory are accepted.
This commit is contained in:
Juan Fernando Castro
2026-08-29 01:46:14 -07:00
committed by GitHub
parent d5e9c14b0d
commit 81d700d146
5 changed files with 150 additions and 5 deletions
+2 -2
View File
@@ -38,8 +38,8 @@ Who to share with. A string containing `@` is treated as an email address, and a
How much access to grant. Defaults to `'read'`.
- `'read'` - Read the item.
- `'write'` - Read and change the item. Does **not** allow re-sharing it.
- `'manage'` - Everything `'write'` allows, plus re-sharing the item with other people.
- `'write'` - Read and change the item. Does **not** allow re-sharing it, or publishing a directory as a website.
- `'manage'` - Everything `'write'` allows, plus re-sharing the item with other people and publishing a shared directory as a website.
- `'list'`, `'see'` - Weaker than `read`; useful for making an item discoverable without exposing its contents.
#### `options` (Object) (optional)
+2
View File
@@ -23,6 +23,8 @@ A string containing the name of the subdomain you want to create.
A string containing the path to the directory you want to serve.
The directory must be one you own. Hosting serves everything under it publicly, including files added later, so a directory someone shared with you can only be published if they gave you `manage` access — [`share()`](/FS/share/) calls that level "Can edit & share".
#### `options` (Object) (optional)
Alternative way to create hosting via options.