VulnVibes: Building an AI Agent That Reasons Across Microservices to Find Real Vulnerabilities
A prototype AI agent that validates real AppSec issues across repositories, infrastructure, and microservice boundaries.
Disclaimer: This is a cross post from my tech blog, co-authored by my personal AI assistant Sage.
Background Context
Picture this: you’re reviewing a pull request. A developer on your team has added a new API endpoint that fetches content from a URL the user provides. There’s even a role check — only admins can use it. Looks reasonable, right?
But here’s the thing — that endpoint runs inside a Docker container, on the same network as your authentication service, your database, and your internal admin tools. An attacker who gets admin access could point that URL at http://auth-service:3001/ and read your internal service responses. Or hit the cloud metadata endpoint at 169.254.169.254 and grab your AWS credentials.
The code review in the PR diff looks fine. The vulnerability is invisible unless you also check the Docker Compose file in your infra repo, the nginx config in your gateway repo, and the network topology that ties everything together.
This is the fundamental problem with how we do security reviews in microservice architectures: the vulnerability lives in the gaps between services, not in any single repo.
I’ve seen this problem time and again and haven’t come across a single SAST (AI native or traditional) platform that can reliably help tackle this at scale. If there is any, please let me know!
So, I’ve been tinkering with a few different approaches. And, I have a prototype agent built that is not production ready by any means but good enough to demonstrate the problem (with a lab I’ve built) and a high level solution of how it can potentially be solved.
If this piques your interest, continue reading.
Introducing VulnVibes
Everything is a “vibe” these days. So, keeping with the SecureVibes naming theme, I’m calling this one VulnVibes 😄
VulnVibes is an AI-powered agent that analyzes pull requests for security vulnerabilities — and its superpower is that it doesn’t just look at the PR’s repo. It searches across your entire GitHub organization to understand your architecture, verify what security controls actually exist, and determine whether a suspicious code change is a real vulnerability or a false alarm.
For a more technical explanation, VulnVibes is an open-source CLI that takes a GitHub PR URL, threat models the changes, and then validates each threat by investigating code across multiple repos in your org. I demo it below against three real PRs and show it catching an SSRF vulnerability, making a nuanced judgment call on a CORS change, and correctly ignoring a safe code refactor.
It’s a concept tool — fully vibecoded — but it demonstrates something important: AI agents can reason across repository boundaries the way a human security engineer does.
Why Current Tools Don’t Work for Microservices
Let’s say your company runs a typical microservice architecture — an auth service, an API backend, a frontend app, and some infrastructure configs. Four repos, four teams, one system.
Now a developer opens a PR in the API repo. They’re adding a new feature. A traditional SAST scanner (think Semgrep, CodeQL, or similar) will analyze that PR against the code in that repo only. If the code looks suspicious — say, it makes an HTTP request using user input — the scanner flags it.
But is it actually exploitable? To answer that, you need context that lives outside that repo:
Is there a Web Application Firewall (WAF) that inspects request bodies before they reach the API? (Check the infra repo.)
Are internal services reachable from this container? (Check the Docker Compose file.)
Does the API gateway add any security headers or validate tokens? (Check the nginx config.)
What sensitive data exists on those internal services? (Check the auth service repo.)
Not a lot of SAST tools — even the AI-powered ones — can answer these questions. They operate within repository boundaries. They literally can’t see the other repos.
What does a human security engineer do? They open four browser tabs, read the nginx config, trace the request flow, check Docker networking, and mentally piece together whether the attack path is viable. It takes 30 minutes to an hour for a single PR, if they’re thorough.
VulnVibes automates that entire process. It has access to your GitHub org, can read files from any repo, search for patterns across the codebase, and reason about whether a threat is real based on the full architectural context.
How VulnVibes Works
The analysis happens in two stages:
Stage 1: Threat Modeling (The Quick Filter)
When you point VulnVibes at a PR, the first thing it does is fetch the diff and ask: “Is there anything security-relevant here?”
It produces a structured threat model:
What changed? A plain-English summary of the PR
What could go wrong? Specific threats, each tagged with a CWE (a standardized vulnerability classification)
What do we need to verify? A list of investigation questions for each threat
Which investigation skills should we use? Matched to specific testing methodologies (SSRF testing, auth testing, etc.)
If Stage 1 finds nothing security-relevant — like a pure code refactor — it stops here. Done in under a minute, minimal cost. No wasted effort.
If it does find something worth investigating, it moves to Stage 2.
Stage 2: Cross-Repo Investigation (The Deep Dive)
This is where VulnVibes is different from everything else I’ve seen.
For each threat identified in Stage 1, the agent performs a full investigation. It doesn’t just look at the PR — it goes hunting across the organization:
Reads the PR code in full context — not just the diff, but the entire file and related files
Searches across other repos — looking for infrastructure configs, middleware, security controls
Checks the infrastructure layer — Docker networking, nginx configs, deployment files, environment variables
Follows a structured methodology — each vulnerability type has its own investigation playbook
Produces a verdict with a full reasoning chain — TRUE_POSITIVE (real vulnerability), FALSE_POSITIVE (looks bad but isn’t), or NO_SKILL_AVAILABLE (can’t test this type)
Every verdict comes with a confidence score, a risk level, and a step-by-step explanation of how the agent reached its conclusion. You can read the reasoning and decide whether you agree.
The Demo: Three PRs, Three Outcomes
To demonstrate this, I set up a test environment: microvibes-lab, a GitHub organization with four microservices that form a document management system.
Services in the lab
auth-service (Node.js): Handles login and issues JWT tokens
doc-api (Python/FastAPI): Document storage with role-based access
frontend-app (Next.js): The web UI
infra-ops (Nginx + Docker): API gateway and infrastructure configs
It’s a realistic setup — JWT authentication shared between services, nginx routing requests, everything running on a Docker network, role-based access control (admins see everything, staff see only public documents).
I ran VulnVibes against three different PRs to show three different outcomes. Here’s a video walkthrough of all three cases:
Case 1: Catching a Real Vulnerability
PR: doc-api#13 — Add document import from URL feature
What the PR Does
A developer adds a new endpoint that lets admins import documents from external URLs. Here’s the code:
@app.post("/documents/import")
def import_document(url: str, user: dict = Depends(get_current_user)):
if user["role"] != "sys_admin":
raise HTTPException(status_code=403, detail="Admin access required")
response = requests.get(url)
return {"content": response.text, "status_code": response.status_code}
Nine lines of code. On the surface, it looks reasonable — there’s a role check ensuring only admins can use it. A code reviewer might glance at this and move on.
What’s Actually Wrong
This is a classic Server-Side Request Forgery (SSRF) vulnerability. In plain English: the server is making an HTTP request to whatever URL the user provides. The user says “fetch this URL,” and the server obediently does it.
Why is that dangerous? Because the server can reach things the user can’t. It’s sitting inside a Docker network with direct access to internal services. An attacker could tell it to fetch:
http://auth-service:3001/health— to probe internal serviceshttp://169.254.169.254/latest/meta-data/— to steal cloud credentials (a very common attack in AWS environments)Any internal service on the Docker network that’s not exposed to the internet
And the response comes straight back to the attacker, unfiltered.
What VulnVibes Did
Stage 1 (~50 seconds): Identified the core threat — SSRF via unrestricted URL fetch.
Stage 2 is where it gets interesting. Watch where the agent went to validate the SSRF:
✅ Read
main.pyin full — confirmed there’s zero URL validation. No allowlist, no blocklist, no scheme restriction.✅ Read
docker-compose.ymlfrom the infra-ops repo — confirmed all four services share a flat Docker network. Every service can reach every other service by hostname.✅ Read
nginx.conffrom the infra-ops repo — confirmed nginx does nothing but route traffic. No WAF, no request body inspection, no URL filtering.✅ Checked the
Dockerfile— standard Python image, no network restrictions.✅ Checked
requirements.txton the PR branch — no URL validation libraries installed.
The agent traced the full attack path across three different repos and concluded:
“doc-api can reach auth-service:3001, frontend-app:3000, gateway:80 directly via Docker DNS. No network policies restrict egress.”
Verdict: TRUE POSITIVE — HIGH (confidence 10/10)
🎯 Overall Verdict: TRUE_POSITIVE
⚠️ TRUE POSITIVE - Security vulnerability confirmed!
1. SSRF via unrestricted URL fetch — HIGH (10/10)
Duration: 134 seconds | Cost: $0.14
A traditional SAST tool could flag requests.get(url) as a potential SSRF. But it couldn’t tell you whether internal services are actually reachable, whether nginx adds any protection, or whether Docker networking enables the attack. VulnVibes answered all of those questions by reading files from repos the PR author never touched.
Case 2: The Nuanced Judgment Call
PR: auth-service#10 — Enable CORS credentials for cross-origin requests
What the PR Does
A developer updates the CORS (Cross-Origin Resource Sharing) configuration:
// Before: default CORS (allow everything, no credentials)
app.use(cors());
// After: reflect any origin + allow credentials
app.use(cors({
origin: true,
credentials: true
}));
Why This Looks Scary
If you’ve read any web security guide, this combination is a red flag. origin: true means the server will accept requests from any website. credentials: true means the browser will include cookies with those requests. Together, this is the most permissive CORS policy possible.
In a typical web app that uses cookies for authentication, this would be a serious vulnerability. A malicious website could make requests to your API on behalf of a logged-in user, read the responses, and steal session data.
Every security scanner would flag this immediately. And in most cases, they’d be right.
But Is It Actually Exploitable Here?
This is where VulnVibes earns its keep. The agent didn’t just pattern-match on “permissive CORS = bad.” It went investigating:
Searched the entire org for cookie usage —
Set-Cookie,res.cookie(), session middleware — found nothingRead the frontend app source code — discovered authentication uses
localStorage+Authorization: Bearerheaders, not cookiesSearched for
withCredentialsorcredentials: 'include'patterns — found nothingChecked the auth-service dependencies — no session middleware installed
Checked nginx — no cookie handling
The agent’s key insight:
“Auth is entirely header-based using localStorage + Authorization Bearer. No cookies are set anywhere in the codebase. The
credentials: trueflag has no meaningful effect since no cookies exist.”
The CORS configuration looks terrible in isolation. But in this specific architecture, the classic attack doesn’t work because the app doesn’t use cookies at all. An attacker’s website can’t steal what doesn’t exist.
Verdict: FALSE POSITIVE — LOW risk (confidence 8/10)
VulnVibes correctly identified that the CORS configuration, while technically permissive, is not exploitable in this architecture:
🎯 Overall Verdict: FALSE_POSITIVE
✓ FALSE POSITIVE - No security vulnerability found
📊 Investigation Results:
1. Permissive CORS — FALSE_POSITIVE (8/10), Risk: LOW
Duration: 170 seconds | Cost: $0.16
This is exactly the kind of analysis I want from a triage tool. It didn’t blindly flag it as critical — which is what every pattern-matching scanner would do. Instead, it investigated the architecture, confirmed header-based auth, found zero cookie usage across the entire org, and concluded the CORS change isn’t exploitable. A human security engineer would reach the same conclusion — but only after 30+ minutes of reading code across four repos.
Case 3: Knowing When to Stay Quiet
PR: auth-service#12 — Refactor auth code into helper functions
What the PR Does
A developer refactors the JWT token generation logic — extracting inline code into helper functions:
// Before: JWT signing inline in the login handler
const token = jwt.sign(
{ username: user.username, role: user.role, name: user.name },
JWT_SECRET,
{ expiresIn: '1h' }
);
return res.json({ token, user: { username: user.username, ... } });
// After: extracted to a reusable function
function generateToken(user) {
return jwt.sign(
{ username: user.username, role: user.role, name: user.name },
JWT_SECRET,
{ expiresIn: '1h' }
);
}
const token = generateToken(user);
return res.json({ token, user: formatUserResponse(user) });
Why a Naive Tool Would Flag This
This PR modifies authentication code. It touches JWT token generation — the most security-sensitive part of the entire system. A pattern-matching scanner might flag it because “authentication code changed” or “JWT signing logic modified.”
What VulnVibes Did
VulnVibes looked at the diff, compared the before and after, and concluded in 27 seconds:
“This PR is a straightforward code refactoring. The JWT payload, signing secret, and expiration are unchanged. No new endpoints, routes, dependencies, or security-relevant behavior is introduced.”
Zero threats identified. No Stage 2 investigation needed.
ℹ️ No security-relevant changes detected in this PR.
Duration: 27s
This is just as important as catching real vulnerabilities. A tool that flags everything is just as useless as one that catches nothing — because it trains developers to ignore the alerts. VulnVibes understood that despite touching JWT code, the behavior didn’t change. It saved everyone’s time.
The Scorecard
Results at a glance
SSRF — doc-api#13: Looked like a reasonable new feature, but VulnVibes confirmed a real vulnerability across 3 repos. Time: 2.2 min. Cost: $0.14
CORS — auth-service#10: Looked scary, but VulnVibes determined it was a false positive because auth is header-based. Time: 2.8 min. Cost: $0.16
Refactor — auth-service#12: Security-sensitive code changed, but it was a safe refactor and needed no deeper investigation. Time: 27 sec. Cost: minimal
The Bigger Picture
Let me be upfront: VulnVibes is a concept tool, not a production-grade agent. It’s fully vibecoded — I built it to demonstrate an idea, not to replace your security team.
The idea is this: context matters enormously in security, and AI agents can now gather and reason about that context across repository boundaries.
If you work at an organization with a microservice architecture and your existing SAST tools are either missing real vulnerabilities or drowning you in false positives, the problem might not be the scanner itself. The problem might be that the scanner can only see one repo at a time.
VulnVibes shows that it’s possible to build an agent that:
Reads the PR diff and identifies what could go wrong
Searches across your entire org to understand the actual architecture
Checks infrastructure configs to verify what security controls exist (or don’t)
Makes a calibrated judgment — not just “this pattern is bad” but “this pattern is bad and there are no compensating controls at any layer”
The specific implementation matters less than the concept. You could build something similar using any LLM with tool use, a GitHub API integration, and some structured investigation playbooks. The key insight is giving the agent access to cross-repo context and teaching it to verify assumptions against the actual infrastructure.
Getting Started
If you want to try it yourself, VulnVibes is open source:
# Install
pip install -e .
# Analyze a PR
vulnvibes pr analyze https://github.com/your-org/your-repo/pull/123 \
--github-token $GITHUB_TOKEN \
--model sonnet \
--org your-org \
--context-file context.md
You can optionally provide a context file that tells VulnVibes about your architecture:
---
related_repos:
- name: infra-ops
purpose: nginx configs, Docker Compose, k8s manifests
- name: auth-service
purpose: JWT authentication, user management
---
# Architecture Overview
Microservices on Docker with nginx reverse proxy.
JWT-based auth, tokens stored in localStorage.
If you have a Claude Max or Pro subscription and you’re authenticated via Claude Code or Claude CLI, VulnVibes works with OAuth — no API key needed.
The test environment at microvibes-lab has 17 PRs with known expected outcomes if you want to benchmark it yourself.
If you’re interested in trying it out, building something similar, or just want to talk about AI-powered security tooling — feel free to reach out!
GitHub: anshumanbh/vulnvibes
LinkedIn: @anshumanbhartiya
Blog: anshuman.ai
Until next time, ciao! 👋



