<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Walden Wu</title>
    <description>The latest articles on DEV Community by Walden Wu (@waldenwuwei).</description>
    <link>https://dev.to/waldenwuwei</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4128894%2Fc1d439b2-c45a-4bd9-a0f0-8e19c88f6d75.jpg</url>
      <title>DEV Community: Walden Wu</title>
      <link>https://dev.to/waldenwuwei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/waldenwuwei"/>
    <language>en</language>
    <item>
      <title>The OWASP Agentic AI Top 10, read as someone who actually runs agents on his laptop</title>
      <dc:creator>Walden Wu</dc:creator>
      <pubDate>Thu, 17 Sep 2026 11:49:18 +0000</pubDate>
      <link>https://dev.to/waldenwuwei/the-owasp-agentic-ai-top-10-read-as-someone-who-actually-runs-agents-on-his-laptop-3ob1</link>
      <guid>https://dev.to/waldenwuwei/the-owasp-agentic-ai-top-10-read-as-someone-who-actually-runs-agents-on-his-laptop-3ob1</guid>
      <description>&lt;p&gt;OWASP published a Top 10 for Agentic Applications in December 2025 — ten risks, numbered ASI01 through ASI10.&lt;/p&gt;

&lt;p&gt;Most write-ups I found target platform teams. I don't have one. I have a laptop, six agents (Claude Code, Cursor, OpenClaw, DSH and a couple of my own scripts), 14 MCP servers — and a habit of giving them shell access. So here is each item translated into what I actually changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI01 — Agent Goal Hijack
&lt;/h2&gt;

&lt;p&gt;Prompt injection, but the version that matters in practice is indirect: the instruction sits in a file, a web page or an issue your agent reads on the way to doing its job. The gate goes on the tool call, not the text: unauthorized tools rejected, reads of &lt;code&gt;~/.ssh&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt; denied, and a list of injection signal phrases that land in the audit log.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI02 — Tool Misuse
&lt;/h2&gt;

&lt;p&gt;The tool is fine; the arguments aren't. A delete that resolved to a root path, a write that landed on production. Judge by capability, not by tool name: the same filesystem tool is fine reading a project and not fine writing to &lt;code&gt;/etc&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI03 — Identity &amp;amp; Privilege Abuse
&lt;/h2&gt;

&lt;p&gt;Every agent sharing one token means that when something happens, you can't say which agent did it. Each agent gets its own keypair, and delegations narrow hop by hop: a downstream agent only ever gets a subset of what the upstream one had.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI04 — Agentic Supply Chain
&lt;/h2&gt;

&lt;p&gt;This is the one that actually bit me. 12 of my 14 MCP servers were running unpinned — &lt;code&gt;npx -y pkg@latest&lt;/code&gt; means the next launch runs whatever someone uploaded last. Pin versions, check MCP server origins against an allowlist at startup, and audit lifecycle hooks: a plugin "update" is a great place to hang a command that runs on every session.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI05 — Unexpected Code Execution
&lt;/h2&gt;

&lt;p&gt;Anything that can execute code will eventually execute something you didn't intend. Mark exec capabilities separately, require approval, snapshot before execution so there is something to roll back to.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI06 — Memory &amp;amp; Context Poisoning
&lt;/h2&gt;

&lt;p&gt;Content written into &lt;code&gt;CLAUDE.md&lt;/code&gt;, &lt;code&gt;AGENTS.md&lt;/code&gt; or a memory directory doesn't affect this session — it affects every future one. Treat memory as untrusted input. The minimum bar is noticing when it changes, which means a baseline and drift detection.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI07 — Insecure Inter-Agent Communication
&lt;/h2&gt;

&lt;p&gt;The hard one. Cross-org A2A identity is a protocol problem and I don't pretend to solve it. What I can pin down locally: signed delegation chains, capability narrowing, one keypair per agent, so "who authorized whom" has an answer. I'd rather a security tool say plainly what it does not cover than quietly imply it covers everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI08 — Cascading Failures
&lt;/h2&gt;

&lt;p&gt;One poisoned agent talks to five downstream agents, and each of them trusts the message because it came from inside. Circuit breakers that take effect on the next call, plus anomaly detection for delegation bursts.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI09 — Human-Agent Trust Exploitation
&lt;/h2&gt;

&lt;p&gt;The agent writes a confident, plausible explanation for a destructive action and you click approve. "Read more carefully" is not a fix. Approval prompts should show the concrete action and its parameters, not the agent's own narrative; approvals should record who approved and why; a timeout should fail closed.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASI10 — Rogue Agents
&lt;/h2&gt;

