Case Study · Autonomous Security Remediation

From vulnerability research to an applied, backported patch - automatically.

An upstream researcher finds the vulnerability and the upstream fix. Turning that into a patch that actually applies to a specific, drifted codebase was still slow, senior-engineer work. We built an autonomous agent that decides whether a fix is applicable, backports only the security-relevant changes, and generates a self-validated patch - end to end, no human in the loop.

Security · DevSecOps platform
Autonomous multi-phase agent
CVE backporting & patch generation
Research → applicable, validated patch

Published with the engagement anonymised at the client's request - a confidentiality bar we treat as part of the deliverable.

As-built · verifiedby the delivery team
0
Autonomous pipeline phases, end to end
0
Reasoning stages - understand, analyze, apply
0
Integrated systems, research to patch
0
Automated safety gates that can halt a run
The client

A security & DevSecOps platform company whose product helps engineering teams find and remediate vulnerabilities across their codebases. Identity withheld at the client's request.

The challenge

Upstream research was excellent - but turning a known fix into an applicable, backported patch on a specific customer codebase was slow, manual, and gated on scarce senior security engineers.

The build

An autonomous eight-phase agent that takes the researcher's findings, decides applicability, isolates the security-relevant changes, backports them in dependency order, and validates its own patch.

What it produces

Three outputs per run - an applicability determination, a backported set of security-relevant changes, and a validated unified-diff patch with a full metadata report.

01The client

Vulnerability research is only half the job.

The client is a security and DevSecOps platform company. Its product helps engineering teams discover vulnerabilities in their code and dependencies - and, increasingly, close them. Feeding that platform is a stream of high-quality work from upstream vulnerability researchers: a CVE, the underlying weakness class, the exact upstream commits that fixed it, and the versions affected.

That research is precise and trustworthy. The problem sits one step downstream. Knowing how a vulnerability was fixed in a reference project is not the same as having a fix that applies to a specific customer's codebase - a codebase that may be on a different version, structured differently, and drifted away from the reference the fix was written against.

Closing that last gap - from “here is the upstream fix” to “here is a patch that applies to your code and provably covers the CVE” - is what this engagement automated.

CVE & CWE analysis
Fix-commit study
Applicability research
Backporting
Patch generation
Security validation

Two inputs, one hard decision

The structural fact that shapes the whole pipeline.
Upstream research
  • The CVE and its weakness class
  • The exact fix commits
  • The fixed reference codebase
  • Affected version ranges
Target codebase
  • On its own version & branch
  • Structure drifted from the reference
  • May already be partially patched
  • Must not be broken by the fix
Between them sits one question the agent must answer for itself - does this fix apply here, and can it be safely backported?
02The challenge

The research was excellent.
The gap to a fix was expert-gated.

Backporting a security fix sounds mechanical until you try to automate it. The same vulnerability is patched differently across branches and versions; the fix commit bundles security-critical changes with unrelated noise; and a patch that applies cleanly to one version may not apply at all to another - or worse, apply and quietly break something.

One fix, many shapes

The same CVE is patched differently across releases and branches. There is no single canonical diff to apply - the fix has to be reconstructed for the target it is going to.

Noisy fix commits

A single fix commit often bundles the security change with refactors, renames, formatting, and tests. Porting the whole thing is wrong; the security-relevant part has to be isolated first.

Drifted codebases

The target's structure has moved since the reference fix was written. A context-based patch simply won't apply - the surrounding lines it expects are no longer there.

Already-patched blind spots

Sometimes the target already carries the fix. Without an explicit check, engineers waste effort re-patching code that was never vulnerable in the first place.

Expert-gated and slow

Every backport pulled a senior security engineer into a manual, repetitive study-and-reconstruct loop. It didn't scale, and it sat directly between finding a risk and remediating it.

Silent-breakage risk

A patch that applies is not a patch that works. A change can land cleanly and still miss the vulnerable path - success can't be declared just because the diff was accepted.

Why it mattered

The gap between a discovered vulnerability and an applied fix was the bottleneck in the whole remediation story. Research could flow in quickly, but every fix still queued behind a human expert. The value of knowing about a risk is capped by how fast you can close it - and closing it was the slow, unscalable step.

03The mandate

Three non-negotiables, fixed before a line of code.

Focaloid engaged to design and build an agent that could run this backporting work autonomously - trustworthy enough that its output could be reviewed and applied without an expert reconstructing it by hand. Three constraints were set as hard requirements, not aspirations.

