Skip to content

The confirmation gate ignores goto, so a mutating URL runs unconfirmed #3

Description

@royalpinto007

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:

  1. Advisory, as today. Cheap, and the README should say the recipe author
    is responsible for labelling.
  2. 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.
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions