What
hasWriteStep decides whether a recipe needs confirm: true. It looks at one
kind of step:
export function hasWriteStep(recipe: Recipe): boolean {
return (recipe.steps ?? []).some(
(s) => s.action === "click" && s.write === true,
);
}
A goto is never considered. So this recipe runs without confirmation:
{
"url": "https://dashboard.example.com",
"steps": [
{ "action": "goto", "url": "https://dashboard.example.com/orders/123/cancel?confirm=1" }
]
}
Plenty of real dashboards still mutate on a GET: cancel links, /logout,
unsubscribe URLs, "mark as read", one-click approvals in emailed links. The
README says Webhands "refuses any write action unless you explicitly confirm
it", and for navigation that is not currently true.
The deeper version
write is opt-in and declared by whoever wrote the recipe. The gate protects
against writes somebody remembered to label. A recipe that comes from an agent,
or from a human in a hurry, can perform a destructive click with write simply
absent and the gate stays quiet.
Worth deciding which of these Webhands wants to be:
- Advisory, as today. Cheap, and the README should say the recipe author
is responsible for labelling.
- Conservative by default. Anything that is not a known-safe action
(waitFor, extract, a goto to the recipe's own entry URL) counts as a
write. Confirmation is required unless the recipe is provably read-only.
- Enforced at the browser layer, refusing non-idempotent navigations and
form submissions unless confirmed, so the label is not the only line of
defence.
Option 2 is probably the right trade for a tool whose selling point is that it
refuses to act without permission. It makes the safe path the default and puts
the burden on the recipe to prove it is read-only.
Minimum fix
Whatever is chosen, goto steps beyond the entry URL should either be treated
as writes or carry the same explicit write marking clicks do, and the README
sentence should match whatever the code actually does.
What
hasWriteStepdecides whether a recipe needsconfirm: true. It looks at onekind of step:
A
gotois never considered. So this recipe runs without confirmation:{ "url": "https://dashboard.example.com", "steps": [ { "action": "goto", "url": "https://dashboard.example.com/orders/123/cancel?confirm=1" } ] }Plenty of real dashboards still mutate on a GET: cancel links,
/logout,unsubscribe URLs, "mark as read", one-click approvals in emailed links. The
README says Webhands "refuses any write action unless you explicitly confirm
it", and for navigation that is not currently true.
The deeper version
writeis opt-in and declared by whoever wrote the recipe. The gate protectsagainst writes somebody remembered to label. A recipe that comes from an agent,
or from a human in a hurry, can perform a destructive click with
writesimplyabsent and the gate stays quiet.
Worth deciding which of these Webhands wants to be:
is responsible for labelling.
(
waitFor,extract, agototo the recipe's own entry URL) counts as awrite. Confirmation is required unless the recipe is provably read-only.
form submissions unless confirmed, so the label is not the only line of
defence.
Option 2 is probably the right trade for a tool whose selling point is that it
refuses to act without permission. It makes the safe path the default and puts
the burden on the recipe to prove it is read-only.
Minimum fix
Whatever is chosen,
gotosteps beyond the entry URL should either be treatedas writes or carry the same explicit
writemarking clicks do, and the READMEsentence should match whatever the code actually does.