diff --git a/AGENTS.md b/AGENTS.md index eab2d1a..92b9db1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,9 +2,9 @@ > 所有 AI 工具與開發者均須遵守此規範 -**版本 / Version**:v1.4.5 +**版本 / Version**:v1.4.6 -**最後更新 / Last Updated**:2026-08-29 +**最後更新 / Last Updated**:2026-09-05 --- @@ -118,9 +118,19 @@ FCU_NE_Chat_Bot/ 三階段進行,**不得**使用 `field && !regex.test(field)` 這類寫法(空字串為 falsy,會整段跳過驗證,等同必填形同虛設,此為 v1.3.1 BUG-01 的根因)。 正確寫法應先 `trim()` 後檢查是否為空字串,通過才進入格式檢查: - 學號 `/^[a-zA-Z][0-9]{7}$/`、手機 `/^[0-9]{10}$/`、床號 `/^[0-9]{1,3}$/`、 - 房號 `/^[A-Za-z0-9-]{1,8}$/`;姓名與問題描述僅檢查非空與長度上限 + 學號 `/^[a-zA-Z][0-9]{7}$/`、手機 `/^[0-9]{10}$/`、床號 `/^[0-9]$/`(僅 1 位數字)、 + 房號 `/^(H|I|G|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`(須以 H、I、G、FA~FF 開頭, + 後接 1–4 位數字,可選一個連字號再接數字);姓名與問題描述僅檢查非空與長度上限 (50 / 200 字),無格式 RegEx。 + > ⚠️ **v1.4.6 修正(BUG-ROOM-01、BUG-BED-01)**:床號/房號後端正則式原本比前端 + > 寬鬆(床號曾允許 1–3 位數字、房號曾僅檢查「英數字與連字號」),可被繞過前端 + > 直接對 GAS Web App 送出格式不符的資料。現已收緊為與 `js/report.js`/ + > `index.html` 的 `pattern` 屬性完全一致,修改任一端的格式規則時務必同步更新 + > 另一端與本檔案,避免再次脫節。 +- **重複送出防護**:`writeReport()` 以「學號 + 房號 + 問題描述前 50 字」組字串、 + 取 MD5 雜湊後作為 `CacheService` key,120 秒內命中相同指紋直接回傳 + `DUPLICATE_REPORT`(僅於格式驗證通過、寫入成功後才存入快取)。防止使用者 + 手滑重複點擊送出或腳本短時間內重複灌入相同案件(v1.4.6 BUG-DUP-01 新增)。 - **Gemini 備援**:`GEMINI_MODELS_FALLBACK` 依 RPM 配額分三層、共 6 個模型, 遇 429 自動重試切換下一個模型;全部失敗降級至 `_ruleBasedClassify()` (19 語系關鍵字比對)。修改此陣列後,務必同步更新 diff --git a/CHANGELOG.md b/CHANGELOG.md index 54cc683..9752434 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,91 @@ # 更新日誌 (Changelog) -**版本 / Version**:v1.4.5 -**最後更新 / Last Updated**:2026-08-29 +**版本 / Version**:v1.4.6 +**最後更新 / Last Updated**:2026-09-05 + +--- + +## [v1.4.6] - 2026-09-05 (Duplicate-Report Guard, Room/Bed Validation Alignment & Post-Release Documentation Audit / 重複送出防護、房號床號驗證對齊與上線後文件稽核) + +### 繁體中文 + +本版本包含兩部分:(1) 追溯補齊 v1.4.5 發布後、本次正式發版前已合併上線但未同步文件化的兩項後端修復——**120 秒重複送出防護**與**房號/床號後端格式驗證對齊**;(2) 針對全專案原始碼與文件進行的逐行一致性稽核(涵蓋全部 `js/*.js`、`gas/Code.gs`、`css/style.css`、`index.html`、`test/*.js`、`AGENTS.md`、`README.md`、`CHANGELOG.md`、`doc/*.md`,並實際執行 `npm test`、`npm run lint`、`git log`/`git blame` 歷史比對),修復 7 處文件與程式碼不一致之處。本次稽核**未發現任何生產程式碼缺陷**;`css/style.css` 經比對後無需任何修改。 + +#### ✨ 新功能(回補文件 / Newly Documented Features) + +- **報修表單 120 秒重複送出防護**:`gas/Code.gs` 的 `writeReport()` 新增去重機制——以「學號 + 房號 + 問題描述前 50 字」組成字串,取 MD5 雜湊後作為 `CacheService` key,120 秒內偵測到相同指紋即直接拒絕並回傳 `DUPLICATE_REPORT`,防止使用者手滑重複點擊送出或腳本短時間內重複寫入相同案件。僅於格式驗證通過、實際寫入試算表成功後才存入快取,避免驗證失敗的請求誤佔用去重配額。前端 `report.js` 已有對應的友善錯誤訊息(`CONFIG.RESPONSES.*.VALIDATION.DUPLICATE_REPORT`)。新增 2 組迴歸測試(`test/gas-code.test.js`)。 +- **房號/床號後端格式驗證對齊前端**:`gas/Code.gs` 的 `writeReport()` 後端正則式原本比前端寬鬆——房號僅檢查「英數字與連字號」(`/^[A-Za-z0-9-]{1,8}$/`)、床號允許 1–3 位數字(`/^[0-9]{1,3}$/`)——可被繞過前端直接對 GAS Web App 送出格式不符的資料並成功寫入試算表。現已收緊為與 `js/report.js`、`index.html` 的 `pattern` 屬性完全一致:房號 `/^(H|I|G|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`(須以 H、I、G、FA~FF 開頭,後接 1–4 位數字,可選一個連字號再接數字)、床號 `/^[0-9]$/`(僅 1 位數字)。 + +#### 🛠️ 本次稽核修復(Audit Fixes) + +- **[BUG-52/中] 床號格式描述全站過時**:`README.md`、`AGENTS.md`、`doc/architecture.md`(§5.5.2 驗證矩陣)、`doc/data-model.md`(ER 圖、§2.1、§3 驗證矩陣)、`doc/requirements.md`(§3.4)、`doc/project-memory.md`(已確認業務規則)共 6 份文件仍描述床號為「1–3 位數字」/`/^[0-9]{1,3}$/`,與上述 BUG-BED-01 修復後的實際規則(僅 1 位數字)不符。已全數更新為「1 位數字」/`/^[0-9]$/`。 +- **[BUG-53/中] 房號格式描述全站過時**:同一批文件仍描述房號格式為「僅限英數字與連字號」/`/^[A-Za-z0-9-]{1,8}$/`,與 BUG-ROOM-01 修復後的實際規則不符;`doc/data-model.md`、`doc/requirements.md` 內原有的房號範例「A123」在新規則下更是**不合法**範例(`A` 不在允許前綴 H/I/G/FA~FF 之列)。已全數更新為正確格式說明,範例改為合法的 `H0111`。 +- **[BUG-54/高] 報修成功畫面描述與 v1.4.5(BUG-50)已移除的實作直接矛盾**:`doc/project-memory.md`「已確認業務規則」章節、`doc/requirements.md` §3.4、`doc/architecture.md` §4.3 三處仍描述「送出成功後隱藏頂部標題列,僅保留『報修成功!』與進度條(2 秒後自動關閉)」——這正是 `CHANGELOG.md` v1.4.5(BUG-50)記載「經逐一 `grep` 確認零引用後移除」的死碼(`#modal-success-view`/`.has-success`/`.is-hidden`),且與 `project-memory.md` 自身下方 v1.4.4/v1.4.5 決策記錄章節的敘述自相矛盾。已更正為實際行為:送出成功後 Modal **立即關閉**,成功訊息以**聊天泡泡**(bot 訊息)呈現並顯示「回主選單/報修」按鈕,Header 標題列全程不受影響。 +- **[BUG-55/低] 單元測試數量全站過時**:`doc/architecture.md` §5.9、`doc/requirements.md` §4 仍寫「53 項/53/53」;因本版本新增的重複送出防護迴歸測試(見上方新功能),`npm test` 實測現為 **55 項全數通過**,`npm run lint` 維持 0 error / 0 warning。已更新兩份文件(`doc/todo.md` 既有的 G 輪/I 輪為歷史紀錄,維持原樣不予更動,另於下方新增的 J 輪記錄本次變動)。 +- **[BUG-56/低] `index.html` 腳本載入順序註解遺漏 `wifi-modal.js`**:第 326 行註解「載入順序:i18n → config → counter → intent → report → query → teams → chat」自 v1.4.4 新增 `js/wifi-modal.js` 以來就一直沒有把它加進這行列表(實際載入順序在 `teams` 與 `chat` 之間),與 v1.4.1(BUG-27)修復的是同一類別的遺漏,只是這次遺漏的是後來才加入的模組。已補上。 +- **[BUG-57/低] `test/validation.test.js` 的 `VALID_INTENTS` 白名單與正式程式碼不同步**:該測試檔並未 `require` `js/intent.js` 或 `gas/Code.gs`,而是自行重新實作一份驗證邏輯;其 `VALID_INTENTS` 陣列自建立以來僅有 6 項(缺少 `BUTTON_QUERY`),自 v1.4.0(commit `9be9b3a`)新增 `BUTTON_QUERY` 意圖後即與正式程式碼的 7 項不同步,歷經 v1.4.1/v1.4.2/v1.4.3/v1.4.5 多次稽核皆未發現。此缺口不影響 `npm test` 是否通過(無斷言檢查 `BUTTON_QUERY` 是否被此陣列接受),但代表「白名單過濾機制測試」實際涵蓋不完整。已補上 `BUTTON_QUERY` 並新增對應迴歸斷言。 +- **[BUG-58/低] `doc/architecture.md` §5.5.3 查詢學號驗證程式碼片段與實際邏輯不符**:文件內範例程式碼為單一 `if` 判斷合併「空值」與「格式錯誤」,且回傳寫死的中文句子 `'學號格式錯誤'`;但 `gas/Code.gs` 的 `queryReport()` 實際上是兩個獨立判斷、回傳兩種不同的全大寫錯誤代碼(`VALIDATION_QUERY_STUDENT_ID_REQUIRED`/`VALIDATION_QUERY_STUDENT_ID_FORMAT`),此即 v1.4.0(BUG-12)修復的核心,緊接在程式碼片段下方的文字說明其實已正確描述此點,僅程式碼片段本身未同步更新,兩者自相矛盾。已重寫程式碼片段以符合實際邏輯與錯誤代碼慣例。 + +#### 📄 文件同步(Documentation Sync) + +- `README.md`:功能特色、已知安全性說明表格補上重複送出防護與正確的床號/房號格式 +- `AGENTS.md`:GAS 開發規範補上重複送出防護規則、修正床號/房號正則式 +- `doc/architecture.md`:新增 §5.4.3 重複送出防護小節;修正 §4.3 資料流(移除已不存在的「2 秒進度條」描述,改為聊天泡泡呈現;補上去重檢查步驟);修正 §5.5.2 驗證矩陣、§5.5.3 程式碼片段;§5.9 測試數更新為 55;§5.10 OWASP 對照補上去重防護對應 A04 +- `doc/data-model.md`:修正 ER 圖床號註解、§2.1 房號範例與床號描述、§3 驗證矩陣;§4 資料生命週期補上去重防護說明 +- `doc/requirements.md`:§3.4 移除過時的成功畫面描述、修正房號/床號驗證規則與範例、補上重複送出防護說明;§4 測試數更新為 55 +- `doc/project-memory.md`:修正「已確認業務規則」章節的床號位數與報修成功畫面描述;新增「v1.4.6 決策記錄」章節記錄本次退補文件與稽核修復 +- `doc/todo.md`:新增「J 輪:重複送出防護、房號床號驗證對齊、文件稽核」章節(既有 A~I 輪維持原樣不予更動) +- `test/validation.test.js`:`VALID_INTENTS` 補上 `BUTTON_QUERY`,新增對應迴歸斷言 +- `index.html`:腳本載入順序註解補上 `wifi-modal` +- 全站(`package.json`、`package-lock.json`、`README.md`、`CHANGELOG.md`、`AGENTS.md`、`doc/*.md`)版本號統一升級為 **v1.4.6** + +#### ℹ️ 資訊性附註(供專案擁有者參考,本次稽核未逕行變更程式碼) + +- **Gemini 模型清單時效性查核**:以網路搜尋查證 Google 官方文件(2026-09),確認 `GEMINI_MODELS_FALLBACK` 現有 6 個模型(`gemini-3.5-flash-lite`、`gemini-3.1-flash-lite`、`gemini-3.6-flash`、`gemini-3.5-flash`、`gemini-3.1-pro-preview`、`gemini-3-flash-preview`)**目前皆仍為有效、正常運作的模型**,無任何一個已被官方宣告下架日期,本節無需修改程式碼。僅供參考:Google 已於 2026-08-13 發布 `gemini-3.7-flash`(官方描述為目前程式碼與 Agent 任務表現最佳的 Flash 模型),且 `gemini-3.1-flash-lite` 已有預告下架日期 2027-05-07(尚有 8 個月餘裕,非急迫)。是否要將 `gemini-3.7-flash` 納入備援清單或調整優先順序,屬於成本/配額層面的產品決策,建議由專案擁有者評估後另行處理,本次稽核不代為決定。 + +#### 🧪 測試 + +- `test/gas-code.test.js` 新增 2 組迴歸測試(BUG-DUP-01 去重機制),`test/validation.test.js` 新增 1 組迴歸斷言(BUG-57),總測試數由 53 增至 **55**,全數通過;`npm run lint` 維持 **0 error / 0 warning**。 + +### English + +This release has two parts: (1) retroactively documenting two backend fixes that were merged after v1.4.5 but before this formal release — a **120-second duplicate-report guard** and **room/bed backend validation alignment**; and (2) a full line-by-line source-vs-documentation audit (covering every `js/*.js` file, `gas/Code.gs`, `css/style.css`, `index.html`, `test/*.js`, `AGENTS.md`, `README.md`, `CHANGELOG.md`, and `doc/*.md`, including actually running `npm test`, `npm run lint`, and cross-checking `git log`/`git blame` history) that fixed 7 documentation-vs-code inconsistencies. This audit **found no production code defects**; `css/style.css` required no changes after review. + +#### ✨ New Features (Retroactively Documented) + +- **120-second duplicate-report guard on the repair form**: `writeReport()` in `gas/Code.gs` now builds a fingerprint from "student ID + room number + first 50 characters of the description," MD5-hashes it, and uses it as a `CacheService` key. A repeat hit within 120 seconds is rejected outright with `DUPLICATE_REPORT`, preventing accidental double-submits or short-burst scripted resubmission of the same case. The fingerprint is only cached after validation passes and the row is actually written, so failed requests don't consume dedup quota. The frontend (`report.js`) already has a matching friendly error message (`CONFIG.RESPONSES.*.VALIDATION.DUPLICATE_REPORT`). Two new regression tests were added (`test/gas-code.test.js`). +- **Room/bed backend validation aligned with the frontend**: `writeReport()`'s backend regexes were previously looser than the frontend's — room number only checked "alphanumeric plus hyphen" (`/^[A-Za-z0-9-]{1,8}$/`) and bed number allowed 1–3 digits (`/^[0-9]{1,3}$/`) — meaning a request that bypassed the frontend could hit the GAS Web App directly with out-of-spec data and still get written to the spreadsheet. Both are now tightened to exactly match the `pattern` attributes in `js/report.js`/`index.html`: room `/^(H|I|G|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i` (must start with H, I, G, or FA–FF, followed by 1–4 digits, with an optional dash and more digits) and bed `/^[0-9]$/` (a single digit only). + +#### 🛠️ Audit Fixes + +- **[BUG-52/Medium] Bed-number format stale across the docs**: `README.md`, `AGENTS.md`, `doc/architecture.md` (§5.5.2 validation matrix), `doc/data-model.md` (ER diagram, §2.1, §3 validation matrix), `doc/requirements.md` (§3.4), and `doc/project-memory.md` (confirmed business rules) — six files — still described bed number as "1–3 digits" / `/^[0-9]{1,3}$/`, which no longer matches the actual rule after the BUG-BED-01 fix above (single digit only). All updated to "1 digit" / `/^[0-9]$/`. +- **[BUG-53/Medium] Room-number format stale across the docs**: the same set of files still described room format as "alphanumeric and hyphen only" / `/^[A-Za-z0-9-]{1,8}$/`, which no longer matches the BUG-ROOM-01 fix; the room example "A123" used in `doc/data-model.md` and `doc/requirements.md` is now actually **invalid** under the new rule (`A` isn't an allowed prefix). All updated with the correct format description and a valid example (`H0111`). +- **[BUG-54/High] Success-screen description directly contradicts what v1.4.5 (BUG-50) already removed**: `doc/project-memory.md`'s "confirmed business rules" section, `doc/requirements.md` §3.4, and `doc/architecture.md` §4.3 all still described "the header is hidden after a successful submission, leaving only a 'Success!' message and a 2-second auto-closing progress bar" — but this is exactly the dead code that `CHANGELOG.md`'s v1.4.5 entry (BUG-50) documents as removed after an exhaustive grep confirmed zero references, and it directly contradicts `project-memory.md`'s own v1.4.4/v1.4.5 decision-log section further down the same file. Corrected to describe actual behavior: the modal closes **immediately** on success, the success message appears as a **chat bubble** (bot message) with "Back to Main Menu"/"Request Repair" buttons, and the header is never hidden. +- **[BUG-55/Low] Unit test count stale across the docs**: `doc/architecture.md` §5.9 and `doc/requirements.md` §4 still said "53 / 53/53"; with the two new dedup regression tests above, `npm test` now shows **55 passing**, and `npm run lint` remains 0 errors/0 warnings. Both docs updated (`doc/todo.md`'s existing Round G/Round I entries are historical records and were left untouched; a new Round J entry below records this change). +- **[BUG-56/Low] `index.html` script-load-order comment missing `wifi-modal.js`**: line 326's comment ("load order: i18n → config → counter → intent → report → query → teams → chat") was never updated when `js/wifi-modal.js` was added in v1.4.4 (it actually loads between `teams` and `chat`) — the same category of oversight as v1.4.1's BUG-27, just recurring for a module added later. Fixed. +- **[BUG-57/Low] `test/validation.test.js`'s `VALID_INTENTS` whitelist out of sync with the real code**: this test file doesn't `require` `js/intent.js` or `gas/Code.gs` — it re-implements its own copy of the validation logic. Its `VALID_INTENTS` array has had only 6 entries (missing `BUTTON_QUERY`) since the file was created, and has been out of sync with the real 7-entry list since `BUTTON_QUERY` was added in v1.4.0 (commit `9be9b3a`) — a gap that survived the v1.4.1/v1.4.2/v1.4.3/v1.4.5 audits. It doesn't affect whether `npm test` passes (no assertion exercises `BUTTON_QUERY` against this array), but it meant the "intent whitelist filter" test wasn't actually covering all real intents. Added `BUTTON_QUERY` to the array plus a matching regression assertion. +- **[BUG-58/Low] `doc/architecture.md` §5.5.3 code snippet doesn't match the real query-validation logic**: the documented snippet combines "empty" and "malformed" into a single `if` check and returns a hardcoded Chinese sentence, `'學號格式錯誤'`; the real `queryReport()` in `gas/Code.gs` actually uses two separate checks returning two distinct all-caps error codes (`VALIDATION_QUERY_STUDENT_ID_REQUIRED` / `VALIDATION_QUERY_STUDENT_ID_FORMAT`) — which is precisely what v1.4.0's BUG-12 fixed, and which the prose immediately below the snippet correctly describes, so the snippet contradicted the very next line. Rewritten to match the real logic and error-code convention. + +#### 📄 Documentation Sync + +- `README.md`: added the duplicate-report guard to the feature list and security table; corrected bed/room format +- `AGENTS.md`: added the duplicate-report-guard rule to the GAS backend rules; corrected the bed/room regexes +- `doc/architecture.md`: added new §5.4.3 on the duplicate-report guard; fixed §4.3 data flow (removed the no-longer-true "2-second progress bar," replaced with the chat-bubble behavior; added the dedup-check step); fixed the §5.5.2 validation matrix and §5.5.3 code snippet; §5.9 test count updated to 55; §5.10 OWASP mapping now references the dedup guard under A04 +- `doc/data-model.md`: fixed the ER diagram's bed-number annotation, §2.1's room example and bed description, and the §3 validation matrix; §4 lifecycle table now notes the dedup guard +- `doc/requirements.md`: §3.4 no longer describes the removed success screen; room/bed rules and examples corrected; duplicate-report guard documented; §4 test count updated to 55 +- `doc/project-memory.md`: corrected the bed-digit-count and success-screen lines under "confirmed business rules"; added a new "v1.4.6 decision log" section documenting this release's retroactive documentation and audit fixes +- `doc/todo.md`: added a new "Round J" section (Rounds A–I are historical and were left untouched) +- `test/validation.test.js`: added `BUTTON_QUERY` to `VALID_INTENTS` plus a matching regression assertion +- `index.html`: script-load-order comment now includes `wifi-modal` +- Bumped every version string across `package.json`, `package-lock.json`, `README.md`, `CHANGELOG.md`, `AGENTS.md`, and `doc/*.md` to **v1.4.6** + +#### ℹ️ Informational Notes (For Owner Awareness — Not Acted On) + +- **Gemini model-list currency check**: web research against Google's official documentation (as of September 2026) confirms all 6 models in `GEMINI_MODELS_FALLBACK` (`gemini-3.5-flash-lite`, `gemini-3.1-flash-lite`, `gemini-3.6-flash`, `gemini-3.5-flash`, `gemini-3.1-pro-preview`, `gemini-3-flash-preview`) are **currently valid and functioning**, with no announced shutdown date for any of them — no code change is needed here. For awareness only: Google released `gemini-3.7-flash` on 2026-08-13 (officially described as its strongest current Flash model for coding/agentic work), and `gemini-3.1-flash-lite` has an announced (non-urgent — over 8 months out) shutdown date of 2027-05-07. Whether to add `gemini-3.7-flash` to the fallback list or reorder priority is a cost/quota product decision left to the project owner; this audit did not act on it. + +#### 🧪 Testing + +- `test/gas-code.test.js` gained 2 new regression tests (BUG-DUP-01 dedup behavior); `test/validation.test.js` gained 1 new regression assertion (BUG-57). Total test count rose from 53 to **55**, all passing; `npm run lint` remains **0 errors / 0 warnings**. --- diff --git a/README.md b/README.md index a9b97b5..c716914 100644 --- a/README.md +++ b/README.md @@ -4,12 +4,12 @@ [![GitHub Pages](https://img.shields.io/badge/GitHub%20Pages-Live-brightgreen)](https://chinglin-k.github.io/FCU_NE_Chat_Bot/) [![Run Validation Unit Tests](https://github.com/chinglin-k/FCU_NE_Chat_Bot/actions/workflows/test.yml/badge.svg)](https://github.com/chinglin-k/FCU_NE_Chat_Bot/actions/workflows/test.yml) -[![Version](https://img.shields.io/badge/version-v1.4.5-blue.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-v1.4.6-blue.svg)](CHANGELOG.md) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -**版本 / Version**:v1.4.5 +**版本 / Version**:v1.4.6 -**最後更新 / Last Updated**:2026-08-29 +**最後更新 / Last Updated**:2026-09-05 --- @@ -18,7 +18,7 @@ - 📚 **網路教學**:提供 Windows / Mac 宿舍網路設定 PDF 教學,另有站內 **Wi-Fi 機設定教學 Modal**(4 步驟圖文說明,v1.4.4 新增) - ⚙️ **常見問題**:轉接器驅動程式、WiFi 帳號密碼、寢室 WiFi 訊號、冷氣電費儲值等常見問題解答 * 🎯 支援「子主題精準回覆」:問轉接器只顯示轉接器卡片、問帳密只顯示帳密卡片 -- 🔧 **線上報修通報**:填寫報修表單自動寫入 Google 試算表(前後端雙重格式驗證:學號 1 字母+7 數字、手機 10 位數字、床號 1–3 位數字) +- 🔧 **線上報修通報**:填寫報修表單自動寫入 Google 試算表(前後端雙重格式驗證:學號 1 字母+7 數字、手機 10 位數字、床號 1 位數字、房號 H/I/G/FA~FF 開頭+1–4 位數字);120 秒內偵測到相同學號+房號+問題描述的重複送出會自動攔截,避免試算表出現重複案件 - 🔍 **報修案件查詢**:學生輸入學號即可查詢自己的報修案件狀態(僅顯示安全欄位,不含手機號碼等敏感資訊) - 🤖 **LLM 語意分析**:透過 Gemini API 三層 RPM 分級、共 6 個模型自動備援機制判斷使用者意圖 * 支援「理解失敗」 vs 「系統錯誤」兩層 fallback 訊息區分 @@ -157,7 +157,8 @@ GitHub → Settings → Pages → Source: **main** / **(root)** | 請求授權 | 一次性 Session Token(120 秒有效、用一次即失效),配合裝置級 Client ID 做流量限制 | | 流量防護 | 五組雙層(使用者級+全域級)CacheService 限流:`classify` 使用者 12/分鐘、全域 60/分鐘;`report` 使用者 5/分鐘、全域 20/分鐘;`query` 使用者 10/分鐘、全域 40/分鐘;`counter_get` 使用者 30/分鐘、全域 120/分鐘;`counter_increment` 使用者 3/分鐘、全域 500/分鐘(v1.3.1:修復原本使用者級上限 999999 形同不限制的缺陷,並將全域上限由 30 調升為 500,避免新生入住等尖峰時段誤擋合法計數)| | 濫用防護(報修表單) | reCAPTCHA v3 隱形驗證(風險分數門檻 0.5),防止 GAS_URL 外洩後遭腳本大量送出假報修單 | -| 學號 / 手機 / 床號 / 房號格式 | 前端與 GAS 端雙重驗證,且後端一律「先必填、後格式」(學號:1 字母+7 數字;手機:10 位數字;床號:1–3 位數字;房號:僅限英數字與連字號)。v1.3.1 修復:舊版後端寫法在欄位為空字串時會整段跳過驗證,等同必填形同虛設,現已修正 | +| 學號 / 手機 / 床號 / 房號格式 | 前端與 GAS 端雙重驗證,且後端一律「先必填、後格式」(學號:1 字母+7 數字;手機:10 位數字;床號:**1 位數字**;房號:**須以 H、I、G、FA~FF 開頭,後接 1–4 位數字,可選一個連字號再接數字**)。v1.3.1 修復:舊版後端寫法在欄位為空字串時會整段跳過驗證,等同必填形同虛設,現已修正。v1.4.6:床號/房號後端格式已收緊為與前端完全一致(BUG-BED-01、BUG-ROOM-01),修復前後端寬鬆度不一致、可被繞過前端直接送出不合規格資料的問題 | +| 重複送出防護(報修表單) | 120 秒內偵測到相同「學號+房號+問題描述前 50 字」的請求(MD5 雜湊比對)直接拒絕並回傳 `DUPLICATE_REPORT`,防止使用者手滑或腳本在短時間內重複寫入相同案件(v1.4.6 / BUG-DUP-01) | | Prompt Injection | 輸入截斷 500 字、移除控制字元、Zero-Width 字元、引號隔離輸入 | | XSS | 使用者輸入經 `_escapeHTML` 處理(含 `"` `'` 轉義);Bot 訊息來自內部常數,僅供內部常數使用 Markdown 渲染 | | 後端欄位驗證 | GAS `writeReport()` 先檢查必填(trim 後不得為空)、再檢查格式、最後截斷長度,三階段皆通過才寫入試算表(v1.3.1 修復必填繞過問題) | diff --git a/RELEASE_NOTES_v1.4.6.md b/RELEASE_NOTES_v1.4.6.md new file mode 100644 index 0000000..aaa9027 --- /dev/null +++ b/RELEASE_NOTES_v1.4.6.md @@ -0,0 +1,115 @@ +# v1.4.6 — 重複送出防護、房號床號驗證對齊與文件稽核 + +**發布日期 / Release Date**:2026-09-05 +**上一版本 / Previous**:[v1.4.5](https://github.com/chinglin-k/FCU_NE_Chat_Bot/releases/tag/v1.4.5) +**完整比對 / Full Diff**:`v1.4.5...v1.4.6` + +> 本文件為 GitHub Release 頁面用文字,內容與 `CHANGELOG.md` 的 `[v1.4.6]` 條目一致, +> 另加上「升級須知」與「稽核方法」兩節,供閱讀 Release 頁面的使用者快速掌握重點。 + +--- + +## 🔑 重點摘要 / TL;DR + +- 🛡️ **新增報修表單 120 秒重複送出防護**,避免手滑或腳本造成試算表重複案件(`DUPLICATE_REPORT`) +- 🔒 **房號/床號後端格式驗證收緊為與前端完全一致**,修復可被繞過前端直接送出不合規格資料的問題 +- 📄 **修復 7 處文件與程式碼不一致之處**(BUG-52 ~ BUG-58),包含一處文件內部自相矛盾的高風險項目(BUG-54) +- ✅ 單元測試由 53 項增至 **55 項全數通過**,ESLint 維持 **0 error / 0 warning** +- 🔢 全站版本號統一升級為 **v1.4.6** +- ℹ️ 一則資訊性附註:Gemini 模型清單現況查核(未變更程式碼,供專案擁有者參考) + +--- + +## ✨ 新功能(回補文件) + +這兩項功能已於 **2026-09-04** 合併上線(PR #27、#28),但發布時未同步更新文件;本次版本一併補齊文件並正式收錄進 `CHANGELOG.md`: + +### 報修表單 120 秒重複送出防護 + +`gas/Code.gs` 的 `writeReport()` 新增去重機制: + +1. 以「**學號 + 房號 + 問題描述前 50 字**」組成字串 +2. 取 **MD5 雜湊**後編碼為 `CacheService` 的 key +3. 120 秒內若命中相同指紋,直接拒絕並回傳 `DUPLICATE_REPORT` +4. **僅於格式驗證通過、且實際寫入試算表成功後**才存入快取,避免驗證失敗的請求誤佔用去重配額 + +前端 `report.js` 已備妥對應的雙語友善錯誤訊息。新增 2 組迴歸測試。 + +### 房號/床號後端格式驗證對齊前端 + +| 欄位 | 修復前(後端) | 修復後(前後端一致) | +|---|---|---| +| 房號 | `/^[A-Za-z0-9-]{1,8}$/`(僅檢查英數字與連字號) | `/^(H\|I\|G\|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`(須以 H、I、G、FA~FF 開頭) | +| 床號 | `/^[0-9]{1,3}$/`(允許 1–3 位數字) | `/^[0-9]$/`(僅 1 位數字) | + +修復前,後端正則式比前端寬鬆,代表**任何人繞過前端、直接呼叫 GAS Web App**,都能送出前端表單根本不允許的格式並成功寫入試算表——這正是後端驗證存在的意義被架空的典型案例。 + +--- + +## 🛠️ 本次稽核修復(BUG-52 ~ BUG-58) + +本次針對全專案原始碼與文件進行逐行一致性稽核,範圍涵蓋全部 `js/*.js`、`gas/Code.gs`、`css/style.css`、`index.html`、`test/*.js`、`AGENTS.md`、`README.md`、`CHANGELOG.md`、`doc/*.md`,並實際執行 `npm test`、`npm run lint`、`git log`/`git blame` 歷史比對。**未發現任何生產程式碼缺陷**;`css/style.css` 經比對後無需修改。 + +| 編號 | 嚴重度 | 問題 | 影響範圍 | +|---|---|---|---| +| BUG-52 | 中 | 床號格式描述全站過時(仍寫「1–3 位數字」) | 6 份文件 | +| BUG-53 | 中 | 房號格式描述全站過時,且範例「A123」在新規則下不合法 | 6 份文件 | +| **BUG-54** | **高** | 報修成功畫面描述與 v1.4.5(BUG-50)已移除的實作**直接矛盾**,其中一處甚至與同一文件下方的決策記錄自相矛盾 | 3 份文件 | +| BUG-55 | 低 | 單元測試數量過時(53 → 應為 55) | 2 份文件 | +| BUG-56 | 低 | `index.html` 腳本載入順序註解遺漏 `wifi-modal.js` | 1 個檔案 | +| BUG-57 | 低 | 測試檔獨立維護的意圖白名單漏掉 `BUTTON_QUERY`,自 v1.4.0 起未同步 | 1 個檔案 | +| BUG-58 | 低 | 文件內查詢驗證程式碼片段與實際雙錯誤代碼邏輯不符,且與同段文字說明矛盾 | 1 個檔案 | + +**BUG-54 特別說明**:`doc/project-memory.md` 的「已確認業務規則」章節寫著「報修成功介面僅保留『報修成功!』與進度條,隱藏標題列 Header」——但同一份文件再往下 30 行的「v1.4.4/v1.4.5 決策記錄」卻明確記載這個畫面已於 v1.4.5 當作死碼移除。這代表文件在**同一個檔案內就自相矛盾**,且該過時描述還擴散到了 `doc/requirements.md` 與 `doc/architecture.md`。這類「新程式碼已上線、但描述舊行為的文字散落在文件各處未被清乾淨」的情況,正是本次逐行稽核要抓的重點。 + +--- + +## 📄 文件同步 + +以下檔案已更新內容並統一升版為 v1.4.6: + +`README.md`・`AGENTS.md`・`CHANGELOG.md`・`doc/architecture.md`・`doc/data-model.md`・`doc/requirements.md`・`doc/project-memory.md`・`doc/todo.md`・`package.json`・`package-lock.json` + +另修正 `test/validation.test.js`(BUG-57)與 `index.html`(BUG-56)。 + +`doc/architecture.md` 新增 **§5.4.3 重複送出防護** 小節;`doc/todo.md` 新增 **J 輪** 記錄本次工作,A~I 輪等既有歷史紀錄維持原樣不予更動(與本專案一貫做法一致:修正現況描述,但不竄改歷史紀錄本身)。 + +--- + +## 🧪 測試 + +``` +npm test → 55 pass / 0 fail(原 53 項,新增 2 組去重迴歸測試) +npm run lint → 0 error / 0 warning +``` + +--- + +## ℹ️ 資訊性附註(未變更程式碼) + +以網路搜尋查證 Google 官方文件(2026-09 現況),確認 `GEMINI_MODELS_FALLBACK` 現有 6 個模型(`gemini-3.5-flash-lite`、`gemini-3.1-flash-lite`、`gemini-3.6-flash`、`gemini-3.5-flash`、`gemini-3.1-pro-preview`、`gemini-3-flash-preview`)**目前皆仍有效、正常運作**,無任何一個已被官方宣告下架日期。 + +僅供專案擁有者參考,本次稽核未代為決定: + +- Google 已於 **2026-08-13** 發布 `gemini-3.7-flash`,官方描述為目前程式碼/Agent 任務表現最佳的 Flash 模型 +- `gemini-3.1-flash-lite` 已有預告下架日期 **2027-05-07**(尚有 8 個月餘裕,非急迫) + +是否將 `gemini-3.7-flash` 納入備援清單、或調整既有模型的優先順序,屬於成本/RPM 配額層面的產品決策,建議由專案擁有者評估後另行處理。 + +--- + +## ⬆️ 升級須知 + +- 本版本**不需要**任何手動遷移步驟或環境變數變更 +- 房號/床號後端驗證收緊**不影響**任何透過正規前端表單送出的請求——前端從一開始就套用相同(甚至更早生效)的規則;唯一受影響的是繞過前端、直接呼叫 GAS Web App 且送出不合規格資料的請求(無論是誤用或惡意嘗試),這些請求現在會被後端正確拒絕,這正是本次修復的目的 +- 若你的 GAS 部署是手動複製貼上 `gas/Code.gs`(而非透過 clasp 等工具同步),請記得重新部署最新版程式碼,才能套用重複送出防護與房號/床號驗證修復 +- 建議部署後執行一次 `npm test && npm run lint` 確認本地環境結果與本文件記載的 55/0 一致 + +--- + +## 📎 相關連結 + +- 完整技術細節:[`CHANGELOG.md` — \[v1.4.6\]](../CHANGELOG.md) +- 架構設計文件:[`doc/architecture.md`](../doc/architecture.md) +- 資料模型文件:[`doc/data-model.md`](../doc/data-model.md) +- 需求規格書:[`doc/requirements.md`](../doc/requirements.md) diff --git a/VIBECODING_PROMPT_v1.4.6.md b/VIBECODING_PROMPT_v1.4.6.md new file mode 100644 index 0000000..1de2ed9 --- /dev/null +++ b/VIBECODING_PROMPT_v1.4.6.md @@ -0,0 +1,182 @@ +# 給 LLM 的執行提示(Vibecoding Prompt)— FCU_NE_Chat_Bot → v1.4.6 + +> **使用方式**:把這份檔案的全部內容,連同下方列出的 10 個「已修正完成檔案」一併 +> 貼給你的 LLM 編碼工具(Claude Code、Cursor、Copilot Workspace 等具備檔案讀寫權限 +> 的 Agent),並指示它「依照這份文件,把附上的檔案內容套用到 repo 對應路徑」。 +> 所有內容已在乾淨環境中實際執行 `npm test` / `npm run lint` 驗證通過, +> 可直接覆蓋對應檔案,不需要人工再逐行核對 diff。 + +--- + +## 0. 給 LLM 的角色說明(請直接貼給你的編碼工具) + +你正在維護 `chinglin-k/FCU_NE_Chat_Bot`(逢甲大學福星宿舍網路報修 Chatbot)。 +這是一次「版本發布 + 文件稽核」的變更:**沒有新的產品功能要開發**,所有程式邏輯 +早已存在且已通過測試;你的任務純粹是把 **12 個既有檔案的內容替換成下方提供的 +最終版本**、**新增 2 個檔案**,然後提交。請完整遵守以下規則: + +1. **只動下方列出的 14 個檔案**。特別是:**不要**修改 `css/style.css`、 + `gas/Code.gs`、任何 `js/*.js`(`test/validation.test.js` 除外)——這些檔案 + 已經過逐行核對,內容正確,不需要也不應該再變動。 +2. 每個列出的檔案都是**完整檔案內容**,用「整檔覆蓋」的方式套用,不是 diff/patch。 +3. 套用完所有檔案後,依照「§3 驗證步驟」實際執行測試與 lint,**確認結果與本文件 + 記載的一致**(55 項測試全過、0 error / 0 warning)才算完成;如果結果不一致, + 停下來回報差異,不要自行「修正」到符合預期。 +4. **不要**修改 `CHANGELOG.md` 中 `## [v1.4.5]` 以下的任何既有段落,也不要修改 + `doc/todo.md` 中 A 輪~I 輪的既有段落——這些是歷史紀錄,本專案的既有慣例是 + 「發現舊紀錄有誤就用註解/附註標明,絕不悄悄竄改」,已修正版檔案中的這些段落 + 維持原文字,請勿再改動。 +5. `gas/Code.gs` 內的 `GEMINI_MODELS_FALLBACK` 陣列**不要修改**(見 §4 資訊性附註, + 這是留給專案擁有者自行決定的產品決策,不在本次變更範圍)。 +6. 提交前務必依 `AGENTS.md` 既有慣例:分支命名 `feature/*`、`fix/*` 或 `docs/*`, + commit message 格式 `type: 描述(繁體中文)`(如 `docs: v1.4.6 全站文件稽核與版本號統一`), + 透過 Pull Request 合併至 `main`,不要直接 push 到 `main`。 + +--- + +## 1. 背景:這次變更在修什麼 + +於 2026-09-04,兩個安全性修復已合併上線但未同步文件(commit `741a0f3` +「BUG-DUP-01 重複送出防護」、commit `642758e`「BUG-ROOM-01/BUG-BED-01 房號床號 +驗證對齊」)。本次(2026-09-05)對全 repo 進行逐行「程式碼 vs. 文件」稽核, +確認程式碼本身**沒有任何缺陷**,但發現 **7 處文件內容與現況不符**(詳見 §4), +其中一處(BUG-54)甚至造成單一文件內部前後矛盾。本次變更即修正這 7 處文件問題、 +補齊上述兩項未文件化的安全修復、將全站版本號統一升級為 **v1.4.6**,並新增本 +Release 所需的說明文件。 + +完整背景與逐項理由請見附上的 `CHANGELOG.md`(已內含新的 `[v1.4.6]` 條目, +中英雙語,逐條列出 BUG-52 ~ BUG-58)。 + +--- + +## 2. 要套用的檔案清單(14 個) + +### 2.1 覆蓋既有檔案(12 個)— 已提供完整最終內容 + +| # | 檔案路徑 | 這次改了什麼(一句話) | +|---|---|---| +| 1 | `package.json` | `version` 欄位 `1.4.5` → `1.4.6` | +| 2 | `package-lock.json` | 兩處 `version` 欄位同步改為 `1.4.6` | +| 3 | `README.md` | 版本號/日期;床號改「1 位數字」;房號補上正確格式;新增重複送出防護說明(功能特色 + 安全表格新增一列) | +| 4 | `AGENTS.md` | 版本號/日期;GAS 開發規範內床號/房號正則式修正;新增「重複送出防護」規則段落 | +| 5 | `CHANGELOG.md` | 版本號/日期;**於檔案最上方新增完整的 `[v1.4.6]` 條目**(中英雙語),`[v1.4.5]` 以下完全不變 | +| 6 | `doc/architecture.md` | 版本號/日期;§1 圖表小幅補充;§4.3 移除已不存在的「2 秒進度條」描述並補上去重步驟;新增 §5.4.3;§5.5.2/§5.5.3 修正;§5.9 測試數 53→55;§5.10 補充 | +| 7 | `doc/data-model.md` | 版本號/日期;ER 圖床號註解;§2.1 房號範例與床號描述;§3 驗證矩陣表;§4 補上去重防護說明 | +| 8 | `doc/requirements.md` | 版本號/日期;§3.4 移除過時成功畫面描述、修正房號床號規則與範例、補上去重防護;§4 測試數 53→55 | +| 9 | `doc/project-memory.md` | 版本號/日期;「已確認業務規則」修正床號位數與成功畫面描述(修復自相矛盾);新增「v1.4.6 決策記錄」章節 | +| 10 | `doc/todo.md` | 版本號/日期;新增「J 輪」章節,A~I 輪維持原樣 | +| 11 | `test/validation.test.js` | `VALID_INTENTS` 補上 `BUTTON_QUERY`(BUG-57),新增 1 組迴歸斷言 | +| 12 | `index.html` | 第 326 行載入順序註解補上 `wifi-modal`(BUG-56) | + +> 這 12 個檔案的完整最終內容,就是你在本次對話中另外收到的、與上述路徑同名的 +> 已修正檔案——請逐一開啟,用其**完整內容**覆蓋 repo 內對應路徑的檔案。 + +### 2.2 新增檔案(2 個) + +| # | 建議路徑 | 用途 | +|---|---|---| +| 13 | `RELEASE_NOTES_v1.4.6.md`(repo 根目錄,或直接貼入 GitHub Release 頁面說明欄,二擇一) | v1.4.6 的 GitHub Release 文字稿,可直接複製貼上到「Releases → Draft a new release → v1.4.6」的說明欄位 | +| 14 | 本檔案(`VIBECODING_PROMPT_v1.4.6.md`) | 純粹是這次任務的執行說明,**不需要**加進 repo,套用完其他 13 個檔案後可捨棄 | + +--- + +## 3. 驗證步驟(套用完檔案後執行) + +```bash +npm ci +npm test +npm run lint +``` + +**預期結果**(與本次實際執行結果一致): + +``` +# tests 55 +# pass 55 +# fail 0 +``` + +``` +> fcu-ne-chat-bot@1.4.6 lint +> eslint . "gas/**/*.gs" + +(無任何輸出 = 0 error / 0 warning) +``` + +再額外確認: + +```bash +node -e "console.log(require('./package.json').version)" # 應輸出 1.4.6 +grep -c "BUTTON_QUERY" test/validation.test.js # 應 ≥ 2(陣列 + 斷言各一次以上) +grep "載入順序" index.html # 應包含 wifi-modal +``` + +若任何一項與預期不符,代表覆蓋檔案時有遺漏或版本不對,請重新核對 §2.1 清單, +不要手動「補洞」——所有正確內容都已在附上的檔案裡。 + +--- + +## 4. 完整問題清單(供 commit / PR 說明引用) + +以下與 `CHANGELOG.md` `[v1.4.6]` 條目內容一致,供你在 PR 描述中引用: + +1. **BUG-DUP-01**(回補文件):`writeReport()` 新增 120 秒重複送出防護(學號+房號+ + 描述前 50 字 MD5 雜湊,命中回傳 `DUPLICATE_REPORT`),2026-09-04 已上線。 +2. **BUG-ROOM-01 / BUG-BED-01**(回補文件):後端房號/床號正則式收緊為與前端 + 一致(房號 `/^(H|I|G|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`、床號 `/^[0-9]$/`), + 2026-09-04 已上線。 +3. **BUG-52(中)**:6 份文件仍寫床號「1–3 位數字」,已修正為「1 位數字」。 +4. **BUG-53(中)**:6 份文件仍寫房號「僅限英數字與連字號」,且範例 `A123` 在新 + 規則下不合法,已修正為正確格式與合法範例 `H0111`。 +5. **BUG-54(高,文件自相矛盾)**:`doc/project-memory.md`/`doc/requirements.md`/ + `doc/architecture.md` 三處仍描述 v1.4.5(BUG-50)已確認移除的「Modal 內成功 + 畫面+隱藏 Header+2 秒進度條」死碼行為,其中 `project-memory.md` 甚至與**同一 + 檔案下方的決策記錄自相矛盾**。已修正為實際行為:Modal 立即關閉、聊天泡泡顯示 + 成功訊息、Header 不隱藏。 +6. **BUG-55(低)**:`doc/architecture.md`/`doc/requirements.md` 測試數仍寫 53, + 實際(因新增去重迴歸測試)已是 55,已修正。 +7. **BUG-56(低)**:`index.html` 腳本載入順序註解自 v1.4.4 起漏列 `wifi-modal`, + 已補上。 +8. **BUG-57(低)**:`test/validation.test.js` 獨立維護的 `VALID_INTENTS` 白名單 + 自 v1.4.0 起漏了 `BUTTON_QUERY`(該檔案未直接 `require` 正式模組,是重新實作 + 的副本),已補上並新增迴歸斷言。 +9. **BUG-58(低)**:`doc/architecture.md` §5.5.3 查詢驗證程式碼片段與 `queryReport()` + 實際的雙錯誤代碼邏輯不符,且與緊接其後的文字說明矛盾,已重寫。 + +### ℹ️ 資訊性附註(不在本次變更範圍內,僅供專案擁有者參考) + +已用網路搜尋核實:`gas/Code.gs` 的 `GEMINI_MODELS_FALLBACK` 現有 6 個模型 +(`gemini-3.5-flash-lite`、`gemini-3.1-flash-lite`、`gemini-3.6-flash`、 +`gemini-3.5-flash`、`gemini-3.1-pro-preview`、`gemini-3-flash-preview`)截至 +2026-09 皆仍是有效、正常運作的模型,**不需要修改**。僅供參考:Google 已於 +2026-08-13 發布 `gemini-3.7-flash`(官方稱為目前程式碼/Agent 任務表現最佳的 +Flash 模型);`gemini-3.1-flash-lite` 有預告下架日期 2027-05-07(尚有餘裕, +非急迫)。**是否要把 `gemini-3.7-flash` 加入備援清單或調整優先序,請專案擁有者 +自行評估後另開任務處理,這次請勿主動修改 `GEMINI_MODELS_FALLBACK` 陣列。** + +--- + +## 5. 建議的 commit / PR 訊息 + +``` +docs: v1.4.6 全站文件稽核與版本號統一 + +- 回補 BUG-DUP-01(120 秒重複送出防護)與 BUG-ROOM-01/BUG-BED-01 + (房號床號驗證對齊前端)兩項 2026-09-04 已上線但未文件化的修復 +- 修復 BUG-52~BUG-58 共 7 處文件與程式碼不一致(含 1 處文件內部 + 自相矛盾:project-memory.md 的成功畫面描述與同檔案決策記錄矛盾) +- test/validation.test.js 補上 BUTTON_QUERY 白名單與迴歸斷言(BUG-57) +- index.html 修正腳本載入順序註解(BUG-56) +- 全站版本號統一升級為 v1.4.6,新增 CHANGELOG 條目與 Release Notes + +npm test: 55 pass / 0 fail(原 53) +npm run lint: 0 error / 0 warning +``` + +--- + +## 6. 若你的 LLM 編碼工具支援「先看 diff 再套用」 + +如果你想先檢視差異而非整檔覆蓋,可以請它針對每個檔案跑 +`diff <(git show HEAD:路徑) 附上的新檔案`,逐一確認後再寫入——效果相同, +只是多一道人工確認的手續,適合你想更謹慎審閱的情況。 diff --git a/doc/architecture.md b/doc/architecture.md index d1d9d8f..43447b2 100644 --- a/doc/architecture.md +++ b/doc/architecture.md @@ -1,7 +1,7 @@ # 架構設計文件 (Architecture Design Document) -**版本 / Version**:v1.4.5 -**最後更新 / Last Updated**:2026-08-29(v1.4.5: 新增 Wi-Fi 機設定教學模組、`WifiModal` ESLint 全域缺漏修復、全站版本號對齊) +**版本 / Version**:v1.4.6 +**最後更新 / Last Updated**:2026-09-05(v1.4.6:新增 120 秒重複送出防護、房號/床號後端驗證對齊前端,並修復 7 處文件與程式碼不一致之處,詳見 `CHANGELOG.md`) --- @@ -20,7 +20,7 @@ graph TD GAS -->|"siteverify API (score >= 0.5)"| reCAPTCHA GAS -->|"REST API (6-Model Fallback)"| Gemini["Gemini API\n(六模型三層 RPM 備援)"] GAS -->|"19 語系關鍵字匹配"| RuleEngine["Rule-based 備援分類器\n(19-Language Classifier)"] - GAS -->|"報修:格式雙重強驗證 & 寫入 / 查詢:學號比對後讀取"| Sheet["Google 試算表\n(報修案件記錄)"] + GAS -->|"報修:格式雙重強驗證+120秒去重防護 & 寫入 / 查詢:學號比對後讀取"| Sheet["Google 試算表\n(報修案件記錄)"] GAS -->|"讀寫計數器"| Props["Script Properties\n(USER_COUNT)"] Admin["👤 網管人員"] -->|"查看 / 更新狀態"| Sheet ``` @@ -125,17 +125,21 @@ graph TD ### 4.3 報修送出 ``` 使用者填寫表單 → 點擊送出 -→ report.js 前端驗證(必填欄位 + 學號 1字母+7數字 + 手機10位數字 + 床號1–3位數字) +→ report.js 前端驗證(必填欄位 + 學號 1字母+7數字 + 手機10位數字 + + 床號1位數字 + 房號須以 H/I/G/FA~FF 開頭+1–4位數字) → grecaptcha.execute() 取得 reCAPTCHA v3 一次性 token(隱形驗證,無需使用者互動) → POST GAS(Body: text/plain,內容:{action:"report", payload, token, clientId, recaptchaToken}, AbortController 30 秒 Timeout) → GAS doPost() 驗證 token → writeReport(): → 雙層流量限制(使用者 5/分鐘、全域 20/分鐘) → _verifyRecaptcha():驗證 token、action、風險分數 ≥ 0.5 - → 後端格式再次驗證(學號/手機/床號) + → 後端格式再次驗證(學號/手機/床號/房號,先必填、後格式、再截斷) + → 重複送出檢查:學號+房號+描述前50字 MD5 雜湊,120 秒內命中則回傳 + DUPLICATE_REPORT(v1.4.6 新增,詳見 §5.4.3) → 寫入試算表(先設定儲存格為純文字格式,避免手機號碼開頭 0 被吃掉) → 回傳 {success:true} -→ Modal 顯示成功畫面(2 秒進度條後自動關閉) +→ report.js 立即關閉 Modal,於聊天區以 bot 訊息泡泡顯示「報修成功!」 + 並附上「回主選單/報修」後續按鈕(Header 標題列全程不受影響) ``` ### 4.4 報修案件查詢 @@ -257,6 +261,27 @@ _consumeToken() → CacheService.get() → 驗證存在 → CacheService.remo - 隱形驗證,對真實使用者無任何操作負擔 - reCAPTCHA Secret Key 僅存於 Script Properties,不外露 +#### 5.4.3 重複送出防護(v1.4.6 新增) + +**對應威脅**:CWE-841 Improper Enforcement of Behavioral Workflow(重複提交造成資料重複) + +```javascript +const fingerprint = studentId + '|' + roomNumber + '|' + description.slice(0, 50); +const hash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, fingerprint); +const cacheKey = 'dedup_' + Utilities.base64EncodeWebSafe(hash); +if (CacheService.getScriptCache().get(cacheKey)) { + return { success: false, error: 'DUPLICATE_REPORT' }; +} +// ...格式驗證通過、reCAPTCHA 通過、寫入試算表成功後: +CacheService.getScriptCache().put(cacheKey, '1', 120); // 120 秒內視為重複 +``` + +- 指紋組成:**學號 + 房號 + 問題描述前 50 字**,取 MD5 雜湊後編碼為 CacheService key +- 僅在「格式驗證通過 **且** 實際寫入試算表成功」後才存入快取,避免驗證失敗的請求誤佔用去重配額、或誤擋合法的後續重試 +- 有效期 120 秒,與 Session Token 相同時窗,過期後允許再次送出(例如網管要求補件重送) +- 防止使用者手滑重複點擊「送出」按鈕,或腳本在短時間內重複灌入相同案件造成試算表資料重複 +- 與 §5.4.1 的頻率限制彼此獨立、互不取代:頻率限制防止「大量不同內容」的濫用,本機制防止「內容幾乎相同」的重複 + --- ### 5.5 輸入驗證 (Input Validation) @@ -282,20 +307,23 @@ Gemini Prompt 中以「」符號明確隔離使用者輸入,標示為「純文 |---|---|---|---| | 姓名 | ✅ | 無(僅非空字串) | 50 字 | | 學號 | ✅ | `/^[a-zA-Z][0-9]{7}$/` | 8 字 | -| 房號 | ✅ | `/^[A-Za-z0-9-]{1,8}$/` | 8 字 | -| 床號 | ✅ | `/^[0-9]{1,3}$/` | 3 字 | +| 房號 | ✅ | `/^(H\|I\|G\|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`(須以 H、I、G、FA~FF 開頭) | 8 字 | +| 床號 | ✅ | `/^[0-9]$/`(僅 1 位數字) | 3 字 | | 手機 | ✅ | `/^[0-9]{10}$/` | 10 字 | | 可維修時間 | ✅(前端) | 小時 0–23、分鐘 0–59 | 20 字(後端) | | 問題描述 | ✅ | 無(僅非空字串) | 200 字 | -> **注意**:舊版後端使用 `field && !regex.test(field)` 寫法,空字串因短路求值整段跳過驗證,等同必填形同虛設。v1.3.1 已修正為「先 `trim()` 後判斷空字串,通過才進入格式正則驗證」(BUG-01)。 +> **注意**:舊版後端使用 `field && !regex.test(field)` 寫法,空字串因短路求值整段跳過驗證,等同必填形同虛設。v1.3.1 已修正為「先 `trim()` 後判斷空字串,通過才進入格式正則驗證」(BUG-01)。v1.4.6 進一步修正房號/床號後端正則式原本比前端寬鬆的問題,收緊為與前端完全一致(BUG-ROOM-01、BUG-BED-01);「長度截斷」欄位維持原有截斷長度(房號 8 字、床號 3 字)不變——即使格式規則已收緊,截斷仍作為格式驗證失敗後的最後一道防線,避免未來格式規則再度放寬時欄位無上限。 #### 5.5.3 查詢學號驗證(`queryReport`) ```javascript -const sid = studentId.trim().toUpperCase(); -if (!sid || !/^[A-Z][0-9]{7}$/.test(sid)) { - return { success: false, error: '學號格式錯誤' }; +const sid = String(studentId || '').trim().toUpperCase(); +if (!sid) { + return { success: false, error: 'VALIDATION_QUERY_STUDENT_ID_REQUIRED' }; +} +if (!/^[A-Z][0-9]{7}$/.test(sid)) { + return { success: false, error: 'VALIDATION_QUERY_STUDENT_ID_FORMAT' }; } ``` @@ -392,7 +420,7 @@ finally { lock.releaseLock(); } | 測試項目 | 工具 / 方法 | 覆蓋範圍 | |---|---|---| -| 單元測試(53 項) | Node.js `node --test` + `gas-mocks.js` | `_checkRateLimit`(雙層限流)、`_verifyRecaptcha`(三項條件)、`writeReport`(必填/格式/截斷迴歸)、`queryReport`(學號驗證/欄位安全/限流)、`_ruleBasedClassify`、`doGet`/`doPost` 路由 | +| 單元測試(55 項) | Node.js `node --test` + `gas-mocks.js` | `_checkRateLimit`(雙層限流)、`_verifyRecaptcha`(三項條件)、`writeReport`(必填/格式/截斷/重複送出防護迴歸)、`queryReport`(學號驗證/欄位安全/限流)、`_ruleBasedClassify`、`doGet`/`doPost` 路由 | | 靜態程式碼分析 | ESLint(`eslint.config.js`,0 error / 0 warning) | JS 語法錯誤、未使用變數、未聲明全域等 | | CI/CD 自動化 | GitHub Actions(`.github/workflows/test.yml`) | 每次 push 自動執行 `npm test`,確保安全修復不回歸 | @@ -405,7 +433,7 @@ finally { lock.releaseLock(); } | A01 Broken Access Control | 一次性 Token(120s)+ 雙層限流;`queryReport` 僅回傳本人案件的安全欄位 | | A02 Cryptographic Failures | 機密僅存 Script Properties;全程 HTTPS;無明文硬編碼 | | A03 Injection | Prompt Injection 防護(截斷+控制字元移除+引號隔離);公式注入防護(`_sanitizeForSpreadsheet`);HTML 轉義防 XSS(`_esc`);學號格式正則驗證 | -| A04 Insecure Design | 雙層限流(5 個 action × 使用者+全域);reCAPTCHA v3 隱形驗證;個資最小化原則 | +| A04 Insecure Design | 雙層限流(5 個 action × 使用者+全域);reCAPTCHA v3 隱形驗證;120 秒重複送出防護(§5.4.3);個資最小化原則 | | A05 Security Misconfiguration | `doGet` 明確拒絕 classify/report;GAS 部署設定文件化;ESLint 0 warning 基準 | | A06 Vulnerable Components | 生產環境無外部套件依賴(純 Vanilla JS + GAS);開發依賴(ESLint)定期更新 | | A07 Auth Failures | 一次性 UUID Token;Token 即用即廢;前端 Token 存記憶體而非 localStorage | diff --git a/doc/data-model.md b/doc/data-model.md index 064d1f6..3851f97 100644 --- a/doc/data-model.md +++ b/doc/data-model.md @@ -1,8 +1,8 @@ # 資料模型文件 (Data Model Specification) -**版本 / Version**:v1.4.5 +**版本 / Version**:v1.4.6 **建立日期 / Created**:2026-07-17 -**最後更新 / Last Updated**:2026-08-29 (v1.4.5: 版本號對齊;本次稽核未變更資料模型) +**最後更新 / Last Updated**:2026-09-05 (v1.4.6:修正房號/床號格式描述與範例,反映 BUG-ROOM-01/BUG-BED-01 後端驗證收緊) --- @@ -16,7 +16,7 @@ erDiagram string student_id "學號 (1字母+7數字)" string name "姓名" string room_number "房號" - string bed_number "床號 (1-3數字)" + string bed_number "床號 (1位數字)" string phone "手機號碼 (10數字)" string repair_time "可維修時間(純文字)" string description "問題描述" @@ -53,8 +53,8 @@ erDiagram | time | String | 系統自動 | 通報時間 HH:mm:ss | | student_id | String | 學生填寫 | 必填;1 位英文字母 + 7 位數字(如 D1234567),前後端雙重 RegEx 驗證 | | name | String | 學生填寫 | 必填;最長 50 字 | -| room_number | String | 學生填寫 | 必填,例:A123 | -| bed_number | String | 學生填寫 | 必填;1–3 位數字,前後端雙重 RegEx 驗證 | +| room_number | String | 學生填寫 | 必填;`/^(H\|I\|G\|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`(須以 H、I、G、FA~FF 開頭,如 H0111),前後端雙重 RegEx 驗證 | +| bed_number | String | 學生填寫 | 必填;1 位數字,前後端雙重 RegEx 驗證 | | phone | String | 學生填寫 | 必填;10 位數字,前後端雙重 RegEx 驗證 | | repair_time | String | 學生填寫 | 必填,純文字(如 18:00–21:00) | | description | String | 學生填寫 | 必填;最長 200 字 | @@ -98,13 +98,17 @@ erDiagram > (BUG-01 修復前,空字串會因 `field && !regex.test(field)` 的短路寫法 > 整段跳過驗證,等同必填形同虛設;v1.3.1 已修正)。下表已對照 `gas/Code.gs` > 目前實際邏輯逐欄核實更新。 +> +> ⚠️ **v1.4.6 修正(BUG-ROOM-01、BUG-BED-01)**:房號/床號的後端正則式原本比 +> 前端寬鬆(房號僅檢查英數字與連字號、床號允許 1–3 位數字),可被繞過前端直接 +> 送出格式不符的資料並成功寫入。現已收緊為前後端完全一致,下表同步更新。 -| 欄位 | 前端驗證 (RegEx / MaxLen) | GAS 後端驗證(v1.3.1:必填 → 格式 → 截斷) | +| 欄位 | 前端驗證 (RegEx / MaxLen) | GAS 後端驗證(先必填 → 格式 → 截斷) | |---|---|---| | 姓名 | 不得為空(最長 50 字) | 必填(trim 後不得為空字串)+ 截斷 `slice(0, 50)` | | 學號 | 必填;`/^[a-zA-Z][0-9]{7}$/` (如 D1234567) | 必填 + 格式驗證 `!/^[a-zA-Z][0-9]{7}$/` + 截斷 8 字 | -| 房號 | 必填(最長 8 字) | 必填 + 格式驗證 `!/^[A-Za-z0-9-]{1,8}$/`(僅限英數字與連字號)+ 截斷 8 字 | -| 床號 | 必填;`/^[0-9]{1,3}$/` | 必填 + 格式驗證 `!/^[0-9]{1,3}$/` + 截斷 3 字 | +| 房號 | 必填;`/^(H\|I\|G\|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`(如 H0111) | 必填 + 格式驗證 `!/^(H\|I\|G\|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`(須以 H、I、G、FA~FF 開頭)+ 截斷 8 字 | +| 床號 | 必填;`/^[0-9]$/`(僅 1 位數字) | 必填 + 格式驗證 `!/^[0-9]$/` + 截斷 3 字 | | 手機 | 必填;`/^[0-9]{10}$/` | 必填 + 格式驗證 `!/^[0-9]{10}$/` + 截斷 10 字 | | 可維修時間 | 小時 0–23、分鐘 0–59 範圍驗證 | 截斷 `slice(0, 20)`(純文字欄位,後端無格式 RegEx,僅做長度防護)| | 問題描述 | 必填(最長 200 字) | 必填(trim 後不得為空字串)+ 截斷 `slice(0, 200)` | @@ -113,7 +117,8 @@ erDiagram studentId / phone / bedNumber / roomNumber / name / description 皆為**空字串** 的請求,後端會因為上述短路寫法的缺陷而**照樣寫入試算表**,等同後端「必填」 從未真正生效過。此問題與試算表資料品質直接相關,故列於本文件而非僅列於 -`architecture.md`。 +`architecture.md`。同理,v1.4.6 之前房號/床號後端格式雖有檢查但比前端寬鬆, +攻擊者可送出前端會擋下、但後端仍會放行的格式(如 3 位數床號),現已一併收緊。 --- @@ -121,6 +126,6 @@ studentId / phone / bedNumber / roomNumber / name / description 皆為**空字 | 資料 | 建立 | 更新 | 刪除 | |---|---|---|---| -| 報修案件 | 使用者送出表單時(經 reCAPTCHA v3 + Token 驗證) | 網管人員手動更新試算表 | 不刪除(永久保存) | +| 報修案件 | 使用者送出表單時(經 reCAPTCHA v3 + Token 驗證 + 120 秒重複送出防護,v1.4.6 新增,詳見 `architecture.md` §5.4.3) | 網管人員手動更新試算表 | 不刪除(永久保存) | | 累積計數器 | 首次呼叫 increment 時 | 每次新 session (Atomic LockService) | 不刪除 | diff --git a/doc/project-memory.md b/doc/project-memory.md index 3b5dfb7..4f7424a 100644 --- a/doc/project-memory.md +++ b/doc/project-memory.md @@ -3,8 +3,8 @@ > 僅在確認新決策、修改既有決策或發現重要限制時更新。 > 每次更新須記錄日期、原因與影響範圍。 -**版本 / Version**:v1.4.5 -**最後更新 / Last Updated**:2026-08-29 +**版本 / Version**:v1.4.6 +**最後更新 / Last Updated**:2026-09-05 --- @@ -42,8 +42,8 @@ - 可維修時間建議範圍:**18:00–21:00**(本人需在場) - 新生入住期間 **12:00–17:00** 網管看到後會盡速前往 -- 報修欄位必填:姓名、學號(1字母+7數字)、房號、床號(1–3數字)、手機(10數字)、可維修時間、問題描述 -- 報修成功介面僅保留「報修成功!」與進度條,隱藏標題列 Header +- 報修欄位必填:姓名、學號(1字母+7數字)、房號(須以 H/I/G/FA~FF 開頭+1–4位數字)、床號(1位數字)、手機(10數字)、可維修時間、問題描述 +- 報修成功後 Modal 立即關閉,於聊天區以訊息泡泡顯示「報修成功!」並附上「回主選單/報修」按鈕,頂部標題列 Header 全程不隱藏(v1.4.6 修正:此行原描述 v1.1.0 之前、已於 v1.4.5 BUG-50 確認移除的舊版「Modal 內成功畫面+隱藏 Header」死碼,與下方 v1.4.4/v1.4.5 決策記錄自相矛盾,予以更正) - 試算表中「是否派人」「是否完成」「備註」由網管手動填寫,系統不填 - 非網管業務(冷氣、洗手台等)轉介至宿舍服務台或行動逢甲 App @@ -75,3 +75,14 @@ | 2026-08-29 | 全面稽核修復(BUG-44~51,詳見 `CHANGELOG.md`) | 例行程式碼 vs. 文件一致性稽核,本次聚焦新功能(Wi-Fi Modal)上線後遺漏的 `eslint.config.js` 全域宣告、i18n 涵蓋率、無障礙焦點管理、死碼清理 | 詳見 `CHANGELOG.md` v1.4.5 | | 2026-08-29 | 移除報修表單「Modal 內成功畫面」死碼 | `_handleSuccess()` 自 v1.1.0 行為回復後即直接關閉 Modal、於聊天區顯示成功訊息,`#modal-success-view`/`.is-hidden`/`.has-success`/`.text-en` 等標記與樣式已無任何程式路徑會觸發,經逐一 `grep` 確認零引用後移除 | `index.html`、`js/report.js`、`js/i18n.js`、`css/style.css` | +--- + +## v1.4.6 決策記錄(2026-09-04/2026-09-05) + +| 日期 | 決策 | 理由 | 影響檔案 | +|---|---|---|---| +| 2026-09-04 | 新增報修表單 120 秒重複送出防護(BUG-DUP-01) | 學號+房號+問題描述前 50 字 MD5 雜湊比對,防止使用者手滑重複點擊送出或腳本短時間內重複灌入相同案件;僅於格式驗證通過且寫入成功後才存入快取 | `gas/Code.gs`、`test/gas-code.test.js` | +| 2026-09-04 | 房號/床號後端驗證收緊為與前端完全一致(BUG-ROOM-01、BUG-BED-01) | 後端正則式原本比前端寬鬆(房號僅檢查英數字與連字號、床號允許 1–3 位數字),可被繞過前端直接送出格式不符的資料並成功寫入試算表;改為房號 `/^(H\|I\|G\|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`、床號 `/^[0-9]$/` | `gas/Code.gs`、`test/gas-code.test.js` | +| 2026-09-05 | 全站文件稽核修復(BUG-52~58,詳見 `CHANGELOG.md` v1.4.6) | 上述兩項 2026-09-04 的修復合併上線後未同步更新文件;例行稽核另發現床號/房號格式描述、報修成功畫面描述、測試數量、`index.html` 載入順序註解、`test/validation.test.js` 白名單、`doc/architecture.md` 查詢驗證程式碼片段共 7 處過時或自相矛盾內容,其中「已確認業務規則」章節的報修成功畫面描述與本文件自身下方 v1.4.4/v1.4.5 決策記錄直接矛盾 | `README.md`、`AGENTS.md`、`doc/architecture.md`、`doc/data-model.md`、`doc/requirements.md`、`doc/todo.md`、`doc/project-memory.md`(本檔)、`test/validation.test.js`、`index.html` | +| 2026-09-05 | 全站版本號統一升級為 v1.4.6 | 例行版本發布,涵蓋上述兩項功能與本次文件稽核 | `package.json`、`package-lock.json`、`README.md`、`CHANGELOG.md`、`AGENTS.md`、`doc/*.md` | + diff --git a/doc/requirements.md b/doc/requirements.md index 43d0abc..b5fea58 100644 --- a/doc/requirements.md +++ b/doc/requirements.md @@ -1,9 +1,9 @@ # 逢甲大學福星宿舍網路報修 Chatbot — 需求規格書 # FCU Fuxing Dormitory Network Repair Assistant — Requirements Specification -**版本 / Version**:v1.4.5 +**版本 / Version**:v1.4.6 **建立日期 / Created**:2026-07-17 -**最後更新 / Last Updated**:2026-08-29 (v1.4.5: 補上 Wi-Fi 機設定教學需求說明) +**最後更新 / Last Updated**:2026-09-05 (v1.4.6: 補上重複送出防護需求說明;修正房號/床號驗證規則與報修成功畫面過時描述) **專案倉庫 / Repository**:https://github.com/chinglin-k/FCU_NE_Chat_Bot --- @@ -53,18 +53,20 @@ Chatbot 啟動後顯示四個主要選項按鈕,Header 右上角另有 Teams ### 3.4 報修(通報)模組與反濫用 (Repair Submission & Anti-Abuse) -收集以下資料,送出時經 reCAPTCHA v3 隱形驗證(風險分數 ≥ 0.5),成功後隱藏頂部標題列,僅保留「報修成功!」與進度條: +收集以下資料,送出時經 reCAPTCHA v3 隱形驗證(風險分數 ≥ 0.5)與 120 秒重複送出防護(相同學號+房號+問題描述前 50 字視為重複,v1.4.6 新增)。送出成功後 Modal 立即關閉,於聊天區以訊息泡泡顯示「報修成功!」並附上「回主選單/報修」按鈕,頂部標題列全程不受影響: | 欄位 / Field | 說明 / Description | 驗證規則 / Validation | |---|---|---| | 姓名 / Name | 學生姓名 | 必填(最長 50 字);v1.3.1 起前後端皆強制必填,後端不再僅做長度截斷 | | 學號 / Student ID | 學生學號 | 必填;前後端雙重強驗證 **1 位英文字母 + 7 位數字**(例:`D1234567`)| -| 房號 / Room No. | 宿舍房號(例:A123) | 必填;前後端雙重強驗證(僅限英數字與連字號,最長 8 字)| -| 床號 / Bed No. | 床位號碼(例:1) | 必填;前後端雙重強驗證 **1–3 位數字** | +| 房號 / Room No. | 宿舍房號(例:H0111) | 必填;前後端雙重強驗證,**須以 H、I、G、FA~FF 開頭,後接 1–4 位數字,可選一個連字號再接數字**,最長 8 字(v1.4.6 起後端與前端規則完全一致,見 BUG-ROOM-01)| +| 床號 / Bed No. | 床位號碼(例:1) | 必填;前後端雙重強驗證 **1 位數字**(v1.4.6 起後端與前端規則完全一致,見 BUG-BED-01)| | 手機 / Phone | 聯絡電話 | 必填;前後端雙重強驗證 **10 位數字** | | 可維修時間 / Repair Time | 本人需在場 | 必填;小時 0–23、分鐘 0–59 | | 問題描述 / Description | 網路或設備問題說明 | 必填(最長 200 字);v1.3.1 起後端不再僅做長度截斷,未填一律拒絕 | +- **重複送出防護(v1.4.6 新增)**:120 秒內偵測到相同「學號+房號+問題描述前 50 字」的請求(MD5 雜湊比對),直接拒絕並回傳 `DUPLICATE_REPORT`,避免使用者手滑重複點擊送出或腳本短時間內重複灌入相同案件 + ### 3.5 累積使用人數統計 (Usage Counter) - 每次新 session 開啟時累加一次(Atomic LockService 防競態,並透過 `sessionStorage` 進行單次 Session 防重) @@ -97,9 +99,9 @@ Chatbot 啟動後顯示四個主要選項按鈕,Header 右上角另有 Teams | 項目 / Item | 要求 / Specifications | |---|---| -| 安全性 / Security | Gemini Key 與 reCAPTCHA Secret 僅存於 GAS Script Properties;通訊全面採用 **POST Body** + **一次性 Token** + **Client ID 五組限流(classify/report/query/counter_get/counter_increment)** + **reCAPTCHA v3** + **Strict CSP**;後端例外一律回傳固定代碼,不外洩原始錯誤訊息(CWE-209 防護) | +| 安全性 / Security | Gemini Key 與 reCAPTCHA Secret 僅存於 GAS Script Properties;通訊全面採用 **POST Body** + **一次性 Token** + **Client ID 五組限流(classify/report/query/counter_get/counter_increment)** + **reCAPTCHA v3** + **120 秒重複送出防護** + **Strict CSP**;後端例外一律回傳固定代碼,不外洩原始錯誤訊息(CWE-209 防護) | | 效能 / Performance | LLM 分類時顯示打字指示器,提升等待體驗 | | 相容性 / Compatibility | 支援桌機與手機瀏覽器,Viewport 允許無障礙縮放,使用 `100dvh` 行動版高度切齊 | | 語言 / Language | 介面全面提供繁體中文與英文對照 (Fully bilingual UI support);v1.3.1 修正可維修時間 4 個欄位的 aria-label 中英文切換失效問題 | -| 測試 / Testing | 原生 Node.js 測試框架與 `gas-mocks.js` 單元測試(53/53 Passing)與 ESLint 規範(0 error / 0 warning)| +| 測試 / Testing | 原生 Node.js 測試框架與 `gas-mocks.js` 單元測試(55/55 Passing)與 ESLint 規範(0 error / 0 warning)| diff --git a/doc/todo.md b/doc/todo.md index 97be257..cc43a05 100644 --- a/doc/todo.md +++ b/doc/todo.md @@ -1,7 +1,7 @@ # 開發待辦清單(todo.md) -**版本 / Version**:v1.4.5 -**最後更新 / Last Updated**:2026-08-29 +**版本 / Version**:v1.4.6 +**最後更新 / Last Updated**:2026-09-05 --- @@ -143,6 +143,25 @@ --- +## J 輪:重複送出防護、房號床號驗證對齊、文件稽核(2026-09-05 / v1.4.6) + +| 功能 | 狀態 | 說明 | +|---|---|---| +| 報修表單 120 秒重複送出防護 | ✅ Done | `writeReport()` 以學號+房號+描述前 50 字 MD5 雜湊防重複,120 秒內命中回傳 `DUPLICATE_REPORT`(BUG-DUP-01,2026-09-04 上線、本輪回補文件) | +| 房號/床號後端驗證對齊前端 | ✅ Done | 房號改為 `/^(H\|I\|G\|F[ABCDEF])[0-9]{1,4}(-[0-9]+)?$/i`、床號改為 `/^[0-9]$/`,修復後端原本比前端寬鬆、可被繞過的問題(BUG-ROOM-01、BUG-BED-01,2026-09-04 上線、本輪回補文件) | +| 床號/房號格式描述全站修正(BUG-52、BUG-53) | ✅ Done | `README.md`、`AGENTS.md`、`doc/architecture.md`、`doc/data-model.md`、`doc/requirements.md`、`doc/project-memory.md` 共 6 份文件更新為正確格式與合法範例(如 `H0111`) | +| 報修成功畫面過時描述修正(BUG-54) | ✅ Done | `doc/project-memory.md`「已確認業務規則」、`doc/requirements.md` §3.4、`doc/architecture.md` §4.3 三處與 v1.4.5(BUG-50)已移除的死碼描述矛盾,更正為「Modal 立即關閉+聊天泡泡顯示成功訊息」的實際行為 | +| 單元測試數量文件修正(BUG-55) | ✅ Done | `doc/architecture.md` §5.9、`doc/requirements.md` §4 由「53」更新為實測的「55」(新增 2 組去重迴歸測試) | +| `index.html` 載入順序註解修正(BUG-56) | ✅ Done | 補上 v1.4.4 新增、但一直未列入註解的 `wifi-modal` | +| `test/validation.test.js` 白名單同步修正(BUG-57) | ✅ Done | `VALID_INTENTS` 補上自 v1.4.0 就存在、但此檔案獨立副本一直漏列的 `BUTTON_QUERY`,並新增對應迴歸斷言 | +| `doc/architecture.md` §5.5.3 查詢驗證程式碼片段修正(BUG-58) | ✅ Done | 改為與 `queryReport()` 實際邏輯一致的雙錯誤代碼寫法 | +| 全站文件補上重複送出防護說明 | ✅ Done | `README.md`、`AGENTS.md`、`doc/architecture.md`(新增 §5.4.3)、`doc/data-model.md`、`doc/requirements.md`、`doc/project-memory.md` 補齊 | +| 全站版本號對齊 v1.4.6 | ✅ Done | 所有 `.md`、`package.json`、`package-lock.json` 版本號統一為 v1.4.6 | +| 單元測試增至 55 項全數通過 | ✅ Done | `npm test` 55 pass / 0 fail;`npm run lint` 0 error / 0 warning | +| Gemini 模型清單時效性查核(資訊性,未變更程式碼) | ℹ️ Info | 網路查證確認現有 6 個模型皆仍有效運作;Google 已於 2026-08-13 發布 `gemini-3.7-flash`,是否納入備援清單屬產品決策,留待專案擁有者評估 | + +--- + ## 未來優化(v2) | 功能 | 說明 | diff --git a/index.html b/index.html index 217544a..a16cb3c 100644 --- a/index.html +++ b/index.html @@ -323,7 +323,7 @@