AI-LECTURER 速報課|2026-11-04|約 28 分鐘

打開一個 API 工具,AI 助手的設定就被改了?用「設定體檢」揪出悄悄裝進來的外掛

📍 真實場景
阿凱,32 歲,接案後端工程師,每天用 Codex 和 Claude Code 這兩個 AI 寫程式助手趕專案

他照常打開 Postman(測試 API 的桌面軟體),準備替客戶的訂單系統接一支新介面

😖 卡住的地方:之後兩個 AI 助手都開始怪怪的:一直跳出「驗證失敗」警告,而且只要他一提到 API,AI 就搶著說「我要改用 Postman 的技能來處理」。他完全沒有裝過任何東西,卻不知道是誰動了設定,也不知道該去哪裡查。
💡 這課帶你看懂 Postman 事件的來龍去脈,並給你一套唯讀的「AI 助手設定體檢」指令,五分鐘內盤點自己電腦上到底裝了什麼、哪些你不認得,還有發現可疑項目時『先記錄、再查來源、最後才停用』的具體做法。

🧭 這到底是什麼(白話版)

2026 年 10 月,有使用者在 Postman 的 GitHub 回報區貼了一張標題很兇的單子(#14370),並加上 SEVERE嚴重等級標籤,代表回報者認為這是最高等級的問題;它是回報者自己貼的標籤,不代表官方已經確認 標記。內容是:他只是打開 Postman 桌面版,事後卻發現自己的 Codex 與 Claude Code(兩種 AI 寫程式助手)被裝上了 Postman 的 外掛plugin,替 AI 助手額外加上的功能包,可以帶來新技能、新指令或新連線,連 MCP一種讓 AI 助手連到外部服務的標準接頭,就像 USB 孔,插上去 AI 才能用該服務 連線也一併被打開,另外還多了一個 Postman 命令列工具。

最讓人在意的是一個叫 hook掛鉤,指在某個時機點自動執行的小動作,例如「每次開新對話就先跑這個」 的設定。Postman 的外掛登記了一個 SessionStart(開新對話)掛鉤,它的 matcher比對條件,決定在什麼時機觸發這個掛鉤;這裡的 startup|clear|compact 就是「剛啟動、清空對話、壓縮對話」三種時機都會觸發 寫成 startup|clear|compact,意思是幾乎每次你開新對話它都會執行,並把一段指示塞給 AI:「只要是稍微複雜的 API 工作,先載入 postman:api-engineer 技能再動手。」也就是說,一個你只是拿來測 API 的軟體,把自己設成了你其他 AI 工具的預設工作流程。

回報者還讀了安裝程式的原始碼,發現它在裝完外掛後會呼叫 approvePostmanHook,去取得掛鉤的 trusted_hash信任指紋;程式用一串固定長度的亂碼代表某個檔案的內容,內容一變,指紋就跟著變。把指紋記進「已信任」清單,AI 工具就不會再問你要不要允許這個掛鉤,並寫進 Codex 的設定裡。回報者很誠實地說:原始碼證明了「有嘗試替自己核准」,但設定檔裡那筆信任紀錄,並不能單獨證明是誰寫的。外掛在 Claude Code 這邊則是以 user scope使用者層級,意思是裝一次,你電腦上所有專案都會生效,而不是只限某個資料夾 安裝。

這一課的重點不是批評哪家公司,而是學會一件事:AI 助手的設定檔是「共用空地」,任何裝在你電腦上的軟體都有機會寫進去。所以你要會自己做體檢。體檢要看幾個地方:Claude Code 的 settings.json(裡面的 enabledPlugins 是「啟用中的外掛清單」)、installed_plugins.jsonClaude Code 的外掛登記簿,記錄這台電腦裝了哪些外掛、哪個版本、何時安裝、裝在哪個層級、Codex 的 hooks 與 config.toml 設定,以及 ~/.local/bin使用者自己的「小程式放置區」,放在這裡的可執行檔,只要這個資料夾在 PATH 裡,直接打名字就能執行。

🎯 為什麼值得你花時間

AI 助手握有你的程式碼與權限Codex 和 Claude Code 能讀你的專案、跑指令、連外部服務。誰能悄悄改它的指示,就等於能影響它替你做的每一件事,所以「誰有權改設定」是安全問題,不只是方便問題。
同意被繞過比功能本身更危險外掛有用不是重點,重點是「你沒同意」。一個軟體既裝了掛鉤、又試圖自己簽核信任,等於把『要不要問使用者』這道關卡拿掉。這種模式以後會在越來越多軟體重演。
你不需要等官方,自己五分鐘就能查設定都是放在你電腦裡的純文字檔。只要懂得去哪裡看、看什麼欄位,就能用唯讀指令自己盤點,不必依賴任何人通知你。

⚙️ 它是怎麼運作的

1
1. 你打開 Postman 桌面版看起來只是啟動一個 API 測試軟體,沒有出現任何「是否要安裝 AI 外掛」的提問。
▼
2
2. 安裝程式偵測到你電腦上有 Codex 與 Claude Code它找得到這兩個工具的設定資料夾(~/.codex 與 ~/.claude),因為這些位置是固定、公開的。
▼
3
3. 寫入外掛與掛鉤在 Claude Code 啟用 postman 外掛並記入登記簿;在 Codex 放入掛鉤檔 codex-hooks.json,內容是開新對話時要印給 AI 看的指示。
▼
4
4. 嘗試替自己的掛鉤簽核信任原始碼顯示它會向 Codex 查詢掛鉤的指紋(hash),再寫回設定當成「已信任」。這是回報單的核心爭議。
▼
5
5. 之後每次開新對話都被影響掛鉤把「API 工作請先載入 Postman 技能」塞進 AI 的開場白,所以 AI 看到 API 任務就自動走 Postman 路線,並反覆出現 MCP 驗證警告。
「你自己裝的」與「不認得的」要怎麼分
判斷線索通常是我自己裝的需要查證的
名稱我叫得出名字、記得為什麼裝完全沒印象的名稱,或是我只裝了軟體、沒裝它的 AI 外掛
時間installedAt、檔案修改時間對得上我動手的那天時間剛好是我打開某個軟體的那一分鐘,而且好幾個位置同時間更新
範圍只在某個專案資料夾生效user scope(全專案生效),而我不記得選過
多處連動只出現一處同一個名稱同時出現在外掛清單、掛鉤、信任紀錄、~/.local/bin

🔍 程式碼漫遊(點有 ● 的行看白話講解)

這是體檢腳本的核心。它只用「讀」的指令把四個位置列出來,不會修改任何東西。

●$s = Join-Path $HOME '.claude\settings.json'
💬 組出 Claude Code 設定檔的路徑。Join-Path 只是在拼字串,還沒碰檔案。
●(Get-Content $s -Raw | ConvertFrom-Json).enabledPlugins | Format-List
💬 Get-Content 只讀檔,ConvertFrom-Json 把文字轉成物件,最後列出 enabledPlugins。這就是「啟用中外掛」清單。
●Get-Content $p -Raw
💬 原樣印出 installed_plugins.json。要看的是 scope、version、installedAt 三個欄位。
●Select-String -Path $t -Pattern 'hooks|trusted_hash|plugin|mcp'
💬 只把 Codex 的 config.toml 裡含關鍵字的行撈出來,而不是整份印出,減少洩漏其他設定的機會。
●Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-30) } | Sort-Object LastWriteTime -Descending
💬 只留 30 天內改過的檔案,並由新到舊排序。最新的在最上面,一眼就能對上『我打開某軟體那天』。