&lt;p&gt;You cannot detect intent. Put the boundary on actions instead: what runs, what needs approval, what gets snapshotted — and keep records you can verify later, so "what was allowed at the time" is answerable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two things I took away
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Nearly every item reduces to the same move: put the gate at the action, not at the prompt. You can't filter text well enough; you can decide whether a specific tool call with specific arguments runs.&lt;/li&gt;
&lt;li&gt;The other half is evidence. If you can't reconstruct what the agent did and which policy was in force, you're guessing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've been building &lt;a href="https://gitee.com/suhuisoftwares/pod" rel="noopener noreferrer"&gt;pod&lt;/a&gt; around those two ideas: &lt;code&gt;pod scan&lt;/code&gt; (read-only, finds unpinned servers and plaintext keys), &lt;code&gt;pod policy draft&lt;/code&gt; (compiles a least-privilege policy from what your agent actually did), &lt;code&gt;pod serve&lt;/code&gt; (enforces it), &lt;code&gt;pod verify-audit&lt;/code&gt; (tamper-evident evidence).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://gitee.com/suhuisoftwares/pod/raw/v0.3.2/scripts/install.sh | sh
pod scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The boundary, since it's a security tool: not a sandbox, doesn't see what your agents say to each other, won't stop someone who already has your shell. It sits in front of tool calls and keeps receipts.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>opensource</category>
      <category>mcp</category>
    </item>
    <item>
      <title>I scanned my own laptop: 6 AI agents, 14 MCP servers, 12 unpinned, 5 plaintext keys</title>
      <dc:creator>Walden Wu</dc:creator>
      <pubDate>Thu, 17 Sep 2026 06:51:07 +0000</pubDate>
      <link>https://dev.to/waldenwuwei/i-scanned-my-own-laptop-6-ai-agents-14-mcp-servers-12-unpinned-5-plaintext-keys-1k81</link>
      <guid>https://dev.to/waldenwuwei/i-scanned-my-own-laptop-6-ai-agents-14-mcp-servers-12-unpinned-5-plaintext-keys-1k81</guid>
      <description>&lt;p&gt;I have six AI agents installed on one laptop: Claude Code, Cursor, Codex, OpenCode, OpenClaw and DeepSeek Harness. Between them they run 14 MCP servers.&lt;/p&gt;

&lt;p&gt;I wrote a read-only scanner to answer a question none of them could answer on their own: &lt;strong&gt;what can all of my agents touch, together?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It finished in 0.19 seconds. This is what came back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 1: 12 of the 14 MCP servers are unpinned
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context7   -&amp;gt; npx @upstash/context7-mcp                (unpinned)
playwright -&amp;gt; npx @playwright/mcp@latest               (@latest)
github     -&amp;gt; npx @modelcontextprotocol/server-github  (unpinned)
... 12 in total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every &lt;code&gt;npx -y &amp;lt;package&amp;gt;&lt;/code&gt; is a supply-chain decision. &lt;code&gt;@latest&lt;/code&gt; means the code that runs at startup is whatever was published to npm most recently — not the version you tested. My GitHub MCP server holds a personal access token and pulls &lt;code&gt;@latest&lt;/code&gt; on every start.&lt;/p&gt;

&lt;p&gt;This is not theoretical. 2025 already had real malicious MCP packages on npm harvesting &lt;code&gt;.env&lt;/code&gt; files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 2: five API keys sit in plaintext in agent config files
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.dsh/mcp-manager.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GitHub token x2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.codex/config.toml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.cursor/mcp.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;GitHub token&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.config/opencode/opencode.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;OpenAI key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The scanner masks values and never prints them. But the values are there, in plaintext, on disk.&lt;/p&gt;

&lt;p&gt;Any process — or any agent — that can read those files has my GitHub and OpenAI accounts. This is close to the default configuration on most developer machines right now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding 3: six agents, no inventory
&lt;/h2&gt;

&lt;p&gt;Cursor knows its own permissions. OpenClaw knows its own. Nothing answers "what can all of my agents do together?" That is the shadow-agent problem, and it is why the scan report matters more than any single finding in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I changed
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pin versions.&lt;/strong&gt; &lt;code&gt;npx -y pkg@1.2.3&lt;/code&gt;, never &lt;code&gt;@latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Move secrets to a keychain.&lt;/strong&gt; Tokens out of agent config files and into a system keychain or secret manager.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put a gate in front of tool calls.&lt;/strong&gt; A policy layer that denies sensitive paths, blocks secret output, and hash-chains every call for audit.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Run the scan yourself
&lt;/h2&gt;

