CVE-2026-102527 — nuxt-og-image
Fix: nuxt-modules/og-image@e6c5b53CVE-2026-102527 is a CWE-349 vulnerability in nuxt-og-image. A fix is available for nuxt-og-image — see the affected versions and patch details below.
Nuxt OG Image cache poisoning through derivable cache keys on unsigned dynamic image routes
Exploitation Status
No confirmed exploitation observed yet
- CISA assesses this as automatable — exploitation doesn’t require manual, per-target effort, which raises the odds of mass scanning and opportunistic attacks.
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for CVE-2026-102527.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
Real-World Exposure
How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.
nuxt-og-imagenpmDescription
Vendor: Nuxt
Product: Nuxt OG Image
Version: 6.2.5
Description: Due to the specifics of caching and key generation in the Open Graph image cache created by the module, an unauthenticated attacker can modify the content of the image (for example, the title parameter) that is delivered to the user.
Impact: Content Spoofing
Vulnerable component: route /_og/d/*.png
Exploitation conditions: Access to the application
Mitigation: Upgrade to 6.7.2 and set a sufficiently secure secret in config ogImage
Researcher: Dmitry Vanin (Positive Technologies)
Research
During the previous vulnerability research, it was discovered that version 6.2.5 changed the mechanism for generating cache indexes; it now takes into account the parameters passed during image generation.
However, the image is still generated from data transmitted to the attacker.
Listing 1. New code for generating indexes in cache
export function resolvePathCacheKey(e: H3Event, path: string, resolvedOptions?: Record<string, any>) {
const siteConfig = getSiteConfig(e, {
resolveRefs: true,
})
const basePath = withoutTrailingSlash(withoutLeadingSlash(normalizeKey(path)))
const hashParts: any[] = [
basePath,
import.meta.prerender ? '' : siteConfig.url,
]
// Hash resolved options (not raw query string) so unknown/extra query params
// cannot produce unique cache keys and bypass the cache.
if (resolvedOptions)
hashParts.push(hash(resolvedOptions))
return [
(!basePath || basePath === '/') ? 'index' : basePath,
hash(hashParts),
].join(':')
}
In this case, basePath is obtained from the _path parameter, resolvedOptions are the normalized settings of the component, for example:
Listing 2. An example of resolvedOptions
{"emojis":"noto","extension":"png","width":1200,"height":600,"cacheMaxAgeSeconds":259200,"component":"OgImageDefaultImageTakumi"}
From the analysis of resolvedOptions, it was concluded that, in general, all parameters are known either from the direct link to the OG Image or from the server response.
Vulnerability reproduction
-
Find the route to OG Image. Figure 1. Demonstration of finding the route to OG Image
<img width="974" height="361" alt="image" src="https://github.com/user-attachments/assets/d3ee6b54-d545-4a3b-8cbf-f82df99ed4d0" /> -
Retrieve parameters from the server’s response, especially
<img width="974" height="245" alt="image" src="https://github.com/user-attachments/assets/84a4cd8b-9281-47ef-b524-0047a0f6077a" />cacheMaxAgeSeconds(cache-controlheader,s-maxagekey), the other parameters are known from the meta tags as well as from thec_<component name>parameter. Figure 2. Obtaining parameters -
Generate a cache key using a script. Listing 3. Srcipt
import {hash} from "ohash";
var url = "https://contoso.com"
var obj = JSON.parse(`{"emojis":"noto","extension":"png","width":1200,"height":600,"cacheMaxAgeSeconds":259200,"component":"OgImageBlogPostTakumi"}`)
var basePath = '/'
var hashParts = [
basePath,
url
]
if (obj)
hashParts.push(hash(obj))
console.log([
(!basePath || basePath === '/') ? 'index' : basePath,
hash(hashParts),
].join(':'))
- Repeat the steps as in the vulnerability 1, but transmit the obtained
cacheKeyin addition. - Thus, the same result as in the vulnerability 1 was obtained. Figure 4. Server response <img width="974" height="729" alt="image" src="https://github.com/user-attachments/assets/5895c0e8-e497-410d-bef9-f6f2f9649261" />
Credits
Researcher: Dmitry Vanin (Positive Technologies)
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 📦npm | nuxt-og-image | ≥ 6.2.5&&< 6.7.0 | 6.7.0npm install nuxt-og-image@6.7.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for nuxt-og-image, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update nuxt-og-image to 6.7.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2026-102527 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
Frequently Asked Questions
Is CVE-2026-102527 in your dependencies?
Find it across npm, including transitive dependencies.