Skip to Content

In code we trust? Why the most trusted software receives the least scrutiny.

Recent CVEs across GitHub, Anthropic, Google, dbt, and MISP expose a simple truth: the most trusted code is often the least questioned. As organizations connect AI agents to production systems, those overlooked assumptions can become powerful new attack paths.
He Wei

This article is part of an ongoing series from our own specialists, sharing the cutting-edge research they use to strengthen our world-leading Managed Detection and Response (MDR) services and protect customers against evolving AI Era threats.

A team ships a security fix, closes the advisory, and moves on. Weeks later, analysts discover a closely related vulnerability — only a few lines away from the original patch. From the outside, these findings may appear to be chance, or good luck, or perhaps an exceptional talent for hunting vulnerabilities. In practice, they are more often the result of a highly disciplined approach to code review.

In my own experience reporting CVEs in well-known projects, the findings that stand up to scrutiny come from a handful of reviewing habits applied consistently over time. Those habits are simple: patience, persistence, and a willingness to read code that others have already decided is fine.

This article explores those review habits, walks through a public vulnerability disclosure in detail, and examines the risks of connecting AI agents to production infrastructure. It’s a guide for all code specialists — from authors to analysts — to sharpen their ability to identify vulnerabilities before attackers do and build secure, resilient software.

Four habits that pay off

Most of the vulnerabilities discussed in this article were discovered using the same tight set of code review techniques; none of which are novel or complex. They are simply practical habits that focus your attention on the areas of a codebase where assumptions often accumulate and scrutiny fades. Here are my top four.

Variant hunting

Treat every fix as a lead, not a closed case. The same error often appears more than once: a developer who made a mistake in one place may have made it elsewhere too, around the same time, and under the same assumptions. A known bug or vulnerability is often the fastest way to find the next one.

Patch-gap reading

Read the fix, not just the original bug report. Security patches are often narrower than the problem they aim to solve. A patch may close the reported path while leaving a neighboring path open. Understanding what a fix changes — and, just as importantly, what it does not change — is one of the most reliable ways to uncover related vulnerabilities.

Manual source review over scanners

Use scanners to find patterns; rely on source review to understand behavior. Automated tools are effective at identifying known classes of issues, but far less effective at spotting flaws in business logic, authorization, or trust boundaries. Those vulnerabilities often exist in the gap between what the code does and what its author intended it to do. Finding them often requires manually reading the code.

Consistency checking

Look for exceptions. In a mature codebase, similar problems are often solved in similar ways. Anything that breaks from those patterns deserves a closer look: the function that builds its own query instead of using the shared approach; the request handler that skips a validation step; the tool that behaves differently from its equivalents. These inconsistencies are often where assumptions break down, and vulnerabilities emerge.

There is nothing pioneering here. The thread through all of these habits is focused, patient, and intentional code review — aimed especially at code that has already been “fixed” and is therefore assumed to be safe.

Where to look

Your target selection matters just as much as your review technique. Most large codebases contain far more code than anyone could reasonably examine in detail, so knowing where to focus your attention is often the difference between finding something important and finding nothing at all.

Some areas consistently reward closer scrutiny. This is not because they are inherently flawed, but because they tend to accumulate assumptions, complexity, and trust over time.

Recently changed code

Look for code that has undergone recent changes, especially recently patched code. Fresh fixes can introduce fresh assumptions, and those assumptions may not have been thoroughly challenged. A patch can resolve a reported issue while leaving related paths unexplored.

Authorization and permission logic

Authorization logic — who can do what, to whom, and under which conditions — is notoriously difficult to model. Automated tools rarely understand it well, and developers may implement it differently each time. This makes authorization code a frequent source of subtle vulnerabilities.

Integration points

Integration points are the boundaries where one system must trust another system’s input — where data transitions from untrusted to trusted. Sometimes this transition occurs without an explicit decision or validation step to justify the trust, creating room for vulnerabilities to emerge unchecked.

