# 2026-07-25 資安三工具重掃 — MEDIUM 級分析報告

> 執行時間：2026-07-25 03:57~04:30（排程任務）。掃描範圍：BE + FE，工具 trivy / semgrep / SonarQube。
> 對照基準：2026-07-24 修正後掃描（handoff §1）。掃描期間 runner session 同時在工作，
> 本次結果已含 runner 的 Sonar 清理與 TipTap 收斂 commits（BE 至 `547471d6`、FE 至 `1dd0aba`）。

## 1. 前後對比總表

| 指標 | 前次（07-24 修正後） | 本次（07-25） | 說明 |
|------|------|------|------|
| BE Trivy 總計 | 6 | **2**（MEDIUM ×2） | jedi 18 套件發版生效：flask LOW×2 / marshmallow / dotenv 4 條歸零 ✅ |
| BE Trivy HIGH | 0 | 0 | 維持 |
| FE Trivy MEDIUM+ | HIGH 18 | HIGH 17 + MEDIUM 13 | preact 已解 ✅、quill 主 lock 已拔 ✅；殘留大宗是 `docs/reference/ultima-vue` 範本 + 間接依賴（見 §3） |
| Semgrep BE ERROR | 38（修正前）→ 預期歸零 | **9** | 全部是 `avoid-sqlalchemy-text` 常數插值，判誤報（見 §4） |
| Semgrep FE ERROR | 1 | **0** | ✅ |
| Sonar BE 總 issues | 875 | 825 | runner S1481/S1192 清理生效 |
| Sonar FE 總 issues | 1065 | 757 | runner S2486/a11y/TipTap 清理生效 |
| Trivy secret（版控內） | 0 | **1 個真實外洩（jedi-issue wheel）** | ⚠️ 新發現 P1，見 §2 |

## 2. ⚠️ 新發現 P1：jedi-issue 套件 wheel 內含 .env（GitHub/GitLab PAT）

Trivy secret 掃描在 `.venv/lib/python3.11/site-packages/jedi_issue/.env` 掃到 `github-pat` + `gitlab-pat`。追查結果：

- 源頭是 `~/Projects/Jedicogy/module/jedi-python-package/jedi-issue/jedi_issue/.env`——**位於套件目錄內**，被 poetry `packages = [{ include = "jedi_issue" }]` 整包打進 wheel
- **該檔在 jedi monorepo git 內被版控**（初始 commit `b740989` 就進來了），含 `GITLAB_PRIVATE_TOKEN`、`GITHUB_PRIVATE_TOKEN` 實值
- 歷次發版（至少 0.0.14、0.0.15）的 wheel 與 sdist **都含此檔**，已散佈在 Nexus 上，所有裝過 jedi-issue 的環境 site-packages 內都有一份
- 本機 `.venv` 內那份就是安裝時帶進來的

**處置（已開 Notion case，需 user/團隊執行 token revoke）**：
1. Revoke 該 GitLab PAT + GitHub PAT（token 持有人處理；從 git log 看是 James 初始 commit 帶入）
2. jedi-issue 移除 `jedi_issue/.env`（改 `.env.sample` placeholder 放套件外）+ `.gitignore` 補排除
3. 重新發版（0.0.16）、主專案升 pin；Nexus 上舊版 wheel 視管理需要決定是否下架
4. 其餘 17 套件已抽查無同樣問題（僅 jedi-issue 的 `.env` 在套件目錄內）

其餘 trivy secret 8,484 條全部位於未版控路徑（`.env`、`log/`、`pki/`、`reports/`、`.venv`），大宗是 log 檔內的 JWT token（8,478 條），屬本地開發產物，非外洩。

## 3. Trivy MEDIUM+ 全清單與修正方式

### BE（2 條，皆 MEDIUM）

| 套件 | 現裝 | 修復版 | CVE | 判定與修法 |
|------|------|--------|-----|-----------|
| pytest | 8.4.2 | 9.0.3 | CVE-2025-71176 | **新出現**（8.4.2 是昨夜剛升的目標版，此 CVE 需跨大版 9.x）。被 jedi 18 套件 `pytest>=8.4,<9` 卡住，修復需再放寬一輪到 `<10`；純 dev 工具、不進 runtime image → 已開 case 等裁決（再發版一輪 vs 風險接受） |
| weasyprint | 68.1 | 無 | CVE-2026-49452 | 上游 no-fix（CSS injection），既有裁決風險接受，不重複開 case |

