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.
Published with the engagement anonymised at the client's request - a confidentiality bar we treat as part of the deliverable.
A security & DevSecOps platform company whose product helps engineering teams find and remediate vulnerabilities across their codebases. Identity withheld at the client's request.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
Build working knowledge of the CVE and its weakness class from the fix commits, and derive the security patterns that characterise the fix.
Diff target against the fixed reference, extract AST-aware hunks from the commits, and categorise each by security relevance.
Score each hunk for CVE relevance, order changes by dependency, and apply them in phases with role-aware verification and retry.
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.
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 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.
Illustrative run with representative data - in the delivered agent every hunk, score, and validation is computed from the real fix commits and target codebase.
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 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.
The as-delivered system - redrawn from the engagement's architecture documentation, anonymised.
From the fix commits, the agent builds knowledge of the CVE and its weakness class, then derives the security patterns that characterise the fix.
It diffs the target against the fixed reference to understand exactly how far the codebase has drifted and where the fix would land.
Unified diffs from the commits are parsed into AST-aware hunks, in order, and categorised by whether they look security-relevant.
A hunk-level dependency graph is built to find independent and critical hunks and compute a safe order of application.
Each hunk is AI-scored for genuine CVE relevance; dependency-preserving filtering keeps the fix and drops the noise.
Hunks are applied to a draft of the target in dependency phases, each verified for its security role, with retry when one fails.
Coverage, vulnerability-pattern, and code-quality checks confirm the CVE is genuinely closed - and surface anything missed.
A unified diff is generated, statistics computed, applicability re-validated, and the patch saved with a full metadata report.
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.
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.
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.
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.
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:
All hunks are parsed from the fix commits, AST-aware and in order - the raw material, noise included.
Each hunk is AI-scored for genuine CVE relevance and categorised security-critical, supporting, or unrelated.
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.
The primary application mechanism retries each hunk up to three times within its dependency phase - transient apply failures recover without derailing the run.
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.
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.
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.
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.
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.
A working draft is created from the target - the real codebase is never touched directly.
A hunk role-context mapping records what each change is supposed to do.
Hunks are applied in dependency order, phase by phase.
Each applied hunk is checked to confirm it plays its security-critical or supporting role.
Failed security hunks retry with the initial failure reason as context.
Application results are validated and persisted before the patch is generated.
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.
Confirms the applied changes cover the security-critical behaviour the fix was meant to introduce.
Analyses the patched target for the vulnerability pattern - and searches for any instances the port missed.
Validates that the patched code is well-formed and the change didn't degrade the surrounding code.
A final security-completeness check before the patch is generated and saved with its report.
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 gate | Trigger | Outcome | Why it exists |
|---|---|---|---|
| Early fix detection | Fix already present in target | Exit · success | Skips redundant work; avoids changing code that was never at risk |
| Zero hunks extracted | Nothing parseable from the fix commits | Exit · failure | There is no fix to port - fail fast rather than emit noise |
| Zero hunks applied | No relevant hunk could be backported | Exit · failure | The fix does not apply to this codebase; a human is flagged |
| Empty patch generated | No net change after application | Exit · failure | A patch that changes nothing is never handed off as success |
| Missed pattern found | Vulnerable instances remain in target | Generate fix hunks | Coverage is measured against the target, not just the reference |
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.
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.
Same expert judgment, encoded into an autonomous, traceable pipeline.
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.
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.
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.
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.
| Before - manual backporting | After - the autonomous agent |
|---|---|
| A senior engineer reads and reconstructs every fix by hand | Research flows straight into an automated, eight-phase run |
| Applicability judged case by case, already-patched code missed | Applicability decided up front; already-patched targets exit early with success |
| Whole fix commits ported, noise and all | Each hunk scored for CVE relevance; only the fix is backported |
| Coverage judged by eye | Coverage validated against the target, with missed patterns flagged or filled |
| One backport at a time, gated on a scarce expert | Runs in parallel across many codebases, no human in the loop |
| Little record of why a change was made | Every decision traced in Langfuse; every artifact and report retained |
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.
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.