Repository navigation
Releases: Jeng-linLi/student-absence-system
Releases · Jeng-linLi/student-absence-system
Release list
v1.1.0 — 交易原子化、併發守衛、待辦分頁、通知可靠性
主要變更
- 開放重定向修正:400/413 的導回目標只沿用 Host 與本站相同的 referrer(referrer 是攻擊者可控的完整 URL)
- 審批/撤回併發守衛:
action='pending'/status='pending'寫進 UPDATE 條件,雙擊或併發只生效一次 - 交易原子化:新增
db.transaction(),提交/審批/撤回的多表寫入要麼全部生效、要麼全部不發生 - SMTP 逾時:連線預設 10 秒逾時(
LEAVE_SMTP_TIMEOUT),郵件伺服器不可達時不再掛死 worker - 空院系審批死角:學生/院系帳號新增與編輯強制填寫院系
- 待辦清單分頁:
/approvalstodo 視圖每頁 20 筆、越界收斂 - 單則通知標為已讀:
POST /notifications/<id>/read - 撤回院系級假單同步通知院系;登入限流 429 補
Retry-After;電話格式校驗;debug 模式改由LEAVE_DEBUG控制
測試
- 單元測試 82 → 96(新增
tests/test_v11.py) - 冒煙測試 139 → 165(新增
[+13]段)
完整記錄見 CHANGELOG.md。
v1.0.0 — 越權缺口收官、後台稽核日誌、邊界收拾
v1.0 里程碑:v0.7.0–v0.9.1 先後補了統計/匯出/審批清單/假單詳情的範圍隔離,本版複審發現兩個漏網的越權缺口與一批邊界問題,一次收清;同時把後台觀測性補上。
Fixed |修正
/approvals學生越權(真實漏洞):路由只有login_required,任何登入的學生打開「待我審批」就能看到全系統所有待審假單。v0.9.1 修了?scope=all,漏了預設的 todo 分支。現已加上role_required。- 儀表板彙總數字越權:導師/院系的儀表板一律顯示全系統統計,現依
role_scope收斂,標籤改為範圍感知。 - 同日「下午→上午」矛盾時段:天數算 0.5、受影響課程卻按全天計,兩個口徑對不起來——入口阻擋。
- 請求體過大沒有友善處理:附件超過 8MB 不再回白底 413 錯誤頁,改 flash 提示並導回。
- 超長附件檔名可能 500:主檔名截斷至 80 字元,不再撞檔案系統 255 位元組上限。
Added |新增
- 後台稽核日誌頁
/admin/logs:動作篩選+分頁,僅管理員。 - 撤回假單通知導師:待辦數字與實際一致。
- 密碼長度上限 128 字元;管理員重設密碼不可與舊密碼相同。
Tests |測試
- 單元測試 71 → 82(
tests/test_v1.py);冒煙測試 127 → 139。本機與 CI 全綠。
v0.9.1 — 範圍隔離補齊、假別管理收拾
v0.9.0 發佈後的複審:把「範圍隔離」這條線補完整,並收拾假別管理的幾個坑。資料庫結構不變。
Fixed |修正
/approvals?scope=all越權:套用role_scope(u)——導師只看自己的學生、院系只看本院系、管理員看全部(v0.7.0 修了/stats、/export,唯獨漏了這裡)- 假單詳情頁越權:
/request/<rid>新增導師與院系範圍校驗。查看權限不應寬於審批權限——decide本就限定範圍,詳情頁卻能翻開別人的病假事由、聯絡電話與附件 - 重複假別代號變 500:
admin_add_type先預檢代號是否已存在,不再直 INSERT 撞leave_types.code的 UNIQUE 約束 - 停用假別仍可提交:
new_request改查active=1,後台停用的假別不只從表單隱藏,直 POST 也提交不進來 - 上午/下午時段無白名單:
start_period/end_period限定AM/PM
Added |新增
- 假別停用/啟用:
/admin/types/<id>/toggle,管理介面補上操作按鈕(帶確認對話框),動作寫入 audit log;既有假單不受影響 - 假別代號格式校驗(1–20 字元,僅字母、數字與
- _)與名稱長度上限(30 字) - 單元測試 64 → 71、冒煙測試 113 → 127:範圍隔離(審批清單、假單詳情、跨院系)、重複代號拒絕、假別停用後不出現於表單且直 POST 被擋、時段白名單
完整記錄見 CHANGELOG.md。
v0.9.0 — 弱密碼擋鎖、一次性密碼呈現、通知分頁、附件內容嗅探
主軸
把密碼與通知這條體驗鏈補齊,並收拾最後幾處裝飾性防護。資料庫結構不變,舊庫無需遷移。
Added
- 常見弱密碼擋鎖:
COMMON_PASSWORDS靜態清單(約 40 組外洩檔高頻密碼,忽略大小寫)。Password1、Pass@123這類「合格卻爛大街」的組合同樣拒絕;舊示範預設密碼Pass@123正在鎖內,示範密碼改為Demo@2026 - 通知分頁與未讀篩選:
/notifications?page=N&filter=unread,每頁 20 筆,越界頁碼自動收斂 - 附件內容嗅探:上傳時讀取檔案頭魔術位元組(
%PDF/PNG/JFIF/OLE2/ZIP),副檔名說謊直接拒絕 - 單元測試 42 → 64(新增
tests/test_robustness.py)
Changed
- 管理端密碼改「一次性呈現」:明文密碼只在回應本文顯示一次,不再寫進 session cookie(Flask cookie 只簽名不加密,任何人都能讀出 payload)
- 登入限流增加「僅帳號」鍵:換 IP/漫遊 Proxy 也躲不掉;登入成功兩把鎖一併清除
- 管理員新增帳號未填密碼時自動產生臨時密碼(舊版是寫死的
Pass@123) - 冒煙測試 93 → 113 項;頁腳版本號改由
APP_VERSION注入
Fixed
- 明文密碼落進 session cookie(簽名 cookie payload 可讀)
- 後台
advisor_id/email零校驗(亂填撞外鍵約束直接 500) - 限流提示文案寫死「5 分鐘」與常數脫節
Security
- 動態頁面加
Cache-Control: no-store(登出後「後退」不復現資料) - 附件防護從「副檔名白名單」升級為「副檔名+內容頭」雙重檢查
本地驗證:pytest 64 passed、smoke 113 passed。
v0.8.0 — 認證與帳號管理加固:限流持久化、session 失效、後台防呆
v0.8.0 — 認證與帳號管理加固 🔐
複審 v0.7.0 時發現一個規律:好幾個「以為已經有」的防護其實是裝飾——記在記憶體裡的限流、
不會作廢的 session、沒有防呆的後台開關。本版把它們補成真的。
✅ 新增
- 閒置 session 逾時:預設 480 分鐘無操作即需重新登入(
LEAVE_SESSION_IDLE_MINUTES可調) - 帳號/角色白名單校驗:新增帳號時強制(1–50 字元,僅字母數字與
._-@;角色限四種) - 單元測試 26 → 42 項:新增
tests/test_auth_hardening.py與tests/conftest.py
♻️ 變更
- 登入限流改資料庫持久化(
login_attempts表):舊版進程內 dict 重啟清零、多 worker 各記一份,
限流形同虛設;現在跨進程、重啟後仍有效 - 冒煙測試 85 → 93 項;
/healthz追加核心表查詢
🐛 修正
- 管理員重設密碼後,目標的舊 session 仍然有效——密碼都換了,舊 cookie 還能繼續用;
現在pwd_changed_at讓早於變更時間登入的 session 一律作廢(自行改密碼同樣生效) - 管理員可停用自己/最後一位管理員——誤操作即鎖死整個後台;現在兩者皆攔阻,
並補存在性檢查與 audit log - 帳號枚舉時序攻擊:帳號不存在時也跑一次固定 dummy hash 比對,拉平回應時間差
- 修改密碼允許新舊密碼相同(等於沒改)→ 已攔阻
🗄️ 資料庫
- 新增
login_attempts表與users.pwd_changed_at欄位;舊庫啟動時經由通用 migrate
框架自動升級,既有資料不受影響(已驗證遷移路徑與冪等性)
🧪 品質
- 本機 + GitHub Actions 三 job(unit 42 項 / smoke 93 項 / lint)全綠
- 時間戳記精細到微秒,消滅「重設後舊 session 仍有效」的秒級競態(冒煙連跑 4 次驗證穩定)
Full changelog: https://github.com/Jeng-linLi/student-absence-system/blob/main/CHANGELOG.md
v0.7.0 — 模組拆分落地、統計範圍修正、CSV 注入防護、密碼策略補齊
主軸是把 v0.4.0 聲稱的模組拆分真正落地,外加一輪安全與正確性加固。資料庫結構未變動,既有資料可直接沿用;舊庫補欄位改走新的通用 migrate 框架,升級更穩。
Added |新增
- 健康檢查
GET /healthz:無需登入,回傳 JSON 健康狀態(含資料庫探測),供負載均衡/容器探測/CI 使用;資料庫異常時回 503。 - 通用 migrate 框架:
db.py以MIGRATIONS清單驅動「舊庫缺欄位自動補上」,affected_courses遷移改走此框架;日後新增欄位只需在清單加一行。 - 單元測試 +10 項(16 → 26):
tests/test_password_policy.py(強度策略、不可與帳號相同、臨時密碼產生器)、tests/test_csv_safe.py(公式注入防護)。
Changed |變更
app.py真正模組化:常數與純函式一律from utils import ...,SQLite 連線/查詢from db import ...,刪除app.py內所有重複定義——v0.4.0 的文件聲稱已拆分,但實際上app.py仍留著第二份實作(改一邊不會同步到另一邊,是潛在的漂移來源)。順帶刪除從未使用的paginate()。- 冒煙測試由 73 項擴充至 85 項(密碼策略補齊、CSV 公式注入、統計範圍隔離、
/healthz)。 - 「修改密碼」頁與「新增帳號」的密碼提示同步更新為新策略。
Fixed |修正
- 統計頁假別彙總越權:
/stats的「依假別統計」查詢原本沒有套用導師/院系範圍,導師會看到全系統的假別天數彙總;現在與逐學生統計共用同一範圍條件(role_scope)。 - 學生儀表板白查全系統統計:學生身份也會執行 4 個全系統 COUNT 查詢——頁面雖不顯示,但既浪費也擴大資訊暴露面;現在只有審批者/管理員才查。
- 提交假單時
overlapping_requests被呼叫兩次(一次判斷、一次取衝突單號)→ 只查一次。 - 企業版 DDL 補齊課表模型:
saas/schema.sql新增timetable表與absence_requests.affected_courses JSONB——v0.6.0 只在原型加了課表功能,企業版 PostgreSQL DDL 一直沒同步,正式環境據此落地會缺欄位。
Security |安全性
- CSV 公式注入防護:CSV 匯出對
= + - @開頭的自由文字(事由等)加'前綴,避免匯出檔在 Excel/Google Sheets 開啟時被當公式執行。 - 密碼強度策略補齊(兌現 v0.5.0 的 Notes):
validate_password(pw, username)新增「不得與帳號相同」「須同時含大寫、小寫字母與數字」。管理員重設密碼的自動產生器改用temp_password()——舊的secrets.token_urlsafe()有機率產生不符合新政式的密碼,導致重設被自己的校驗擋下。
Notes |備註
- 密碼策略暫不要求特殊符號(可用性優先);上生產前可再收緊(常見弱密碼擋鎖、強度評分)。
/healthz只探測程序與資料庫可達性,不含業務層語義。
Full Changelog: v0.6.1...v0.7.0
v0.6.1 — 受影響課程隱藏教室
v0.6.0 的小幅修正:受影響課程清單不再顯示教室欄位。資料庫結構未變動,既有資料可直接沿用。
Changed |變更
- 受影響課程隱藏教室:提交頁即時預覽與假單詳情頁的「受影響課程(依課表推算)」清單,改為只列課程代碼、類型與起訖時間,不再顯示教室;
timetable.room資料欄位保留不變,正式環境仍可完整留存教室資訊。 - 同步更新根目錄
README.md與leave-system/README.md的版本號與功能描述。
v0.6.0 — 課表 × 請假(受影響課程)
在 v0.4.0/v0.5.0 之上,於請假流程加入「課表 × 請假」連動:學生填寫起訖日期與上午/下午時段時,即時推算並顯示會錯過的課程;假單送出時也把受影響課程一併存檔,讓導師審批時一眼看到課程衝突。
Added |新增
- 課表(timetable):新資料表
timetable(學生、星期 0–4、起訖時間、課程代碼、類型、教室、備註)。正式環境應由 SIS / LMS 同步;此版本內建示範課表(demo 學生共用,週一至週五)。 - 提交頁即時預覽:選定起訖日期與時段後,前端呼叫
GET /api/affected_courses(需登入),即時列出該區間受影響的課程(日期/星期/時段/課程+教室)。 - 假單存檔受影響課程:
leave_requests.affected_courses(JSON)寫入推算結果;假單詳情頁新增「受影響課程(依課表推算)」區塊。 - 按半天粒度推算:同日 AM→PM = 全天;單一時段只列對應上午或下午的課;中間日一律全天。
Database |資料庫結構
leave_requests新增affected_courses TEXT NOT NULL DEFAULT ''。既有資料庫於啟動時自動 migrate 補欄位(ALTER TABLE,不影響既有資料)。- 新增
timetable資料表與idx_tt_student索引。 - 冒煙測試由 66 項擴至 73 項(課表寫入、整天/半天受影響課程、API、提交後存檔)。
v0.2.0 — 請假系統原型:安全性與完整性強化
v0.2.0 — leave-system 原型:安全性與完整性強化
資料庫結構未變動,既有資料可直接沿用。完整記錄見 CHANGELOG.md。
Fixed |修正
- 附件權限漏洞:
/uploads/<file>原本只驗證「已登入」,任何學生都能下載他人的病假證明(個資)。現僅限本人、該生導師、同院系審批人與管理員。 - 可重複請假:同一學生原本可在重疊區間提交多張假單,現在會阻擋並提示衝突的假單編號(已駁回/已撤回不佔用區間)。
- 新增單張假單 90 天上限、最多回溯補請 30 天的校驗;移除通知已讀處的重複 commit。
Added |新增
- 修改密碼頁;管理員可重設密碼(留空自動產生隨機臨時密碼)與編輯帳號資料。
- 我的假單 / 全部假單分頁(每頁 20 筆);提交頁即時天數試算;學生儀表板顯示本學年已核准天數。
LEAVE_DB/LEAVE_UPLOAD_DIR環境變數,供測試隔離。
Security |安全
- CSRF 防護:所有 POST 表單需帶 session token,不符者拒絕且不寫入資料庫。
- Session cookie
HttpOnly+SameSite=Lax;登入成功重設 session 防 fixation。 - 登入限流:同一 IP + 帳號 5 分鐘內失敗 8 次即鎖定(進程內計數)。
- 附件檔名加入隨機字串,避免 URL 被猜測枚舉。
Test |測試
- 冒煙測試改用全新暫存資料庫,檢查項目由 10 項擴充至 49 項。
- 新增 GitHub Actions CI:每次 push / PR 自動執行冒煙測試與語法檢查。