Skill risk evaluations on skills.sh — skills.sh 上的 skill 風險評估¶
Terminology rule (zh-TW pages)
技術名詞首次出現以「中文 (English original)」格式呈現,例:依賴注入
(dependency injection)。不自創翻譯——若無公認譯名直接保留英文
(如 embedding、tokenizer)。代碼、API 名、CLI flag、套件名、檔名一律不翻。
skills.sh 對這個 repo 裡每個 skill 跑三組獨立的 audit,把結果展示在 catalog 頁面:
| Auditor | 檢查什麼 |
|---|---|
| Gen (Agent Trust Hub) | Gen 自家模型給出的高層次「這個 skill 裝下去安全嗎?」評論。 |
| Socket | 對 skill 檔案做靜態掃描,找 credential 形狀字串、可疑 install script、網路呼叫、混淆碼等。 |
| Snyk | 標準 CVE / SCA 對宣告的 dependency 跑,再加一組 AI-skill 專用規則 (W0xx),針對 prompt injection / agent-facing 風險。 |
這頁記錄目前的 finding 與每一條的判讀理由,讓讀者(或未來的我)不用每次都重新推一遍 這個 flag 是真風險還是預期內的雜訊。
目前的 finding¶
以 skills.sh/daviddwlee84/agent-skills 最近一次 audit 為準:
| Skill | Gen | Socket | Snyk |
|---|---|---|---|
mkdocs-site-bootstrap |
Safe | 0 alerts | Med Risk |
project-knowledge-harness |
Safe | 0 alerts | Low Risk |
agent-history-hygiene |
Safe | 1 alert | Low Risk |
兩條被標的項目都是預期內,根因是 skill 的核心功能本身,不是真漏洞。下面是判讀。
agent-history-hygiene — Socket 1 alert(預期內)¶
- 觸發檔案:
tests/fixtures/real_anthropic.md - Pattern: Anthropic API key 形狀 (
sk-ant-api03-aaaa…AA) - Severity: medium / confidence high
這個 skill 的核心職責就是偵測並 redact agent transcript 與 plan 檔裡的秘密。
要驗 gitleaks 規則 + redact_secrets.py 的替換邏輯真的會 fire,就需要一份故意做得很像
真的的秘密形狀字串作為 corpus。tests/fixtures/ 因此刻意放了:
real_anthropic.md—sk-ant-api03-後面跟 93 個 filler 字元再加AA, 剛好是嚴格 Anthropic 規則要求的形狀real_openai.md—sk-proj-+ 100 字元,命中 OpenAI project-key 規則private_key.md— 假的-----BEGIN RSA PRIVATE KEY-----block
Socket 的 secret-pattern scanner 沒辦法分辨「全 a 的 placeholder filler,本來就是要被
match 的」與「外洩的真 credential」,所以會在這個 fixture 上 fire。把 corpus 拿掉會默默
弄壞測試套件,等於把整個 skill 的存在意義抹掉。所以保留 alert。
如果在 downstream dashboard 上太礙眼,可以在 skills.sh / Socket 把它標 false positive, 理由寫「intentional secret-shape test corpus for a secret-redaction skill」。
讓 fixture 預設落在掃描範圍之外¶
這些 fixture 是故意做成會觸發 scanner 的,所以長期目標是「假的 test vector 預設落在 掃描範圍之外,只有在測試特別重新植入 (re-plant) 時才 fire」。在會掃這個 repo(以及 下游安裝)的各個表面上,這條原則是這樣落實的:
- gitleaks(本 repo、下游、
npx skills add複製過去的副本)。 fixture 裡每一條會 fire 的行末尾都帶一個 inline<!-- gitleaks:allow -->marker(test_redact_secrets.py的兩個 PEM header 用# gitleaks:allow註解)。marker 跟著檔案走,所以下游使用者剛 bootstrap 出來的 gitleaks hook 也會一併被抑制。corpus 與 shell 測試在 stage 進 throwaway repo 之前會先把 marker 剝掉,讓規則在那裡照樣 fire。byte-identical 剝除的 invariant 見 skill 的tests/README.md「Test-vector hygiene」段落。 - GitHub 原生 secret scanning / push protection。 既不認 marker 也不認 gitleaks
設定,所以 fixture 目錄改用路徑排除,寫在
.github/secret_scanning.yml。 - 被移除的東西。 先前 repo 根目錄有個
.gitleaksignore,用file:rule:line把每條 finding 釘死;這種 fingerprint 只要有人在上面插一行就會 drift(其中一條當時已經失準), 而且不會跟著下游走。改用與 secret 同行的 marker 取代它。
Socket 是另一個 scanner,上面這些它一個都不認 — 它是從檔案內容重新推導 credential 形狀 — 所以針對 Socket dashboard,上面那條「標 false positive」的建議仍然成立。
mkdocs-site-bootstrap — Snyk Med Risk(預期內)¶
這條不是 transitive dependency 的 CVE — pin、bump、或拔掉某個 package 都不會改變 分數。
- Rule:
W011— Third-party content exposure / indirect prompt injection - Risk score: 0.90
- Snyk 原文理由:
The agent will read untrusted, user-generated third-party docs that could contain instructions and thus enable indirect prompt injection.
這個 skill 的 template 預設裝 mkdocs-llmstxt 與 mkdocs-copy-to-llm,會產出:
/llms.txt與/llms-full.txt— 給 LLM 吃的全內容 dump- 每頁的 "copy as markdown for LLM" 按鈕
多語站會由 managed scripts/build-docs-site.py 隔離 mkdocs-llmstxt 與
mkdocs-static-i18n:root llms 檔與 raw .md sidecar 只包含預設語言,
翻譯 HTML 則在另一個 strict pass 建立。直接 mkdocs build --strict 是
HTML-only preview。這修正空檔/語言錯誤的輸出,但不是信任邊界,也不會
清理 (sanitize) 預設語言 docs 裡的內容。
任何吃這些端點的 LLM agent 等於在讀 docs/ 裡有什麼。如果 docs/ 收外部貢獻(例如
open-source PR),攻擊者可以在某個 doc 頁面埋 prompt injection payload,然後讓那段內容
流進下游 agent 的 context。Snyk 的 W0xx ruleset 標的就是這個 agent-facing 表面。
為什麼接受這個分數¶
整個 dogfood mkdocs-llmstxt + copy-to-llm 的目的就是讓 docs 對 LLM 友善;削弱這層就等於
把 skill 的價值砍掉。真正的修法是威脅建模 (threat modeling),不是改 dependency:
- 把
docs/當作 LLM 輸入邊界(在docs/**上設 CODEOWNERS、外部 PR 一律走 review)。 - 在 skill 文件裡寫清楚這個威脅,讓下游使用者知情地 opt in。
未來給 init-docs-site.sh 加一個 opt-out flag(例如 --no-llmstxt,給不需要 LLM-facing
表面的 repo 用)已經記在 TODO.md,不會搶在前面 land。
延伸閱讀¶
reference/mkdocs-2-and-zensical.md— 為什麼 pinmkdocs<2(不同理由,同個 skill)。reference/docs-stack-recipe.md— bootstrap 實際裝了什麼。- i18n + llmstxt migration guide — 為什麼既有下游站點在更新 skill 後,仍需顯式 audit/migration。
為什麼還沒上 README badge¶
skills.sh 目前沒有 first-party badge endpoint。我們探過幾條常見路徑,全部 404:
https://skills.sh/<owner>/<repo>/badge.svg → 404
https://skills.sh/badge/<owner>/<repo>.svg → 404
https://skills.sh/api/badge/<owner>/<repo> → 404
如果之後想在 README.md 上一排 audit badge,有三條路,由輕到重:
shields.io靜態 badge + 手刻連結。 每條 badge 的內容(Safe、1 alert、Med (W011))手寫,圖片連到 skills.sh 詳情頁。零基礎建設,但 audit 一重跑就會 drift。shields.ioendpointschema + 自架 JSON。 排程 GitHub Action 抓 skills.sh, 把badges/{gen,socket,snyk}.json寫到gh-pages,shields.io 的 endpoint badge 對著那份 JSON 渲染。 會自動更新,建置成本約半天。- 直接用 Snyk / Socket 官方 badge。 Snyk 與 Socket 都各自出 badge,但他們的 scope 是 whole-repo 或 npm-package,不對應 skills.sh 的 per-skill 觀點,意義不一樣。
三條都先擱著。等到 (a) skills.sh 出 first-party badge endpoint,或 (b) audit 訊號變成 trust 決策的關鍵(例如外部貢獻者會看 verdict 決定要不要裝),再回頭評估。
什麼時候該回來更新這頁¶
- 新 skill landed,第一次拿到非平凡的 audit verdict。
- 既有 finding 翻盤(例如 Socket 規則更新後 alert 消失,或新的 W0xx fire)。
- skills.sh 換 auditor 陣容,或開始發 badge URL。