Vector: How We Moved Jane's Secure SDLC Beyond Traditional Scanners with AI-Native Security

Our secure SDLC ran on traditional scanners, and it wasn't keeping up with how fast we ship. So we built Vector, an internal platform where LLM agents do the reading and reasoning a security engineer would otherwise do by hand: triaging findings, verifying fixes, and scanning our own code.

Share
Vector: How We Moved Jane's Secure SDLC Beyond Traditional Scanners with AI-Native Security

Hi, I'm Mohammad Kamrani, a Senior Application Security Engineer at Jane.

A while back we set out to answer a question a lot of security teams are sitting with: how do you keep security work moving at the pace of a fast-growing engineering team without giving up depth or coverage? We're a healthcare platform, so the bar is high. We take protecting patient data seriously because that's the job, not a checkbox. Most of that work lives in what's usually called the secure software development lifecycle, or S-SDLC: the security checks built into how code actually gets written, reviewed, and shipped. Ours ran on traditional scanners, and it wasn't keeping up. What came out of that is an internal tool we call Vector, an AI-native platform where LLM-driven agents do the reading and reasoning a security engineer would otherwise do by hand. It started small and turned into something the team leans on every day. Here's what it does and how we use it.

What Vector is

Vector, which stands for Vulnerability Evaluation and Comprehensive Testing with Orchestrated Retesting, is our internal security platform. It pulls together a set of workflows that used to be manual, disconnected, or honestly not happening at all. It has four modules: a retest engine that verifies vulnerability fixes, a verification system that triages scanner findings, an AI-native SAST engine that scans our own repositories, and a pull request scanner. Each one solves a different problem, and they all feed one dashboard with vulnerability metrics, accuracy stats, and cost breakdowns. Under that, it's a service-oriented setup with a relational database behind it, and LLM agents do the heavy lifting on the AI side. It connects to Jira, GitHub, and Slack, which was deliberate. Security work should show up where people already work.

Every module runs through roughly the same AI-native pipeline. Work arrives, whether that's a fresh scan of a repository, a pull request, or a finding someone reported. An agent then gathers the context it needs from Jira and GitHub, and reasons about the specific question in front of it: is this exploitable here, is this fix real, does this diff introduce something new? It writes up a verdict with the evidence behind it, a person approves or overrides that verdict, and the result gets posted back where the work already lives. What makes this AI-native rather than a rules engine with a language model bolted on is that the reasoning is the product: there's no signature list underneath deciding what counts. The agent does the reading and the tracing. A human still makes the call.

Retest: verifying that a fix actually holds

The problem: verifying a fix by hand doesn't scale

Our red team runs penetration tests, mostly internal, though we do external assessments too. A vulnerability gets found, it gets reported, a developer fixes it, and then someone has to confirm the fix holds. That someone used to be a person re-running the same test by hand, often the person who found it in the first place. It worked, but it ate time, and it meant findings sat in a "probably fixed" state longer than we liked. That workflow held up fine when we had a handful of findings a month. It didn't hold up once the number of tests grew and confirmed-fix requests started stacking up faster than one person could re-run them by hand.

Dynamic retest: rebuilding the attack against a running app

That's the gap the retest module was built to close: instead of a person re-running the test by hand, an AI agent does it, in one of two ways. The first is dynamic: point the retest module at the original Jira ticket, and an AI agent reads the vulnerability description, works out which endpoint was affected, and pulls the relevant source from GitHub, the route definition, parameter validation, and the middleware in front of it, so it can judge whether the developer's change addresses the root cause or only the path described in the ticket. From there it writes out several attack scenarios covering different paths, roles, and edge cases, then works through them against the running app, rebuilding the attack the way a tester would rather than replaying the original request, and going after the controls the developer added as well as the original path. Verdicts are FIXED, NOT_FIXED, PARTIALLY_FIXED, or NEEDS_HUMAN_REVIEW, and that last one matters: when the change is ambiguous, the agent says so and hands it to a person instead of guessing.

Static retest: a safe first check before anything runs

