---
generated_at: 2026-02-04 12:00:00
metrics:
  claims_total: 140
  claims_with_evidence: 136
  claims_without_evidence: 4
confidence_derived: 0.97
---

# 根拠レポート：Component-PasswordHasher 単体テストケース一覧

## 本レポートについて

### 目的
本レポートは、生成された単体テストケース一覧CSVの信頼性を検証し、人間レビュアーが効率的にレビューできるようにすることを目的としています。

### チェック方法
以下の観点でドキュメントの内容（Claim：主張）を検証しています：

1. **根拠の有無確認**：各主張に対して、ソースコード・既存設計書・要件定義書などの根拠（Evidence）が存在するか
2. **根拠との整合性**：主張の内容が根拠と矛盾していないか
3. **網羅性**：参照すべき情報源を適切にカバーしているか

### 信頼度スコアの算出
- **confidence_derived** = 根拠あり件数 / 総主張件数
- 状態「○」：根拠あり、「△」：根拠不足または要確認

### 本レポートの使い方
1. まず「サマリー」で全体の信頼度と優先レビュー項目を確認
2. 「Claims と根拠の対応」で △ の項目を重点的にレビュー
3. 「不足情報」で補完が必要な情報源を確認

---

## 1) サマリー（まず見るところ）
- 総合信頼度（derived）：**0.97**
  - 根拠あり：136 / 140、根拠なし：4
- 優先レビュー（高）
  1. **C-023 (UT-PWH-023)**: argonハッシュのsodium拡張検証パスは実行環境依存で根拠確認が限定的
  2. **C-129 (UT-PWH-129)**: LegacyPasswordHasherのsalt出力テストはexecuteメソッドの結合動作に依存
  3. **C-134 (UT-PWH-134)**: createPasswordQuestionはprivateメソッドで直接テスト不可
  4. **C-136 (UT-PWH-136)**: generateSaltはprivateメソッドで直接テスト不可

## 2) 参照した情報（Evidence一覧）
> ここに「実在するもの」だけ列挙。

- E-01: `src/Symfony/Component/PasswordHasher/PasswordHasherInterface.php` - MAX_PASSWORD_LENGTH=4096定数、hash/verify/needsRehashインターフェース定義
- E-02: `src/Symfony/Component/PasswordHasher/LegacyPasswordHasherInterface.php` - salt引数付きhash/verifyメソッド定義
- E-03: `src/Symfony/Component/PasswordHasher/Hasher/NativePasswordHasher.php` - コンストラクタバリデーション(opsLimit>=3, memLimit>=10k, cost 4-31)、hash/verify/needsRehashの実装
- E-04: `src/Symfony/Component/PasswordHasher/Hasher/SodiumPasswordHasher.php` - isSupported静的メソッド、コンストラクタバリデーション、hash/verify/needsRehashの実装
- E-05: `src/Symfony/Component/PasswordHasher/Hasher/Pbkdf2PasswordHasher.php` - hash_pbkdf2使用、encodedLengthチェック、needsRehash常にfalse
- E-06: `src/Symfony/Component/PasswordHasher/Hasher/MessageDigestPasswordHasher.php` - mergePasswordAndSalt(salt内{/}禁止)、iteration stretch、hashLengthチェック
- E-07: `src/Symfony/Component/PasswordHasher/Hasher/PlaintextPasswordHasher.php` - ignorePasswordCaseオプション、mergePasswordAndSalt、needsRehash常にfalse
- E-08: `src/Symfony/Component/PasswordHasher/Hasher/MigratingPasswordHasher.php` - bestHasher委譲、extraHashersフォールバック、needsRehash判定ロジック
- E-09: `src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherFactory.php` - getPasswordHasher(string/object/PasswordHasherAwareInterface)、createHasher(algorithm switch)、getMigratingPasswordConfig
- E-10: `src/Symfony/Component/PasswordHasher/Hasher/UserPasswordHasher.php` - hashPassword/isPasswordValid/needsRehash、LegacyPasswordAuthenticatedUserInterface salt対応、getPassword() nullチェック
- E-11: `src/Symfony/Component/PasswordHasher/Hasher/CheckPasswordLengthTrait.php` - isPasswordTooLongメソッド、MAX_PASSWORD_LENGTH比較
- E-12: `src/Symfony/Component/PasswordHasher/Command/UserPasswordHashCommand.php` - execute/configure/getUserClass/createPasswordQuestion/generateSalt/completeメソッド
- E-13: `src/Symfony/Component/PasswordHasher/Exception/InvalidPasswordException.php` - デフォルトメッセージ 'Invalid password.'、ExceptionInterface実装
- E-14: `src/Symfony/Component/PasswordHasher/Exception/LogicException.php` - ExceptionInterface実装
- E-15: `src/Symfony/Component/PasswordHasher/Hasher/PasswordHasherAwareInterface.php` - getPasswordHasherName(): ?string