### FE — 真實專案依賴（`package-lock.json`）

| 套件 | 現裝 | 修復版 | Severity | 引入鏈 | 修法 |
|------|------|--------|----------|--------|------|
| linkify-it | 5.0.0 | 5.0.2 | HIGH ×2 | @tiptap/pm → prosemirror-markdown → markdown-it（**runtime**） | npm overrides |
| markdown-it | 14.1.0 | 14.2.0 | MEDIUM ×2 | 同上（**runtime**） | npm overrides |
| socket.io-parser | 4.2.4 | 4.2.6 | HIGH | socket.io-client（**runtime**） | npm overrides |
| underscore | 1.13.7 | 1.13.8 | HIGH | mammoth（**runtime**） | npm overrides |
| minimatch | 5.1.6 | 5.1.8+ | HIGH ×3 | mocha/eslint（dev） | npm overrides |
| brace-expansion | 2.0.2 | 2.1.2 | HIGH + MEDIUM | eslint/mocha（dev） | npm overrides |
| picomatch | 2.3.1 | 2.3.2 | HIGH + MEDIUM | mocha/chokidar（dev） | npm overrides |
| serialize-javascript | 6.0.2 | 7.0.3 | HIGH + MEDIUM | mocha（dev） | npm overrides |
| uuid | 10.0.0 / 8.3.2 | 11.1.1+ | MEDIUM | **direct dep** ^10.0.0 + cypress/mochawesome | direct 升 ^11、間接 overrides |

→ 已開 case「FE 依賴 CVE 第二輪」，一次 overrides + uuid 升版 + build 驗證。

### FE — `docs/reference/ultima-vue-6.4.0/`（範本殘留，不修）

brace-expansion / glob / minimatch 9.0.5 / picomatch / postcss / nanoid / yaml / quill 全數只出現在此參考範本的 lock 檔。前次已裁決：範本非建置產物、不進 bundle，維持不修（若要歸零可整目錄刪除或加 `.trivyignore`，留給 CI case 一併決定）。

## 4. Semgrep 判定

### BE ERROR ×9 — 全部 `avoid-sqlalchemy-text`，判**誤報**

位置：`infra/oscal/repository/ssp_control_implementation_query.py`（×3）、`resource_library_query.py`（×3）、`app/oscal/service/resource_library_app_service.py`（×2）、`app/project/service/project_start_app_service.py`（×1）。
逐條看過：f-string 插值的都是**模組常數**（表名 `_MF`、固定 SQL 片段 `xml_expr`/`join_trans`），使用者輸入全走 bound parameters（`:uid` 等）。無注入面。不開 case；若要消音可加 `# nosemgrep` 註記，屬美化。

### BE WARNING ×19 — 逐類判定

| 規則 | 條數 | 判定 |
|------|------|------|
| non-literal-import | 7 | 誤報——模組註冊機制（`REGISTERED_APPS` 動態 import）與 scripts，來源是程式內常數 |
| python-logger-credential-disclosure | 4 | 誤報——逐條看過，log 內容只有 tenant_id / job_id，無憑證值（drive_sync_worker / webhook / token_manager） |
| insecure-hash-algorithm-sha1 | 4 | 誤報——sha1 用於 diff identity key（party 配對指紋），非密碼學用途；要消音可換 sha256（一行），非必要 |
| missing-integrity | 2 | 低風險——`scripts/evidence/classify/prototype-ui/` 內部原型的 CDN script 無 SRI，不進產品 |
| make-response-with-unknown-content | 2 | 誤報——SSP/MF 匯出 route 回傳檔案 bytes，本來就是二進位 response |

### FE — ERROR 0；WARNING ×14 判定

- `avoid-v-html` ×8：逐條驗證——`ReferenceDocumentList` 過 DOMPurify ✅、diff 元件 `renderDiff()` 有 `escapeHtml()` ✅、`ControlImplSection`/`ObjectiveDiffRow` 同 diff 家族 escape ✅。唯一留意點：`App.vue:11` toast `message.detail` 直接 v-html——目前 detail 全部來自 i18n 常數與程式組字串，無使用者輸入路徑，判低風險；日後若把 BE 回傳訊息直塞 toast 需先 sanitize（已記入報告，不開 case）
- `path-join-resolve-traversal` ×4：build-time 腳本 `sync-translations.js`，非 runtime，誤報
- `insecure-object-assign` ×2：`useSurveyMock.js` mock 資料組裝，誤報