&lt;p&gt;Read-only, offline, nothing uploaded, secrets masked in the report:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://gitee.com/suhuisoftwares/pod/raw/v0.3.2/scripts/install.sh | sh
pod scan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apache-2.0. Local-first.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://gitee.com/suhuisoftwares/pod" rel="noopener noreferrer"&gt;https://gitee.com/suhuisoftwares/pod&lt;/a&gt; (mirror: &lt;a href="https://github.com/suhui-organization/pod" rel="noopener noreferrer"&gt;https://github.com/suhui-organization/pod&lt;/a&gt;)&lt;br&gt;
Site: &lt;a href="https://podsec.vercel.app" rel="noopener noreferrer"&gt;https://podsec.vercel.app&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The numbers come from a real scan of the author's machine (first scan 2026-09-01, re-confirmed 2026-09-12). No key values are published — run &lt;code&gt;pod scan&lt;/code&gt; to get your own.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>opensource</category>
      <category>mcp</category>
    </item>
    <item>
      <title>I scanned 14 MCP servers on my laptop, then compiled a least-privilege policy from what my agent actually did</title>
      <dc:creator>Walden Wu</dc:creator>
      <pubDate>Thu, 17 Sep 2026 02:55:37 +0000</pubDate>
      <link>https://dev.to/waldenwuwei/i-scanned-14-mcp-servers-on-my-laptop-then-compiled-a-least-privilege-policy-from-what-my-agent-3de9</link>
      <guid>https://dev.to/waldenwuwei/i-scanned-14-mcp-servers-on-my-laptop-then-compiled-a-least-privilege-policy-from-what-my-agent-3de9</guid>
      <description>&lt;p&gt;I run six agent platforms on one laptop. Between them they have 14 MCP servers configured, 12 of them unpinned (&lt;code&gt;npx -y pkg@latest&lt;/code&gt;), and five plaintext API keys sitting in config files. I only knew that after writing a read-only scanner.&lt;/p&gt;

&lt;p&gt;That scanner turned into &lt;strong&gt;pod&lt;/strong&gt;. The piece I care about is the middle step that most existing tooling skips:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;record    run your agent through pod in record-only mode for a few days
compile   pod policy draft  -&amp;gt; least-privilege rules from real observed calls
enforce   pod serve         -&amp;gt; deny &amp;gt; approve &amp;gt; allow, fail-closed
prove     pod verify-audit  -&amp;gt; SHA-256 hash chain you can hand to someone else
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gateways give you a place to enforce rules you write by hand. Scanners tell you what is exposed. pod writes the rules from behavior — &lt;code&gt;read_file&lt;/code&gt; gets denied because it touched &lt;code&gt;.env&lt;/code&gt; exactly once out of 214 calls, and tools that never appeared in the corpus are simply not granted.&lt;/p&gt;

&lt;p&gt;Two things that make it different in practice:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The evidence is independently verifiable.&lt;/strong&gt; Every call goes into a hash chain; &lt;code&gt;pod export-evidence&lt;/code&gt; produces a bundle anyone can verify without trusting me or the host. Sensitive content is stored as hashes only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You own the rules.&lt;/strong&gt; Thresholds, regexes, severities and trusted sources live in &lt;code&gt;~/.pod/rules.json&lt;/code&gt;. Invalid rules fail closed instead of silently falling back to defaults.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is also a control-plane side most tools ignore: lifecycle hooks, frozen config, agent identities, delegation chains, memory-file drift. All of it lands in the same audit chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install (macOS/Linux, builds from source)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://gitee.com/suhuisoftwares/pod/raw/v0.3.2/scripts/install.sh | sh
pod scan        &lt;span class="c"&gt;# read-only, no network&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apache-2.0. Local-first: policies, audits and secrets stay on your machine; the optional cloud control plane is in the same repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest boundaries
&lt;/h2&gt;

&lt;p&gt;The threat model is explicit: no sandboxing, no A2A/mTLS protocol implementation, and no behavioral-drift-as-primary-defense. If you need those, this is not the tool.&lt;/p&gt;

&lt;p&gt;Repo: &lt;a href="https://gitee.com/suhuisoftwares/pod" rel="noopener noreferrer"&gt;https://gitee.com/suhuisoftwares/pod&lt;/a&gt; (mirror: &lt;a href="https://github.com/suhui-organization/pod" rel="noopener noreferrer"&gt;https://github.com/suhui-organization/pod&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Website: &lt;a href="https://podsec.vercel.app" rel="noopener noreferrer"&gt;https://podsec.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would especially like feedback on the compile step: whether the observed-behavior-to-policy mapping is too coarse for real workflows, and where it produces obviously wrong rules.&lt;/p&gt;

</description>
      <category>security</category>
      <category>ai</category>
      <category>opensource</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
