---
title: I3 檢查結果：附件上傳下載
---

# I3 檢查結果：附件上傳下載

> 檢查日期 2026-09-09｜耗時 91 分鐘｜對應卡片 CM-1615

---

## 🔴 一句話結論

**附件功能本身沒有新問題**（工具找到的兩條都是前一棒報過的）。**但首腦不同意工具的一項判斷**，另外揪出一個「守門只做了一半」的程式錯誤。

---

## 這一棒在檢查什麼

使用者送意見回饋時可以夾帶附件。這些檔案有三種存法：**存在自己的機器上**、**傳到 GitLab**、**傳到 GitHub**。

這一棒檢查這 14 個檔案——**檔案會不會被放到不該放的地方、能不能讀到別人的檔**。

---

## 找到什麼

| # | 嚴重度 | 這是什麼問題 | 狀態 | 修正卡 |
|---|---|---|---|---|
| 1 | 🟡 中 | 連 GitHub 沒檢查對方身分（從附件功能這條路再次碰到同一支檔） | **和 I1 重複**，不重複計算 | CM-1632 |
| 2 | 🟡 中 | 舊設定檔的密碼留在版控歷史 | **和 I1 重複**（CM-1573 舊案） | CM-1607 |
| 3 | ⚪ **低** | **（首腦補查）刪除附件時，安全檢查只做了一半** | 工具的檢查員 3 票全數否決，**首腦不同意** | **CM-1633** |

**工具在這 14 個檔裡找到的新問題：0 個。**

---

## 🔴 首腦不同意工具否決的那一條

開卡時，首腦特別點名要查「刪除附件會不會刪到別人的檔」。工具查了，**三個檢查員全數判定「這不是問題」**。

首腦逐點核對他們的理由：

| 檢查員說 | 核對結果 |
|---|---|
| 網址只能傳一個檔案編號進來 | ✅ 對（`feedback_route.py:97`） |
| 中間那層把它包成「只有一個元素的清單」 | ✅ 對（`feedback_service.py:285`） |
| 底層有一道檢查會擋掉不屬於這張單的檔案 | ✅ 對（`local_issue_attachment.py:89-90`） |

**他們說「現在打不到」是對的。但他們漏看了下面這件事：**

```python
# jedi-issue/.../local/local_issue_attachment.py

第 85 行   撈出「這張問題單底下有哪些檔案」
第 88 行   算出交集 → matched_file_uids   ← 過濾出「真的屬於這張單的」
第 90 行   如果交集是空的就擋下來          ← 只要有「一個」對得上就放行
...
第 92 行   刪除對應關係，用的是 matched_file_uids   ← 用【過濾後】的 ✅
第 99 行   刪除實體檔案，用的是 file_uids           ← 用回【沒過濾】的原始清單 ❌
```

**同一個函式裡，第 92 行用了過濾後的清單，第 99 行卻用回沒過濾的原始參數。**

**這代表什麼**：只要傳進來的清單裡**混一個合法的檔案編號**，安全檢查就放行，然後**清單裡其他所有檔案的實體檔都會被刪掉**——而對應關係還留著（因為第 92 行只刪了交集），資料庫指向一個已經不存在的檔案。**症狀是靜默的資料損毀，不會報錯。**

**檢查員說「未來多一個呼叫的地方才會有問題」——這個說法低估了。** 不是「未來會有問題」，而是**這個函式的安全檢查本身就寫錯了**：它算了過濾結果卻只用一半。任何傳多個檔案編號進來的呼叫者（例如未來做批次刪除功能）都會踩到。

**判「低」**（目前確實還打不到），開卡 CM-1633 做預防性修正——**改一行就好**：

```diff
- file_is_deleted = self.file_upload_service.delete_files(file_uids)
+ file_is_deleted = self.file_upload_service.delete_files(list(matched_file_uids))
```

---

## 💡 這一棒換到的教訓

**工具的檢查員判斷「現在打不到」是可信的，但他們的推論停在「打得到嗎」——不會告訴你這段程式碼本身是不是寫錯的。**

所以：**卡片點名要查的重點，就算被工具否決了，首腦仍然要自己打開檔案看一眼。**

（這條已經寫進首腦手冊，給後面的棒次用。）

---

## 這份結果可信到什麼程度

### 「這些是真的嗎」→ ✅ 可信

工具找到的兩條都是 3 票全過。首腦補查的那條是自己打開檔案看到的。

**關於報告上的 `unverified` 標記**：原因是「第 2 條指的檔案已經從最新版刪掉了，工具找不到所以拒絕寫進正式報告」——**投票本身是完整的**（9 票全投、零漏投）。

### 「是不是只有這些」→ ❌ 不可信

快篩模式。14 個檔裡有 6 個是幾乎空白的檔案，實際有內容的只有 8 個。

**這一棒的真正價值是「排除」**：附件功能經過檢查，沒有發現路徑穿越、任意檔案讀取這類問題。**這是有價值的結論，不是零產出。**

---

## 執行概況（技術細節）

| 項目 | 數字 |
|---|---|
| 檢查範圍 | 14 個檔案（六棒中最小） |
| 派出／回報的研究員 | 2 / 2 |
| 候選問題 → 去除重複 | 4 → 3 |
| 投票數 | 9 |
| 沒投到票的 | 0 |
| 被檢查員否決的 | 1（**首腦不同意，見上**） |
| 耗時 | 91 分鐘 |
