FR-086 · 需求索引 · 本頁由 build 掃資料夾生成
🔄 三棒掃完兩棒(2026-09-11)。B1/B2 已掃完並經首腦驗收——B2 證實這條路四層全空,另人工追出「任何登入者可取得本租戶物件儲存帳密明文」。B1b 待派。B1 找到 4 條(HIGH 3/MEDIUM 1),全是「只驗身分不驗歸屬」——任何登入者憑檔案編號即可下載/預覽/刪除/換發免登入通行證;卡片列為最重要的①路徑穿越經查不成立。B2(CM-1653)/B1b(CM-1654)待派
B1 掃完了。結論:只要有一個能登入的帳號,再知道某個檔案的編號,就能把別家客戶的檔案下載走、線上預覽、永久刪除——系統從頭到尾只檢查「你有沒有登入」,從來沒檢查過「這個檔是不是你的」。
而且還能替別人的檔案換發一張免登入的下載通行證,直接把網址傳給公司外面的任何人。另有一條:上傳一個網頁檔,別人一點預覽就會在我們自己的網域裡執行。
四條全是同一個病:認得出你是誰,但不管你能碰什麼。四條共用同一道修法。
好消息:卡片列為「本棒最重要」的路徑穿越經查不成立——但它是靠執行順序意外擋住的,不是有人刻意防的,重構就會長回來。
B2 回答了 B1 留下的問題:宿主那半邊有沒有補上檢查?沒有。 合起來是一條四層全空的路徑——網址入口只驗登入、業務邏輯零檢查、資料查詢無範圍條件、資料表連租戶欄位都沒有(隔離也是關的)。沒有任何一層會問「這個檔是不是你的」。
B2 另外人工追出一條同級的:任何登入者打一支查設定的 GET,就拿到自己租戶物件儲存的位址、帳號、密碼明文——拿到後可完全繞過系統直接連上倉庫,應用層再怎麼補都管不到。三處小改可修。
產品裡所有「上傳檔案、下載檔案、預覽檔案」都經過這支套件。它決定三件事:
這個 arc 要回答:有沒有辦法把檔案寫到不該寫的地方、有沒有辦法下載到別人的檔案、儲存憑證會不會外洩。
| # | 看到什麼 | 為什麼可疑 |
|---|---|---|
| 1 | 上傳時用使用者可控的字串拼出存檔路徑(api/routes/upload_file_route.py:101) |
那兩個值來自請求內容,沒看到 ../ 過濾 |
| 2 | 任何登入者可替任意檔案編號換一張下載憑證(:150-156,只掛 @auth_required) |
中間沒有「這個檔是不是你的」檢查 |
| 3 | 查檔案的資料層完全沒有範圍條件(infra/repository/upload_file_repo_impl.py:41-45) |
加上那張表沒有租戶欄位,猜到編號就取得檔案 |
| 4 | 檔名處理很薄(common/utils/file_utils.py:16) |
無副檔名白名單、無檔名正規化、無單檔大小上限 |
| 5 | 物件儲存憑證存在資料庫 JSONB,加密預設為關(app/upload_file/service/managed_file_upload_service.py:99-119) |
secure=v.get('secure', False) |
第 1、2、3 條若都成立,合起來就是一條完整的跨租戶取檔路徑——這正是三棒要分別驗證的。
| 棒 | 範圍 | 檔數 | scanRoot | 為什麼獨立一棒 |
|---|---|---|---|---|
| B1 HTTP 邊界與契約 | api/+app/+ports/+common/+plugin.py |
35 | jedi monorepo | 上傳/下載/預覽/發憑證四條對外路徑同一條呼叫鏈,三個最直接的疑點都在這 |
| B2 宿主接線 | app/upload_file/+infra/upload_file/+core/upload_file_wiring.py+di_containers/upload_file/ |
10 | BE repo | 檔數少但密度最高:用哪個後端、拿誰的憑證、算不算你的檔全在這 |
| B1b 儲存後端與持久層 | infra/+domain/+migrations/ |
26 | jedi monorepo | 檔案真正被讀寫的地方;路徑穿越真正寫出界的動作在這裡 |
三棒是一條路徑的三段:B1 查「請求進來時路徑是不是使用者可控」→ B2 查「挑後端時有沒有驗歸屬」→ B1b 查「落地時有沒有兜底」。驗收時要互相對照。
偵察建議過一棒「remote_agent 檔案通道」,首腦查證後砍掉——它與 FR-077 的 CM-1594(R3 宿主接線,17 檔)範圍完全重疊。要掃那條路請派 CM-1594,不要在本 arc 重開。
跨 arc 總表的排名表寫「61 檔/2,510 行」,實際是 68 檔/2,877 行(排除測試與報告目錄後),低估約 11%。已據實際數字切棒。
| 棒 | 卡 | 檔 | 面板 | 發現 | 報告 |
|---|---|---|---|---|---|
| B1 HTTP 邊界與契約 | CM-1652 | 35 | ✅ 18 票全投出,verified |
4 條(HIGH 3/MEDIUM 1)+ 人工另記 1 條 LOW | scan-B1-http-boundary.md |
| B2 宿主接線 | CM-1653 | 10 | ✅ 36 票全投、verified |
範圍內 1H +人工 4 條(1H 1M 1L 1 記錄) ⚠️ 另 9 條範圍外全是舊案重複 |
報告 |
| B1b 儲存後端與持久層 | CM-1654 | 26 | ⬜ 待派 | — | — |
(掃完驗收後才會有。依決策者裁定,修正卡先不開,發現登記進跨 arc 總表 §3。)
~/Projects/Jedicogy/module/jedi-python-package/jedi-file-upload/app/upload_file/、infra/upload_file/、core/upload_file_wiring.py.claude/skills/security-scan-lead/SKILL.mdsecurity-scan-consolidated/FR-075/handoff/security-scan-STATE.md以下全部由 build 掃資料夾產生,新增檔案重 build 即自動出現。標題連結指向渲染後的 HTML,md 連向源檔。
| 文件 | 類型 | 標題 | 最後更新 |
|---|---|---|---|
| scan-B1-http-boundary / md | 文件 | FR-086.B1 掃描報告:檔案上傳下載的 HTTP 邊界與契約(jedi-file-upload) | 2026-09-11 |
| scan-B2-host-wiring / md | 文件 | FR-086.B2 掃描報告:宿主接線——儲存解析與租戶憑證 | 未提交 |