Skip to content

fix(message): stop repeating private member fields in reaction badges - #63

Merged
yufoxda merged 4 commits into
developfrom
refactor/reaction-payload-pii
Jul 27, 2026
Merged

fix(message): stop repeating private member fields in reaction badges#63
yufoxda merged 4 commits into
developfrom
refactor/reaction-payload-pii

Conversation

@yufoxda

@yufoxda yufoxda commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

⚠️ スタックPRです。ベースは refactor/community-neutral-types(#62)。#61 → #62 → 本PR の順にマージしてください。

経緯 — 当初のレビュー指摘を訂正します

レビューで「リアクション取得が6テーブルを手書きjoinして21列引いている。許可リストビューを入れるべき」と指摘しましたが、コードを詳しく読んだ結果その案は誤りでした。

CSV出力(buildReactionMembersCsv)を見ると、学籍番号・緊急連絡先・アレルギー詳細は意図的にエクスポートされている運用機能です。イベント主催者が参加者の緊急連絡先やアレルギーを把握するのは正当な要件で、許可リストでこれらを落とせば実在する機能を壊します

実際の問題

代わりに、より確かな問題が見つかりました。

レスポンス 中身 クライアントの利用
members 全PII テーブル + CSV ✅ 必要
reactions[].users 全PIIの複製 表示名のみ ❌ 不要

buildEventDetailViewModel は badges を reaction.users.map(getDisplayName) としか使っていません。つまり緊急連絡先・学籍番号・アレルギー情報が、表示名を出すためだけに、リアクション数ぶん重複してネットワークに流れていました

1人が3種類の絵文字で反応すると、その人の私的情報が 4回(badges 3回 + members 1回)直列化されます。送信量が「部員数」ではなく「リアクション数」に比例して増える構造です。

変更内容

  • ReactionParticipant 型を追加(discordUserId / discordUsername / discordGlobalName / memberName / displayName)
  • reactions[].users をこの型に変更。OpenAPI スキーマも分離
  • members従来どおり完全なまま(CSV出力の要件を維持)
  • フロントの型も実態に合わせて追従

検証

対象 結果
community 37 pass / 1 skip、tsc --noEmit クリーン
frontend 25 pass、型チェッククリーン、lint エラー0、本番ビルド成功

回帰テストを追加しました: private member fields are never repeated inside the reaction badges — badges を直列化し、緊急連絡先・学籍番号・学生メール・アカウントメールが含まれないことを検証します。

あわせて members 側に緊急連絡先と学籍番号が残っていることも検証し、CSV機能を壊していないことを担保しています。

残る改善余地(別PR)

このエンドポイントは管理者限定(403)ですが、名簿ビューのようなDB層の多層防御(security_barrier + 閲覧者の資格チェック)はありません。ハンドラの管理者チェックが唯一の防壁です。DBビュー化は移行を伴うため、別途検討が妥当です。

🤖 Generated with Claude Code

The port was generic in name only. Its methods returned DiscordGuildMembership,
DiscordMessage and DiscordReactionUser, and every identifier was validated
against the Discord snowflake format, so the identifier regex reached callers
that have no reason to know what a snowflake is. Replacing the provider would
have meant editing the interface and both api_v0 services rather than swapping
an adapter.

The port now speaks CommunityRole, CommunityMembership, CommunityMessage,
CommunityReactionUser and CommunityAccountProfile, treats identifiers as opaque
strings, and drops the Discord message length limit. Discord's snowflake
format, its 2000-character limit, and the global_name field it returns are
refinements applied in discord/schema.ts, which is the only layer that issues
those values. The provider-specific field name is mapped to displayName at the
adapter boundary, matching the provider_display_name column it is stored in.

Behaviour is unchanged: the adapter still rejects a malformed provider response
and a guild member response for another user, both of which depend on the
snowflake assertion that moved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@yufoxda
yufoxda force-pushed the refactor/community-neutral-types branch from 636e09e to c18ece5 Compare July 27, 2026 07:45
The reaction summary returned the complete member record — student ID, student
email, emergency contact, insurance and allergy details — inside every
reaction's user list as well as in `members`. A member who reacted with three
emoji had those fields serialised four times, so the private data on the wire
grew with the number of reactions rather than the number of members.

The badges only ever rendered names: the client maps that list through
getDisplayName and reads nothing else from it. They now carry a
ReactionParticipant with the identity and name fields, while `members` keeps
the full record the admin table and its CSV export need, including the
emergency contact and allergy details an organiser relies on.

Adds a regression test asserting no private field appears in the badge payload.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@yufoxda
yufoxda force-pushed the refactor/reaction-payload-pii branch from 4299e4d to f2650c9 Compare July 27, 2026 07:46
@yufoxda
yufoxda changed the base branch from refactor/community-neutral-types to develop July 27, 2026 12:53
@yufoxda
yufoxda merged commit c11eda3 into develop Jul 27, 2026
3 checks passed
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