🛠️ 動手做:AI 助手設定體檢:五分鐘唯讀盤點

這是一個真實小專案:下載(或複製)檔案,照步驟在你電腦上跑起來。

📄 audit.ps1 ⬇ 下載
# READ-ONLY audit (Windows PowerShell). Only Get-/Select-/Test- commands: nothing is written, changed or deleted.
# Labels are ASCII on purpose so the file prints correctly in any console encoding.
$h = $HOME
Write-Output '=== 1. enabledPlugins in ~/.claude/settings.json ==='
$s = Join-Path $h '.claude\settings.json'
if (Test-Path $s) { (Get-Content $s -Raw | ConvertFrom-Json).enabledPlugins | Format-List } else { Write-Output '(not found)' }
Write-Output ''
Write-Output '=== 2. ~/.claude/plugins/installed_plugins.json ==='
$p = Join-Path $h '.claude\plugins\installed_plugins.json'
if (Test-Path $p) { Get-Content $p -Raw } else { Write-Output '(not found)' }
Write-Output ''
Write-Output '=== 3. Codex hooks files and config.toml (matching lines only) ==='
$c = Join-Path $h '.codex'
if (Test-Path $c) {
  Get-ChildItem $c -Recurse -File -Filter '*hooks*.json' -ErrorAction SilentlyContinue | Sort-Object LastWriteTime -Descending | Select-Object LastWriteTime, FullName | Format-Table -AutoSize
  $t = Join-Path $c 'config.toml'
  if (Test-Path $t) { Select-String -Path $t -Pattern 'hooks|trusted_hash|plugin|mcp' | ForEach-Object { '{0}: {1}' -f $_.LineNumber, $_.Line } } else { Write-Output '(config.toml not found)' }
} else { Write-Output '(no .codex folder)' }
Write-Output ''
Write-Output '=== 4. ~/.local/bin files changed in the last 30 days (newest first) ==='
$b = Join-Path $h '.local\bin'
if (Test-Path $b) { Get-ChildItem $b -File -ErrorAction SilentlyContinue | Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-30) } | Sort-Object LastWriteTime -Descending | Select-Object LastWriteTime, Length, Name | Format-Table -AutoSize } else { Write-Output '(not found)' }
📄 audit.sh ⬇ 下載
#!/bin/sh
# READ-ONLY audit (Mac/Linux). Only grep/cat/find/ls: nothing is written, changed or deleted.
echo '=== 1. enabledPlugins in ~/.claude/settings.json ==='
grep -n -A15 'enabledPlugins' "$HOME/.claude/settings.json" 2>/dev/null || echo '(not found)'
echo ''
echo '=== 2. ~/.claude/plugins/installed_plugins.json ==='
cat "$HOME/.claude/plugins/installed_plugins.json" 2>/dev/null || echo '(not found)'
echo ''
echo '=== 3. Codex hooks files and config.toml (matching lines only) ==='
find "$HOME/.codex" -type f -name '*hooks*.json' -exec ls -lt {} + 2>/dev/null
grep -n -E 'hooks|trusted_hash|plugin|mcp' "$HOME/.codex/config.toml" 2>/dev/null || echo '(no matching lines or no config.toml)'
echo ''
echo '=== 4. ~/.local/bin files changed in the last 30 days (newest first) ==='
find "$HOME/.local/bin" -maxdepth 1 -type f -mtime -30 -exec ls -lt {} + 2>/dev/null || echo '(not found)'
📄 sample_output.txt ⬇ 下載
(這是示範輸出,數字與路徑為虛構;你的格式會因系統略有不同)