Non-negotiable · 01

Applicability-first

Decide early whether a fix is relevant to the target - and whether the code is already patched - before spending any effort. Don't attempt every fix; know which ones to attempt.

Non-negotiable · 02

Security relevance

Port the fix, and only the fix. Every change is scored for genuine CVE relevance so refactors, renames, and tests are left behind and the security-critical hunks are the ones that land.

Non-negotiable · 03

Self-validation

Never declare success because a patch applied. The agent must prove the vulnerability pattern is actually covered - and flag anything it missed - before a run is called done.

The decision that shaped everything: build on the researcher's work, don't redo it. The upstream findings - the CVE, the weakness class, the fix commits - are trusted inputs. The agent's job is to reason forward from them to an applicable patch, not to re-discover the vulnerability. Everything downstream is designed around consuming research, not reproducing it.

A gated path from research input to validated patch
P1

Domain expertise

Build working knowledge of the CVE and its weakness class from the fix commits, and derive the security patterns that characterise the fix.

P2

Analysis & extraction

Diff target against the fixed reference, extract AST-aware hunks from the commits, and categorise each by security relevance.

P3

Filter & incremental apply

Score each hunk for CVE relevance, order changes by dependency, and apply them in phases with role-aware verification and retry.

P4

Validation & patch generation

Confirm the vulnerability pattern is covered, run code-quality and completeness checks, then emit a unified-diff patch with a metadata report.

Scope discipline as a safety feature. The agent deliberately does not port tests, does not force a patch when the fix doesn't apply, and does not carry across unrelated refactors. In an autonomous remediation tool, what the system refuses to do - and where it chooses to fail fast - is as much a design decision as what it applies.

04How it works

Research goes in. An applicable, validated patch comes out.

The agent takes the upstream findings as its starting point and works through the pipeline like an expert security engineer would - first deciding whether the fix even belongs here, then isolating the changes that matter, backporting them in the right order, and validating that the vulnerability is genuinely closed before emitting a patch.

The questions the agent answers on every run
Decided autonomously
Reported with the patch
Is this CVE already patched here?
Does the fix apply to this codebase?
Which changes actually fix the CVE?
In what order must they be applied?
Did each security hunk land in its role?
Is the vulnerability pattern fully covered?
Are there patterns the port missed?
Does the patched code still hold quality?

Watch research become an applicable, validated patch.

The console on the right replays a representative run: the agent checks whether the fix is already present, builds domain knowledge, extracts and scores the fix's hunks, backports only the security-relevant ones, then validates coverage before emitting the patch - narrating each step as it happens.

  • Applicability decided first. Every run begins by comparing target against the fixed reference - already-patched code exits early with success, no wasted work.
  • Only the fix, not the noise. Each hunk is scored for CVE relevance; refactors, renames, and tests are filtered out before anything is applied.
  • Applied in dependency order, verified by role. Hunks land in phases; each is checked to confirm it fulfils its security-critical or supporting role, with retry when one doesn't.
  • Validated, then packaged. Coverage and completeness checks run before a unified diff is generated, saved, and reported - the patch and its evidence, together.
Patcher agent · run console · target: service-api @ v2.3
↻ replay

Illustrative run with representative data - in the delivered agent every hunk, score, and validation is computed from the real fix commits and target codebase.

05Architecture

Not a script that runs patch. A reasoning pipeline with a clean tool boundary.

The agent runs as a containerised service on EKS, triggered through a REST API and reviewed in a web UI. Its reasoning is separated from its access to code and issues: the pipeline reasons, an MCP server exposes GitLab, Jira, and the analysis tools it can call, and PostgreSQL, S3, and Langfuse handle state, artifacts, and traceability.

