Files
windmill/backend/windmill-api-flows
Ruben Fiszel 74f15e0e51 fix: bound what a builder-authored app can invoke at run time
Round 2 caught that the app-side check was bound to the wrong surface. It
checked the policy triggerables, on the premise that they are what
`execute_component` resolves. That holds for `rawscript/<sha>`, which is why the
raw-script refusal binds, but not for `script/`/`flow/`: in `ExecutionMode::Viewer`
run mode falls back to a default triggerable for any such path, so a path absent
from the map is invocable rather than forbidden, and the job runs as the viewer.
A builder could therefore ship an app pointing at a runnable nobody granted them,
and an admin who merely opened it would run it as themselves.

- `ExecutionMode::Viewer` is refused for a builder-authored app. `Publisher` and
  `Anonymous` have no fallback, so the triggerables are exhaustive for them and
  the deploy-time checks below are an authorization boundary rather than advice.
- The value's `runnableByPath` entries are authorized too. They are a separate
  surface from the policy: the deployed bundle resolves a `runnable_id` against
  the stored `runnables` and sends that path, so an app with an empty
  triggerables map still reaches one.

Also from round 2: `check_flow_is_composition_only`'s doc now states what it
actually returns, and the flow-side refs are deduped, so a flow stepping through
one script thirty times stops issuing thirty round trips on every write, preview
and dependency job.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 14:00:17 +00:00
..