Diver
Investigation Report Document No. DV-2026-RPT-001
Version 1.0
Client-side bundle analysis

ticketdive.com
メールアドレス入力フォームの
バリデーション仕様

Next.js production bundle を取得・整形し、zod スキーマと React Hook Form の振る舞いを特定。
対象: /account/email/signup。実機検証を含む。

01

調査の目的と結論

ticketdive.com (Next.js / SPA) の「メールアドレス入力フォーム」が submit 時にどんなバリデーションを行っているかを、 production の JS chunk を静的解析し、ブラウザ上で実機検証して確定させる。

Key Findings

メール検証は /signup/account/email で別実装。共通して zod スキーマで 必須・形式・+拒否 を行い、 /signup はさらに 176 件のドメイン typo ブラックリストblur 時の入力値エコー UX を持つ。 ドメイン照合は完全一致 (Levenshtein 等のあいまい一致は無し)。

02

検証対象と手法

対象

ページファイル役割
/account/email pages/account/email-a5b27c8de29b72b3.js ログイン後のメールアドレス変更
/signup pages/signup-df663788b5bbdc5e.js 新規会員登録

手法

  • Bundle 取得該当ページを読み込んだ際に <script src> から各 page chunk を列挙し、curl で取得。
  • 整形prettier --parser babel で minified を可読化。
  • 静的解析zod schema・register()onBlur・refine の引数を読み取り。
  • 実機検証本番フォームに既知の typo / 大文字 / + 入りメールを入力し、表示されるエラー文言と DOM 状態を確認。
03

/account/email の仕様

zod スキーマ (再整形)

email: z.string()
  .min(1,   t("form.required"))      // 必須
  .email(   t("form.emailValid"))    // RFC 風の形式チェック
  .refine(e => !e.includes("+"),     // ★ "+" を 1 文字でも含むと NG
          { message: t("form.emailValid") }),

password: z.string().min(1, t("form.required")),
verificationCode: z.string()
  .length(6, t("form.verificationCodeLength"))
  .regex(/^\d{6}$/, t("form.verificationCodeFormat")),