Every step traced & auditable
Interface
Review UI
Engineers trigger runs and review the generated patch, hunk scores, and validation report
Interface
REST API
Programmatic entry point - accepts a CVE + target, returns run status and results
Vulnerability Patcher Agent
8-phase pipeline · Python
Pipeline orchestrator
runs phases in order, gates between them, halts on failure
AI-driven reasoning
↓ each phase owns a job
Domain expertise
CVE / CWE knowledge
Phase 1
Hunk extraction
AST-aware diffs
Phase 3
Dependency analysis
apply order
Phase 4
Security filtering
CVE relevance
Phase 5
Incremental apply
role-aware
Phase 6
Tool boundary
MCP Tool Server
Exposes source control and issue-tracking as structured, callable tools with a consistent response envelope
fetch commits
read repo
diff analysis
hunk extraction
pattern search
issue sync
Source of truth
GitLab - fixed reference & target repos
Fix commits, affected files, and the customer codebase the patch is generated against
State
PostgreSQL
Pipeline state, per-phase results, run metadata and history
Artifacts
Amazon S3
Diffs, extracted hunks, dependency graphs, generated patches & reports
Observability
Langfuse
LLM tracing - every reasoning path, tool call, and relevance score visible

Every decision is auditable. The agent's reasoning is traced end to end in Langfuse - domain → extract → filter → apply → validate - and its findings sync back to Jira, so a security team can see exactly why each hunk was kept, dropped, or flagged.

Issues sync to Jira · artifacts to S3
Synchronous phase
Traced reasoning
Existing client systems - GitLab, Jira

The as-delivered system - redrawn from the engagement's architecture documentation, anonymised.

The life of a patch, phase by phase

PHASE 01

Domain expertise

From the fix commits, the agent builds knowledge of the CVE and its weakness class, then derives the security patterns that characterise the fix.

PHASE 02

Project analysis

It diffs the target against the fixed reference to understand exactly how far the codebase has drifted and where the fix would land.

PHASE 03

AST & hunk extraction

Unified diffs from the commits are parsed into AST-aware hunks, in order, and categorised by whether they look security-relevant.

PHASE 04

Dependency analysis

A hunk-level dependency graph is built to find independent and critical hunks and compute a safe order of application.

PHASE 05

Security filtering

Each hunk is AI-scored for genuine CVE relevance; dependency-preserving filtering keeps the fix and drops the noise.

PHASE 06

Incremental application

Hunks are applied to a draft of the target in dependency phases, each verified for its security role, with retry when one fails.

PHASE 07

Final validation

Coverage, vulnerability-pattern, and code-quality checks confirm the CVE is genuinely closed - and surface anything missed.

PHASE 08

Patch generation

A unified diff is generated, statistics computed, applicability re-validated, and the patch saved with a full metadata report.

06Inside the build

The decisions that made autonomous patching trustworthy.

Anyone can run a diff and hope it applies. The engineering that makes a backport safe enough to run without an expert lives in a handful of deliberate choices - each one below is as-built.

Decision A - Applicability first

Decide whether to patch before deciding how.

The most expensive mistake in automated backporting is doing work that shouldn't be done at all - patching code that's already fixed, or forcing a fix onto a codebase it doesn't belong to. So the pipeline front-loads that judgment: an early fix-detection step compares the target against the fixed reference for each commit, and exits with success the moment it finds the fix already applied.

What follows is a chain of cheap questions that decide the run's fate before any hunk is touched - and route it to an early exit when the answer is clear.

“Is the fix already applied?”
“Do the affected files exist here?”
“Has the structure drifted?”
“Which hunks touch the CVE path?”
“Can the hunks apply in order?”
Applicabilitydetect · gate
early-fix-detection
PHASE 0
project-analysis
PHASE 2
ast-diff
PHASE 3
security-filter
PHASE 5
dependency-graph
PHASE 4
An early exit is a feature, not a failure. Detecting an already-patched codebase and stopping with success saves the entire pipeline's effort - and, just as importantly, avoids introducing a redundant change into code that was never at risk.
Decision B - Grounding

Source control and analysis, exposed as callable tools.

Rather than hard-wiring the pipeline to GitLab's and Jira's APIs, everything the agent can touch is exposed through an MCP tool server with a consistent response envelope. That separates what the agent can access from how the agent reasons - new tools ship without changing how the pipeline calls them, and the analysis steps become inspectable, testable units.

{ "status": "ok", "phase": "security_filtering", "hunks": { "critical": 5, "supporting": 3, "dropped": 6 }, "data": { …dependency graph & scores } }

Why MCP over direct API calls? A clean, standardised contract between reasoning and data. Every tool returns the same envelope - status, which phase ran, and the structured result - which makes the agent's tool selection reliable and lets analysis steps be built and tested against fixtures, without constant live access to every repository.

Decision C - Isolating the fix

Score every hunk, keep only what fixes the CVE.

A fix commit is messy. To port the fix and only the fix, every extracted hunk runs through a three-beat filter before anything is applied:

1

Extract everything

All hunks are parsed from the fix commits, AST-aware and in order - the raw material, noise included.

2

Score for relevance

Each hunk is AI-scored for genuine CVE relevance and categorised security-critical, supporting, or unrelated.

3

Keep & order

Only the relevant hunks survive - dependency-preserving, so a kept hunk never loses a change it relies on.

The fix lands; the noise doesn't. Refactors, renames, formatting, and tests are filtered out, so the patch that reaches the target is the security change and its genuine dependencies - nothing incidental that the commit happened to carry along.

Decision D - Guardrails

Fail fast, retry smart, never loop forever.

RETRY · 3

Phased retry

The primary application mechanism retries each hunk up to three times within its dependency phase - transient apply failures recover without derailing the run.

FAIL-FAST

Exit gates

Zero hunks extracted, zero hunks applied, or an empty generated patch each halt the run with a clear failure - the agent never emits a meaningless patch.

CONTEXT

Enhanced retry

Security hunks that fail get a second, context-aware pass seeded with the initial failure reason - the retry knows why the first attempt didn't land.

Decision E - Transparency

Every reasoning step is traced - by design.

An autonomous agent that changes source code has to be inspectable. Each phase publishes its reasoning, tool calls, and relevance scores to Langfuse, so a reviewer can reconstruct exactly why a given hunk was kept, dropped, applied, or flagged - and see the token cost of each run.

domain
CVE knowledge built
extract
hunks parsed from commits
filter
relevance scored
apply
phased & verified
validate
coverage confirmed

In a system that rewrites code, “here is why this hunk was kept and this one dropped” beats a black-box diff. The trace is both a debugging tool for the team and the evidence a security reviewer needs to trust the patch.

Decision F - Safe application

Backporting with a contract: draft, verify, validate.

The fix is never applied straight to the target. The agent works on a draft copy and follows the same six-beat contract every run - and where a hunk can't be made to fulfil its role, that's surfaced, not silently accepted.

01

Draft

A working draft is created from the target - the real codebase is never touched directly.

02

Map roles

A hunk role-context mapping records what each change is supposed to do.

03

Apply phased

Hunks are applied in dependency order, phase by phase.

04

Verify role

Each applied hunk is checked to confirm it plays its security-critical or supporting role.

05

Retry smart

Failed security hunks retry with the initial failure reason as context.

06

Validate & save

Application results are validated and persisted before the patch is generated.

07Validation & gates

Success means the vulnerability is covered - not that a patch applied.

The pipeline's final phase is a chain of checks that a diff-and-hope approach skips entirely. A patch only earns the word “done” once the agent has proven the CVE is closed, searched for anything the port missed, and confirmed the patched code still holds up.

Check 1

Security coverage

Confirms the applied changes cover the security-critical behaviour the fix was meant to introduce.

Check 2

Pattern coverage

Analyses the patched target for the vulnerability pattern - and searches for any instances the port missed.

Check 3

Code quality

Validates that the patched code is well-formed and the change didn't degrade the surrounding code.

Check 4

Completeness

A final security-completeness check before the patch is generated and saved with its report.

Analyze applied patterns
Search target for gaps
Generate hunks for misses

Validation can extend the fix. If the pattern-coverage analysis finds vulnerable instances the upstream fix didn't reach in this codebase, the agent generates additional fix hunks for them - so coverage is measured against the target, not just against the reference commit.

Safety gateTriggerOutcomeWhy it exists
Early fix detectionFix already present in target
Exit · success
Skips redundant work; avoids changing code that was never at risk
Zero hunks extractedNothing parseable from the fix commits
Exit · failure
There is no fix to port - fail fast rather than emit noise
Zero hunks appliedNo relevant hunk could be backported
Exit · failure
The fix does not apply to this codebase; a human is flagged
Empty patch generatedNo net change after application
Exit · failure
A patch that changes nothing is never handed off as success
Missed pattern foundVulnerable instances remain in target
Generate fix hunks
Coverage is measured against the target, not just the reference

Refusing to patch is part of the product.

When a fix genuinely does not apply - the affected code isn't present, or no relevant hunk can be backported - the agent doesn't force a broken diff. It returns a clear “not applicable” determination with the reasoning behind it, and hands off to a human. In autonomous remediation, a well-explained “no” is safer than a confident wrong patch.

