Skip to content

fix(backend): close D4 (anonymous vote results) and D5 (waitlisted submission read) - #195

Merged
caviri merged 2 commits into
developfrom
fix/feedback-security-d4-d5-20260819
Aug 19, 2026
Merged

fix(backend): close D4 (anonymous vote results) and D5 (waitlisted submission read)#195
caviri merged 2 commits into
developfrom
fix/feedback-security-d4-d5-20260819

Conversation

@caviri

@caviri caviri commented Aug 19, 2026

Copy link
Copy Markdown
Member

Two more backend authorization holes from the audit, same shape as D1/D2/D3.

D4 — anonymous could read vote results

ListVoteResults used RequireSubject, which admits the anonymous subject, so an unauthenticated caller could read placements the moment VIEW_RESULTS was open — even though the handler's own comment says "any signed-in user". Switched to RequireUser. The capability gate still lets organisers review a tally before publishing, and the only caller is the authenticated /my/hackathon/[id]/voting route, so nothing public breaks. (The VIEW_RESULTS-defaults-open half is left as-is: all six capabilities seed open by documented design — "a hackathon states its policy explicitly" — and the organiser can close it in the panel.)

D5 — waitlisted registrants read every team's submission

GetSubmission/ListSubmissions fall back to a hackathon-wide Submission:Read so participants can read every team's work to vote on it. But Join grants the Member role even to waitlisted registrants (so they can propose), and Member carries that hackathon-wide read — so an unapproved registrant could read all submissions. A composition nobody chose.

Fix: a shared authorizeSubmissionRead helper — team-scoped read unchanged; the hackathon-wide path now excludes callers holding a waitlisted participant row. Confirmed participants (no waiting row) and organizers/admins (who reach it via Owner/admin, not a participant row) are unaffected.

Tests (both pinned)

  • submission_read_gate_test.go — a waitlisted joiner gets PermissionDenied reading another team's submission; approval lifts it to NotFound (past the gate) — a different code proves the gate opened.
  • D4 relies on RequireUser, already covered by require_user_test.go.

Verification (devcontainer, fast tier)

  • go build ./... clean · go test ./internal/service/... ./internal/middleware/... OK (D5's new spec passes) · gofmt -l empty

caviri added 2 commits August 19, 2026 03:24
ListVoteResults used RequireSubject, which admits the anonymous subject, so an
unauthenticated caller could read placements the moment VIEW_RESULTS was open —
the handler's own comment says "any signed-in user". Switch to RequireUser. The
capability gate still lets organisers review a tally before publishing, and the
only caller is the authenticated /my/hackathon/[id]/voting route, so nothing
public breaks.
…ission read (D5)

GetSubmission/ListSubmissions let a team member read their own team's submission
(team-scoped) and fell back to a hackathon-wide Submission:Read so participants
can read every team's work to vote on it. But Join grants the Member role even
to waitlisted registrants (so they can propose), and Member carries that
hackathon-wide read — so an unapproved registrant could read every team's
submissions, a composition nobody chose.

Extract a shared authorizeSubmissionRead helper: team-scoped read is unchanged,
and the hackathon-wide path now excludes callers holding a waitlisted
participant row. Confirmed participants (no waiting row) and organizers/admins
(who reach it via Owner/admin, not a participant row) are unaffected.

Pinned by submission_read_gate_test.go: a waitlisted joiner gets PermissionDenied
reading another team's submission, and approval lifts it to NotFound (past the
gate, no submission) — a different code proving the gate opened.
@caviri
caviri merged commit 86b8b71 into develop Aug 19, 2026
1 check passed
@caviri
caviri deleted the fix/feedback-security-d4-d5-20260819 branch August 19, 2026 01:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant