Security: Require course membership for auto-generated survey invitations - #8939
Merged
AngelFQC merged 3 commits intoAug 25, 2026
Merged
Conversation
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.
Answering a survey with invitationCode=auto created an invitation for the current user without checking that they belong to the course. Membership of the course or session context is now required, through a new UserHelper::isMemberOfCurrentCourse() that both survey providers call. A last commit also drops the required flag on the Get operation's cid, which is what made surveys unusable outside a course context.
Verified against a running instance, and the exploitable path is narrower than the report suggests. With cid in the query the course context listener already answers first: an unenrolled student gets 403 on a course whose visibility is REGISTERED, and on an OPEN_PLATFORM course they hold the course student role by design, before and after this change. What was genuinely unprotected is the POST, where cid is optional and the course falls back to the survey's own course, so no context check ran: an unenrolled student could POST /api/survey/answer/{id}?invitationCode=auto with no cid against a closed course and get 201, answer and invitation persisted. That now returns 403 with nothing written, while an enrolled student answering with cid still gets 201.
The cid change fixes a separate, pre-existing bug the same tests surfaced: an invitee holding a real invitation code could not read the survey at all on a closed course — without cid the Get died in parameter validation (422), and with cid the context listener denied the course (403), leaving only a blind POST. With cid optional the provider resolves the course from the survey, as the Post already documented, and the invitation is what authorises the read. Checked on a closed course with an unenrolled invitee: their own code returns the questions with or without cid, another user's code returns 403, no code and no invitation returns 403, invitationCode=auto returns 403 through the new guard, preview stays teacher-only, and an enrolled member with cid is unaffected.
One point for review: the anonymous link keeps working as before, since that flow exists to be shared outside the course. The same unguarded auto branch in SurveyMeetingProvider is fixed here as well.
Refs GHSA-3c47-mrf9-6w4f