None of the areas above are obscure. They are primarily where the gap between what code was intended to do and what it actually does is at its widest.

CVE-2026-54358: A finding end-to-end

One public example illustrates how these recommended reviewing habits work together. It uses a straightforward web application vulnerability, unrelated to newer areas such as AI security, because the method matters more than the target.

MISP is a widely used open-source threat intelligence platform. A recent fix for authorization vulnerability, CVE-2026-44380, had tightened the privilege boundary by keeping lower-privileged organization administrators away from site-administrator accounts.

From an offensive security perspective, that kind of fix is a lead rather than a closed case. It establishes a rule about who may act on a site-administrator account, raising a simple question: is that rule enforced everywhere it should be?

In most administrative paths, it was. However, one stood out. The code responsible for administrative email, including an optional password reset action, ran its own user query instead of enforcing the same boundary in the same way. It filtered recipients by organization but never excluded accounts with the site-administrator role. Patch-gap reading and consistency checking both pointed to the same location: a path the earlier fix had not reached, behaving differently from the code around it.

Before reporting, I reproduced the issue on a throwaway local instance of my own, using secure test accounts created specifically for validation. That step matters — a proof of concept exists to remove doubt and should never involve systems or data belonging to others.

In practice, the vulnerability enabled an organization administrator to trigger a password reset for a site-administrator account within the same organization, one above them in the privilege model. The public advisory describes the confirmed impact as interference with that higher-privileged account. With an additional condition, such as interception of the reset email on shared or misconfigured mail infrastructure, that interference could move towards account takeover.

The issue was reported to the project's coordinating team and was subsequently published as CVE-2026-54358 with a CVSS v4.0 score of 7.5 (High). No working exploit is provided here; the public advisory contains the information required by those who need it.

The finding required no memory corruption, no novel exploitation technique, and no “good luck”. It came from reading a known fix closely and asking whether it was fully complete. The vulnerability was not hidden; it existed in the one path the original fix had never reached.

Other projects using this approach

Most of my current research in this area has focused on the layer where AI agents connect to real systems: the Model Context Protocol (MCP) servers and Software Development Kit (SDKs) that vendors are shipping fast. The same habits carry over, and they have led to vendor-acknowledged CVEs in several major projects:

  • dbt Labs' dbt-mcp: Three reported issues, including argument injection in CLI tool wrappers (CVE-2026-44968, CVSS 6.3) and two cases where sensitive tool arguments could reach logs or telemetry without redaction (CVE-2026-44969 and CVE-2026-44970).
  • GitHub's MCP server: A multi-user state-confusion issue in lockdown mode (CVE-2026-48529), independently reported and credited alongside another researcher who discovered the same issue.
  • Anthropic's MCP Python SDK: An HTTP transport that handled session requests without checking the authenticated principal (CVE-2026-52869, CVSS 7.1), independently reported and credited alongside another reporter.
  • Google's MCP Toolbox for Databases: An authorization bypass in which per-tool scope checks were skipped on older protocol versions (CVE-2026-11719, CVSS 8.6).
  • Contentful's MCP server: Export and import tools that passed an LLM-controlled host parameter into the management client, allowing the server's access token to be redirected to an attacker-controlled endpoint (CVE-2026-53957, CVSS 7.7).

None of these findings came from a big sweep or a custom-built tool. They are the kinds of issues that emerge when the same review habits are applied carefully and systematically, one project at a time. Each was reported privately and fixed through the responsible disclosure process.

(The CVSS values are the published base scores from the corresponding advisories.)

Reporting is half the job

Finding a vulnerability is only part of the process. Turning findings into fixes requires a report that maintainers can act on quickly. Writing that report is a skill in its own right.