## 5. Sonar 判定（VULNERABILITY + BLOCKER/CRITICAL bug）

### BE：VULN 34 條，無需修 code 的真實洞

- `S5332 http://` ×26（MINOR）：全是 XML namespace 常數（`http://camunda.org/schema/...`）與內網 dev 預設 URL，非實際不安全連線，誤報
- `S4502 CSRF disabled` ×2（CRITICAL）：`core/app_factory.py:35` + backfill script——本系統走 JWT header 認證（無 cookie session），CSRF 保護不適用，屬設計決策。風險接受，於此報告留檔
- `S2245 pseudorandom` ×5（MAJOR）：captcha 圖形的隨機顏色/字型（視覺雜訊非安全用途）+ `common_util.py:117`，誤報
- `Web:S5247 autoescape` ×1（MAJOR）：`project_summary_report_template.html`——上游已在 07-24 arc 加 bleach sanitize（summary 富文本），模板層 autoescape off 是刻意（渲染已消毒 HTML），風險已控
- BLOCKER/CRITICAL bug：**0 條**（前次 7 條 S3516 是 CODE_SMELL 類，仍在 backlog 禁區內不動）

### FE：VULN 8 條全誤報；CRITICAL bug **7 條（新發現，已開 case）**

- VULN：`S2245` ×6（sessionShare tab id 前次已判誤報、_archived 死碼、demo dashboard、DynamicPrimeComponent key 生成）+ `S5332` ×2（BPMN XML namespace 常數），全誤報
- **CRITICAL bug 7 條是重掃新發現**（runner Round 1 修掉 duplicate id "key" 與 AppMenuItem 後，同檔還有同型別 bug）：
  - `Web:S7930` duplicate id ×5：`ComplianceFrameworkVersionManage.vue` 868/880/892（version / release_date / publish_status）、`ModuleFrameItems.vue:488`（frequency）、`WorkflowSetupEditor.vue:860`（actionInfo）
  - `javascript:S930` 傳參給無參函式 ×2：`ModuleFrameItems.vue:129`、`WorkflowSetupEditor.vue:106`（AppMenuItem 同族 bug）
  → 已開 case（含 S930 修前必讀 source 護欄，比照 AppMenuItem 教訓）

## 6. 修正生效驗證（對照 07-24/25 已修內容）

| 修正 | 驗證結果 |
|------|---------|
| jedi 18 套件發版（flask 3.1.3 / marshmallow 3.26.2 / dotenv 1.2.2 / pytest 8.4.2） | ✅ 4 條 CVE 從 BE trivy 消失（pytest 另冒**新** CVE 需 9.x，見 §3） |
| FE preact 升版（runner） | ✅ trivy 無 preact 條目 |
| TipTap 收斂拔 quill + ckeditor5（runner） | ✅ 主 lock 無 quill/ckeditor5 CVE（僅 docs/reference 範本殘留） |
| BE Sonar S1481/S1192 清理（runner） | ✅ 總數 875 → 825 |
| FE Sonar S2486/a11y 清理（runner） | ✅ 總數 1065 → 757 |

## 7. 本次開立的 Notion case

| Case | 內容 | 優先 |
|------|------|------|
| [P1][資安] jedi-issue wheel 內含 .env（GitHub/GitLab PAT） | revoke + 套件移除 .env + 重發版 | P1 |
| [資安] FE 依賴 CVE 第二輪（runtime overrides + uuid 升版） | linkify-it / markdown-it / socket.io-parser / underscore / dev 依賴 overrides | P2 |
| [討論][資安] BE pytest 9.0.3（CVE-2025-71176）——jedi pin 再放寬 vs 風險接受 | 需 user 裁決（dev 工具、要再發版一輪） | P3 |
| [優化] FE Sonar 重掃新增 CRITICAL bug ×7 | duplicate id ×5 + S930 ×2 | P2 |

不重複開（既有 case / 裁決涵蓋）：weasyprint no-fix、docs/reference 範本殘留、CSRF 設計決策、S3776/S3516 backlog 禁區。