The other way is static, for when you don't want to touch the live app or can't. It answers a narrower question purely from the code: did the fix land in the source, or in a linked pull request? It never sends a request, so it's a safe, fast first check on a fix that hasn't deployed yet, and it's often enough on its own to catch a fix that never actually landed, before anyone spends time on a full dynamic run.

Verification: working out which scanner findings are real

The problem: detection isn't triage

We use a commercial application security posture management tool (ASPM) that covers code scanning, dependencies, secret detection, and infrastructure as code. Detection isn't the problem. Triage is. Traditional scanners work on patterns and signatures, so they flag everything that matches the shape: a call to a dangerous function, a string that looks like SQL, user input flowing somewhere risky. They don't know our data flow, our role restrictions, or whether something already blocks the issue, so a person has to read each one and decide whether it's real. Most of the time the answer is obvious once you have that context: a parameterized query, an unreachable function, a secret that's just a test fixture. But obvious still takes time at scale, and on a codebase our size it becomes a bottleneck.

Static verification: what the code alone tells us

That's the bottleneck Vector's verification module was built to close: instead of a person reading every finding, an agent does it, in one of two ways. The first is static: most findings get resolved from the code alone. Vector's verification agent picks up the Jira ticket the scanner opens for each finding, pulls the relevant source from GitHub, and reasons through it based on the finding type: tracing input through the call chain for code findings, checking reachability for dependencies, checking the environment for infrastructure, and confirming whether a secret is real or just a fixture. It returns TRUE_POSITIVE, FALSE_POSITIVE, or NEEDS_REVIEW with full reasoning, an analyst signs off, and false positives close automatically. Static triage answers whether the code supports the issue, and it's what carried the heaviest load. We pointed it at the backlog, more than 2,000 tickets sitting across dozens of repositories, and let it run. By the time it finished, our backlog had dropped by more than 80%.

Dynamic verification: confirming it in a running app

The other way is dynamic, for the findings that need more than the code can tell us: there's a dynamic pass that authenticates and sends real requests to confirm exploitability, using the same machinery the retest module uses. Dynamic triage answers whether the issue actually happens, not just whether it looks possible on paper. It's the slower path, so we only reach for it when static analysis alone can't settle the question, but it's the only way to catch a vulnerability that only shows up once the application is running with real data and real permissions in front of it.

AI-native SAST: reads code like an attacker

The SAST module is where we moved off traditional scanning, the kind that drowns you in noise and still misses the issues that matter, to an AI-native engine that reasons through our own repositories instead of waiting for a traditional SAST scanner to flag something. It's built on DeepSec, an open-source AI-native scanning engine that runs on AI agents under the hood. We point it at a full repository, a branch diff, or a specific set of files. Every repository gets its own board, so you can see where that codebase stands right now. A scan moves through distinct stages. It starts with a fast pattern pass that walks the code and marks candidate files worth a closer look, with no AI involved at all, just matching. Those candidates go to AI agents one file at a time. The agent doesn't just look at the flagged line. It traces how data actually moves through that file, checks whether a mitigation is already in place, weighs what it's seeing against the context brief we keep for that repository, and only then writes up a finding with a severity rating, if it decides the risk is real. Files that come back clean get marked as checked. After DeepSec exports what it found, Vector adds a pass of its own on each finding: exploit steps, meaning a concrete request chain or call sequence showing how to reproduce it, and an impact section covering what exploitation would actually mean, what data could be exposed, whether accounts or sessions are affected, whether availability is at stake, and any compliance angle worth naming.

Context management: enriching the AI-native scan

Context is what keeps this useful over time instead of getting noisier. Every repository carries a short brief that tells the scanner how that codebase works and what's already known to be safe. An AI agent drafts it: it reads the README, any AGENTS.md, and the manifest, then picks its own representative code files to open, the entry points and auth helpers that name real primitives instead of guesses. It's also handed a fixed set of security-critical files up front: auth and authorization code, webhooks and public routes, the routing config, tenancy and signature checks. That way, coverage of the parts that matter doesn't depend on the model happening to pick the right file on a large repo. Once there's a draft, a separate adversarial pass re-reads the cited code and tries to refute every claim in it, stripping out anything that doesn't hold up before a human ever sees it.

