---
title: I5 檢查結果：業務邏輯層
---

# I5 檢查結果：業務邏輯層

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

---

## 🔴 一句話結論

**找到一個全新的問題：公司內部的套件倉庫走的是沒加密的連線，而且 26 個專案全部一樣**——有心人可以在我們安裝套件時掉包，把惡意程式塞進出貨的產品裡。

**另外，這一棒還回答了一個困擾很久的問題：一次檢查 40 個檔案，工具撐不撐得住？答案是撐得住。**

---

## 這一棒在檢查什麼

問題單的「業務邏輯層」——建單、改單、查單、標籤、成員、附件的流程安排，以及誰能做什麼的判斷。共 40 個檔案。

**這是刻意的一次壓力測試**：之前的經驗是「一棒不要超過 30 個檔」，這次故意放到 40，看工具撐不撐得住。

---

## 找到什麼

| # | 嚴重度 | 這是什麼問題 | 出事會怎樣 | 要先有什麼才打得到 | 在哪裡 | 修正卡 |
|---|---|---|---|---|---|---|
| 1、2 | 🟡 中 | 連 GitHub 沒檢查對方身分 | — | — | — | **和前面重複**（第五次被撈到） |
| 3 | 🟡 中<br>⚠️ 範圍外 | 舊設定檔的密碼留在版控歷史 | — | — | — | 重複（CM-1573 舊案） |
| **4** | 🟡 **中** | 🆕 **公司內部的套件倉庫走沒加密的連線（HTTP），而且被設成「主要來源」**——所有套件都從這裡下載 | 能在公司內網做手腳的人，可以在有人執行 `poetry update` 時**掉包任何一個套件**。Python 安裝套件時會執行安裝腳本，等於**在開發機和打包機上執行攻擊者的程式碼**——而**打包機產出的正是要交給客戶的安裝檔** | ① 攻擊者要在公司內網、且位於開發機與套件倉庫之間<br>② 要有人執行 `poetry update`（或沒有版本鎖定檔的全新安裝） | `pyproject.toml` 第 77 行 | **CM-1634** |

**這棒的新問題：1 個。**

---

## 詳細說明：內部套件倉庫走明文連線

**白話**：我們公司自己架了一個「套件倉庫」（叫 Nexus），所有專案要用到的套件都從那裡下載。**但那個倉庫用的是沒加密的 HTTP 連線**，而且被設定成「主要來源」——連公開的第三方套件也都繞道走它。

```toml
# jedi-issue/pyproject.toml 第 76-78 行
[[tool.poetry.source]]
name = "nexus"
url = "http://192.168.50.171:8082/repository/pypi-group/simple"
#      ↑ http 沒有 s，代表沒有加密
priority = "primary"
#          ↑ 主要來源，所有套件都走這條
```

**出事會怎樣**：沒加密的連線，中途可以被改。有心人在公司內網做手腳，就能在有人執行 `poetry update` 的時候，**把某個套件換成他的版本**。Python 裝套件時會執行安裝腳本，所以那等於**直接在開發機或打包機上執行他的程式碼**。

**最麻煩的是**：打包機（188 那台）產出的就是**要交給客戶的安裝檔**。而且被換掉的套件雜湊值會寫進版本鎖定檔，**之後每次安裝都照著用**——包括打包進客戶的產品裡。

**首腦另外查了報告沒查的範圍**：

| 查什麼 | 結果 |
|---|---|
| 這個專案的版本鎖定檔 | **93 個套件來源全部是這個 http 網址** |
| jedi 套件庫裡有幾支這樣 | **25 支套件全部一樣** |
| 主專案呢 | **也是同一個**（`pyproject.toml` 第 266 行） |

**合計 26 個專案。**

**為什麼是「中」不是「高」**：攻擊者必須人在公司內網、而且剛好位於開發機與倉庫之間的路徑上。另外，**已經有版本鎖定檔、且雜湊驗證通過的安裝不受影響**——除非鎖定檔本身已經被汙染。

**怎麼修**：這一條**不是改程式碼能解決的**，要基礎設施先動：
1. **讓 Nexus 改走 HTTPS**（用公司內部的憑證也可以，前提是憑證要發到所有開發機和打包機並被信任）
2. 然後把 26 個專案的網址從 `http://` 改成 `https://`
3. **過渡期的做法**：把 `poetry update` 當成特權操作，只在可信任的網段執行

---

## 🔴 這一棒最有價值的產出：40 個檔的壓力測試通過了

這比找到的問題更重要。

**背景**：工具的驗證機制是「三個獨立檢查員對每條發現各投一票」。範圍太大時，這些檢查員會因為額度用盡而集體掛掉——之前 FR-077 那次就是這樣，**105 個檢查員全部掛掉，21 張票一張都沒投出來，整輪報告是空的**。

從那之後，規矩訂成「一棒不要超過 30 個檔」。

**這次故意放到 40 個檔，結果**：

| | 檔數 | 投票結果 | 當時的條件 |
|---|---:|---|---|
| FR-077 那次 | 42 | ❌ **全滅**（105 個檢查員全掛、21 票 0 投） | 連續跑，和其他棒搶額度 |
| **這一棒** | **40** | ✅ **15 票全投、零漏投、零中斷、一輪跑完** | **分開跑，獨佔額度** |

**同樣是 40 個檔級別，一個全滅一個全過——差別不在檔數，在有沒有和別的工作搶額度。**

**所以規矩可以改成：「40 個檔可行，前提是一次只跑一棒。」**

這一整個 arc 六棒**一次都沒有撞到額度**，對照 FR-079 那棒撞了兩次、跑了 14 小時、還踩到工具的併檔陷阱。**這是決策者堅持「分開來跑」直接換到的結論。**

---

## 順帶一提：被否決的那條，證實了 I4 的結論

檢查員 3 票否決了一條「成員名單可能外洩」的候選，理由有兩個：那段程式碼沒有任何入口到得了、以及**插件裝配時缺少認證設定會直接拒絕掛載**。

**後者正是 I4 那棒首腦自己打開檔案查到的事。工具這次獨立確認，兩邊對上了。**

---

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

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

全部 3 票全過，15 票全數投出、零漏投、零中斷，而且檢查員沒有降低任何一條的嚴重度。第 4 條首腦自己打開檔案核對過，還多查了影響範圍（26 個專案）。

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

快篩模式。40 個檔裡有 18 個是幾乎空白的檔案，實際有內容的約 22 個。

---

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

| 項目 | 數字 |
|---|---|
| 檢查範圍 | **40 個檔案**（壓力測試） |
| 派出／回報的研究員 | 2 / 2 |
| 候選問題 → 去除重複 | 7 → 5 |
| 投票數 | 15（5 條 × 3 個檢查員） |
| 沒投到票的 | **0** |
| 投票中斷的 | **0** |
| 驗證輪次 | 1（一輪就跑完） |
| 耗時 | 87 分鐘 |
