Safeguard
Application Security•

How to Read a GitHub Security Advisory, Using GHSA-2vc4-c4r6-p6v6

A field-by-field reading of a real GitHub advisory published on 15 October 2026 for @opennextjs/cloudflare, with the OSV command to pull the raw record.

Safeguard Security Team
5 min read

On 15 October 2026 GitHub published advisory GHSA-2vc4-c4r6-p6v6 for @opennextjs/cloudflare, rated High and aliased to CVE-2026-100409. It is a good advisory to learn on: short, specific, and honest about the conditions that make you vulnerable. This post reads it field by field.

The advisory says that when cache interception is enabled, the package can interpret percent encoded paths differently during middleware matching and cache lookup. An unauthenticated attacker may then retrieve cached content from routes meant to be protected by middleware.

What's affected

From the OSV record of the advisory:

  • Package: @opennextjs/cloudflare on npm
  • Introduced: 0.3.0
  • Fixed: 1.20.2
  • Severity: High, CVSS 3.1 vector AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
  • Weaknesses: CWE-177 (improper handling of URL encoding) and CWE-180 (validate before canonicalize)

The vector tells you the shape of the risk: network reachable, low complexity, no privileges, no user interaction, high confidentiality impact, no integrity or availability impact. It leaks data and changes nothing.

The preconditions matter more than the score. You are affected only if you explicitly enable enableCacheInterception (disabled by default), rely on middleware for access control, and have a matching cached route.

Reading the ID and the review status

A GHSA ID has the form GHSA-xxxx-xxxx-xxxx. Each x is a lowercase character from the set 23456789cfghjmpqrvwx. GitHub's own validation pattern is:

GHSA(-[23456789cfghjmpqrvwx]{4}){3}

Advisories come in two kinds. GitHub-reviewed advisories are checked by hand, mapped to packages in a supported ecosystem, and are the ones that trigger Dependabot alerts. Unreviewed advisories are imported automatically from the National Vulnerability Database feed, with no check for validity or completeness, and do not trigger alerts. This advisory carries github_reviewed: true in its record. Not every advisory has a CVE either, since malware advisories, for example, come from other sources.

How to check if you're exposed

Pull the raw record in the OSV format that GitHub publishes advisories in:

curl -s https://api.osv.dev/v1/vulns/GHSA-2vc4-c4r6-p6v6 \
  | jq '{id, aliases, published, severity, cwe: .database_specific.cwe_ids, affected: .affected[0].ranges}'

Then check your project:

npm ls @opennextjs/cloudflare
grep -rn "enableCacheInterception" . --include=*.ts --include=*.js --exclude-dir=node_modules

The advisory's own example shows the attack. With a middleware matcher of /admin/:path* and a cached /admin route, a request to GET /%61dmin does not match the matcher, so authorization never runs. The cache then decodes %61dmin to admin and may return the protected page.

Reading the references

The References section of an advisory is a map of the fix. In this one it links the advisory page, the NVD record, two upstream pull requests in the opennextjs-aws repository (numbers 1199 and 1200) and a commit in the Cloudflare adapter repository. Read the pull requests, not just the summary. They show exactly which behavior changed: the patch aligns encoded path handling across middleware matching and cache lookup, and rejects malformed paths instead of partially decoding them.

The advisory also gives a second example worth testing. A request to /%61dmin/%ZZ contains a malformed segment. The previous code decoded each segment on its own, turned %61dmin into admin, kept %ZZ as it was, and carried on with the cache lookup. A partially decoded path is a good fuzzing input for your own routing code.

What to do now

  1. Run npm ls @opennextjs/cloudflare and compare against 1.20.2.
  2. Search for enableCacheInterception: true. If it is absent, the vulnerable path is off.
  3. Upgrade to 1.20.2 or later.
  4. If you cannot upgrade, remove enableCacheInterception: true, or enforce authorization inside the route handlers rather than only in middleware, as the advisory suggests.
  5. Re-run your auth tests with encoded variants of protected paths.

How this could have been avoided

  • Authorize at the resource. Middleware only checks paths it matches. A check inside the handler runs whatever form of the URL arrived.
  • Leave optional features off until needed. The default of disabled meant most deployments were never exposed.
  • Normalize a path once. Two components decoding the same path in different ways is the root cause, and a single decode step before both removes it.
  • Test encoded paths. A test that requests /%61dmin and expects a 401 would likely have caught this in a pull request.

Why it matters

Reading an advisory means ignoring the headline and going to the conditions. The same High rating means very different things to a team that enabled the feature and a team that did not.

This is also a reachability question. Whether your configuration reaches the vulnerable code decides whether the advisory is an emergency or a ticket.

Try it yourself

Pick one advisory your scanner flagged this week and fill in this list from its OSV record: package, introduced, fixed, preconditions, workaround. If you cannot fill in preconditions, you have not finished reading it.

Click through it at your own pace, or open it full screen.

Quick check

Which part of this advisory tells you whether you are actually vulnerable?

The preconditions in the Impact section: the feature must be enabled, you must rely on middleware for access control, and a matching cached route must exist.

Why did the request to /%61dmin bypass the middleware?

Middleware matched the encoded pathname, which did not match the admin matcher, while the cache layer decoded it to admin and served the cached response.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.