From f58deffbd43cb571a27855e0e988d8f75c169e15 Mon Sep 17 00:00:00 2001 From: 128Na Date: Wed, 26 Aug 2026 21:02:05 +0900 Subject: [PATCH 1/2] =?UTF-8?q?test:=20assurance-audit=E6=8C=87=E6=91=98?= =?UTF-8?q?=E3=81=AEC3/D4=E3=83=86=E3=82=B9=E3=83=88=E4=B8=8D=E5=82=99?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SecretScrubberTest: json_encode経由のアサートは例外オブジェクトが protected/privateプロパティのため常に{}になり、伏字化の有無に かかわらず結果が変わらない見せかけの保証だった。scrubArray()の 戻り値を直接アサートする形に修正し、5文字境界値の伏字化テストを追加。 - UpdateOrCreatePageWithPaksTest: 「DB::transactionが呼ばれたか」の モック確認と正常系コミットのみで、途中失敗時のロールバックを 検証するテストが無かった欠落を追加。SyncPak/UpdateOrCreatePageは finalで差し替え不可なため、契約違反値を渡し実コードの本物の失敗 経路でロールバックを検証する形にした。 - 修正前の実装にバグを注入し、両テストが実際にredになることを 確認済み(見せかけの保証ではないことの検証)。 - docs/known-risks.md のC3/B4/D4テスト件数を追加分に合わせて更新。 --- docs/known-risks.md | 6 ++-- .../UpdateOrCreatePageWithPaksTest.php | 33 +++++++++++++++++ .../Actions/Logging/SecretScrubberTest.php | 35 ++++++++++++++++--- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/docs/known-risks.md b/docs/known-risks.md index 1cb1dc0..d9ae6cb 100644 --- a/docs/known-risks.md +++ b/docs/known-risks.md @@ -25,7 +25,7 @@ | B1 | 再実行で Notion ページを重複作成しない | Preventive(URL 突合で create/update 分岐) | 2(`test_sync_action_creates_updates_and_deletes_pages` + `test_does_not_create_duplicate_when_page_already_exists_in_notion`) | 🟢 OK | — | 2026-06-22 | | B2 | 1 件の Notion API エラーでバッチ全体が止まらない | Preventive(delete/add 両方とも項目単位 try/catch + 継続 + 失敗件数を error ログ。`addNewNotionPages` は `keyNotionPagesByUrl()` で URL 解析失敗も個別隔離。URL が無い Notion ページは削除対象から除外) | 3(`test_continues_syncing_when_one_item_fails` + `test_continues_deleting_when_one_delete_fails` + `test_does_not_delete_notion_page_without_url`) | 🟢 OK | — | 2026-06-22 | | B3 | 未変更項目を毎回 re-PUSH せず API クォータを浪費しない | None(`synced_at`/状態フラグ無し) | 0 | 🔴 Missing | **記録のみ**。API 呼び出し回数が問題化したら `synced_at` を導入 | 2026-06-22 | -| B4 | Notion シークレットを例外/ログ/Discord に流出させない | Preventive(`SecretScrubber` で送出前に伏字化。C3 と一体) | 7(C3 のテストと共有) | 🟢 OK | — | 2026-06-22 | +| B4 | Notion シークレットを例外/ログ/Discord に流出させない | Preventive(`SecretScrubber` で送出前に伏字化。C3 と一体) | 8(C3 のテストと共有) | 🟢 OK | — | 2026-06-22 | ## C. シークレット / 資格情報の取扱い @@ -33,7 +33,7 @@ |----|----------------------------------|------|--------|--------|----------|--------| | C1 | デバッグページが config/スタックトレースを露出しない | Preventive(`.env.example`/`.ci`/`.deploy` すべて `APP_DEBUG=false`、コード既定も false) | 2(`Http/ApiErrorResponseTest` + `Http/WebErrorResponseTest`) | 🟢 OK | — | 2026-06-22 | | C2 | API 例外が生のトレースを返さない | Preventive(`APP_DEBUG=false` で汎用 JSON エラー) | 2(`Http/ApiErrorResponseTest`×2) | 🟢 OK | — | 2026-06-22 | -| C3 | 例外レポート(log/Discord)に機密値を混入させない | Preventive(`SecretScrubber`(伏字化対象は5文字以上の値のみ。"root"等の短い開発用パスワードでの誤爆を回避)+ `RedactSecretsProcessor`/tap + `ConvertDiscord`(親クラスの `addMessageStacktrace` を no-op 化し、未伏字化の生トレースでの上書きも防止) で送出前に伏字化) | 7(`SecretScrubberTest`×4 + `RedactSecretsProcessorTest` + `ConvertDiscordTest`×2) | 🟢 OK | — | 2026-06-22 | +| C3 | 例外レポート(log/Discord)に機密値を混入させない | Preventive(`SecretScrubber`(伏字化対象は5文字以上の値のみ。"root"等の短い開発用パスワードでの誤爆を回避)+ `RedactSecretsProcessor`/tap + `ConvertDiscord`(親クラスの `addMessageStacktrace` を no-op 化し、未伏字化の生トレースでの上書きも防止) で送出前に伏字化) | 8(`SecretScrubberTest`×5 + `RedactSecretsProcessorTest` + `ConvertDiscordTest`×2) | 🟢 OK | — | 2026-08-26(assurance-audit是正: `test_scrub_array_recurses_and_masks_throwable` が `json_encode` 経由でアサートしており例外オブジェクトの伏字化を検証できていなかった欠陥を修正。`scrubArray()` の戻り値を直接アサートする形に変更し、5文字境界値テストを追加) | | C4 | 実シークレットを git に混入させない | Preventive(`.gitignore` で `.env` 除外、committed な `.env.*` は空値) | N/A | 🟢 OK | — | 2026-06-22 | ## D. 公開検索 / Feed / API @@ -43,7 +43,7 @@ | D1 | raw_pages(生 HTML)を公開経路に露出しない | Preventive(検索/Feed で rawPage を eager-load しない、Page に html 列無し) | 2(`PageResourceTest::test_search_does_not_eager_load_raw_page_relation` + `SearchPage/FeedActionTest`) | 🟢 OK | — | 2026-06-22 | | D2 | 非公開コンテンツを検索/Feed に出さない | N/A(Page に公開状態の概念が無い) | — | — | 対象外(将来 status 列を足す場合は再評価) | 2026-06-22 | | D3 | API/Feed 応答に内部フィールドを含めない | Preventive(PageResource 許可リスト + `Page::toFeedItem()`) | 2(`PageResourceTest::test_resource_exposes_only_whitelisted_fields` + `test_feed_item_does_not_expose_internal_fields_or_raw_html`) | 🟢 OK | — | 2026-06-22 | -| D4 | 半分書かれた Page 行を検索に見せない(原子性) | Preventive(`UpdateOrCreatePageWithPaks` が Page 更新 + Pak 同期を `DB::transaction()` で原子化。3 Handler すべてこれを使用) | 2(`Extract/UpdateOrCreatePageWithPaksTest`) | 🟢 OK | — | 2026-06-22 | +| D4 | 半分書かれた Page 行を検索に見せない(原子性) | Preventive(`UpdateOrCreatePageWithPaks` が Page 更新 + Pak 同期を `DB::transaction()` で原子化。3 Handler すべてこれを使用) | 3(`Extract/UpdateOrCreatePageWithPaksTest`) | 🟢 OK | — | 2026-08-26(assurance-audit是正: 「DB::transaction が呼ばれたか」のモック確認と正常系コミットのみで、実際に途中失敗させてロールバックを確認するテストが無かった欠落を発見。`test_page_update_is_rolled_back_when_pak_sync_fails` を追加し、SyncPak/UpdateOrCreatePage が final のため実コードの契約違反経路で本物の失敗を起こしロールバックを検証) |