Skip to content

Commit 9ecf359

Browse files
Merge commit from fork
* fix: image proxy doesn't adhere to config adapted and based on the generic endpoint https://github.com/withastro/astro/blob/main/packages/astro/src/assets/endpoint/generic.ts the custom image proxy endpoint should adhere and follow user configuration for remote domains * chore: add tests * fix: disallow remote images completly * fix: filename * chore: remove comment * chore: add failing test to show that remote images are not downloaded * chore: also add remote image to content collection for prerendering * chore: add more test cases * chore: fix typo Co-authored-by: Matt Kane <m@mk.gg> * refactor: return redirect * fix: tests * fix: test follows redirect * chore: clean up unused exports * refactor: use isRemoteAllowed from core * chore: add redirect comment --------- Co-authored-by: Matt Kane <m@mk.gg>
1 parent 4823c42 commit 9ecf359

6 files changed

Lines changed: 90 additions & 10 deletions

File tree

.changeset/evil-rabbits-flow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@astrojs/cloudflare': patch
3+
---
4+
5+
Improves the image proxy endpoint when using the default compile option to adhere to user configuration regarding the allowed remote domains

packages/integrations/cloudflare/src/entrypoints/image-endpoint.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
// @ts-expect-error
2+
import { imageConfig } from 'astro:assets';
3+
import { isRemotePath } from '@astrojs/internal-helpers/path';
14
import type { APIRoute } from 'astro';
5+
import { isRemoteAllowed } from 'astro/assets/utils';
26

37
export const prerender = false;
48

@@ -11,5 +15,14 @@ export const GET: APIRoute = (ctx) => {
1115
});
1216
}
1317

18+
if (isRemotePath(href)) {
19+
if (isRemoteAllowed(href, imageConfig) === false) {
20+
return new Response('Forbidden', { status: 403 });
21+
} else {
22+
// Redirect here because it is safer than a proxy, remote image will be served by remote domain and not own domain
23+
return Response.redirect(href, 302);
24+
}
25+
}
26+
1427
return fetch(new URL(href, ctx.url.origin));
1528
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import * as assert from 'node:assert/strict';
2+
import { after, before, describe, it } from 'node:test';
3+
import { fileURLToPath } from 'node:url';
4+
import { astroCli, wranglerCli } from './_test-utils.js';
5+
6+
const root = new URL('./fixtures/compile-image-service/', import.meta.url);
7+
8+
describe('CompileImageService', () => {
9+
let wrangler;
10+
before(async () => {
11+
await astroCli(fileURLToPath(root), 'build');
12+
13+
wrangler = wranglerCli(fileURLToPath(root));
14+
await new Promise((resolve) => {
15+
wrangler.stdout.on('data', (data) => {
16+
// console.log('[stdout]', data.toString());
17+
if (data.toString().includes('http://127.0.0.1:8788')) resolve();
18+
});
19+
wrangler.stderr.on('data', (_data) => {
20+
// console.log('[stderr]', data.toString());
21+
});
22+
});
23+
});
24+
25+
after(() => {
26+
wrangler.kill();
27+
});
28+
29+
it('forbids http://', async () => {
30+
const res = await fetch('http://127.0.0.1:8788/_image?href=http://placehold.co/600x400');
31+
const html = await res.text();
32+
const status = res.status;
33+
assert.equal(html, 'Forbidden');
34+
assert.equal(status, 403);
35+
});
36+
37+
it('forbids https://', async () => {
38+
const res = await fetch('http://127.0.0.1:8788/_image?href=https://placehold.co/600x400');
39+
const html = await res.text();
40+
const status = res.status;
41+
assert.equal(html, 'Forbidden');
42+
assert.equal(status, 403);
43+
});
44+
45+
it('forbids //', async () => {
46+
const res = await fetch('http://127.0.0.1:8788/_image?href=//placehold.co/600x400');
47+
const html = await res.text();
48+
const status = res.status;
49+
assert.equal(html, 'Forbidden');
50+
assert.equal(status, 403);
51+
});
52+
53+
it('allows trusted with redirect', async () => {
54+
const res = await fetch('http://127.0.0.1:8788/_image?href=https://astro.build/_astro/HeroBackground.B0iWl89K_2hpsgp.webp', { redirect: "manual" })
55+
const header = res.headers.get("location")
56+
const status = res.status;
57+
assert.equal(header, "https://astro.build/_astro/HeroBackground.B0iWl89K_2hpsgp.webp")
58+
assert.equal(status, 302)
59+
})
60+
61+
it('allows local', async () => {
62+
const res = await fetch('http://127.0.0.1:8788/_image?href=/_astro/placeholder.gLBdjEDe.jpg');
63+
const blob = await res.blob();
64+
const status = res.status;
65+
assert.equal(blob.type, 'image/jpeg');
66+
assert.equal(status, 200);
67+
});
68+
});

packages/integrations/cloudflare/test/fixtures/compile-image-service/astro.config.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,7 @@ export default defineConfig({
66
imageService: 'compile',
77
}),
88
output: 'static',
9+
image: {
10+
domains: ["astro.build"]
11+
}
912
});

packages/integrations/cloudflare/test/fixtures/compile-image-service/src/content/blog/post/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
image: './placeholder.jpg'
33
---
44

5-
![placeholder](./placeholder.jpg)
5+
![placeholder](./placeholder.jpg)

packages/integrations/cloudflare/test/fixtures/compile-image-service/src/pages/blog/[...slug].astro

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
---
2-
import { Image } from "astro:assets";
32
import { getEntry, type CollectionEntry } from "astro:content";
43
54
export const prerender = false;
@@ -21,14 +20,6 @@ const { Content } = await post.render();
2120
<title>Document</title>
2221
</head>
2322
<body>
24-
<div class="aspect-video w-full overflow-hidden flex items-end rounded-lg">
25-
<Image
26-
class="aspect-[4/3] object-cover object-left w-full"
27-
src={post.data.image}
28-
alt=""
29-
/>
30-
</div>
31-
3223
<Content />
3324
</body>
3425
</html>

0 commit comments

Comments
 (0)