cd /news/ai-agents/building-a-security-reviewer-subagen… · home › topics › ai-agents › article
[ARTICLE · art-169724] src=dev.to ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Building a Security Reviewer Subagent

A developer built a dedicated security-reviewer subagent in Claude Code to audit a login feature across a frontend and backend, then reviewed its findings and triaged each into implement, defer, or ignore. The audit flagged missing CSP and security headers, absent rate limiting on the sign-in route, no helmet middleware, and unset trust proxy, while confirming no dangerouslySetInnerHTML, eval, or dynamic href/src usage in the source. The developer noted subagents produce more consistent output than repeated ad-hoc prompts and that the agent creation wizard has been removed in favor of prompting the main agent to generate the agent file.

by read4 min views1 publishedNov 14, 2026

The next lesson in the course is to create a subagent. And now that there's at least one feature (login) working in both the frontend and the backend, it's a good time to run a security audit using a subagent dedicated to that.

A subagent is an AI assistant that has specific instructions for completing tasks and can run in the background, so it doesn't interrupt the main thread, and when it's done, it reports its findings or the changes that it made. I had the security-reviewer subagents report the findings of the security audit. I reviewed the reports and made decisions on what to implement and what to ignore. Some of the benefits of a subagent over entering a regular prompt for the main agent are that I don't have to repeat all the specific instructions every time and it produces a more consistent and predictable output.

Building the subagents was a lot easier than I expected. The agent creation wizard was removed, so now the only way to build a subagent is by sending a regular prompt to the main agent instructing it to create one along with the it's purpose. Claude generates the agent file in a specific folder, depending on the scope. The way I used it this time was by explicitly asking the main agent to use the subagent.

Medium

No CSP or security headers. index.html has no CSP and the repo has no hosting config. If an XSS bug shows up later, nothing limits the damage, and the logged-in app could be framed (clickjacking). Set these at the host:

frame-ancestors only works as a real header, not in a meta tag.

💡 Decision: Add as a task in the implementation plan - CSP can’t be written until the API address exists.

Low

LoginPage.tsx:23, set in RequireAuth.tsx:15-19). It isn't exploitable today, because state.from is a router location object that a link or query string can't set. It would become a problem if you switch to a ?next= param. In that case, accept only paths that start with / and not //, and that contain no \.

💡 Decision: Ignore - I’m not using the `?next=` param to redirect after login.

shadcn is in dependencies. npm audit findings (2 low, 4 moderate). Those are build-time only and don't ship in the browser bundle. Move it to devDependencies.

💡 Decision: Implement - Self explanatory.

cn@^0.5.2 is an unusual package.AppShell.tsx:1 imports it where the usual helper is clsx + tailwind-merge. Check it's the package you meant, or swap it. All dependencies use ^ ranges, so install with npm ci.

💡 Decision: Ignore - It’s a replacement for `clsx` + `tailwind-merge`.

Checked and clean

dangerouslySetInnerHTML, eval or dynamic href/ src in src/./login sits under RequireAuth. console.* calls, and no direct fetch calls..env is gitignored, and the only VITE_* variable is the API URL.src/app.ts:18, src/lib/auth.ts:6-15). The reviewer believes it is on by default only in production and keeps its counts in memory, but didn't check that against the docs. Fix: set rateLimit in betterAuth() with a strict rule for /sign-in/email (for example 5 per minute).

💡 Decision: Implement - Even though it’s not an issue at the moment, I wanted to make sure that route has an extra protection and it’s a quick fix.

trust proxy isn't setsrc/app.ts:13), and you deploy behind a proxy. Client IPs will be wrong, and a spoofed X-Forwarded-For could dodge IP-based limits. Fix: app.set('trust proxy', 1) and set the IP header explicitly in Better Auth.

💡 Decision: Add as a task in the implementation plan - Can’t test until deployment.

helmet isn't used (src/app.ts:14-16). Fix: add helmet(). HSTS is the main benefit.

💡 Decision: Implement - Agree with the main benefit listed.

BETTER_AUTH_URL and FRONTEND_ORIGIN default to localhost, even in production. A missed variable would fail quietly.

💡 Decision: Ignore - Don’t have production URLs, will update after deployment.

X-Request-Id is trusted without length or format limits (src/middleware/requestLogger.ts:8-10). Pino escapes it, so there's no log injection. Restrict it to /^[\w-]{1,64}$/.

💡 Decision: Implement - It’s a one-line change that keeps junk out of my logs.

HttpError.details are returned as-is (src/middleware/errorHandler.ts:18,25). A future error with internal wording could leak. Allow-list the status codes.

💡 Decision: Ignore - It’ll be an issue when I integrate third party APIs. Will worry about this then.

/openapi.json is public (src/app.ts:25). Impact is minimal today, but gate it once real routes exist.

💡 Decision: Implement - The next change in the backend will be adding real routes.

Clean:

Something I noticed was that the security-reviewer would bring up concerns about possible future problems. I will run the subagent a few more times to see if it keeps doing it. If that’s the case, I will update the instructions to only report the vulnerabilities as the code stands today.

Frontend Repo: cc-outreach-fe

Backend Repo: cc-outreach-be

── more in #ai-agents 4 stories · sorted by recency
── more on @claude code 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/building-a-security-…] indexed:0 read:4min 2026-11-14 · —