Found in the 1.0.0-alpha9 pre-release audit (finding I2). Info-level: the impact is transient and UI-only (a status message dropped); no engine state, tokens, work items, or persisted data are affected. Filing it because the retract mechanism is fragile and worth hardening.

Where

modules/orchestra_interaction/src/ContextMessages.php: display() posts the context to the messenger and records it in $posted; retract() withdraws it.

What happens

display() posts each resolved context message to the messenger and records its rendered string under $posted, keyed by severity. retract() then calls the messenger's deleteByType() for that severity, which drains every flash message of that severity, and re-adds only those whose string does not equal an entry in $posted. Message identity is therefore purely rendered-string plus severity.

Two consequences follow:

  • A coincidentally identical message from any other source at the same severity is dropped and never re-added, which contradicts the retract() docblock claim that "other modules' messages are left untouched."
  • ContextMessages is a request-shared singleton and $posted accumulates across every display() call, so two orchestra webforms rendered on one page share $posted: submitting one deletes all status messages of that severity and suppresses re-adding the other form's still-relevant context.

Why it is low impact

Both sub-scenarios are logically reproducible, but the effect is a transient flash message dropped, nothing more. The collision window is small: the message format is distinctive (a label and value line) and core de-dups identical adds, and pages with two orchestra interaction forms are unusual. ContextMessagesTest exercises retract() with a distinct unrelated string but does not cover the identical-string collision.

Fix direction

Withdraw only the messages this service posted, by tracking a per-message identity rather than the rendered string: keep the posted message objects (or a stable per-message marker) for the request and remove exactly those, instead of deleteByType() plus a string-compared re-add. Add tests covering the identical-string collision (an unrelated message with the same text and severity must survive retract()) and the two-forms-on-one-page case.

Issue fork orchestra-3608544

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mably created an issue. See original summary.

mably’s picture

Status: Active » Needs review

Opened MR !293 against 1.x.

retract() is now scoped per step: display() and retract() key their record by (instance, token), so completing one step withdraws only its own postings and withdraws at most as many of each rendered string as it posted; another step's context (a second orchestra form on the page) and other modules' messages survive. A new ContextMessagesTest case covers this and was verified to fail without the scoping.

One limit is documented rather than fixed: Messenger::addMessage normalizes every message to a plain Markup and collapses equal strings, so a per-message object marker cannot survive the API and a byte-identical string from another source cannot be told apart. The per-step, count-bounded scoping is the strongest guarantee the public messenger API allows. Local run: ContextMessagesTest (7) and OrchestraInteractionHandlerTest (15) green, phpcs and cspell clean. Setting to Needs review pending CI.

  • mably committed 2a4bbbde on 1.x
    fix: #3608544 ContextMessages::retract() withdraws messenger messages by...
mably’s picture

Status: Needs review » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.