Pre-alpha4 audit fixes: interaction token model, single binding element, and docs

Fixes the confirmed findings of the pre-alpha4 audit, almost all in the new orchestra_interaction and orchestra_interaction_webform submodules (never audited before).

orchestra_interaction (security + correctness)

  • Capability token is now self-encoding and time-limited: one opaque string ("instance.expiry.signature") that names its own instance and carries its expiry, both bound into the HMAC. It cannot be forged, repointed at another instance, or have its expiry extended; a leaked link stops working once it expires (no longer a permanent bearer credential). Findings: token never expires, redirect leaks a permanent token.
  • The state-mutating signal is no longer a bare GET: a visitor outcome (cancel, back) is resumed through a confirm form, shown on GET and resumed only on its POST, so a prefetch, link preview, or scanner cannot resume a run.
  • The dispatcher no longer 500s when a node's declared interaction plugin is uninstalled: the resolver treats a missing plugin as "no interaction" and shows the neutral message.
  • parkedToken() resolves deterministically (lowest id); the single-interactive-wait-per-instance model is documented.
  • RedirectInteraction warns that "pass a return link" discloses the (now time-limited) token to the destination.

orchestra_interaction_webform

  • A single "Orchestra interaction" Webform element binds a form to its instance: it auto-captures the self-encoding token, so the builder adds ONE element instead of hand-adding the instance and token hidden fields, and the handler needs no element-key configuration. The handler binds the submission on first save once the token validates, and trusts that binding on re-edits.
  • latestSubmission() is scoped to the step's own webform, so two webform steps no longer reopen each other's submission.

Tests

  • InteractionTokenTest: issue/resolve, expiry, tamper rejection.
  • InteractionAccessTest: the public dispatcher token gate (missing/wrong/cross-instance) and the confirm form's outcome allow-list.
  • InteractionResolverTest: the missing-plugin guard.
  • OrchestraResumeHandlerTest: bind-and-resume, wrong-token rejection, re-edit no-op.
  • UserTaskHandoffTest: the inbox signed-URL expiry-rejection branch.

Docs

  • New "External interaction" page; both submodules added to the module tables and mkdocs nav.
  • Script task type renamed to Pass-through in concepts, maestro and roadmap.

The one audit finding that was refuted (a timeout duration variable name that happens to parse as a literal) is intended behavior and left as-is.

Verified locally: phpcs (Drupal, DrupalPractice) and phpstan clean; orchestra_interaction + orchestra_interaction_webform kernel suites green (16 tests), inbox handoff test green.

Merge request reports

Loading