diff --git a/.changeset/image-endpoint-cleanup.md b/.changeset/image-endpoint-cleanup.md new file mode 100644 index 000000000000..320cbdab3dcc --- /dev/null +++ b/.changeset/image-endpoint-cleanup.md @@ -0,0 +1,6 @@ +--- +'astro': patch +'@astrojs/internal-helpers': patch +--- + +Tightens `isRemotePath()` to reject control characters after a leading slash and fixes the dev image endpoint origin check diff --git a/packages/astro/src/assets/endpoint/dev.ts b/packages/astro/src/assets/endpoint/dev.ts index 21dd9d351c3f..32761d53eb85 100644 --- a/packages/astro/src/assets/endpoint/dev.ts +++ b/packages/astro/src/assets/endpoint/dev.ts @@ -55,7 +55,7 @@ async function loadLocalImage(src: string, url: URL) { const sourceUrl = new URL(src, url.origin); // This is only allowed if this is the same origin if (sourceUrl.origin !== url.origin) { - returnValue = undefined; + return undefined; } return loadRemoteImage(sourceUrl); } diff --git a/packages/internal-helpers/src/path.ts b/packages/internal-helpers/src/path.ts index 11b4c843bf21..58b0a67cf69e 100644 --- a/packages/internal-helpers/src/path.ts +++ b/packages/internal-helpers/src/path.ts @@ -148,9 +148,9 @@ export function isRemotePath(src: string) { return false; } - // Check for Unix absolute path (starts with / but not // or /\) + // Check for Unix absolute path (starts with / followed by a normal path character) // This needs to be before the backslash check - if (decoded[0] === '/' && decoded[1] !== '/' && decoded[1] !== '\\') { + if (decoded[0] === '/' && /^\/[\w.@-]/.test(decoded)) { return false; } diff --git a/packages/internal-helpers/test/path.test.ts b/packages/internal-helpers/test/path.test.ts index 2981450c08b6..3b1f89768261 100644 --- a/packages/internal-helpers/test/path.test.ts +++ b/packages/internal-helpers/test/path.test.ts @@ -78,6 +78,17 @@ describe('isRemotePath', () => { '\\%0Aexample.com/test', '\\%0Dexample.com/test', + // CRLF injection in path (control chars after leading slash cause URL parser + // to treat the remainder as a protocol-relative URL) + '/%0d%0a/example.com', + '/%0d%0a/example.com:8080/path', + '/%0a/example.com', + '/%0d/example.com', + '/\r\n/example.com', + '/\n/example.com', + '/\r/example.com', + '/\t/example.com', + // IP addresses 'http://192.168.1.1/test', '//192.168.1.1/test',