CVE-2026-33769
Astro: Remote allowlist bypass via unanchored matchPathname wildcard
Astro is a web framework. From version 2.10.10 to before version 5.18.1, this issue concerns Astro's remotePatterns path enforcement for remote URLs used by server-side fetchers such as the image optimization endpoint. The path matching logic for /* wildcards is unanchored, so a pathname that contains the allowed prefix later in the path can still match. As a result, an attacker can fetch paths outside the intended allowlisted prefix on an otherwise allowed host. This issue has been patched in version 5.18.1.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- Low
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
2.9
Astro: Remote allowlist bypass via unanchored matchPathname wildcard
Astro is a web framework. From version 2.10.10 to before version 5.18.1, this issue concerns Astro's remotePatterns path enforcement for remote URLs used by server-side fetchers such as the image optimization endpoint. The path matching logic for /* wildcards is unanchored, so a pathname that contains the allowed prefix later in the path can still match. As a result, an attacker can fetch paths outside the intended allowlisted prefix on an otherwise allowed host. This issue has been patched in version 5.18.1.
- Record
CVE-2026-33769- CVSS 4.0
- 2.9 Low
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P - Weakness
- CWE-20
- Updated
- 2026-11-02
EUVDEUVD-2026-14984
2.9
Astro is a web framework. From version 2.10.10 to before version 5.18.1, this issue concerns Astro's remotePatterns path enforcement for remote URLs used by server-side fetchers such as the image optimization endpoint. The path matching logic for /* wildcards is unanchored, so a pathname that contains the allowed prefix later in the path can still match. As a result, an attacker can fetch paths outside the intended allowlisted prefix on an otherwise allowed host. This issue has been patched in version 5.18.1.
- Record
EUVD-2026-14984- CVSS 4.0
- 2.9 Low
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-g735-7g2w-hh3f
2.9
Astro: Remote allowlist bypass via unanchored matchPathname wildcard
Summary
This issue concerns Astro's remotePatterns path enforcement for remote URLs used by server-side fetchers such as the image optimization endpoint. The path matching logic for /* wildcards is unanchored, so a pathname that contains the allowed prefix later in the path can still match. As a result, an attacker can fetch paths outside the intended allowlisted prefix on an otherwise allowed host. In our PoC, both the allowed path and a bypass path returned 200 with the same SVG payload, confirming the bypass.
Impact
Attackers can fetch unintended remote resources on an allowlisted host via the image endpoint, expanding SSRF/data exposure beyond the configured path prefix.
Description
Taint flow: request -> transform.src -> isRemoteAllowed() -> matchPattern() -> matchPathname()
User-controlled href is parsed into transform.src and validated via isRemoteAllowed():
const url = new URL(request.url);
const transform = await imageService.parseURL(url, imageConfig);
const isRemoteImage = isRemotePath(transform.src);
if (isRemoteImage && isRemoteAllowed(transform.src, imageConfig) === false) {
return new Response('Forbidden', { status: 403 });
}
isRemoteAllowed() checks each remotePattern via matchPattern():
export function matchPattern(url: URL, remotePattern: RemotePattern): boolean {
return (
matchProtocol(url, remotePattern.protocol) &&
matchHostname(url, remotePattern.hostname, true) &&
matchPort(url, remotePattern.port) &&
matchPathname(url, remotePattern.pathname, true)
);
}
The vulnerable logic in matchPathname() uses replace() without anchoring the prefix for /* patterns:
} else if (pathname.endsWith('/*')) {
const slicedPathname = pathname.slice(0, -1); // * length
const additionalPathChunks = url.pathname
.replace(slicedPathname, '')
.split('/')
.filter(Boolean);
return additionalPathChunks.length === 1;
}
Vulnerable code flow:
isRemoteAllowed()evaluatesremotePatternsfor a requested URL.matchPathname()handlespathname: "/img/*"using.replace()on the URL path.- A path such as
/evil/img/secretincorrectly matches because/img/is removed even when it's not at the start. - The image endpoint fetches and returns the remote resource.
PoC
The PoC starts a local attacker server and configures remotePatterns to allow only /img/*. It then requests the image endpoint with two URLs: an allowed path and a bypass path with /img/ in the middle. Both requests returned the SVG payload, showing the path restriction was bypassed.
Vulnerable config
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({ mode: 'standalone' }),
image: {
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.example', pathname: '/img/*' },
{ protocol: 'http', hostname: '127.0.0.1', port: '9999', pathname: '/img/*' },
],
},
});
Affected pages
This PoC targets the /_image endpoint directly; no additional pages are required.
PoC Code
import http.client
import json
import urllib.parse
HOST = "127.0.0.1"
PORT = 4321
def fetch(path: str) -> dict:
conn = http.client.HTTPConnection(HOST, PORT, timeout=10)
conn.request("GET", path, headers={"Host": f"{HOST}:{PORT}"})
resp = conn.getresponse()
body = resp.read(2000).decode("utf-8", errors="replace")
conn.close()
return {
"path": path,
"status":…
- Record
GHSA-g735-7g2w-hh3f- CVSS 4.0
- 2.9 Low
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P - CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-20, CWE-183
- Updated
- 2026-11-02
NVD
2.9
Astro is a web framework. From version 2.10.10 to before version 5.18.1, this issue concerns Astro's remotePatterns path enforcement for remote URLs used by server-side fetchers such as the image optimization endpoint. The path matching logic for /* wildcards is unanchored, so a pathname that contains the allowed prefix later in the path can still match. As a result, an attacker can fetch paths outside the intended allowlisted prefix on an otherwise allowed host. This issue has been patched in version 5.18.1.
- Record
CVE-2026-33769- CVSS 4.0
- 2.9 Low
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-20
- Updated
- 2026-11-02
OSV
2.9
Astro: Remote allowlist bypass via unanchored matchPathname wildcard
Astro is a web framework. From version 2.10.10 to before version 5.18.1, this issue concerns Astro's remotePatterns path enforcement for remote URLs used by server-side fetchers such as the image optimization endpoint. The path matching logic for /* wildcards is unanchored, so a pathname that contains the allowed prefix later in the path can still match. As a result, an attacker can fetch paths outside the intended allowlisted prefix on an otherwise allowed host. This issue has been patched in version 5.18.1.
- Record
CVE-2026-33769- CVSS 4.0
- 2.9 Low
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P - Weakness
- CWE-20
- Updated
- 2026-11-02
OSVGHSA-g735-7g2w-hh3f
2.9
Astro: Remote allowlist bypass via unanchored matchPathname wildcard
Summary
This issue concerns Astro's remotePatterns path enforcement for remote URLs used by server-side fetchers such as the image optimization endpoint. The path matching logic for /* wildcards is unanchored, so a pathname that contains the allowed prefix later in the path can still match. As a result, an attacker can fetch paths outside the intended allowlisted prefix on an otherwise allowed host. In our PoC, both the allowed path and a bypass path returned 200 with the same SVG payload, confirming the bypass.
Impact
Attackers can fetch unintended remote resources on an allowlisted host via the image endpoint, expanding SSRF/data exposure beyond the configured path prefix.
Description
Taint flow: request -> transform.src -> isRemoteAllowed() -> matchPattern() -> matchPathname()
User-controlled href is parsed into transform.src and validated via isRemoteAllowed():
const url = new URL(request.url);
const transform = await imageService.parseURL(url, imageConfig);
const isRemoteImage = isRemotePath(transform.src);
if (isRemoteImage && isRemoteAllowed(transform.src, imageConfig) === false) {
return new Response('Forbidden', { status: 403 });
}
isRemoteAllowed() checks each remotePattern via matchPattern():
export function matchPattern(url: URL, remotePattern: RemotePattern): boolean {
return (
matchProtocol(url, remotePattern.protocol) &&
matchHostname(url, remotePattern.hostname, true) &&
matchPort(url, remotePattern.port) &&
matchPathname(url, remotePattern.pathname, true)
);
}
The vulnerable logic in matchPathname() uses replace() without anchoring the prefix for /* patterns:
} else if (pathname.endsWith('/*')) {
const slicedPathname = pathname.slice(0, -1); // * length
const additionalPathChunks = url.pathname
.replace(slicedPathname, '')
.split('/')
.filter(Boolean);
return additionalPathChunks.length === 1;
}
Vulnerable code flow:
isRemoteAllowed()evaluatesremotePatternsfor a requested URL.matchPathname()handlespathname: "/img/*"using.replace()on the URL path.- A path such as
/evil/img/secretincorrectly matches because/img/is removed even when it's not at the start. - The image endpoint fetches and returns the remote resource.
PoC
The PoC starts a local attacker server and configures remotePatterns to allow only /img/*. It then requests the image endpoint with two URLs: an allowed path and a bypass path with /img/ in the middle. Both requests returned the SVG payload, showing the path restriction was bypassed.
Vulnerable config
import { defineConfig } from 'astro/config';
import node from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: node({ mode: 'standalone' }),
image: {
remotePatterns: [
{ protocol: 'https', hostname: 'cdn.example', pathname: '/img/*' },
{ protocol: 'http', hostname: '127.0.0.1', port: '9999', pathname: '/img/*' },
],
},
});
Affected pages
This PoC targets the /_image endpoint directly; no additional pages are required.
PoC Code
import http.client
import json
import urllib.parse
HOST = "127.0.0.1"
PORT = 4321
def fetch(path: str) -> dict:
conn = http.client.HTTPConnection(HOST, PORT, timeout=10)
conn.request("GET", path, headers={"Host": f"{HOST}:{PORT}"})
resp = conn.getresponse()
body = resp.read(2000).decode("utf-8", errors="replace")
conn.close()
return {
"path": path,
"status":…
- Record
GHSA-g735-7g2w-hh3f- CVSS 4.0
- 2.9 Low
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N/E:P - CVSS 3.1
- 5.3 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N - Weakness
- CWE-183, CWE-20
- Updated
- 2026-11-02
Show 15 moreShow less
OSVCLSA-2026-1777941830 No score
TuxCare security update for astro (10 CVEs)
TuxCare rebuilt astro to address 10 CVEs. Fixed in 2.10.15-tuxcare.1.
- Record
CLSA-2026-1777941830- Updated
- 2026-11-02
OSVCLSA-2026-1778114666 No score
TuxCare security update for 5 packages (9 CVEs)
TuxCare rebuilt 5 packages to address 9 CVEs. Fixed in 4.16.19-tuxcare.1.
- Record
CLSA-2026-1778114666- Updated
- 2026-11-02
OSVCLSA-2026-1778633121 No score
TuxCare security update for astro (11 CVEs)
TuxCare rebuilt astro to address 11 CVEs. Fixed in 3.6.5-tuxcare.1.
- Record
CLSA-2026-1778633121- Updated
- 2026-11-02
OSVCLSA-2026-1792161544 No score
TuxCare security update for @astrojs/internal-helpers (11 CVEs)
TuxCare rebuilt @astrojs/internal-helpers to address 11 CVEs. Fixed in 2.10.15-tuxcare.1.
- Record
CLSA-2026-1792161544- Updated
- 2026-11-02
OSVCLSA-2026-1792161545 No score
TuxCare security update for @astrojs/internal-helpers (10 CVEs)
TuxCare rebuilt @astrojs/internal-helpers to address 10 CVEs. Fixed in 4.16.19-tuxcare.1.
- Record
CLSA-2026-1792161545- Updated
- 2026-11-02
OSVCLSA-2026-1792161546 No score
TuxCare security update for @astrojs/internal-helpers (13 CVEs)
TuxCare rebuilt @astrojs/internal-helpers to address 13 CVEs. Fixed in 3.6.5-tuxcare.1.
- Record
CLSA-2026-1792161546- Updated
- 2026-11-02
OSVCLSA-2026-1792161683 No score
TuxCare security update for @astrojs/prism (11 CVEs)
TuxCare rebuilt @astrojs/prism to address 11 CVEs. Fixed in 2.10.15-tuxcare.1.
- Record
CLSA-2026-1792161683- Updated
- 2026-11-02
OSVCLSA-2026-1792161684 No score
TuxCare security update for @astrojs/prism (10 CVEs)
TuxCare rebuilt @astrojs/prism to address 10 CVEs. Fixed in 4.16.19-tuxcare.1.
- Record
CLSA-2026-1792161684- Updated
- 2026-11-02
OSVCLSA-2026-1792161686 No score
TuxCare security update for @astrojs/prism (13 CVEs)
TuxCare rebuilt @astrojs/prism to address 13 CVEs. Fixed in 3.6.5-tuxcare.1.
- Record
CLSA-2026-1792161686- Updated
- 2026-11-02
OSVCLSA-2026-1792161759 No score
TuxCare security update for @astrojs/studio (10 CVEs)
TuxCare rebuilt @astrojs/studio to address 10 CVEs. Fixed in 4.16.19-tuxcare.1.
- Record
CLSA-2026-1792161759- Updated
- 2026-11-02
OSVCLSA-2026-1792161775 No score
TuxCare security update for @astrojs/underscore-redirects (11 CVEs)
TuxCare rebuilt @astrojs/underscore-redirects to address 11 CVEs. Fixed in 2.10.15-tuxcare.1.
- Record
CLSA-2026-1792161775- Updated
- 2026-11-02
OSVCLSA-2026-1792161776 No score
TuxCare security update for @astrojs/underscore-redirects (10 CVEs)
TuxCare rebuilt @astrojs/underscore-redirects to address 10 CVEs. Fixed in 4.16.19-tuxcare.1.
- Record
CLSA-2026-1792161776- Updated
- 2026-11-02
OSVCLSA-2026-1792161777 No score
TuxCare security update for @astrojs/underscore-redirects (13 CVEs)
TuxCare rebuilt @astrojs/underscore-redirects to address 13 CVEs. Fixed in 3.6.5-tuxcare.1.
- Record
CLSA-2026-1792161777- Updated
- 2026-11-02
OSVCLSA-2026-1792161847 No score
TuxCare security update for @astrojs/upgrade (10 CVEs)
TuxCare rebuilt @astrojs/upgrade to address 10 CVEs. Fixed in 4.16.19-tuxcare.1.
- Record
CLSA-2026-1792161847- Updated
- 2026-11-02
OSVCLSA-2026-1792161848 No score
TuxCare security update for @astrojs/upgrade (13 CVEs)
TuxCare rebuilt @astrojs/upgrade to address 13 CVEs. Fixed in 3.6.5-tuxcare.1.
- Record
CLSA-2026-1792161848- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
astronpm |
2.10.10 up to 5.18.1 | 5.18.1 |
astroTuxCare:npm From an advisory covering 10 CVEs |
2.10.15-tuxcare.0 up to 2.10.15-tuxcare.1, plus listed versions 2.10.15 |
2.10.15-tuxcare.1 |
@astrojs/dbTuxCare:npm From an advisory covering 8 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
@astrojs/rssTuxCare:npm From an advisory covering 8 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
@astrojs/telemetryTuxCare:npm From an advisory covering 8 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
astroTuxCare:npm From an advisory covering 8 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
create-astroTuxCare:npm From an advisory covering 8 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
astroTuxCare:npm From an advisory covering 11 CVEs |
3.6.5-tuxcare.0 up to 3.6.5-tuxcare.1, plus listed versions 3.6.5 |
3.6.5-tuxcare.1 |
@astrojs/internal-helpersTuxCare:npm From an advisory covering 11 CVEs |
2.10.15-tuxcare.0 up to 2.10.15-tuxcare.1, plus listed versions 2.10.15 |
2.10.15-tuxcare.1 |
@astrojs/internal-helpersTuxCare:npm From an advisory covering 9 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
Show 10 moreShow less
| Package | Affected | Fixed |
|---|---|---|
@astrojs/internal-helpersTuxCare:npm From an advisory covering 13 CVEs |
3.6.5-tuxcare.0 up to 3.6.5-tuxcare.1, plus listed versions 3.6.5 |
3.6.5-tuxcare.1 |
@astrojs/prismTuxCare:npm From an advisory covering 11 CVEs |
2.10.15-tuxcare.0 up to 2.10.15-tuxcare.1, plus listed versions 2.10.15 |
2.10.15-tuxcare.1 |
@astrojs/prismTuxCare:npm From an advisory covering 9 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
@astrojs/prismTuxCare:npm From an advisory covering 13 CVEs |
3.6.5-tuxcare.0 up to 3.6.5-tuxcare.1, plus listed versions 3.6.5 |
3.6.5-tuxcare.1 |
@astrojs/studioTuxCare:npm From an advisory covering 9 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
@astrojs/underscore-redirectsTuxCare:npm From an advisory covering 11 CVEs |
2.10.15-tuxcare.0 up to 2.10.15-tuxcare.1, plus listed versions 2.10.15 |
2.10.15-tuxcare.1 |
@astrojs/underscore-redirectsTuxCare:npm From an advisory covering 9 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
@astrojs/underscore-redirectsTuxCare:npm From an advisory covering 13 CVEs |
3.6.5-tuxcare.0 up to 3.6.5-tuxcare.1, plus listed versions 3.6.5 |
3.6.5-tuxcare.1 |
@astrojs/upgradeTuxCare:npm From an advisory covering 9 CVEs |
4.16.19-tuxcare.0 up to 4.16.19-tuxcare.1, plus listed versions 4.16.19 |
4.16.19-tuxcare.1 |
@astrojs/upgradeTuxCare:npm From an advisory covering 13 CVEs |
3.6.5-tuxcare.0 up to 3.6.5-tuxcare.1, plus listed versions 3.6.5 |
3.6.5-tuxcare.1 |
History
| Recorded | Changed | Sources |
|---|---|---|
| affected, facts | ||
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-33769 is a low vulnerability (CVSS 2.9) in Astro, published March 24, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.34%, the 27th percentile. Fixed in astro 5.18.1.
References
Left out here: the rest of 2 source descriptions. The JSON record has them.