=== 1. enabledPlugins in ~/.claude/settings.json ===
superpowers@claude-plugins-official : True     <- 【認得】我去年自己裝的
postman@claude-plugins-official     : True     <- 【不認得?】我只裝了 Postman 軟體,沒有要求裝它的 AI 外掛

=== 2. ~/.claude/plugins/installed_plugins.json ===
"postman@claude-plugins-official": [ { "scope": "user", "version": "2.0.1", "installedAt": "2026-10-24T09:34:02.610Z" } ]
   ^ 看三個欄位:scope=user(全專案生效)、version、installedAt(那天我有裝東西嗎?)

=== 3. Codex hooks files and config.toml (matching lines only) ===
LastWriteTime        FullName
2026/10/24 09:34     C:\Users\me\.codex\plugins\postman\hooks\codex-hooks.json   <- 時間與上面 installedAt 同一分鐘
41: [hooks.state."postman:SessionStart"]
42: trusted_hash = "sha256:ab12...ef"      <- 有「已信任」紀錄。我沒印象點過同意,是誰寫的?先記錄、再查

=== 4. ~/.local/bin files changed in the last 30 days (newest first) ===
LastWriteTime        Length  Name
2026/10/24 09:34     48213   postman        <- 又是同一分鐘,四個位置連成一條線
2026/10/02 20:11      9120   my-backup-tool <- 【認得】我自己放的

判讀重點:
1. 同一個名稱出現在 1、2、3、4 四段 + 時間幾乎同一分鐘 = 是同一個安裝動作順手做的,不是巧合。
2. 你能說出「何時、為何裝」的才算『我自己裝的』;說不出來的就是『不認得』。
3. 看到不認得的項目,不要急著刪。照 steps 的順序:先記錄、再查來源、最後才停用。
📄 triage_order.txt ⬇ 下載
發現不認得項目時的處理順序(照順序做,不要跳步)