HTML 属性と検証タイミング

  • type"text" (type="email" ではない)。inputMode="email"autoComplete="email" はソフトキーボード等のヒントのみ。
  • HTML5 pattern[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$ が付与されるが、送信は onClick: handleSubmit() 経由で native form submit ではない。よって pattern 制約検証は走らない (実機で VALID.User@Example.COM も通過することを確認)。
  • 検証タイミングreact-hook-form のデフォルト mode: onSubmit。入力中・blur 時は何も走らない。
  • 典型エラー文言必須 → 「必須項目です」 / 形式・+「有効なメールアドレスを入力してください」

実機検証 (個人情報マスク済み)

入力値結果備考
test+demo@example.com Rejected refine による "+" 拒否が発火
VALID.User@Example.COM Passed 大文字混じり可。HTML pattern は実質効いていない
test@gmial.com Passed このフォームに typo ブラックリストは無い
04

/signup の仕様 (本題)

signup ページにはメール検証が 2 系統ある。①は submit 時の zod refine、②は blur 時の純粋な UX (typo 検出ではない再確認プロンプト)。

① submit 時 — typo ブラックリスト

固定リスト z をモジュール初期化時に Object.values({...}).flat() でフラット化し、refine で ローカル部以降を完全一致 比較する。

const z = Object.values({
  icloud:   [/* @icluod.com, @icloud.cmo, ... 30件 */],
  gmail:    [/* @gamil.com,  @gmial.com,  ... 31件 */],
  docomo:   [/* @docmo.ne.jp, @docom.ne.jp, ... 29件 */],
  ezweb:    [/* @ezwbe.ne.jp, @ezweb.ne.jo, ... 29件 */],
  softbank: [/* @i.softabnk.jp, @i.softbakn.jp, ... 27件 */],
  yahoo:    [/* @yhoo.co.jp, @yaho.co.jp, ... 30件 */],
}).flat();

email: z.string()
  .min(1, "form.required")
  .email("form.validEmail")
  .refine(e => {
    const idx    = e.lastIndexOf("@");
    const domain = idx === -1 ? "" : e.slice(idx).toLowerCase();
    return !z.includes(domain) && !e.includes("+");
  }, { message: "form.validEmail" });
  • 照合方式完全一致のみ。Levenshtein 距離やキーボード近傍などの fuzzy match は無し。リストに無い typo (例: @gaamail.com) は通過する。
  • 正規化toLowerCase() のみ。前後空白の trim はしない。
  • エラー文言typo ヒット時も「有効なメールアドレスを入力してください」(form.validEmail)。ユーザーには typo として識別できる文言ではない。

② blur 時 — 入力値エコー UX

ここがユーザー体感での 「メールアドレスに誤りがないか確認してください。」 の出所。バリデーションではなく、入力した値をそのまま赤色で再描画する確認プロンプトである。

const [R, setR] = useState("");

<Input
  {...register("email", {
    onBlur: e => setR(e.currentTarget.value),
  })}
  type="email"
  ...
/>

{R.trim().length > 0 && (
  <Stack>
    <Text fontWeight={600}>
      {t("form.emailConfirmationNote")}      {/* ← 「メールアドレスに誤りがないか確認してください。」 */}
    </Text>
    <Text color="red1" fontWeight={600}>
      {R}                                     {/* ← 入力値を赤で再表示 */}
    </Text>
  </Stack>
)}
  • 発火条件email 入力欄を blur した時。中身が空白以外なら必ず表示。
  • typo 検出ではない正しい foo@gmail.com でも、ブラックリスト外の typo foo@gaamail.com でも、同じプロンプトが出る。
  • 表示位置入力欄の下、error 表示とは別の領域。
signup フォームに sample@gaamail.com を入力し blur した直後の状態。「メールアドレスに誤りがないか確認してください。」のテキスト直下に、入力値そのものが赤字で再表示されている。
Fig. 01 実機検証: sample@gaamail.com を入力 → blur 直後。 「メールアドレスに誤りがないか確認してください。」+ 入力値の赤字再表示が出る。 これは typo 検出ではなく無条件で発火する確認 UX
Misreadable behaviour

このエコー UX は「typo を検出して指摘している」ように見える。実際は単に再描画しているだけなので、 @gaamail.com 等のブラックリスト外 typo は submit すれば普通に通る

05

ドメイン typo ブラックリスト全件

signup chunk に含まれる固定リスト。先頭の @ は省略表記。合計 176 件

icloud.com 30 entries

  • @icluod.com
  • @icloud.cmo
  • @iclud.com
  • @iclod.com
  • @icloud.co
  • @icloud.ocm
  • @iclooud.com
  • @icloud.con
  • @iclou.com
  • @ilcoud.com
  • @icloud.xom
  • @iclodu.com
  • @icloud.vom
  • @icloud.clm
  • @ickoud.com
  • @icloud.comn
  • @icloud.cok
  • @icloud.dom
  • @iciood.com
  • @icloud.come
  • @iccloud.com
  • @iclpud.com
  • @icloud.cop
  • @icloud.cam
  • @icloud.coj
  • @icloud.oc
  • @icloid.com
  • @iclous.com
  • @icloud.cim
  • @iclous.cow

gmail.com 31 entries

  • @gamil.com
  • @gmial.com
  • @gmaill.com
  • @gmail.cmo
  • @gmail.co
  • @gail.com
  • @gmai.com
  • @gmali.com
  • @gml.com
  • @gmaol.com
  • @gmail.con
  • @gmaio.com
  • @gimail.com
  • @gamil.con
  • @gmail.xom
  • @gmqil.com
  • @gmaik.com
  • @gmail.vom
  • @gnail.com
  • @gmail.clm
  • @gmsil.com
  • @gmaul.com
  • @gmail.coj
  • @gma.com
  • @gmail.ocm
  • @gmal.com
  • @gmail.cim
  • @gmall.com
  • @gmail.cok
  • @hmail.com
  • @gmail.cow

docomo.ne.jp 29 entries

  • @docmo.ne.jp
  • @dcom.ne.jp
  • @docmoo.ne.jp
  • @docom.ne.jp
  • @odcomo.ne.jp
  • @docoom.ne.jp
  • @doco.ne.jp
  • @doocmo.ne.jp
  • @dokomo.ne.jp
  • @dkcomo.ne.jp
  • @focomo.ne.jp
  • @docpomo.ne.jp
  • @docomone.jp
  • @docomo.me.jp
  • @docom0.ne.jp
  • @docomo.n.jp
  • @docomo.ne.jo
  • @docomo.ne.j
  • @docomo.ne.kp
  • @docomo.ne.ip
  • @docomo.ne.jpp
  • @docomo.be.jp
  • @docomo.fe.jp
  • @docomo.ge.jp
  • @docomo.he.jp
  • @docomo.nr.jp
  • @docomo.ne.jl
  • @docomo.ne.ji
  • @docomo.ne.jm

ezweb.ne.jp 29 entries

  • @ezwbe.ne.jp
  • @ezweb.ne.jo
  • @ezweb.ne.j
  • @ezweb.ne.jpp
  • @ezweb.nw.jp
  • @ezweb.ne.kp
  • @ezweb.me.jp
  • @ezweb.ne.ip
  • @ezweb.ne.ji
  • @ezweb.ne.jm
  • @ezweb.de.jp
  • @ezweb.ne.hp
  • @ezweb.nr.jp
  • @ezweb.ne.lp
  • @ezweb.ne.jl
  • @ezweb.ne.jlp
  • @ezweb.he.jp
  • @ezweb.be.jp
  • @ezwweb.ne.jp
  • @ezwbeb.ne.jp
  • @ezeweb.ne.jp
  • @ezeb.ne.jp
  • @ezwebb.ne.jp
  • @ezwrb.ne.jp
  • @ezweb.n.ep
  • @ezweb.nf.jp
  • @ezweb.ne.hjp
  • @ezweb.ne.jph
  • @ezweb.ne.pj

i.softbank.jp 27 entries

  • @i.softabnk.jp
  • @i.softbakn.jp
  • @i.sofbank.jp
  • @i.softbak.jp
  • @i.softbanl.jp
  • @i.softnank.jp
  • @i.softbamk.jp
  • @i.softbanj.jp
  • @i.softbanm.jp
  • @i.softbank.jo
  • @i.softbank.jl
  • @i.softbankk.jp
  • @i.sofrbank.jp
  • @i.softtbank.jp
  • @i.softbank.jpj
  • @i.softbank.kp
  • @i.softbnak.jp
  • @i.softbajk.jp
  • @i.softbank.j
  • @i.softbank.ip
  • @i.softbank.jpp
  • @i.softbnk.jp
  • @i.softdank.jp
  • @i.softgank.jp
  • @i.softban.jp
  • @i.softbannk.jp
  • @i.softbank.jm

yahoo.co.jp 30 entries

  • @yhoo.co.jp
  • @yaho.co.jp
  • @yaoho.co.jp
  • @yahho.co.jp
  • @yahoo.oc.jp
  • @yahoo.cp.jp
  • @yahoo.cl.jp
  • @yahoo.co.kp
  • @yahoo.co.ip
  • @yahoo.co.jo
  • @yahoo.co.j
  • @yahoo.coo.jp
  • @yahoo.co.jpp
  • @yhaoo.co.jp
  • @yahoo.co.jl
  • @yahoo.co.ji
  • @yahoo.co.jm
  • @yajoo.co.jp
  • @yahio.co.jp
  • @yahoi.co.jp
  • @yahop.co.jp
  • @yahoo.co.pj
  • @yaoo.co.jp
  • @ahoo.co.jp
  • @yahoo.c.jp
  • @yahoo.co.lj
  • @yahoo.co.jpn
  • @yauoo.co.jp
  • @yahhoo.co.jp
  • @yahoo.com.jp
06

/account/email と /signup の差分

項目/signup/account/email
必須 (zod .min(1))YesYes
形式 (zod .email())YesYes
"+" 拒否YesYes
ドメイン typo ブラックリストYes (176 件)No
blur 時の入力値エコーYesNo
HTML5 pattern 属性Yes (実効性なし)Yes (実効性なし)
input type"email""text"
追加要件現在のパスワード + 6 桁認証コード

signup だけが ① 既存ユーザーの大半を占めるであろう 6 大ドメインを守るために typo リストを持ち、② 入力値エコーで目視確認も促す。 account/email 変更時はすでに本人確認 (パスワード + 認証コード) があるため、typo の救済より 「本人の意図した変更を尊重する」 方針と読み取れる。

07

結論と所感

  • signup の typo 対策は固定リスト依存キーボード隣接エラー (@gmaul.com 等) や .com のミス (.cmo, .con 等) はカバーされているが、リストに無い typo (例: @gaamail.com) は素通りする。
  • UX とロジックの分離が浅いblur 時の「メールアドレスに誤りがないか確認してください。」は 常に 出るプロンプトであり、ユーザーは typo 検出と誤解しやすい。
  • エラー文言の使い回し形式エラー / + 拒否 / typo ヒットがすべて form.validEmail = 「有効なメールアドレスを入力してください」。原因切り分けが難しく、サポート問い合わせの粒度を上げにくい。
  • + 拒否は両ページ共通foo+tag@gmail.com 系のエイリアスはサインアップ・変更ともに不可。RFC 上は valid。意図的な制約と推測 (重複登録抑止 / 配信都合)。
  • HTML pattern は装飾的送信は JS でハンドルしているため pattern 制約検証は事実上効かない。大文字や + を含む入力でも HTML5 レベルではエラーにならない。
Recommendation

fuzzy match (Levenshtein 距離 1〜2) を追加すれば現在のリスト依存の盲点 (@gaamail.com 等) を一気にカバーできる。同時に「形式」「typo」「+」のエラー文言を分けると、ユーザーが入力ミスを修正しやすい。