## 3) Claims と根拠の対応（レビューの主戦場）
| Claim ID | 主張 | Evidence | 状態 |
|---|---|---|---|
| C-001 | NativePasswordHasherがデフォルトパラメータで生成される | E-03 (L34-72: コンストラクタ, cost??=13, opsLimit??=4, memLimit??=64MB) | ○ |
| C-002 | カスタムcostパラメータで生成される | E-03 (L48-49: $cost < 4 \|\| 31 < $cost チェック) | ○ |
| C-003 | algorithm=bcryptで生成される | E-03 (L53-63: $algorithms配列, '2y' => PASSWORD_BCRYPT) | ○ |
| C-004 | opsLimit<3で例外 | E-03 (L40-42: if (3 > $opsLimit) throw) | ○ |
| C-005 | memLimit<10kで例外 | E-03 (L44-46: if (10 * 1024 > $memLimit) throw) | ○ |
| C-006 | cost<4で例外 | E-03 (L48-49: if ($cost < 4 \|\| 31 < $cost) throw) | ○ |
| C-007 | cost>31で例外 | E-03 (L48-49: 同上) | ○ |
| C-008 | cost=4の境界値 | E-03 (L48: $cost < 4 条件) | ○ |
| C-009 | cost=31の境界値 | E-03 (L48: 31 < $cost 条件) | ○ |
| C-010 | opsLimit=3の境界値 | E-03 (L40: 3 > $opsLimit 条件) | ○ |
| C-011 | memLimit=10240の境界値 | E-03 (L44: 10 * 1024 > $memLimit 条件) | ○ |
| C-012 | NativePasswordHasher hashで通常パスワードハッシュ | E-03 (L74-85: hash メソッド, password_hash呼出) | ○ |
| C-013 | bcryptで72バイト超パスワードがsha512プリハッシュ | E-03 (L80-82: 72 < strlen チェック, base64_encode(hash('sha512'))) | ○ |
| C-014 | bcryptでNUL文字含むパスワードがsha512プリハッシュ | E-03 (L80: str_contains($plainPassword, "\0")) | ○ |
| C-015 | 4096バイト超でInvalidPasswordException | E-03 (L76-78), E-01 (L25: MAX_PASSWORD_LENGTH = 4096), E-11 | ○ |
| C-016 | 4096バイトの境界値 | E-01 (L25), E-11 (isPasswordTooLong比較) | ○ |
| C-017 | 空文字列のhash | E-03 (L76: isPasswordTooLong チェックは通過する) | ○ |
| C-018 | verify正常系true | E-03 (L87-111: verifyメソッド, password_verify呼出) | ○ |
| C-019 | verify不正パスワードfalse | E-03 (L99: password_verify結果) | ○ |
| C-020 | bcrypt 72バイト超のverify | E-03 (L95-96: bcryptハッシュでの72バイト超チェック) | ○ |
| C-021 | verify空文字列false | E-03 (L89: '' === $plainPassword) | ○ |
| C-022 | verify 4096バイト超false | E-03 (L89: $this->isPasswordTooLong) | ○ |
| C-023 | argonハッシュのsodium検証 | E-03 (L93, L102-104: str_starts_with('$argon'), sodium_crypto_pwhash_str_verify) | △ |
| C-024 | needsRehash true | E-03 (L113-116: password_needs_rehash委譲) | ○ |
| C-025 | needsRehash false | E-03 (L113-116) | ○ |
| C-026 | SodiumPasswordHasherデフォルト生成 | E-04 (L32-48: コンストラクタ) | ○ |
| C-027 | sodium未ロードでLogicException | E-04 (L34-36: if (!self::isSupported()) throw) | ○ |
| C-028 | Sodium opsLimit<3で例外 | E-04 (L41-43) | ○ |
| C-029 | Sodium memLimit<10kで例外 | E-04 (L45-47) | ○ |
| C-030 | isSupported | E-04 (L50-53: 静的メソッド, バージョン比較) | ○ |
| C-031 | Sodium hash正常系 | E-04 (L55-70: sodium_crypto_pwhash_str呼出) | ○ |
| C-032 | Sodium hash 4096超例外 | E-04 (L57-59) | ○ |
| C-033 | Sodium verify正常true | E-04 (L72-100: verifyメソッド) | ○ |
| C-034 | Sodium verify不正false | E-04 (L72-100) | ○ |
| C-035 | Sodium verify空文字false | E-04 (L74-76: '' === $plainPassword) | ○ |
| C-036 | Sodium verify 4096超false | E-04 (L78-79: isPasswordTooLong) | ○ |
| C-037 | Sodium verify bcryptフォールバック | E-04 (L82-89: !str_starts_with('$argon'), password_verify) | ○ |
| C-038 | Sodium verify bcrypt 72バイト超 | E-04 (L83-85) | ○ |
| C-039 | Sodium needsRehash | E-04 (L102-113) | ○ |
| C-040 | Pbkdf2デフォルト生成 | E-05 (L43-54) | ○ |
| C-041 | Pbkdf2カスタム生成 | E-05 (L43-48: コンストラクタ引数) | ○ |
| C-042 | Pbkdf2 hash saltあり | E-05 (L56-69: hash_pbkdf2呼出) | ○ |
| C-043 | Pbkdf2 hash saltなし | E-05 (L66: $salt ?? '') | ○ |
| C-044 | Pbkdf2 hex出力 | E-05 (L68: bin2hex) | ○ |
| C-045 | Pbkdf2 hash 4096超例外 | E-05 (L58-60) | ○ |
| C-046 | Pbkdf2 未サポートアルゴリズム例外 | E-05 (L62-64: in_array hash_algos チェック) | ○ |
| C-047 | Pbkdf2 verify正常true | E-05 (L71-78: hash_equals比較) | ○ |
| C-048 | Pbkdf2 verify不正false | E-05 (L77: hash_equals) | ○ |
| C-049 | Pbkdf2 verifyハッシュ長不正false | E-05 (L73: strlen !== encodedLength) | ○ |
| C-050 | Pbkdf2 verify $含むfalse | E-05 (L73: str_contains('$')) | ○ |
| C-051 | Pbkdf2 verify 4096超false | E-05 (L77: isPasswordTooLong) | ○ |
| C-052 | Pbkdf2 needsRehash常にfalse | E-05 (L80-83: return false) | ○ |
| C-053 | MessageDigestデフォルト生成 | E-06 (L34-44) | ○ |
| C-054 | MessageDigestカスタム生成 | E-06 (L34-38: コンストラクタ引数) | ○ |
| C-055 | MessageDigest hash saltあり | E-06 (L46-65: mergePasswordAndSalt + hash) | ○ |
| C-056 | MessageDigest hash saltなし | E-06 (L83-85: !$salt return $password) | ○ |
| C-057 | MessageDigest hex出力 | E-06 (L64: bin2hex) | ○ |
| C-058 | MessageDigest iterationストレッチ | E-06 (L60-62: forループ) | ○ |
| C-059 | MessageDigest hash 4096超例外 | E-06 (L48-50) | ○ |
| C-060 | MessageDigest 未サポートアルゴリズム例外 | E-06 (L52-54) | ○ |
| C-061 | MessageDigest verify正常true | E-06 (L67-74: hash_equals) | ○ |
| C-062 | MessageDigest verify不正false | E-06 (L73) | ○ |
| C-063 | MessageDigest verifyハッシュ長不正false | E-06 (L69: strlen !== hashLength) | ○ |
| C-064 | MessageDigest verify $含むfalse | E-06 (L69: str_contains('$')) | ○ |
| C-065 | MessageDigest verify 4096超false | E-06 (L73: isPasswordTooLong) | ○ |
| C-066 | MessageDigest needsRehash常にfalse | E-06 (L76-79: return false) | ○ |
| C-067 | mergePasswordAndSalt saltあり | E-06 (L81-92: return $password.'{'.$salt.'}') | ○ |
| C-068 | mergePasswordAndSalt salt空 | E-06 (L83-85: if (!$salt) return $password) | ○ |
| C-069 | mergePasswordAndSalt salt内{例外 | E-06 (L87-89: strrpos チェック, throw) | ○ |
| C-070 | mergePasswordAndSalt salt内}例外 | E-06 (L87-89) | ○ |
| C-071 | Plaintextデフォルト生成 | E-07 (L31-34) | ○ |
| C-072 | Plaintext ignoreCase=true生成 | E-07 (L31: $ignorePasswordCase引数) | ○ |
| C-073 | Plaintext hash saltなし | E-07 (L36-43: mergePasswordAndSalt) | ○ |
| C-074 | Plaintext hash saltあり | E-07 (L42: mergePasswordAndSalt) | ○ |
| C-075 | Plaintext hash 4096超例外 | E-07 (L38-40) | ○ |
| C-076 | Plaintext hash salt内{例外 | E-07 (L71-73) | ○ |
| C-077 | Plaintext verify正常true | E-07 (L45-58: hash_equals) | ○ |
| C-078 | Plaintext verify大文字小文字区別false | E-07 (L53-54: !$this->ignorePasswordCase, hash_equals) | ○ |
| C-079 | Plaintext verify大文字小文字無視true | E-07 (L57: strtolower比較) | ○ |
| C-080 | Plaintext verify saltあり | E-07 (L51: mergePasswordAndSalt) | ○ |
| C-081 | Plaintext verify 4096超false | E-07 (L47-49) | ○ |
| C-082 | Plaintext needsRehash常にfalse | E-07 (L60-63: return false) | ○ |
| C-083 | Migrating bestHasher+extraHashers生成 | E-08 (L29-34) | ○ |
| C-084 | Migrating bestHasherのみ生成 | E-08 (L31: variadic引数) | ○ |
| C-085 | Migrating hash bestHasher委譲 | E-08 (L36-39: return $this->bestHasher->hash) | ○ |
| C-086 | Migrating hash salt委譲 | E-08 (L38: $salt引数) | ○ |
| C-087 | Migrating verify bestHasher成功true | E-08 (L43-45: if bestHasher->verify return true) | ○ |
| C-088 | Migrating verify extraHasherフォールバック | E-08 (L47-55: needsRehash true時にextraHashers試行) | ○ |
| C-089 | Migrating verify全失敗false | E-08 (L57: return false) | ○ |
| C-090 | Migrating verify needsRehash=falseでextraスキップ | E-08 (L47-49: !needsRehash return false) | ○ |
| C-091 | Migrating needsRehash bestHasher委譲 | E-08 (L60-63) | ○ |
| C-092 | Factory コンストラクタ | E-09 (L29-32) | ○ |
| C-093 | Factory getPasswordHasher文字列 | E-09 (L45-48: foreach $this->passwordHashers) | ○ |
| C-094 | Factory getPasswordHasherオブジェクト | E-09 (L46: is_object($user) && $user instanceof $class) | ○ |
| C-095 | Factory PasswordHasherAwareInterface | E-09 (L38-43: getPasswordHasherName(), $hasherKey = $hasherName) | ○ |
| C-096 | Factory 未設定ハッシャー名RuntimeException | E-09 (L39-41: !array_key_exists throw RuntimeException) | ○ |
| C-097 | Factory マッチなしRuntimeException | E-09 (L53-55: null === $hasherKey throw RuntimeException) | ○ |
| C-098 | Factory instanceキー | E-09 (L71-74: isset config['instance']) | ○ |
| C-099 | Factory キャッシュ | E-09 (L57-59: $this->passwordHashers[$hasherKey] = ...) | ○ |
| C-100 | Factory algorithm=auto MigratingPasswordHasher | E-09 (L111-133: 'auto' case) | ○ |
| C-101 | Factory algorithm=plaintext | E-09 (L140-144) | ○ |
| C-102 | Factory algorithm=pbkdf2 | E-09 (L146-155) | ○ |
| C-103 | Factory algorithm=bcrypt | E-09 (L157-161) | ○ |
| C-104 | Factory algorithm=native | E-09 (L163-171) | ○ |
| C-105 | Factory algorithm=sodium | E-09 (L173-180) | ○ |
| C-106 | Factory 未知アルゴリズム MessageDigest | E-09 (L207-214: defaultフォールバック) | ○ |
| C-107 | Factory migrate_from MigratingPasswordHasher | E-09 (L135-137, L217-237: getMigratingPasswordConfig) | ○ |
| C-108 | Factory classキー未設定例外 | E-09 (L83-84: throw InvalidArgumentException) | ○ |
| C-109 | Factory argumentsキー未設定例外 | E-09 (L86-87) | ○ |
| C-110 | UserPasswordHasherコンストラクタ | E-10 (L26-29) | ○ |
| C-111 | UserPasswordHasher hashPassword通常 | E-10 (L31-41: hash呼出) | ○ |
| C-112 | UserPasswordHasher hashPassword Legacy salt | E-10 (L34-36: LegacyPasswordAuthenticatedUserInterface getSalt) | ○ |
| C-113 | UserPasswordHasher hashPassword salt=null | E-10 (L33: $salt = null デフォルト) | ○ |
| C-114 | UserPasswordHasher isPasswordValid true | E-10 (L43-57: verify呼出) | ○ |
| C-115 | UserPasswordHasher isPasswordValid false | E-10 (L56: hasher->verify結果) | ○ |
| C-116 | UserPasswordHasher isPasswordValid password=null | E-10 (L50-52: null === getPassword() return false) | ○ |
| C-117 | UserPasswordHasher isPasswordValid Legacy salt | E-10 (L46-48) | ○ |
| C-118 | UserPasswordHasher needsRehash true | E-10 (L59-68: needsRehash呼出) | ○ |
| C-119 | UserPasswordHasher needsRehash false | E-10 (L67) | ○ |
| C-120 | UserPasswordHasher needsRehash password=null false | E-10 (L61-63: null === getPassword() return false) | ○ |
| C-121 | CheckPasswordLengthTrait 4096以下false | E-11 (L22-24: MAX_PASSWORD_LENGTH < strlen) | ○ |
| C-122 | CheckPasswordLengthTrait 4097以上true | E-11, E-01 (L25: MAX_PASSWORD_LENGTH = 4096) | ○ |
| C-123 | CheckPasswordLengthTrait 空文字false | E-11 (strlen('') = 0 < 4096) | ○ |
| C-124 | CheckPasswordLengthTrait 1文字false | E-11 | ○ |
| C-125 | UserPasswordHashCommandコンストラクタ | E-12 (L41-46) | ○ |
| C-126 | Command execute正常 | E-12 (L96-159: executeメソッド) | ○ |
| C-127 | Command execute empty-salt | E-12 (L105, L110-112: saltlessWithoutEmptySalt) | ○ |
| C-128 | Command execute非インタラクティブパスワードなしエラー | E-12 (L114-119: if !$password && !isInteractive return 1) | ○ |
| C-129 | Command executeLegacyHasherでsalt出力 | E-12 (L108, L145-147, L150-151) | △ |
| C-130 | Command getUserClass引数指定 | E-12 (L191-192) | ○ |
| C-131 | Command getUserClass非インタラクティブデフォルト | E-12 (L199-200: reset($this->userClasses)) | ○ |
| C-132 | Command getUserClasses空RuntimeException | E-12 (L195-197: throw RuntimeException) | ○ |
| C-133 | Command complete補完候補 | E-12 (L161-166: suggestValues) | ○ |
| C-134 | Command createPasswordQuestion生成 | E-12 (L171-181) | △ |
| C-135 | Command createPasswordQuestion空白バリデーション | E-12 (L176-178: trim($value) === '' throw) | ○ |
| C-136 | Command generateSalt | E-12 (L184-187: base64_encode(random_bytes(30))) | △ |
| C-137 | InvalidPasswordExceptionデフォルトメッセージ | E-13 (L19: message = 'Invalid password.') | ○ |
| C-138 | InvalidPasswordExceptionカスタムメッセージ | E-13 (L19: コンストラクタ引数) | ○ |
| C-139 | InvalidPasswordException ExceptionInterface実装 | E-13 (L17: implements ExceptionInterface) | ○ |
| C-140 | LogicException ExceptionInterface実装 | E-14 (L17: implements ExceptionInterface) | ○ |

## 4) 不足情報（Unknown / Missing）
- **C-023**: NativePasswordHasher::verifyにおけるargonハッシュのsodium拡張検証パスは、実行環境のsodium拡張のバージョンに依存するため、テスト環境によっては該当パスを通過しない可能性がある
  - 候補: sodium拡張のモック / 統合テスト環境での検証
- **C-129**: executeメソッド内のLegacyPasswordHasherInterface判定とsalt出力の結合動作は、コマンドの統合テストで検証するのが適切
  - 候補: CommandTesterを使用した結合テスト
- **C-134**: createPasswordQuestionはprivateメソッドであり、直接の単体テストが困難。execute経由での間接テストが必要
  - 候補: リフレクション / execute経由のインタラクティブテスト
- **C-136**: generateSaltはprivateメソッドであり、直接の単体テストが困難。execute経由での間接テストが必要
  - 候補: リフレクション / execute経由でsalt生成結果の検証

## 5) リスクフラグ（レビュー観点）
- 0: NativePasswordHasher/SodiumPasswordHasher/Pbkdf2PasswordHasher/MessageDigestPasswordHasher/PlaintextPasswordHasher/MigratingPasswordHasher/CheckPasswordLengthTrait - ソースコードから直接テスト観点を導出しており低リスク
- 0: PasswordHasherFactory - algorithmのswitch分岐が多いが全て網羅しており低リスク
- 0: UserPasswordHasher - モック経由のテストが主であり低リスク
- 1: UserPasswordHashCommand - privateメソッド(createPasswordQuestion, generateSalt)の直接テストが困難なため中リスク
- 0: Exception classes - 単純な継承構造で低リスク

## 6) レビュアーチェックリスト（最小）
- [ ] NativePasswordHasherのbcrypt 72バイト制限対策（sha512プリハッシュ）のテストケースが適切か確認
- [ ] SodiumPasswordHasherのisSupported()判定と環境依存テストの扱いが適切か確認
- [ ] PasswordHasherFactoryのalgorithm=argon2i / argon2id分岐が環境依存であることを考慮したテストケースの妥当性確認
- [ ] MigratingPasswordHasherのverifyメソッドにおけるneedsRehash判定とextraHashersフォールバックのロジック網羅性確認
- [ ] UserPasswordHashCommandのprivateメソッド(createPasswordQuestion, generateSalt)のテスト方針が妥当か確認
- [ ] mergePasswordAndSaltメソッドのsalt内{/}文字チェックがMessageDigestPasswordHasherとPlaintextPasswordHasherの両方で適切にテストされているか確認