第 1 步【先記錄】(5 分鐘,完全不動設定)
  a. 把體檢輸出整段複製到你自己的筆記,檔名加上今天日期。
  b. 對每個不認得的項目寫一行:名稱 / 版本 / 安裝時間 / 出現在哪幾段(1~4)。
  c. 順手截圖。之後要回報或還原,這是證據。

第 2 步【再查來源】(10 分鐘,只讀不改)
  a. 對時間:installedAt 與檔案修改時間,那一分鐘你在做什麼?是不是剛打開或更新某個軟體?
  b. 看內容:用 Get-Content(Mac/Linux 用 cat)讀掛鉤檔 codex-hooks.json,看它會印什麼指示給 AI。
  c. 查官方說明:搜尋「軟體名稱 + plugin + Codex/Claude Code」,看官方文件或 GitHub issues 有沒有承認這個行為。
  d. 判定:有說明且你願意用 → 保留;沒說明或你不要 → 進第 3 步。

第 3 步【最後才停用】(這是唯一會改設定的步驟,也不在體檢指令裡)
  a. 先備份:把 settings.json 與 config.toml 各複製一份到別的資料夾。
  b. 優先用官方方式停用:在 Claude Code 輸入 /plugin 找到該外掛並停用,而不是直接刪檔。
  c. 停用後重跑體檢,確認該項目的 enabledPlugins 變成 False 或消失。
  d. 若有信任紀錄或 ~/.local/bin 殘留,先停用外掛、觀察一天,沒有異常再決定是否清理,並把結果補進第 1 步的筆記。
  1. 【準備】Windows:按開始鍵輸入 PowerShell 開啟視窗。把附件 audit.ps1 存到桌面(資料夾與 AI 設定無關即可)。Mac/Linux:把 audit.sh 存到桌面。
  2. 【確認它是唯讀】audit.ps1 只使用 Test-Path、Get-Content、Get-ChildItem、Select-String 等讀取指令;audit.sh 只使用 grep、cat、find、ls。兩者都不會寫入、刪除或修改任何檔案,也不會連網。
  3. 【執行】Windows 輸入:powershell -NoProfile -ExecutionPolicy Bypass -File $HOME\Desktop\audit.ps1 (Bypass 只對這一次執行有效,不會改變你系統的執行原則)。Mac/Linux 輸入:sh ~/Desktop/audit.sh。你也可以直接把檔案內容整段貼進終端機執行。
  4. 【預期結果】畫面會依序出現 1~4 四段標題。找不到的項目會顯示 (not found),這是正常的,代表你沒裝那個工具。~/.local/bin 在 Windows 上常常不存在,也是正常。輸出貼到別處前請先看一遍,把可能出現的帳號或金鑰字樣遮掉。
  5. 【對照範例】打開 sample_output.txt,逐段比對你的輸出:第 1 段看 enabledPlugins 的名稱;第 2 段看 scope、version、installedAt;第 3 段看 hooks 檔的修改時間與 config.toml 的 trusted_hash;第 4 段看最近改過的程式。
  6. 【分辨你自己裝的 vs 不認得的】對每個項目問三題:①我叫得出名字嗎?②時間對得上我動手的那天嗎?③我說得出為什麼裝嗎?三題都答得出來才算『我自己裝的』;任何一題答不出來,標成『不認得』。特別注意:同一個名稱在四段都出現、且時間在同一分鐘,幾乎可以確定是某個軟體的安裝程式一次做完的。
  7. 【如果有不認得的項目】照 triage_order.txt 的順序做:第 1 步先記錄(複製輸出到筆記、寫一行摘要、截圖);第 2 步再查來源(對時間、讀掛鉤檔內容、查官方說明);第 3 步最後才停用(先備份設定,再用 /plugin 官方介面停用,停用後重跑體檢確認)。注意:前兩步完全不會動到設定,只有第 3 步會,所以千萬不要跳步。
  8. 【如果全部都認得】把這次的輸出存進筆記當作『乾淨基準』。下次安裝新軟體後重跑一次,對照多了哪些項目,一眼就能看出差異。
  9. 【延伸題】根據你剛剛的體檢結果回答:列出你清單中哪一項來源不明(或最近 30 天內才出現),你打算用哪一個具體步驟確認它的來源?(例如:我會對照 installedAt 與我何時更新了哪個軟體。)若沒有不明項目,請寫出你認得的三項各是何時、為何安裝。