Feedback loop: where humans help tune the scan

That's how a brief gets written, but it doesn't stay fixed once scanning starts. The best additions to that brief come from developers, not drafts, and this is the feedback loop that lets Vector improve itself scan over scan. If someone thinks a finding is wrong or rated too high, they say so on the Jira ticket and tag the Vector bot, rather than arguing it out in a comment thread nobody will read later. That kicks off an AI pass that checks the claim against the code and comes back with an advisory read: agree, disagree, or uncertain. It never gets the final word, because an analyst has to sign off. Once approved, we don't just close the ticket. We turn the claim into one plain, code-grounded fact, and drop it into the right place in that repository's brief so the next scan knows about it. That closes the loop: a developer's correction today becomes something the scanner already knows tomorrow, which is how it keeps getting better without anyone maintaining it by hand.

Retest and verification, reused: is it real, is it fixed

Retest and verification came before SAST existed, but the two questions they answer, is this real and is this fixed, apply here just as well. So instead of inventing something new, we reused the machinery we already had from retest and verification. If there's a Jira ticket attached, the verdict posts there as a comment. If there isn't, it works off the finding directly and stays in Vector.

The results: less noise, more of it real

We ran this across most of our repositories, and the results were interesting: the share of true positives was high enough that it surprised us. It didn't just catch textbook injection bugs, the kind signature scanners are built for. It found business logic and access control issues too, the kind that are hard for a signature-based scanner to catch just by pattern-matching source code, because it doesn't stop at one suspicious line. It reads the way an attacker reads, following small, individually harmless pieces until they connect into something that isn't, which is the kind of chain we'd normally expect only a skilled human tester to find. A few of those catches stood out enough that we pulled them into a follow-up review just to walk through exactly how the chain worked.

How we know it's getting it right

If there's no signature list underneath, the obvious question is how we know the agent is getting it right. A finding on its own isn't proof, so in the SAST module every finding is logged with the evidence behind it and the repository context it was judged against. That isn't an eval framework yet. It's the record we'd need to build one, and it's what will let us test a new prompt or model version against past findings before it touches live work. An agent nobody can audit isn't worth much, however well it writes.

Pull request scanning: security review at the speed of development

Our developers ship a lot of code, and there aren't enough of us to review every pull request without becoming the bottleneck ourselves, though some PRs touch authentication logic, payment handling, or patient data access and need eyes on them. Our commercial ASPM tool does have PR scanning, but it flags a lot that doesn't matter and misses things that do, so we kept doing manual reviews anyway. Vector's PR scan module takes a GitHub pull request URL, fetches the diff, works out the stack and frameworks in play, and looks for real problems: injection, cross-site scripting, authentication bypasses, broken object-level authorization, unsafe data handling. Findings go back as inline comments on the exact lines, ranked from critical through informational, so developers see them in the review they were already doing. There are three modes. Full analyzes every file for every category of vulnerability. Quick checks only for high and critical severity issues, for a fast sanity check rather than a full review. Verify-fix checks whether a specific pull request actually addresses a previously reported vulnerability, the retest question asked at the code level.

Impact: what changed for the team

We went from a large backlog of uncertain findings that nobody could stay on top of to a short list of real ones with suggested fixes. The gap between those two states is enormous, and not only for our security posture. It changed the team's week. Each module moved something specific. Red team findings get verified with evidence when fixes land, instead of sitting in "probably fixed". Scanner noise gets filtered before it reaches anyone's plate. Security feedback arrives while code is still in review rather than months later in an audit. And we're finding real, exploitable issues in our own code rather than only reacting to whatever a traditional SAST scanner decides to flag. Most of it runs without anyone driving. Pipelines pick up new findings, agents triage them, analysts review the queue, results flow back to Jira and GitHub. People spend their time on judgment calls, architecture decisions, and the creative security thinking machines still can't do. Vector let a small security team cover a lot more ground. If your engineering team is growing faster than your security headcount, this is roughly how you keep up. We're still building it.