Security coverage validation
Full Langfuse trace per run
Patch report with metadata
Applicability re-validated
Fail-fast on empty patch
Tests deliberately not ported
08Infrastructure

Engineered as a production service, not a script.

The agent is a containerised pipeline running on EKS, reachable through a REST API and a review UI, grounded on an MCP tool server, and backed by three purpose-chosen data systems. It fits into the client's existing security workflow rather than sitting beside it - GitLab for code, Jira for findings.

Before - manual backporting

Gated on a senior engineer

  • An expert reads the CVE, the fix commits, and the target by hand
  • Security-relevant changes separated from noise manually
  • The fix reconstructed line by line for a drifted codebase
  • Coverage judged by eye - easy to miss an instance
  • Slow, repetitive, and impossible to run across many codebases at once
  • The bottleneck between finding a risk and closing it

Same expert judgment, encoded into an autonomous, traceable pipeline.

After - autonomous agent

A run, not a queue

  • Applicability decided automatically, already-patched code exits early
  • Security-relevant hunks isolated by AI scoring
  • Backported in dependency order with role-aware verification
  • Coverage validated against the target, misses flagged or filled
  • Runs in parallel across many codebases on EKS
  • Every decision traced in Langfuse, every artifact in S3
PostgreSQL

Pipeline state, per-phase results, run metadata, and history across every backport attempt.

Why: relational and durable - a run's progress through eight gated phases is exactly the structured, queryable state a relational store handles best.

Amazon S3

Diff analyses, extracted hunks, dependency graphs, generated patches, and the metadata report for each run.

Why: cheap, durable object storage for the artifacts a reviewer or a re-run needs - every intermediate output is kept, not just the final patch.

Langfuse

Self-hosted LLM tracing over every reasoning path, tool call, and relevance score, with per-run token cost.

Why: an audit lens and a cost lens from day one - the evidence that makes an autonomous, code-changing agent reviewable and trustworthy.

09Outcomes

What shipped, and what it changes.

We report build facts, not projections. The figures below are as-delivered and verified by the engineering team; the before/after is a description of how the work changes, not a claimed metric. Buyers in security appreciate the difference - so do we.

✓ As-built · verified

The delivered agent, in countable facts

0
Autonomous pipeline phases, research to patch
0
Outputs per run - applicability, backport, validated patch
0
Integrated systems across the stack
0
Automated safety gates that can halt a run
0
Hunk roles - security-critical & supporting
Before - manual backportingAfter - the autonomous agent
A senior engineer reads and reconstructs every fix by handResearch flows straight into an automated, eight-phase run
Applicability judged case by case, already-patched code missedApplicability decided up front; already-patched targets exit early with success
Whole fix commits ported, noise and allEach hunk scored for CVE relevance; only the fix is backported
Coverage judged by eyeCoverage validated against the target, with missed patterns flagged or filled
One backport at a time, gated on a scarce expertRuns in parallel across many codebases, no human in the loop
Little record of why a change was madeEvery decision traced in Langfuse; every artifact and report retained
10What's next

Built to extend beyond a single fix.

The pipeline's phase structure and MCP tool boundary were chosen so the agent can grow - more languages, more sources, richer validation - without re-architecting the core.

Roadmap

Where it goes next

  • Broader language & ecosystem coverage for AST-aware extraction beyond the initial set.
  • Deeper missed-vulnerability generation - extending hunk synthesis for patterns the upstream fix never reached.
  • More integrations across source-control and issue-tracking systems beyond GitLab and Jira.
  • Measured outcomes - conversion rates and time-to-remediation from live usage, once enough runs accrue.
The reusable pattern

This generalises.

Applicability-first gating, MCP-grounded analysis tools, dependency-aware incremental application, and self-validation over the target - the pattern transfers to any research-to-remediation workflow where a known fix has to be reasoned onto a codebase that has moved on.

CVE backporting
Dependency upgrades
Automated remediation
Code migration
SAST fix generation

The stack, end to end

K8s
Amazon EKS
MCP
MCP Tool Server
API
REST API
UI
Review UI
PY
Python · AST parsing
PG
PostgreSQL
S3
Amazon S3
LF
Langfuse
GL
GitLab
JR
Jira
Let's build

Have an expert-gated workflow that should run autonomously?

We design and ship production-grade AI agents for high-stakes work - grounded, validated, and traceable at every step, so the output can be trusted without an expert rebuilding it by hand.