🧠 工程思維透鏡(資深工程師看到的是什麼)

🔭 為什麼設定要放在使用者家目錄的固定位置,這件事對安全有什麼取捨?
固定位置(~/.claude、~/.codex)讓所有工具、安裝程式、使用者都找得到,生態系因此好整合;但同樣的便利也讓任何有使用者權限的程式都能寫入。取捨是「易用與整合性」對上「隔離與同意」。資深工程師的想法是:只要寫入不需要額外授權,就要假設會有人寫,所以防線必須放在『讀取時驗證』,例如 AI 工具啟動掛鉤前要求使用者明確點同意,並比對 hash。
🔭 trusted_hash 這種「信任指紋」機制,為什麼設計成這樣?它又為何會被繞過?
指紋的設計理由是:我同意的是『這份內容』,不是『這個名字』。如果掛鉤檔被偷換成惡意內容,指紋就對不上、需要重新同意。但這個設計有個前提:寫入『已信任』清單的那一步,必須只有使用者能做。若安裝程式自己能呼叫 config 寫入介面替自己簽核,指紋就只是形式。這是典型的『信任根』問題:誰能蓋章,比章本身更重要。
🔭 為什麼體檢要「先記錄、再查來源、最後才停用」,而不是發現就刪?
刪除是不可逆且會消滅證據的動作。先記錄讓你保有時間點快照,之後要回報問題或還原都有依據;再查來源避免誤殺你其實需要的工具,也避免你自己憑猜測下判斷;最後才停用,且優先走官方停用介面,因為直接刪檔可能留下殘缺設定導致 AI 工具報錯。順序的設計理由是:先做可逆、低成本的事,再做不可逆、高影響的事。
🔭 這件事用『最小驚訝原則』來看,問題出在哪?
最小驚訝原則是說:軟體的行為應該符合使用者的合理預期。使用者開啟 API 測試軟體,合理預期是『它在自己範圍內運作』,而不是『我的其他 AI 助手也被改了』。越界行為即使有善意,也應該改成『先詢問、預設關閉、可一鍵撤回』。回報者的訴求其實就是這三點。

📝 隨堂考(點選答案,立即回饋)

Q1. Postman 事件中,最被質疑的『信任邊界』問題是什麼?
✅ 重點是未經同意跨越到別的工具,並且嘗試自己簽核信任,而不是效能或收費問題。
Q2. 下列哪一組線索最能判斷某個外掛『不是我自己裝的』?
✅ 跨位置連動加上時間吻合,再加上自己說不出安裝理由,是最強的線索。版本號與 True 本身不能說明來源。
Q3. 發現不認得的項目時,正確的處理順序是?
✅ 先做可逆、不破壞證據的事(記錄),再做只讀的查證,最後才做會改設定的停用。
Q4. 本課的體檢腳本為什麼可以放心執行?
✅ 腳本只有 Test-Path、Get-Content、Get-ChildItem、Select-String(或 grep、cat、find、ls),沒有任何寫入動作。

🃏 翻牌記憶卡(先想答案,再點開對答)

matcher 是什麼?點我翻面
掛鉤的比對條件,決定在哪些時機觸發。startup|clear|compact 代表啟動、清空、壓縮對話時都會觸發。
trusted_hash 在防什麼?點我翻面
防止掛鉤內容被偷換:同意的是『這份內容的指紋』。但前提是只有使用者能寫入『已信任』清單。
installed_plugins.json 記了什麼?點我翻面
Claude Code 的外掛登記簿:裝了哪些外掛、版本、scope(層級)、installedAt(安裝時間)。
user scope 的風險是什麼?點我翻面
一次安裝,你所有專案都生效,影響範圍最大,所以要特別確認是不是自己選的。
發現不認得項目的三步順序?點我翻面
先記錄 → 再查來源 → 最後才停用(停用前先備份設定,優先用官方介面)。

✅ 離開前自測(全勾=這課真的學會了)

0%