Effective reports minimize the time between reading and reproducing. A concise summary, the precise location in the code, the conditions required to trigger the issue, and a minimal proof of concept shared privately are often enough for a maintainer to confirm a finding within minutes. Severity assessments are useful, but they should be held lightly. Maintainers may provide deployment context or implementation details that change the real-world impact. The goal is not to achieve the highest possible rating, but the most accurate one. Over time, a reputation for clear, good-faith reporting is worth more than any individual CVSS score.

Patience matters just as much. Responsible disclosure means giving vendors a reasonable opportunity to investigate and fix an issue before details go public, following up professionally when discussions stall, and publishing only once a fix is available or an agreed disclosure deadline has passed.

It is rarely the fastest approach. It is, however, the one that best serves maintainers, users, and the security community.

Why this matters beyond the bug tracker

It may be tempting to view a CVE list as a collection of clever exploits or novel techniques. In reality, many vulnerabilities arise from far more ordinary mistakes: a missing role check, an untrusted input passed without validation, or a fix that stops one line short. These are the same classes of vulnerability that application security teams have been addressing for decades.

What has changed is the blast radius. Once an organization grants AI agents access to its databases, repositories, and content systems, the integration layer inherits all the existing bugs and vulnerabilities while gaining a new trigger: untrusted content that can drive an agent's behavior. A missing authorization check is no longer reachable solely by an authenticated user navigating an application. It may also be reachable via content an agent has been asked to read, process, or act upon.

The attack surface expands accordingly. A threat actor may not need an account, a browser session, or direct access to an application. It may be enough to place carefully crafted content where an agent will encounter it. That broadens the range of threats organizations must consider and increases the cost of every authorization gap left unaddressed.

Consider an agent asked to summarize a shared document or triage an inbox. If a line in that content can be interpreted as an instruction, and the agent can act on what it reads, then untrusted text can influence the agent to use whatever permissions it carries.

Encouragingly, many of the same recommended coder review techniques still apply within these contexts. The underlying vulnerabilities are rarely esoteric. They remain issues of trust, boundaries, validation, and authorization — the type of issue that careful source review has always been good at finding.

What to check in your own code

All of the code review habits in this article are useful for finding vulnerabilities, but they are equally useful for preventing them. For maintainers, they provide a set of questions worth asking regularly. Here is what to consider.

When a security fix lands, treat it as the beginning of a review rather than the end of one. Has anyone checked whether the same mistake exists elsewhere — in a sibling function, a parallel code path, or another component the fix never touched? Recently patched code is often some of the most productive ground for review precisely because people naturally assume it is safe.

When a shared security control is introduced, verify that it is enforced everywhere it should be. It is not enough for the rule to exist; it must be applied consistently. A boundary applied to six functions, but is missing from the seventh, is still a vulnerability even though the rule technically exists.

Treat AI-generated tool calls with the same skepticism you would apply to any other untrusted input. If a model can be influenced by the content it reads, anything it produces may be influenced too. Parameters such as hostnames, file paths, and query fragments deserve the same scrutiny as data submitted through a web form.

Finally, pay close attention to the seams between systems. Trust often accumulates gradually as data moves from one component to another. The point at which untrusted data becomes trusted is not always explicit, and it is often where security assumptions begin to fail.

Carrying these insights into client-facing work

All the principles here apply beyond vulnerability research. At Sophos, adversary emulation and threat-led penetration testing are not simply exercises in running scanners and reviewing output. They involve examining systems the way an attacker would: following trust boundaries, challenging assumptions, and looking closely at the paths others have stopped questioning.

That matters even more as organizations connect AI agents to production systems. Every new integration introduces new trust relationships, new data flows, and new assumptions about the path of influence. The key question to ask yourself now is not whether those seams exist, but whether anyone has examined them closely. If that question is difficult to answer, you’ve found a place to start looking.

The organizations and teams that stay ahead of these risks will be the ones who review boundaries continuously, applying the same scrutiny to AI-era integrations that they would to any other high-value attack surface.

To learn more about Sophos MDR services, click here.

This article discusses only findings that have already been publicly disclosed and documented.