fix(workflow): stop leaking peer share tokens from participant session API#6241
Merged
fix(workflow): stop leaking peer share tokens from participant session API#6241
Conversation
…n API The participant-facing endpoints under /api/v1/workflow/participant/* were serializing every peer's shareToken in the WorkflowSessionResponse. A caller holding one valid participant bearer token could read peer tokens from participants[*].shareToken and then call submit-signature or decline as any other participant in the same workflow. Add an includeShareToken(s) flag to WorkflowMapper, defaulting to true so the owner-facing SigningSessionController (which legitimately needs tokens for share-link distribution) is unchanged. Pass false from the four WorkflowParticipantController response sites, which closes the disclosure chain. Caller's own token comes from the URL, so no UX regression. Refs: GHSA-qgg6-mxw4-xg62
The previous commit makes the four participant-facing endpoints emit shareToken: null in their JSON responses (peer-token disclosure fix). The TypeScript interface still declared shareToken: string, which would silently mislead any future consumer into a non-null assertion. Widen to string | null and document why. No runtime change — no consumer currently reads this field; this only makes the type honest. Refs: GHSA-qgg6-mxw4-xg62
Frooodle
approved these changes
Apr 27, 2026
jbrunton96
approved these changes
Apr 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description of Changes
What changed
GET /api/v1/workflow/participant/session?token=<participant_token>was returning the fullWorkflowSessionviaWorkflowMapper.toResponse(session), which serialized every participant'sshareTokenintoparticipants[*].shareToken. Combined withsubmit-signatureanddeclineresolving the target participant solely by token, one valid participant token was enough to act as any peer in the same workflow (force them intoSIGNEDorDECLINED).This PR adds an
includeShareToken(s)flag toWorkflowMapper, defaulting totrueso existing callers (including the owner-facingSigningSessionController, which legitimately needs tokens for share-link distribution) keep their behavior. The four response sites inWorkflowParticipantControllernow passfalse:getSessionByTokengetParticipantDetailssubmitSignaturedeclineParticipationThe participant's own bearer token comes from the URL on the frontend, so stripping it from these responses is UX-neutral. Verified by grepping every consumer of
WorkflowSessionResponse.participants— no frontend code readsparticipants[i].shareToken.Why
CVSS 7.6 (High) —
AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:L. Closing the disclosure breaks the impersonation chain at step 2 of the reporter's PoC.Files
app/proprietary/src/main/java/.../workflow/util/WorkflowMapper.java— new overloads withincludeShareToken(s); legacy signatures delegate withtrue.app/proprietary/src/main/java/.../workflow/controller/WorkflowParticipantController.java— passfalseat four call sites.app/proprietary/src/test/java/.../workflow/util/WorkflowMapperShareTokenTest.java— 5 new tests covering both modes.frontend/src/proprietary/services/workflowService.ts— widenParticipantResponse.shareTokentostring | nullso the TS type matches the new wire payload.SigningSessionControlleris intentionally untouched.Tests
WorkflowMapperShareTokenTest(5 tests) — legacy overload still emits tokens (owner path), new flag strips tokens for all participants including peers, other fields survive.:proprietary:test— 10 workflow tests still green with the change in place.Follow-up:
ParticipantResponse.shareTokenmay be a feature gapWhile auditing this fix I confirmed that nothing in this repo currently reads
participant.shareTokenfrom any response — owner side or participant side:participant.shareToken/p.shareTokenacrossfrontend/src/. The owner-sideSessionDetailWorkbenchViewandSessionActionsPaneliteratesession.participantsfor status only. There is no "copy share link" or "resend invite" UI.participant.getShareToken()call sites are the mapper that puts it on the wire, an expiry log line inUnifiedAccessControlService, and the repositoryfindByShareTokenlookup. No notification/email service uses it. ThecreateSessiondocstring says it "distributes share links, and optionally notifies participants", but I cannot find code that does either.So the field is dead weight as far as this codebase is concerned. Three plausible explanations:
POST /api/v1/security/cert-sign/sessionsand reading the tokens to send invitations themselves. Removing it would silently break them.I deliberately did not drop the field from owner responses in this PR — the blast radius for #2 is unclear, and the security goal (peer-token enumeration) is already addressed by the participant-side strip.
Suggested follow-up: confirm whether owner-side share-link distribution is a feature gap or external-consumer concern, then either build the UI or drop the field from
ParticipantResponseentirely.Refs: GHSA-qgg6-mxw4-xg62
Checklist
General
Testing