<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Wei Builds</title><description>weiwei 的開發筆記。寫 Java 後端、Vue 前端、自己架的伺服器，還有一台 Minecraft 伺服器怎麼營運。</description><link>https://www.weibuilds.com/</link><language>zh-TW</language><atom:link rel="hub" href="https://pubsubhubbub.appspot.com/"/><atom:link rel="self" type="application/rss+xml" href="https://www.weibuilds.com/rss.xml"/><item><title>讓 AI 代理自動批准指令有多危險：Pixel Crew 的 Bash 圍欄是怎麼補的</title><link>https://www.weibuilds.com/blog/2026-10-pixel-crew-auto-approve-fence/</link><guid isPermaLink="true">https://www.weibuilds.com/blog/2026-10-pixel-crew-auto-approve-fence/</guid><description>Pixel Crew 同時跑最多 20 個 Claude Code 與 Codex，自動批准不開沒辦法用。10 月 7 日那次安全審視抓到八種繞過，每一種附一行會繞過的指令、怎麼修，還有明講修不掉的部分。</description><pubDate>Tue, 27 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Pixel Crew 是我做的本機工具，把多個 Claude Code 與 Codex session 放進一間像素辦公室，最多同時 20 個。這種用法下自動批准一定要開，不然 20 個代理每做一件事都要你按一次允許，等於沒有自動化。但自動批准的另一面是，代理一旦被餵了惡意內容，或者單純判斷錯誤，它跑的指令不會再經過你。10 月 7 日我對這塊做了一次完整審視，抓到八種繞過，當天全部修掉。這篇一個一個講：怎麼繞、為什麼會漏、怎麼補。程式碼都在公開 repo，連結放在各段。&lt;/p&gt;
&lt;p&gt;&lt;img __ASTRO_IMAGE_=&quot;{&amp;quot;alt&amp;quot;:&amp;quot;像素辦公室裡一個 NPC 停在「允許執行這個指令？」的卡片前，等人按拒絕、本次皆允許或允許&amp;quot;,&amp;quot;src&amp;quot;:&amp;quot;./approval.webp&amp;quot;,&amp;quot;index&amp;quot;:0}&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;沒開自動批准的樣子：NPC 停在原地等你按。20 個 NPC 都這樣，你一整天就在按允許。&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;先講結論白名單放行黑名單只負責說理由&quot;&gt;先講結論：白名單放行，黑名單只負責說理由&lt;a href=&quot;#先講結論白名單放行黑名單只負責說理由&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;第一版的想法很直覺，列一張危險指令清單，&lt;code&gt;rm -rf&lt;/code&gt;、&lt;code&gt;sudo&lt;/code&gt;、&lt;code&gt;mkfs&lt;/code&gt;、&lt;code&gt;curl | bash&lt;/code&gt;，沒命中就放行。審視之後整個反過來：自動批准只靠一張很窄的白名單，只放行認得出來的唯讀指令；危險指令清單還在，但它的工作變成「被拒絕的時候把理由講清楚」。&lt;/p&gt;
&lt;p&gt;原因很簡單。黑名單只能擋你想得到的形狀，shell 能表達同一件事的方法太多了。下面八個洞，有六個都是「這條指令長得不像危險指令，但做的事是」。&lt;/p&gt;
&lt;p&gt;Pixel Crew 現在有四檔：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;模式&lt;/th&gt;
&lt;th&gt;行為&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;off&lt;/td&gt;
&lt;td&gt;每件事都問&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;safe&lt;/td&gt;
&lt;td&gt;只放行白名單裡的唯讀指令，其餘都問&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;full&lt;/td&gt;
&lt;td&gt;預設放行，只擋黑名單命中的毀滅性指令&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;invincible&lt;/td&gt;
&lt;td&gt;什麼都不擋，等於 Claude Code 的 &lt;code&gt;--dangerously-skip-permissions&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;full 有一件事要講明：非 Bash 的工具呼叫，包括每一台接上來的 MCP server 的每一個工具，在 full 模式是無條件放行的。沒有逐工具的風險分級。所以 full 的意思不是「對 shell 寬鬆一點」，是「MCP server 能做的事都不再問」。這句話我寫在程式碼註解裡，也要求所有介面文案照這個意思寫。&lt;/p&gt;
&lt;p&gt;safe 模式一條 Bash 指令要連過五關才會自動放行，任何一關沒過就回到問你。後面八個洞裡有五個，都是其中某一關原本漏了：&lt;/p&gt;
&lt;figure&gt;
&lt;svg class=&quot;diagram&quot; viewBox=&quot;0 0 760 250&quot; role=&quot;img&quot; aria-label=&quot;safe 模式的五關：指令名在白名單、旗標不在寫入或執行表、引號外沒有括號與萬用字元、串接每一段都唯讀、重導向只有丟棄輸出，全過才放行，任一關沒過就交給人&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;
  &lt;defs&gt;
    &lt;marker id=&quot;ar&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;var(--ink-2)&quot;/&gt;&lt;/marker&gt;
    &lt;marker id=&quot;ar-ok&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;var(--topic-infra)&quot;/&gt;&lt;/marker&gt;
    &lt;marker id=&quot;ar-ask&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto&quot;&gt;&lt;path d=&quot;M0 0L10 5L0 10z&quot; fill=&quot;var(--accent)&quot;/&gt;&lt;/marker&gt;
  &lt;/defs&gt;
  &lt;g font-size=&quot;13&quot; font-family=&quot;inherit&quot; fill=&quot;var(--ink)&quot;&gt;
    &lt;rect x=&quot;12&quot; y=&quot;40&quot; width=&quot;128&quot; height=&quot;72&quot; rx=&quot;6&quot; fill=&quot;var(--paper-2)&quot; stroke=&quot;var(--rule)&quot;/&gt;
    &lt;text x=&quot;76&quot; y=&quot;70&quot; text-anchor=&quot;middle&quot;&gt;指令名在&lt;/text&gt;&lt;text x=&quot;76&quot; y=&quot;90&quot; text-anchor=&quot;middle&quot;&gt;唯讀白名單&lt;/text&gt;
    &lt;rect x=&quot;164&quot; y=&quot;40&quot; width=&quot;128&quot; height=&quot;72&quot; rx=&quot;6&quot; fill=&quot;var(--paper-2)&quot; stroke=&quot;var(--rule)&quot;/&gt;
    &lt;text x=&quot;228&quot; y=&quot;70&quot; text-anchor=&quot;middle&quot;&gt;旗標不在&lt;/text&gt;&lt;text x=&quot;228&quot; y=&quot;90&quot; text-anchor=&quot;middle&quot;&gt;寫入／執行表&lt;/text&gt;
    &lt;rect x=&quot;316&quot; y=&quot;40&quot; width=&quot;128&quot; height=&quot;72&quot; rx=&quot;6&quot; fill=&quot;var(--paper-2)&quot; stroke=&quot;var(--rule)&quot;/&gt;
    &lt;text x=&quot;380&quot; y=&quot;62&quot; text-anchor=&quot;middle&quot;&gt;引號外沒有&lt;/text&gt;&lt;text x=&quot;380&quot; y=&quot;80&quot; text-anchor=&quot;middle&quot;&gt;( ) { } 與&lt;/text&gt;&lt;text x=&quot;380&quot; y=&quot;98&quot; text-anchor=&quot;middle&quot;&gt;萬用字元&lt;/text&gt;
    &lt;rect x=&quot;468&quot; y=&quot;40&quot; width=&quot;128&quot; height=&quot;72&quot; rx=&quot;6&quot; fill=&quot;var(--paper-2)&quot; stroke=&quot;var(--rule)&quot;/&gt;
    &lt;text x=&quot;532&quot; y=&quot;70&quot; text-anchor=&quot;middle&quot;&gt;串接的每一段&lt;/text&gt;&lt;text x=&quot;532&quot; y=&quot;90&quot; text-anchor=&quot;middle&quot;&gt;都唯讀&lt;/text&gt;
    &lt;rect x=&quot;620&quot; y=&quot;40&quot; width=&quot;128&quot; height=&quot;72&quot; rx=&quot;6&quot; fill=&quot;var(--paper-2)&quot; stroke=&quot;var(--rule)&quot;/&gt;
    &lt;text x=&quot;684&quot; y=&quot;70&quot; text-anchor=&quot;middle&quot;&gt;重導向只有&lt;/text&gt;&lt;text x=&quot;684&quot; y=&quot;90&quot; text-anchor=&quot;middle&quot;&gt;丟棄輸出&lt;/text&gt;
    &lt;line x1=&quot;140&quot; y1=&quot;76&quot; x2=&quot;162&quot; y2=&quot;76&quot; stroke=&quot;var(--ink-2)&quot; stroke-width=&quot;1.6&quot; marker-end=&quot;url(#ar)&quot;/&gt;
    &lt;line x1=&quot;292&quot; y1=&quot;76&quot; x2=&quot;314&quot; y2=&quot;76&quot; stroke=&quot;var(--ink-2)&quot; stroke-width=&quot;1.6&quot; marker-end=&quot;url(#ar)&quot;/&gt;
    &lt;line x1=&quot;444&quot; y1=&quot;76&quot; x2=&quot;466&quot; y2=&quot;76&quot; stroke=&quot;var(--ink-2)&quot; stroke-width=&quot;1.6&quot; marker-end=&quot;url(#ar)&quot;/&gt;
    &lt;line x1=&quot;596&quot; y1=&quot;76&quot; x2=&quot;618&quot; y2=&quot;76&quot; stroke=&quot;var(--ink-2)&quot; stroke-width=&quot;1.6&quot; marker-end=&quot;url(#ar)&quot;/&gt;
    &lt;line x1=&quot;684&quot; y1=&quot;112&quot; x2=&quot;684&quot; y2=&quot;160&quot; stroke=&quot;var(--topic-infra)&quot; stroke-width=&quot;1.8&quot; marker-end=&quot;url(#ar-ok)&quot;/&gt;
    &lt;rect x=&quot;628&quot; y=&quot;164&quot; width=&quot;112&quot; height=&quot;40&quot; rx=&quot;20&quot; fill=&quot;var(--topic-infra)&quot;/&gt;
    &lt;text x=&quot;684&quot; y=&quot;189&quot; text-anchor=&quot;middle&quot; fill=&quot;var(--paper)&quot; font-weight=&quot;600&quot;&gt;自動放行&lt;/text&gt;
    &lt;g stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.6&quot; stroke-dasharray=&quot;4 3&quot;&gt;
      &lt;line x1=&quot;76&quot; y1=&quot;112&quot; x2=&quot;76&quot; y2=&quot;150&quot; marker-end=&quot;url(#ar-ask)&quot;/&gt;
      &lt;line x1=&quot;228&quot; y1=&quot;112&quot; x2=&quot;228&quot; y2=&quot;150&quot; marker-end=&quot;url(#ar-ask)&quot;/&gt;
      &lt;line x1=&quot;380&quot; y1=&quot;112&quot; x2=&quot;380&quot; y2=&quot;150&quot; marker-end=&quot;url(#ar-ask)&quot;/&gt;
      &lt;line x1=&quot;532&quot; y1=&quot;112&quot; x2=&quot;532&quot; y2=&quot;150&quot; marker-end=&quot;url(#ar-ask)&quot;/&gt;
    &lt;/g&gt;
    &lt;line x1=&quot;76&quot; y1=&quot;152&quot; x2=&quot;532&quot; y2=&quot;152&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.6&quot; stroke-dasharray=&quot;4 3&quot;/&gt;
    &lt;line x1=&quot;304&quot; y1=&quot;152&quot; x2=&quot;304&quot; y2=&quot;162&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.6&quot;/&gt;
    &lt;rect x=&quot;248&quot; y=&quot;164&quot; width=&quot;112&quot; height=&quot;40&quot; rx=&quot;20&quot; fill=&quot;none&quot; stroke=&quot;var(--accent)&quot; stroke-width=&quot;1.6&quot;/&gt;
    &lt;text x=&quot;304&quot; y=&quot;189&quot; text-anchor=&quot;middle&quot; fill=&quot;var(--accent)&quot; font-weight=&quot;600&quot;&gt;交給人按&lt;/text&gt;
    &lt;text x=&quot;380&quot; y=&quot;236&quot; text-anchor=&quot;middle&quot; fill=&quot;var(--ink-2)&quot; font-size=&quot;12&quot;&gt;超過 20,000 字元的指令不進這條流程，直接交給人。&lt;/text&gt;
  &lt;/g&gt;
&lt;/svg&gt;
&lt;figcaption&gt;safe 模式的五關。虛線是任一關沒過的去向。&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;洞一唯讀指令配上旗標就會寫檔或執行程式&quot;&gt;洞一：唯讀指令配上旗標就會寫檔或執行程式&lt;a href=&quot;#洞一唯讀指令配上旗標就會寫檔或執行程式&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;白名單一開始認的是指令前綴：&lt;code&gt;git diff&lt;/code&gt;、&lt;code&gt;git status&lt;/code&gt;、&lt;code&gt;rg&lt;/code&gt;、&lt;code&gt;ls&lt;/code&gt;、&lt;code&gt;cat&lt;/code&gt; 這些。問題是同一個指令配上某些旗標，就不唯讀了。實測過的兩個：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; diff&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; --output=/tmp/outside.txt&lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;   # 會把外部檔案截斷成空檔&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;rg&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; --pre&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; ./evil.sh&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; TODO&lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;              # 會對每個檔案執行 evil.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修法是在前綴比對之後再比對一張「寫入或執行旗標」的表，而且比對前先把引號跟反斜線剝掉，因為在 shell 裡 &lt;code&gt;&quot;--output=x&quot;&lt;/code&gt;、&lt;code&gt;--out&quot;&quot;put=x&lt;/code&gt;、&lt;code&gt;\--output&lt;/code&gt; 都等於 &lt;code&gt;--output=x&lt;/code&gt;。tsc 的選項名稱不分大小寫，&lt;code&gt;--OUTDIR&lt;/code&gt; 一樣有效，所以那條要忽略大小寫。npm、pnpm、yarn 的 &lt;code&gt;--script-shell&lt;/code&gt;、&lt;code&gt;--node-options&lt;/code&gt;、&lt;code&gt;--prefix&lt;/code&gt; 也在表上。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sed&lt;/code&gt; 乾脆不用前綴放行。就算是 &lt;code&gt;sed -n&lt;/code&gt;，也能用 &lt;code&gt;w&lt;/code&gt; 指令寫檔、用 &lt;code&gt;e&lt;/code&gt; 指令執行外部命令，例如 &lt;code&gt;echo x | sed -n &quot;w /path&quot;&lt;/code&gt;。白名單只收 Codex 讀檔時慣用的那一種整句形式，&lt;code&gt;sed -n &apos;1,200p&apos; 檔案&lt;/code&gt;，腳本只能是行號範圍加 &lt;code&gt;p&lt;/code&gt;，檔案參數不能是選項、不能有萬用字元。&lt;/p&gt;
&lt;h2 id=&quot;洞二zsh-不需要分號就能執行另一個指令&quot;&gt;洞二：zsh 不需要分號就能執行另一個指令&lt;a href=&quot;#洞二zsh-不需要分號就能執行另一個指令&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;白名單原本用一組 shell 中繼字元判斷「這條指令有沒有夾帶別的東西」：換行、&lt;code&gt;;&lt;/code&gt;、&lt;code&gt;&amp;amp;&lt;/code&gt;、&lt;code&gt;|&lt;/code&gt;、&lt;code&gt;&amp;lt;&lt;/code&gt;、&lt;code&gt;&amp;gt;&lt;/code&gt;、反引號、&lt;code&gt;$(&lt;/code&gt;、&lt;code&gt;${&lt;/code&gt;。這在 bash 大致夠用，但 macOS 的預設 shell 是 zsh，Claude 的 Bash 工具也跑在上面，zsh 多了幾種不需要任何中繼字元的寫法：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;ls&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;e:&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;&apos;touch pwned&apos;&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;)    &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# glob qualifier，列檔案的同時執行 touch&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;)                &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# process substitution&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; diff&lt;/span&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt; --out&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;put,}&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt;=x&lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;    # brace expansion 拼出上面擋掉的旗標&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修法：引號外出現 &lt;code&gt;(&lt;/code&gt;、&lt;code&gt;)&lt;/code&gt;、&lt;code&gt;{&lt;/code&gt;、&lt;code&gt;}&lt;/code&gt; 任何一個，就不算白名單安全指令。&lt;/p&gt;
&lt;h2 id=&quot;洞三萬用字元會展開成檔名檔名可以長得像旗標&quot;&gt;洞三：萬用字元會展開成檔名，檔名可以長得像旗標&lt;a href=&quot;#洞三萬用字元會展開成檔名檔名可以長得像旗標&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;rg TODO *.ts&lt;/code&gt; 看起來無害。但 &lt;code&gt;*.ts&lt;/code&gt; 會在執行前被 shell 展開成目錄裡的檔名，而檔名可以就叫 &lt;code&gt;--pre=x.ts&lt;/code&gt;。洞一那張旗標表是在展開前比對的，看不到展開後才出現的選項。&lt;/p&gt;
&lt;p&gt;修法只對有危險旗標的工具收緊：git、rg、eslint、tsc、npm、pnpm、yarn 這幾個，引號外出現 &lt;code&gt;*&lt;/code&gt;、&lt;code&gt;?&lt;/code&gt;、&lt;code&gt;[&lt;/code&gt; 就不自動放行。&lt;code&gt;ls *.ts&lt;/code&gt;、&lt;code&gt;cat *.md&lt;/code&gt; 這類沒有能寫檔或執行的旗標，照舊可以用萬用字元，不然白名單會窄到沒人想用。&lt;/p&gt;
&lt;h2 id=&quot;洞四寫入重導向有很多種寫法&quot;&gt;洞四：寫入重導向有很多種寫法&lt;a href=&quot;#洞四寫入重導向有很多種寫法&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Pixel Crew 對檔案工具有一道圍欄：寫入目標逃出 workspace 就硬擋。Bash 沒有結構化的路徑參數，所以另外做了一個函式，掃出指令裡「寫入型重導向」的字面目標，逃出 workspace 就回 true。第一版只認 &lt;code&gt;&amp;gt;&lt;/code&gt; 跟 &lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt;。審視時補上的：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; 1&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; /etc/x&lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;        # 帶 fd 編號&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; 2&amp;gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; ~/.zshrc&lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;     # fd 加 append，目標還要先展開 ~&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; &amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; $HOME/x       &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# stdout 跟 stderr 一起寫，$HOME 在雙引號內會展開、單引號內不會&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; &amp;gt;|&lt;/span&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt; /tmp/x&lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;        # 強制覆寫，zsh 也認 &amp;gt;!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6F42C1;--shiki-dark:#B392F0&quot;&gt;cmd&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; &amp;gt;&amp;amp;&lt;/span&gt;&lt;span style=&quot;color:#032F62;--shiki-dark:#9ECBFF&quot;&gt; /tmp/x&lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;        # &amp;gt;&amp;amp; 後面接檔名就是寫檔，不是 fd 編號&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同時要排除不是寫檔的：&lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;、&lt;code&gt;&amp;gt;&amp;amp;2&lt;/code&gt;、&lt;code&gt;&amp;gt;&amp;amp;-&lt;/code&gt; 是 fd 複製或關閉，&lt;code&gt;/dev/null&lt;/code&gt;、&lt;code&gt;/dev/stdout&lt;/code&gt; 是丟棄輸出。&lt;code&gt;~user&lt;/code&gt;、&lt;code&gt;~+&lt;/code&gt; 這類靜態判定不了的，一律當成逃逸。&lt;/p&gt;
&lt;p&gt;這個函式的註解我寫得很白：&lt;strong&gt;刻意不做完整 shell 解析，這是防禦縱深，不是圍牆&lt;/strong&gt;。它只認字面重導向目標，對變數展開（&lt;code&gt;p=/etc; echo &amp;gt; $p&lt;/code&gt;）、子殼、&lt;code&gt;python -c &quot;open(...)&quot;&lt;/code&gt;、&lt;code&gt;cp&lt;/code&gt;、&lt;code&gt;mv&lt;/code&gt;、&lt;code&gt;tee&lt;/code&gt; 的目的地，一律「認不出等於不擋」。測試檔裡有一個案例叫 &lt;code&gt;documented residual bypasses are NOT flagged&lt;/code&gt;，專門確認這些繞過真的不會被擋，免得哪天有人以為圍欄比實際上厚。&lt;/p&gt;
&lt;figure&gt;
&lt;svg class=&quot;diagram&quot; viewBox=&quot;0 0 760 236&quot; role=&quot;img&quot; aria-label=&quot;寫入重導向圍欄擋得到的形狀與擋不到的形狀&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;
  &lt;g font-family=&quot;inherit&quot; fill=&quot;var(--ink)&quot;&gt;
    &lt;rect x=&quot;12&quot; y=&quot;12&quot; width=&quot;356&quot; height=&quot;212&quot; rx=&quot;8&quot; fill=&quot;var(--paper-2)&quot; stroke=&quot;var(--rule)&quot;/&gt;
    &lt;rect x=&quot;12&quot; y=&quot;12&quot; width=&quot;356&quot; height=&quot;34&quot; rx=&quot;8&quot; fill=&quot;var(--topic-infra)&quot;/&gt;
    &lt;rect x=&quot;12&quot; y=&quot;30&quot; width=&quot;356&quot; height=&quot;16&quot; fill=&quot;var(--topic-infra)&quot;/&gt;
    &lt;text x=&quot;30&quot; y=&quot;35&quot; font-size=&quot;14&quot; font-weight=&quot;600&quot; fill=&quot;var(--paper)&quot;&gt;擋得到：字面上的寫入重導向&lt;/text&gt;
    &lt;g font-size=&quot;13&quot; font-family=&quot;ui-monospace, Menlo, monospace&quot;&gt;
      &lt;text x=&quot;30&quot; y=&quot;76&quot;&gt;cmd &amp;gt; /etc/x&lt;/text&gt;
      &lt;text x=&quot;30&quot; y=&quot;100&quot;&gt;cmd 2&amp;gt;&amp;gt; ~/.zshrc&lt;/text&gt;
      &lt;text x=&quot;30&quot; y=&quot;124&quot;&gt;cmd &amp;amp;&amp;gt; $HOME/x&lt;/text&gt;
      &lt;text x=&quot;30&quot; y=&quot;148&quot;&gt;cmd &amp;gt;| /tmp/x&lt;/text&gt;
      &lt;text x=&quot;30&quot; y=&quot;172&quot;&gt;cmd &amp;gt;&amp;amp; /tmp/x&lt;/text&gt;
    &lt;/g&gt;
    &lt;text x=&quot;30&quot; y=&quot;206&quot; font-size=&quot;12&quot; fill=&quot;var(--ink-2)&quot;&gt;目標逃出 workspace 就擋；2&amp;gt;&amp;amp;1、/dev/null 不算&lt;/text&gt;
    &lt;rect x=&quot;392&quot; y=&quot;12&quot; width=&quot;356&quot; height=&quot;212&quot; rx=&quot;8&quot; fill=&quot;var(--paper-2)&quot; stroke=&quot;var(--rule)&quot;/&gt;
    &lt;rect x=&quot;392&quot; y=&quot;12&quot; width=&quot;356&quot; height=&quot;34&quot; rx=&quot;8&quot; fill=&quot;var(--accent)&quot;/&gt;
    &lt;rect x=&quot;392&quot; y=&quot;30&quot; width=&quot;356&quot; height=&quot;16&quot; fill=&quot;var(--accent)&quot;/&gt;
    &lt;text x=&quot;410&quot; y=&quot;35&quot; font-size=&quot;14&quot; font-weight=&quot;600&quot; fill=&quot;var(--paper)&quot;&gt;擋不到：認不出就不擋，寫在文件裡&lt;/text&gt;
    &lt;g font-size=&quot;13&quot; font-family=&quot;ui-monospace, Menlo, monospace&quot;&gt;
      &lt;text x=&quot;410&quot; y=&quot;76&quot;&gt;p=/etc; echo x &amp;gt; $p&lt;/text&gt;
      &lt;text x=&quot;410&quot; y=&quot;100&quot;&gt;python -c &quot;open(&apos;/etc/x&apos;,&apos;w&apos;)&quot;&lt;/text&gt;
      &lt;text x=&quot;410&quot; y=&quot;124&quot;&gt;echo x | tee /etc/x&lt;/text&gt;
      &lt;text x=&quot;410&quot; y=&quot;148&quot;&gt;cp x /etc/x&lt;/text&gt;
      &lt;text x=&quot;410&quot; y=&quot;172&quot;&gt;( echo x &amp;gt; /etc/x )&lt;/text&gt;
    &lt;/g&gt;
    &lt;text x=&quot;410&quot; y=&quot;206&quot; font-size=&quot;12&quot; fill=&quot;var(--ink-2)&quot;&gt;防禦縱深，不是圍牆；full 模式下這些會過&lt;/text&gt;
  &lt;/g&gt;
&lt;/svg&gt;
&lt;figcaption&gt;寫入重導向圍欄的兩邊。右邊是刻意不處理、但明講的部分。&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;洞五檢查看的是卡片文字不是完整指令&quot;&gt;洞五：檢查看的是卡片文字，不是完整指令&lt;a href=&quot;#洞五檢查看的是卡片文字不是完整指令&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;核准卡片顯示的指令有長度上限，20,000 字元。原本的檢查是對卡片上的文字跑，所以在危險片段前面塞兩萬個字的填充，檢查就只看得到無害的開頭。&lt;/p&gt;
&lt;p&gt;修法兩條：危險指令的比對一律對完整指令跑；長度超過上限的指令不自動放行，直接交給人，理由寫「指令超過 20,000 字元，無法完整顯示與檢查」。卡片顯示不完整的東西，本來就不該有人幫你按允許。&lt;/p&gt;
&lt;h2 id=&quot;洞六windows-的防線根本沒接上&quot;&gt;洞六：Windows 的防線根本沒接上&lt;a href=&quot;#洞六windows-的防線根本沒接上&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;黑名單有 Unix 的 &lt;code&gt;rm&lt;/code&gt;、&lt;code&gt;mkfs&lt;/code&gt;、&lt;code&gt;dd&lt;/code&gt;，卻漏了 Windows 的對等指令，而 Windows 是 Pixel Crew 的主要平台之一。補上的：&lt;/p&gt;
&lt;pre class=&quot;astro-code astro-code-themes github-light github-dark&quot; style=&quot;background-color:#fff;--shiki-dark-bg:#24292e;color:#24292e;--shiki-dark:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;powershell&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;Remove-Item&lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;Recurse &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;Force C:\repo\dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#005CC5;--shiki-dark:#79B8FF&quot;&gt;Remove-Item&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt; build &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;r &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;fo        &lt;/span&gt;&lt;span style=&quot;color:#6A737D;--shiki-dark:#6A737D&quot;&gt;# PowerShell 參數可以縮寫&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;rd &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;s &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;q C:\repo\dist&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;del &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;f &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;s &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;q &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;.log&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;format D: &lt;/span&gt;&lt;span style=&quot;color:#D73A49;--shiki-dark:#F97583&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#24292E;--shiki-dark:#E1E4E8&quot;&gt;q&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;幾個細節。&lt;code&gt;del&lt;/code&gt;、&lt;code&gt;erase&lt;/code&gt;、&lt;code&gt;rd&lt;/code&gt;、&lt;code&gt;rmdir&lt;/code&gt;、&lt;code&gt;ri&lt;/code&gt; 都是 &lt;code&gt;Remove-Item&lt;/code&gt; 的別名，要一起認。&lt;code&gt;format&lt;/code&gt; 後面要直接接磁碟代號才算，不然會誤中到處都是的 &lt;code&gt;--format&lt;/code&gt; 旗標。Windows 的指令與別名解析不分大小寫，&lt;code&gt;Git Push --Force&lt;/code&gt; 跟 &lt;code&gt;git push --force&lt;/code&gt; 一樣，所以整組比對都要忽略大小寫。&lt;/p&gt;
&lt;p&gt;路徑也有 Windows 專屬的洞。判斷「目標有沒有逃出 workspace」用的是 &lt;code&gt;path.relative()&lt;/code&gt;，看結果是不是 &lt;code&gt;..&lt;/code&gt; 開頭。但跨磁碟（&lt;code&gt;D:\&lt;/code&gt;）、UNC（&lt;code&gt;\\server\share&lt;/code&gt;）、&lt;code&gt;\\?\&lt;/code&gt; 路徑跟 workspace 沒有共同的根，&lt;code&gt;relative()&lt;/code&gt; 會直接回傳目標的絕對路徑，不是 &lt;code&gt;..&lt;/code&gt; 開頭，第一版就放過去了。修法多一個條件：結果是絕對路徑也算逃逸。&lt;/p&gt;
&lt;h2 id=&quot;洞七對外提供檔案的目錄不能是-symlink&quot;&gt;洞七：對外提供檔案的目錄不能是 symlink&lt;a href=&quot;#洞七對外提供檔案的目錄不能是-symlink&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;每個 NPC 有一個 outbox 目錄，裡面的檔案可以從網頁下載。路徑檢查會擋 &lt;code&gt;..&lt;/code&gt;，但如果 outbox 本身是個 symlink 指到別處，檢查過了、讀到的卻是別的地方的檔案。修法是提供檔案前先確認 outbox 目錄不經過 symlink，workspace 裡的路徑檢查本來就會逐層 &lt;code&gt;lstat&lt;/code&gt;，這裡補成一致。&lt;/p&gt;
&lt;h2 id=&quot;洞八read-跟-webfetch-不該預先放行&quot;&gt;洞八：Read 跟 WebFetch 不該預先放行&lt;a href=&quot;#洞八read-跟-webfetch-不該預先放行&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;為了少跳提示，第一版把 Read、Glob、Grep、WebSearch、WebFetch 當成唯讀工具預先放行。問題是 Read 可以讀 workspace 外的檔案，WebFetch 可以抓任意網址，前者外洩，後者是把外部內容餵給代理的入口。現在這兩個在 safe 模式一樣要經過批准，只有讀 workspace 內的檔案才直接過。&lt;/p&gt;
&lt;h2 id=&quot;還有一個不是洞的坑codex-的指令外面包了一層殼&quot;&gt;還有一個不是洞的坑：Codex 的指令外面包了一層殼&lt;a href=&quot;#還有一個不是洞的坑codex-的指令外面包了一層殼&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Codex CLI 回報的每一條指令都包成 &lt;code&gt;/bin/zsh -lc &amp;lt;cmd&amp;gt;&lt;/code&gt;，內層有單引號時改用雙引號、單字時不加引號。前綴白名單永遠對不上這個外殼，等於 Codex 在 safe 模式什麼都要問。修法是只拆「一層」，而且只拆引號內不可能有跳脫或展開的形式；拆不乾淨就回 null，照一般判定走，不會因此多放行任何東西。&lt;/p&gt;
&lt;h2 id=&quot;修完之後的規則&quot;&gt;修完之後的規則&lt;a href=&quot;#修完之後的規則&quot; class=&quot;anchor&quot; aria-label=&quot;這一段的連結&quot;&gt;&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;自動放行只看窄白名單；黑名單負責拒絕理由。&lt;/li&gt;
&lt;li&gt;白名單比對指令名、也比對旗標；比對前剝引號與反斜線。&lt;/li&gt;
&lt;li&gt;引號外出現 &lt;code&gt;(){}&lt;/code&gt; 或對旗標敏感的工具遇到萬用字元，不放行。&lt;/li&gt;
&lt;li&gt;串接指令逐段檢查，每一段都唯讀才整條放行；只容忍 &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;、&lt;code&gt;2&amp;gt;/dev/null&lt;/code&gt; 這類丟棄輸出。&lt;/li&gt;
&lt;li&gt;寫入重導向逃出 workspace 就擋，認不出的形狀不擋，但寫在文件裡。&lt;/li&gt;
&lt;li&gt;看完整指令，超過 20,000 字元交給人。&lt;/li&gt;
&lt;li&gt;Windows 的刪除與格式化指令、跨磁碟與 UNC 路徑，跟 Unix 同等待遇。&lt;/li&gt;
&lt;li&gt;只有使用者親手切換才能進 full 與 invincible，介面要說清楚 full 對 MCP 工具是全放行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;殘餘的風險我也列在文件裡：變數展開、子殼、直譯器內的檔案操作、&lt;code&gt;tee&lt;/code&gt; 與 &lt;code&gt;cp&lt;/code&gt; 的目的地，這些在 full 模式下會過。開 full 的人要知道自己換到的是什麼。&lt;/p&gt;
&lt;p&gt;改到的檔案：&lt;a href=&quot;https://github.com/juinwei7/Pixel-Crew/blob/main/server/src/dangerousCommand.ts&quot;&gt;dangerousCommand.ts&lt;/a&gt;、&lt;a href=&quot;https://github.com/juinwei7/Pixel-Crew/blob/main/server/src/bashWriteFence.ts&quot;&gt;bashWriteFence.ts&lt;/a&gt;、&lt;a href=&quot;https://github.com/juinwei7/Pixel-Crew/blob/main/server/src/safeLocalPath.ts&quot;&gt;safeLocalPath.ts&lt;/a&gt;，以及對應的測試。發現繞過請走 &lt;a href=&quot;https://github.com/juinwei7/Pixel-Crew/security/advisories/new&quot;&gt;私密弱點回報&lt;/a&gt;，七天內回覆。&lt;/p&gt;
</content:encoded><category>AI 與自動化</category><category>claude-code</category><category>codex</category><category>pixel-crew</category><category>安全</category><author>weiwei</author></item></channel></rss>