清掉 v2 白名單裡 6 個資料庫不存在的欄位,並加上機械化守衛 - #1285
Merged
sudoghut merged 3 commits intoAug 28, 2026
Merged
Conversation
allowedFields() 是手寫清單,與資料表實際欄位之間沒有任何保證。列進去卻不存在的欄 會被白名單放行,錯誤要到 DB::table()->update()/->insert() 才發生 ⇒ 使用者拿到 500 而不是 422;API.md 也照著白名單把它們公布為對外契約。 清掉的 6 欄(migration 均未曾建立、prod 也查不到): - ALTNAME_DATA:c_alt_name_pinyin/_pinyin2/_pinyin3/c_alt_name_role(cbdb-project#1284) - BIOG_TEXT_DATA:c_supplement/c_text_year(新守衛抓到的;c_text_year 屬於 TEXT_CODES,走 text-codes 聚合端,本來就不該在這張表的白名單裡) 半年沒被發現的原因是 6 支測試檔各自在合成表裡把這些欄建了出來,於是 PinyinUmlaut Tier 1 的 ALTNAME 分支是對著一個現實中不存在的表形在測。同類的「幻影 c_supplement」 先前已在 ASSOC/KIN/POSSESSION 手工清過,BIOG_TEXT_DATA 這筆漏網。 - 移除 PinyinUmlaut::ALTNAME_PINYIN_V_FIELDS 與兩個 altname handler 的 normalizeFields() 呼叫。刻意不留空常數,免得下一個人以為只是暫時沒欄位而填回。 Tier 2 不受影響——c_alt_name 照舊走前端互動確認、後端不轉 - VariantReplaceScope::EXCLUDED_COLUMNS['ALTNAME_DATA'] 縮為 ['c_alt_name']; 該處「c_alt_name_role 是 prod-only」的註解與 CHAR_VARIANT_MAP_TEXT_COLUMN_ ROLLOUT_PLAN.md 的同旨段落都寫反了,一併更正 - 新增 tests/Feature/MutationAllowedFieldsSchemaDriftTest.php:掛 RefreshDatabase 讓 schema 真的來自 database/migrations(同 VariantReplaceRegistryDriftTest 的 理由),逐一比對全部人物子資源 handler 的 allowedFields()+keyColumns() - 同步 API.md(altnames/texts 白名單)、PINYIN_SAVE_NORMALIZE_DESIGN.md(開頭加 修訂註記)、PINYIN_V_TO_UMLAUT_MIGRATION.md/.en.md、CHANGELOG.md 已執行 ./vendor/bin/phpunit(2988 tests 全綠)與 php-cs-fixer
This comment was marked as off-topic.
This comment was marked as off-topic.
只留現況,不留「這裡以前是錯的」——那種註記本身就是未來的誤導來源, 機械化守衛(MutationAllowedFieldsSchemaDriftTest)才是防復發的手段。 - 移除 5 個 handler 裡的「幻影欄/假欄已移除」註解 (ASSOC/KIN/POSSESSION,前次清理留下的墓誌銘) - 移除 PinyinUmlaut 與 VariantReplaceScope 裡關於已刪欄位的說明段落, 改為直接陳述現況(ALTNAME_DATA 沒有 Tier 1 欄位、只有 c_alt_name 一個排除欄) - PINYIN_SAVE_NORMALIZE_DESIGN.md 拿掉開頭修訂區塊與刪除線標註, 直接改寫成現行設計;掛點表移除已不存在的 #4/#5 與其交叉引用 - CHAR_VARIANT_MAP_TEXT_COLUMN_ROLLOUT_PLAN.md 拿掉更正區塊; CHAR_VARIANT_MAP_CONSOLIDATION_PLAN.md/.en.md 修掉「掛鉤點會對 c_alt_name_pinyin 做 PinyinUmlaut 正規化」的失效描述 - 測試註解同樣只留當下要釘住的合約 CHANGELOG 保留完整記錄——那是變更史該待的地方。 已執行 ./vendor/bin/phpunit(2988 tests 全綠)與 php-cs-fixer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #1284
問題
allowedFields()是一份手寫清單,與資料表的實際欄位之間沒有任何保證。列進去卻不存在的欄會被白名單放行,錯誤要到DB::table()->update()/->insert()才發生 ⇒ 使用者拿到 500 而不是 422。而API.md又照著白名單把它們公布為對外契約,外部協作者照文件實作就會撞 500。清掉的 6 欄
ALTNAME_DATAc_alt_name_pinyin、c_alt_name_pinyin2、c_alt_name_pinyin3、c_alt_name_roleBIOG_TEXT_DATAc_supplement、c_text_year六欄 migration 均未曾建立(baseline
import_cbdb_schema.php的ALTNAME_DATA就是 12 欄),prod 也查不到。c_text_year屬於TEXT_CODES,走 text-codes 聚合端(ResolvesTextAggregateInput),本來就不該出現在BIOG_TEXT_DATA的白名單裡。同一類的「幻影
c_supplement」先前已在AssociationMutationHandler/KinshipMutationHandler/PossessionMutationHandler逐一手工清掉過(原碼註解仍在),BIOG_TEXT_DATA這筆漏網——這正說明手工核對不夠。為什麼半年沒被發現
6 支測試檔各自在合成表裡把這些欄建了出來:
ApiV2CreateAltnameTest、ApiV2MutateAltnameTest、ApiV2DeleteAltnameTest、ApiV2MutateVariantReplacementTest、ProposalResubmitTest、ProposalAuditFieldSemanticsTest、VariantReplaceScopeTest。於是整條 PinyinUmlaut Tier 1 的 ALTNAME 分支(lv → lü)是對著一個現實中不存在的表形在測——測試永遠綠,prod 永遠打不到。防復發:機械化守衛
新增
tests/Feature/MutationAllowedFieldsSchemaDriftTest.php,掛RefreshDatabase讓 schema 真的來自database/migrations(同VariantReplaceRegistryDriftTest的理由:不掛的話Schema只看得到測試自己建的合成表,守衛永遠假綠),逐一比對全部人物子資源 handler 的allowedFields()+keyColumns()。BIOG_TEXT_DATA那兩欄就是它抓出來的——寫完守衛第一次跑就紅。邊界(已寫進類註解):它抓的是「白名單列了 migration 沒有的欄」,抓不到反向的「prod 有、migration 沒有」——那需要對 live schema 比對,不在單元測試能及的範圍。表不在 migration 裡的 handler 會被略過並記錄,不判紅。
連帶改動
PinyinUmlaut::ALTNAME_PINYIN_V_FIELDS移除,兩個 altname handler 不再呼叫normalizeFields()。刻意不留空常數——留著會讓下一個人以為「只是暫時沒有欄位」而重新填進去;原地留了說明註解。c_alt_name照舊走前端互動確認、後端不轉。原本測 Tier 1 的兩支測試改成釘住這件事,另各加一支「送幻影欄必須是 422 而非 500」。VariantReplaceScope::EXCLUDED_COLUMNS['ALTNAME_DATA']從 4 欄縮為['c_alt_name'](拉丁人名欄計數 13 → 10)。該處與docs/CHAR_VARIANT_MAP_TEXT_COLUMN_ROLLOUT_PLAN.md:284都把這四欄記成「prod-only」,是寫反的,一併更正——後者自己記下的線索(「只在合成測試表出現」「結構式掃描碰不到它」)其實正指向真正的結論。API.md(altnames/texts 白名單與v → ü說明)、PINYIN_SAVE_NORMALIZE_DESIGN.md(開頭加修訂註記+逐處標註)、PINYIN_V_TO_UMLAUT_MIGRATION.md/.en.md、CHANGELOG.md。UnicodeNfc類註解裡舉例用的欄名也順手改對。刻意沒動:
docs/CHAR_VARIANT_MAP_CONSOLIDATION_PLAN.md(.en)§132 提到「現行已在這兩個方法內對c_alt_name_pinyin等欄位做正規化」——那是已完成計畫對當時程式碼的描述,屬歷史紀錄,改它等於竄改當初的判斷依據。驗證
./vendor/bin/phpunit全量 2988 tests / 15787 assertions 綠./vendor/bin/php-cs-fixer fix全庫 0 fixed相依
與 #1283 互不相依,皆從
upstream/develop(2adc213f)出發,可各自合併。