astro 5.17.2 → 5.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/runtime/server.js +63 -4
- package/dist/assets/build/remote.js +8 -2
- package/dist/assets/endpoint/generic.js +5 -1
- package/dist/assets/endpoint/shared.js +4 -1
- package/dist/assets/internal.js +15 -6
- package/dist/assets/utils/remoteProbe.d.ts +5 -1
- package/dist/assets/utils/remoteProbe.js +34 -2
- package/dist/assets/vite-plugin-assets.js +8 -5
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/container/index.js +1 -0
- package/dist/content/content-layer.js +3 -3
- package/dist/core/app/types.d.ts +1 -0
- package/dist/core/app/validate-headers.js +3 -1
- package/dist/core/build/generate.js +1 -0
- package/dist/core/build/plugins/plugin-manifest.js +1 -0
- package/dist/core/config/schemas/base.d.ts +6 -0
- package/dist/core/config/schemas/base.js +4 -2
- package/dist/core/config/schemas/relative.d.ts +7 -0
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/errors/errors-data.d.ts +13 -0
- package/dist/core/errors/errors-data.js +7 -0
- package/dist/core/messages.js +2 -2
- package/dist/types/public/config.d.ts +24 -0
- package/dist/vite-plugin-astro-server/plugin.js +2 -0
- package/package.json +4 -4
|
@@ -159,10 +159,14 @@ function getActionContext(context) {
|
|
|
159
159
|
}
|
|
160
160
|
throw error;
|
|
161
161
|
}
|
|
162
|
+
const bodySizeLimit = pipeline.manifest.actionBodySizeLimit;
|
|
162
163
|
let input;
|
|
163
164
|
try {
|
|
164
|
-
input = await parseRequestBody(context.request);
|
|
165
|
+
input = await parseRequestBody(context.request, bodySizeLimit);
|
|
165
166
|
} catch (e) {
|
|
167
|
+
if (e instanceof ActionError) {
|
|
168
|
+
return { data: void 0, error: e };
|
|
169
|
+
}
|
|
166
170
|
if (e instanceof TypeError) {
|
|
167
171
|
return { data: void 0, error: new ActionError({ code: "UNSUPPORTED_MEDIA_TYPE" }) };
|
|
168
172
|
}
|
|
@@ -206,18 +210,73 @@ function getCallerInfo(ctx) {
|
|
|
206
210
|
}
|
|
207
211
|
return void 0;
|
|
208
212
|
}
|
|
209
|
-
async function parseRequestBody(request) {
|
|
213
|
+
async function parseRequestBody(request, bodySizeLimit) {
|
|
210
214
|
const contentType = request.headers.get("content-type");
|
|
211
|
-
const
|
|
215
|
+
const contentLengthHeader = request.headers.get("content-length");
|
|
216
|
+
const contentLength = contentLengthHeader ? Number.parseInt(contentLengthHeader, 10) : void 0;
|
|
217
|
+
const hasContentLength = typeof contentLength === "number" && Number.isFinite(contentLength);
|
|
212
218
|
if (!contentType) return void 0;
|
|
219
|
+
if (hasContentLength && contentLength > bodySizeLimit) {
|
|
220
|
+
throw new ActionError({
|
|
221
|
+
code: "CONTENT_TOO_LARGE",
|
|
222
|
+
message: `Request body exceeds ${bodySizeLimit} bytes`
|
|
223
|
+
});
|
|
224
|
+
}
|
|
213
225
|
if (hasContentType(contentType, formContentTypes)) {
|
|
226
|
+
if (!hasContentLength) {
|
|
227
|
+
const body = await readRequestBodyWithLimit(request.clone(), bodySizeLimit);
|
|
228
|
+
const formRequest = new Request(request.url, {
|
|
229
|
+
method: request.method,
|
|
230
|
+
headers: request.headers,
|
|
231
|
+
body: toArrayBuffer(body)
|
|
232
|
+
});
|
|
233
|
+
return await formRequest.formData();
|
|
234
|
+
}
|
|
214
235
|
return await request.clone().formData();
|
|
215
236
|
}
|
|
216
237
|
if (hasContentType(contentType, ["application/json"])) {
|
|
217
|
-
|
|
238
|
+
if (contentLength === 0) return void 0;
|
|
239
|
+
if (!hasContentLength) {
|
|
240
|
+
const body = await readRequestBodyWithLimit(request.clone(), bodySizeLimit);
|
|
241
|
+
if (body.byteLength === 0) return void 0;
|
|
242
|
+
return JSON.parse(new TextDecoder().decode(body));
|
|
243
|
+
}
|
|
244
|
+
return await request.clone().json();
|
|
218
245
|
}
|
|
219
246
|
throw new TypeError("Unsupported content type");
|
|
220
247
|
}
|
|
248
|
+
async function readRequestBodyWithLimit(request, limit) {
|
|
249
|
+
if (!request.body) return new Uint8Array();
|
|
250
|
+
const reader = request.body.getReader();
|
|
251
|
+
const chunks = [];
|
|
252
|
+
let received = 0;
|
|
253
|
+
while (true) {
|
|
254
|
+
const { done, value } = await reader.read();
|
|
255
|
+
if (done) break;
|
|
256
|
+
if (value) {
|
|
257
|
+
received += value.byteLength;
|
|
258
|
+
if (received > limit) {
|
|
259
|
+
throw new ActionError({
|
|
260
|
+
code: "CONTENT_TOO_LARGE",
|
|
261
|
+
message: `Request body exceeds ${limit} bytes`
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
chunks.push(value);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const buffer = new Uint8Array(received);
|
|
268
|
+
let offset = 0;
|
|
269
|
+
for (const chunk of chunks) {
|
|
270
|
+
buffer.set(chunk, offset);
|
|
271
|
+
offset += chunk.byteLength;
|
|
272
|
+
}
|
|
273
|
+
return buffer;
|
|
274
|
+
}
|
|
275
|
+
function toArrayBuffer(buffer) {
|
|
276
|
+
const copy = new Uint8Array(buffer.byteLength);
|
|
277
|
+
copy.set(buffer);
|
|
278
|
+
return copy.buffer;
|
|
279
|
+
}
|
|
221
280
|
export {
|
|
222
281
|
defineAction,
|
|
223
282
|
formDataToObject,
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import CachePolicy from "http-cache-semantics";
|
|
2
2
|
async function loadRemoteImage(src) {
|
|
3
3
|
const req = new Request(src);
|
|
4
|
-
const res = await fetch(req);
|
|
4
|
+
const res = await fetch(req, { redirect: "manual" });
|
|
5
|
+
if (res.status >= 300 && res.status < 400) {
|
|
6
|
+
throw new Error(`Failed to load remote image ${src}. The request was redirected.`);
|
|
7
|
+
}
|
|
5
8
|
if (!res.ok) {
|
|
6
9
|
throw new Error(
|
|
7
10
|
`Failed to load remote image ${src}. The request did not return a 200 OK response. (received ${res.status}))`
|
|
@@ -22,7 +25,10 @@ async function revalidateRemoteImage(src, revalidationData) {
|
|
|
22
25
|
...revalidationData.lastModified && { "If-Modified-Since": revalidationData.lastModified }
|
|
23
26
|
};
|
|
24
27
|
const req = new Request(src, { headers, cache: "no-cache" });
|
|
25
|
-
const res = await fetch(req);
|
|
28
|
+
const res = await fetch(req, { redirect: "manual" });
|
|
29
|
+
if (res.status >= 300 && res.status < 400) {
|
|
30
|
+
throw new Error(`Failed to revalidate cached remote image ${src}. The request was redirected.`);
|
|
31
|
+
}
|
|
26
32
|
if (!res.ok && res.status !== 304) {
|
|
27
33
|
throw new Error(
|
|
28
34
|
`Failed to revalidate cached remote image ${src}. The request did not return a 200 OK / 304 NOT MODIFIED response. (received ${res.status} ${res.statusText})`
|
|
@@ -8,8 +8,12 @@ async function loadRemoteImage(src, headers) {
|
|
|
8
8
|
try {
|
|
9
9
|
const res = await fetch(src, {
|
|
10
10
|
// Forward all headers from the original request
|
|
11
|
-
headers
|
|
11
|
+
headers,
|
|
12
|
+
redirect: "manual"
|
|
12
13
|
});
|
|
14
|
+
if (res.status >= 300 && res.status < 400) {
|
|
15
|
+
return void 0;
|
|
16
|
+
}
|
|
13
17
|
if (!res.ok) {
|
|
14
18
|
return void 0;
|
|
15
19
|
}
|
|
@@ -6,7 +6,10 @@ import { getConfiguredImageService } from "../internal.js";
|
|
|
6
6
|
import { etag } from "../utils/etag.js";
|
|
7
7
|
async function loadRemoteImage(src) {
|
|
8
8
|
try {
|
|
9
|
-
const res = await fetch(src);
|
|
9
|
+
const res = await fetch(src, { redirect: "manual" });
|
|
10
|
+
if (res.status >= 300 && res.status < 400) {
|
|
11
|
+
return void 0;
|
|
12
|
+
}
|
|
10
13
|
if (!res.ok) {
|
|
11
14
|
return void 0;
|
|
12
15
|
}
|
package/dist/assets/internal.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isRemotePath } from "@astrojs/internal-helpers/path";
|
|
2
|
+
import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
|
|
2
3
|
import { AstroError, AstroErrorData } from "../core/errors/index.js";
|
|
3
4
|
import { DEFAULT_HASH_PROPS } from "./consts.js";
|
|
4
5
|
import {
|
|
@@ -58,13 +59,21 @@ async function getImage(options, imageConfig) {
|
|
|
58
59
|
};
|
|
59
60
|
let originalWidth;
|
|
60
61
|
let originalHeight;
|
|
61
|
-
if (options.inferSize
|
|
62
|
-
const result = await inferRemoteSize(resolvedOptions.src);
|
|
63
|
-
resolvedOptions.width ??= result.width;
|
|
64
|
-
resolvedOptions.height ??= result.height;
|
|
65
|
-
originalWidth = result.width;
|
|
66
|
-
originalHeight = result.height;
|
|
62
|
+
if (options.inferSize) {
|
|
67
63
|
delete resolvedOptions.inferSize;
|
|
64
|
+
if (isRemoteImage(resolvedOptions.src) && isRemotePath(resolvedOptions.src)) {
|
|
65
|
+
if (!isRemoteAllowed(resolvedOptions.src, imageConfig)) {
|
|
66
|
+
throw new AstroError({
|
|
67
|
+
...AstroErrorData.RemoteImageNotAllowed,
|
|
68
|
+
message: AstroErrorData.RemoteImageNotAllowed.message(resolvedOptions.src)
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
const result = await inferRemoteSize(resolvedOptions.src, imageConfig);
|
|
72
|
+
resolvedOptions.width ??= result.width;
|
|
73
|
+
resolvedOptions.height ??= result.height;
|
|
74
|
+
originalWidth = result.width;
|
|
75
|
+
originalHeight = result.height;
|
|
76
|
+
}
|
|
68
77
|
}
|
|
69
78
|
const originalFilePath = isESMImportedImage(resolvedOptions.src) ? resolvedOptions.src.fsPath : void 0;
|
|
70
79
|
const clonedSrc = isESMImportedImage(resolvedOptions.src) ? (
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import type { AstroConfig } from '../../types/public/config.js';
|
|
1
2
|
import type { ImageMetadata } from '../types.js';
|
|
3
|
+
type RemoteImageConfig = Pick<AstroConfig['image'], 'domains' | 'remotePatterns'>;
|
|
2
4
|
/**
|
|
3
5
|
* Infers the dimensions of a remote image by streaming its data and analyzing it progressively until sufficient metadata is available.
|
|
4
6
|
*
|
|
5
7
|
* @param {string} url - The URL of the remote image from which to infer size metadata.
|
|
8
|
+
* @param {RemoteImageConfig} [imageConfig] - Optional image config used to validate remote allowlists.
|
|
6
9
|
* @return {Promise<Omit<ImageMetadata, 'src' | 'fsPath'>>} Returns a promise that resolves to an object containing the image dimensions metadata excluding `src` and `fsPath`.
|
|
7
10
|
* @throws {AstroError} Thrown when the fetching fails or metadata cannot be extracted.
|
|
8
11
|
*/
|
|
9
|
-
export declare function inferRemoteSize(url: string): Promise<Omit<ImageMetadata, 'src' | 'fsPath'>>;
|
|
12
|
+
export declare function inferRemoteSize(url: string, imageConfig?: RemoteImageConfig): Promise<Omit<ImageMetadata, 'src' | 'fsPath'>>;
|
|
13
|
+
export {};
|
|
@@ -1,7 +1,39 @@
|
|
|
1
|
+
import { isRemoteAllowed } from "@astrojs/internal-helpers/remote";
|
|
1
2
|
import { AstroError, AstroErrorData } from "../../core/errors/index.js";
|
|
2
3
|
import { imageMetadata } from "./metadata.js";
|
|
3
|
-
async function inferRemoteSize(url) {
|
|
4
|
-
|
|
4
|
+
async function inferRemoteSize(url, imageConfig) {
|
|
5
|
+
if (!URL.canParse(url)) {
|
|
6
|
+
throw new AstroError({
|
|
7
|
+
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
|
8
|
+
message: AstroErrorData.FailedToFetchRemoteImageDimensions.message(url)
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
const allowlistConfig = imageConfig ? {
|
|
12
|
+
domains: imageConfig.domains ?? [],
|
|
13
|
+
remotePatterns: imageConfig.remotePatterns ?? []
|
|
14
|
+
} : void 0;
|
|
15
|
+
if (!allowlistConfig) {
|
|
16
|
+
const parsedUrl = new URL(url);
|
|
17
|
+
if (!["http:", "https:"].includes(parsedUrl.protocol)) {
|
|
18
|
+
throw new AstroError({
|
|
19
|
+
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
|
20
|
+
message: AstroErrorData.FailedToFetchRemoteImageDimensions.message(url)
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (allowlistConfig && !isRemoteAllowed(url, allowlistConfig)) {
|
|
25
|
+
throw new AstroError({
|
|
26
|
+
...AstroErrorData.RemoteImageNotAllowed,
|
|
27
|
+
message: AstroErrorData.RemoteImageNotAllowed.message(url)
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
const response = await fetch(url, { redirect: "manual" });
|
|
31
|
+
if (response.status >= 300 && response.status < 400) {
|
|
32
|
+
throw new AstroError({
|
|
33
|
+
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
|
34
|
+
message: AstroErrorData.FailedToFetchRemoteImageDimensions.message(url)
|
|
35
|
+
});
|
|
36
|
+
}
|
|
5
37
|
if (!response.body || !response.ok) {
|
|
6
38
|
throw new AstroError({
|
|
7
39
|
...AstroErrorData.FailedToFetchRemoteImageDimensions,
|
|
@@ -101,11 +101,11 @@ function assets({ fs, settings, sync, logger }) {
|
|
|
101
101
|
if (id === resolvedVirtualModuleId) {
|
|
102
102
|
return {
|
|
103
103
|
code: `
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
export { getConfiguredImageService, isLocalService } from "astro/assets";
|
|
105
|
+
import { getImage as getImageInternal } from "astro/assets";
|
|
106
|
+
import { inferRemoteSize as inferRemoteSizeInternal } from "astro/assets/utils/inferRemoteSize.js";
|
|
107
|
+
export { default as Image } from "astro/components/${imageComponentPrefix}Image.astro";
|
|
108
|
+
export { default as Picture } from "astro/components/${imageComponentPrefix}Picture.astro";
|
|
109
109
|
|
|
110
110
|
export { default as Font } from "astro/components/Font.astro";
|
|
111
111
|
export * from "${RUNTIME_VIRTUAL_MODULE_ID}";
|
|
@@ -126,6 +126,9 @@ function assets({ fs, settings, sync, logger }) {
|
|
|
126
126
|
enumerable: false,
|
|
127
127
|
configurable: true,
|
|
128
128
|
});
|
|
129
|
+
export const inferRemoteSize = async (url) => {
|
|
130
|
+
return inferRemoteSizeInternal(url, imageConfig)
|
|
131
|
+
}
|
|
129
132
|
// This is used by the @astrojs/node integration to locate images.
|
|
130
133
|
// It's unused on other platforms, but on some platforms like Netlify (and presumably also Vercel)
|
|
131
134
|
// new URL("dist/...") is interpreted by the bundler as a signal to include that directory
|
package/dist/container/index.js
CHANGED
|
@@ -45,6 +45,7 @@ function createManifest(manifest, renderers, middleware) {
|
|
|
45
45
|
i18n: manifest?.i18n,
|
|
46
46
|
checkOrigin: false,
|
|
47
47
|
allowedDomains: manifest?.allowedDomains ?? [],
|
|
48
|
+
actionBodySizeLimit: 1024 * 1024,
|
|
48
49
|
middleware: manifest?.middleware ?? middlewareInstance,
|
|
49
50
|
key: createKey(),
|
|
50
51
|
csp: manifest?.csp
|
|
@@ -164,7 +164,7 @@ ${contentConfig.error.message}`);
|
|
|
164
164
|
logger.info("Content config changed");
|
|
165
165
|
shouldClear = true;
|
|
166
166
|
}
|
|
167
|
-
if (previousAstroVersion && previousAstroVersion !== "5.
|
|
167
|
+
if (previousAstroVersion && previousAstroVersion !== "5.18.0") {
|
|
168
168
|
logger.info("Astro version changed");
|
|
169
169
|
shouldClear = true;
|
|
170
170
|
}
|
|
@@ -172,8 +172,8 @@ ${contentConfig.error.message}`);
|
|
|
172
172
|
logger.info("Clearing content store");
|
|
173
173
|
this.#store.clearAll();
|
|
174
174
|
}
|
|
175
|
-
if ("5.
|
|
176
|
-
await this.#store.metaStore().set("astro-version", "5.
|
|
175
|
+
if ("5.18.0") {
|
|
176
|
+
await this.#store.metaStore().set("astro-version", "5.18.0");
|
|
177
177
|
}
|
|
178
178
|
if (currentConfigDigest) {
|
|
179
179
|
await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
package/dist/core/app/types.d.ts
CHANGED
|
@@ -71,6 +71,7 @@ export type SSRManifest = {
|
|
|
71
71
|
actions?: () => Promise<SSRActions> | SSRActions;
|
|
72
72
|
checkOrigin: boolean;
|
|
73
73
|
allowedDomains?: Partial<RemotePattern>[];
|
|
74
|
+
actionBodySizeLimit: number;
|
|
74
75
|
sessionConfig?: ResolvedSessionConfig<any>;
|
|
75
76
|
cacheDir: string | URL;
|
|
76
77
|
srcDir: string | URL;
|
|
@@ -39,7 +39,9 @@ function validateForwardedHeaders(forwardedProtocol, forwardedHost, forwardedPor
|
|
|
39
39
|
if (hasProtocolPatterns) {
|
|
40
40
|
try {
|
|
41
41
|
const testUrl = new URL(`${forwardedProtocol}://example.com`);
|
|
42
|
-
const isAllowed = allowedDomains.some(
|
|
42
|
+
const isAllowed = allowedDomains.some(
|
|
43
|
+
(pattern) => matchPattern(testUrl, { protocol: pattern.protocol })
|
|
44
|
+
);
|
|
43
45
|
if (isAllowed) {
|
|
44
46
|
result.protocol = forwardedProtocol;
|
|
45
47
|
}
|
|
@@ -500,6 +500,7 @@ async function createBuildManifest(settings, internals, renderers, middleware, a
|
|
|
500
500
|
},
|
|
501
501
|
actions: () => actions,
|
|
502
502
|
checkOrigin: (settings.config.security?.checkOrigin && settings.buildOutput === "server") ?? false,
|
|
503
|
+
actionBodySizeLimit: settings.config.security.actionBodySizeLimit,
|
|
503
504
|
key,
|
|
504
505
|
csp
|
|
505
506
|
};
|
|
@@ -300,6 +300,7 @@ async function buildManifest(opts, internals, staticFiles, encodedKey) {
|
|
|
300
300
|
buildFormat: settings.config.build.format,
|
|
301
301
|
checkOrigin: (settings.config.security?.checkOrigin && settings.buildOutput === "server") ?? false,
|
|
302
302
|
allowedDomains: settings.config.security?.allowedDomains,
|
|
303
|
+
actionBodySizeLimit: settings.config.security.actionBodySizeLimit,
|
|
303
304
|
serverIslandNameMap: Array.from(settings.serverIslandNameMap),
|
|
304
305
|
key: encodedKey,
|
|
305
306
|
sessionConfig: settings.config.session,
|
|
@@ -64,6 +64,7 @@ export declare const ASTRO_CONFIG_DEFAULTS: {
|
|
|
64
64
|
security: {
|
|
65
65
|
checkOrigin: true;
|
|
66
66
|
allowedDomains: never[];
|
|
67
|
+
actionBodySizeLimit: number;
|
|
67
68
|
};
|
|
68
69
|
env: {
|
|
69
70
|
schema: {};
|
|
@@ -456,6 +457,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
|
|
|
456
457
|
protocol?: string | undefined;
|
|
457
458
|
hostname?: string | undefined;
|
|
458
459
|
}>, "many">>>;
|
|
460
|
+
actionBodySizeLimit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
459
461
|
}, "strip", z.ZodTypeAny, {
|
|
460
462
|
checkOrigin: boolean;
|
|
461
463
|
allowedDomains: {
|
|
@@ -463,6 +465,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
|
|
|
463
465
|
protocol?: string | undefined;
|
|
464
466
|
hostname?: string | undefined;
|
|
465
467
|
}[];
|
|
468
|
+
actionBodySizeLimit: number;
|
|
466
469
|
}, {
|
|
467
470
|
checkOrigin?: boolean | undefined;
|
|
468
471
|
allowedDomains?: {
|
|
@@ -470,6 +473,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
|
|
|
470
473
|
protocol?: string | undefined;
|
|
471
474
|
hostname?: string | undefined;
|
|
472
475
|
}[] | undefined;
|
|
476
|
+
actionBodySizeLimit?: number | undefined;
|
|
473
477
|
}>>>;
|
|
474
478
|
env: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
475
479
|
schema: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, z.ZodIntersection<z.ZodEffects<z.ZodType<{
|
|
@@ -1097,6 +1101,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
|
|
|
1097
1101
|
protocol?: string | undefined;
|
|
1098
1102
|
hostname?: string | undefined;
|
|
1099
1103
|
}[];
|
|
1104
|
+
actionBodySizeLimit: number;
|
|
1100
1105
|
};
|
|
1101
1106
|
experimental: {
|
|
1102
1107
|
clientPrerender: boolean;
|
|
@@ -1344,6 +1349,7 @@ export declare const AstroConfigSchema: z.ZodObject<{
|
|
|
1344
1349
|
protocol?: string | undefined;
|
|
1345
1350
|
hostname?: string | undefined;
|
|
1346
1351
|
}[] | undefined;
|
|
1352
|
+
actionBodySizeLimit?: number | undefined;
|
|
1347
1353
|
} | undefined;
|
|
1348
1354
|
experimental?: {
|
|
1349
1355
|
fonts?: {
|
|
@@ -46,7 +46,8 @@ const ASTRO_CONFIG_DEFAULTS = {
|
|
|
46
46
|
redirects: {},
|
|
47
47
|
security: {
|
|
48
48
|
checkOrigin: true,
|
|
49
|
-
allowedDomains: []
|
|
49
|
+
allowedDomains: [],
|
|
50
|
+
actionBodySizeLimit: 1024 * 1024
|
|
50
51
|
},
|
|
51
52
|
env: {
|
|
52
53
|
schema: {},
|
|
@@ -252,7 +253,8 @@ const AstroConfigSchema = z.object({
|
|
|
252
253
|
protocol: z.string().optional(),
|
|
253
254
|
port: z.string().optional()
|
|
254
255
|
})
|
|
255
|
-
).optional().default(ASTRO_CONFIG_DEFAULTS.security.allowedDomains)
|
|
256
|
+
).optional().default(ASTRO_CONFIG_DEFAULTS.security.allowedDomains),
|
|
257
|
+
actionBodySizeLimit: z.number().optional().default(ASTRO_CONFIG_DEFAULTS.security.actionBodySizeLimit)
|
|
256
258
|
}).optional().default(ASTRO_CONFIG_DEFAULTS.security),
|
|
257
259
|
env: z.object({
|
|
258
260
|
schema: EnvSchema.optional().default(ASTRO_CONFIG_DEFAULTS.env.schema),
|
|
@@ -301,6 +301,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
301
301
|
protocol?: string | undefined;
|
|
302
302
|
hostname?: string | undefined;
|
|
303
303
|
}>, "many">>>;
|
|
304
|
+
actionBodySizeLimit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
304
305
|
}, "strip", z.ZodTypeAny, {
|
|
305
306
|
checkOrigin: boolean;
|
|
306
307
|
allowedDomains: {
|
|
@@ -308,6 +309,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
308
309
|
protocol?: string | undefined;
|
|
309
310
|
hostname?: string | undefined;
|
|
310
311
|
}[];
|
|
312
|
+
actionBodySizeLimit: number;
|
|
311
313
|
}, {
|
|
312
314
|
checkOrigin?: boolean | undefined;
|
|
313
315
|
allowedDomains?: {
|
|
@@ -315,6 +317,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
315
317
|
protocol?: string | undefined;
|
|
316
318
|
hostname?: string | undefined;
|
|
317
319
|
}[] | undefined;
|
|
320
|
+
actionBodySizeLimit?: number | undefined;
|
|
318
321
|
}>>>;
|
|
319
322
|
env: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
320
323
|
schema: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, z.ZodIntersection<z.ZodEffects<z.ZodType<{
|
|
@@ -1020,6 +1023,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
1020
1023
|
protocol?: string | undefined;
|
|
1021
1024
|
hostname?: string | undefined;
|
|
1022
1025
|
}[];
|
|
1026
|
+
actionBodySizeLimit: number;
|
|
1023
1027
|
};
|
|
1024
1028
|
experimental: {
|
|
1025
1029
|
clientPrerender: boolean;
|
|
@@ -1267,6 +1271,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
1267
1271
|
protocol?: string | undefined;
|
|
1268
1272
|
hostname?: string | undefined;
|
|
1269
1273
|
}[] | undefined;
|
|
1274
|
+
actionBodySizeLimit?: number | undefined;
|
|
1270
1275
|
} | undefined;
|
|
1271
1276
|
experimental?: {
|
|
1272
1277
|
fonts?: {
|
|
@@ -1450,6 +1455,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
1450
1455
|
protocol?: string | undefined;
|
|
1451
1456
|
hostname?: string | undefined;
|
|
1452
1457
|
}[];
|
|
1458
|
+
actionBodySizeLimit: number;
|
|
1453
1459
|
};
|
|
1454
1460
|
experimental: {
|
|
1455
1461
|
clientPrerender: boolean;
|
|
@@ -1697,6 +1703,7 @@ export declare function createRelativeSchema(cmd: string, fileProtocolRoot: stri
|
|
|
1697
1703
|
protocol?: string | undefined;
|
|
1698
1704
|
hostname?: string | undefined;
|
|
1699
1705
|
}[] | undefined;
|
|
1706
|
+
actionBodySizeLimit?: number | undefined;
|
|
1700
1707
|
} | undefined;
|
|
1701
1708
|
experimental?: {
|
|
1702
1709
|
fonts?: {
|
package/dist/core/constants.js
CHANGED
package/dist/core/dev/dev.js
CHANGED
|
@@ -22,7 +22,7 @@ async function dev(inlineConfig) {
|
|
|
22
22
|
await telemetry.record([]);
|
|
23
23
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
24
24
|
const logger = restart.container.logger;
|
|
25
|
-
const currentVersion = "5.
|
|
25
|
+
const currentVersion = "5.18.0";
|
|
26
26
|
const isPrerelease = currentVersion.includes("-");
|
|
27
27
|
if (!isPrerelease) {
|
|
28
28
|
try {
|
|
@@ -491,6 +491,19 @@ export declare const FailedToFetchRemoteImageDimensions: {
|
|
|
491
491
|
message: (imageURL: string) => string;
|
|
492
492
|
hint: string;
|
|
493
493
|
};
|
|
494
|
+
/**
|
|
495
|
+
* @docs
|
|
496
|
+
* @message
|
|
497
|
+
* Remote image `IMAGE_URL` is not allowed by your image configuration.
|
|
498
|
+
* @description
|
|
499
|
+
* The remote image URL does not match your configured `image.domains` or `image.remotePatterns`.
|
|
500
|
+
*/
|
|
501
|
+
export declare const RemoteImageNotAllowed: {
|
|
502
|
+
name: string;
|
|
503
|
+
title: string;
|
|
504
|
+
message: (imageURL: string) => string;
|
|
505
|
+
hint: string;
|
|
506
|
+
};
|
|
494
507
|
/**
|
|
495
508
|
* @docs
|
|
496
509
|
* @description
|
|
@@ -175,6 +175,12 @@ const FailedToFetchRemoteImageDimensions = {
|
|
|
175
175
|
message: (imageURL) => `Failed to get the dimensions for ${imageURL}.`,
|
|
176
176
|
hint: "Verify your remote image URL is accurate, and that you are not using `inferSize` with a file located in your `public/` folder."
|
|
177
177
|
};
|
|
178
|
+
const RemoteImageNotAllowed = {
|
|
179
|
+
name: "RemoteImageNotAllowed",
|
|
180
|
+
title: "Remote image is not allowed",
|
|
181
|
+
message: (imageURL) => `Remote image ${imageURL} is not allowed by your image configuration.`,
|
|
182
|
+
hint: "Update `image.domains` or `image.remotePatterns`, or remove `inferSize` for this image."
|
|
183
|
+
};
|
|
178
184
|
const UnsupportedImageFormat = {
|
|
179
185
|
name: "UnsupportedImageFormat",
|
|
180
186
|
title: "Unsupported image format",
|
|
@@ -832,6 +838,7 @@ export {
|
|
|
832
838
|
PrerenderDynamicEndpointPathCollide,
|
|
833
839
|
PrerenderRouteConflict,
|
|
834
840
|
RedirectWithNoLocation,
|
|
841
|
+
RemoteImageNotAllowed,
|
|
835
842
|
RenderUndefinedEntryError,
|
|
836
843
|
ReservedSlotName,
|
|
837
844
|
ResponseSentError,
|
package/dist/core/messages.js
CHANGED
|
@@ -38,7 +38,7 @@ function serverStart({
|
|
|
38
38
|
host,
|
|
39
39
|
base
|
|
40
40
|
}) {
|
|
41
|
-
const version = "5.
|
|
41
|
+
const version = "5.18.0";
|
|
42
42
|
const localPrefix = `${dim("\u2503")} Local `;
|
|
43
43
|
const networkPrefix = `${dim("\u2503")} Network `;
|
|
44
44
|
const emptyPrefix = " ".repeat(11);
|
|
@@ -275,7 +275,7 @@ function printHelp({
|
|
|
275
275
|
message.push(
|
|
276
276
|
linebreak(),
|
|
277
277
|
` ${bgGreen(black(` ${commandName} `))} ${green(
|
|
278
|
-
`v${"5.
|
|
278
|
+
`v${"5.18.0"}`
|
|
279
279
|
)} ${headline}`
|
|
280
280
|
);
|
|
281
281
|
}
|
|
@@ -563,6 +563,30 @@ export interface AstroUserConfig<TLocales extends Locales = never, TSession exte
|
|
|
563
563
|
* When not configured, `X-Forwarded-Host` headers are not trusted and will be ignored.
|
|
564
564
|
*/
|
|
565
565
|
allowedDomains?: Partial<RemotePattern>[];
|
|
566
|
+
/**
|
|
567
|
+
* @docs
|
|
568
|
+
* @name security.actionBodySizeLimit
|
|
569
|
+
* @kind h4
|
|
570
|
+
* @type {number}
|
|
571
|
+
* @default `1048576` (1 MB)
|
|
572
|
+
* @version 5.18.0
|
|
573
|
+
* @description
|
|
574
|
+
*
|
|
575
|
+
* Sets the maximum size in bytes allowed for action request bodies.
|
|
576
|
+
*
|
|
577
|
+
* By default, action request bodies are limited to 1 MB (1048576 bytes) to prevent abuse.
|
|
578
|
+
* You can increase this limit if your actions need to accept larger payloads, for example when handling file uploads.
|
|
579
|
+
*
|
|
580
|
+
* ```js
|
|
581
|
+
* // astro.config.mjs
|
|
582
|
+
* export default defineConfig({
|
|
583
|
+
* security: {
|
|
584
|
+
* actionBodySizeLimit: 10 * 1024 * 1024 // 10 MB
|
|
585
|
+
* }
|
|
586
|
+
* })
|
|
587
|
+
* ```
|
|
588
|
+
*/
|
|
589
|
+
actionBodySizeLimit?: number;
|
|
566
590
|
};
|
|
567
591
|
/**
|
|
568
592
|
* @docs
|
|
@@ -106,6 +106,7 @@ function createVitePluginAstroServer({
|
|
|
106
106
|
}
|
|
107
107
|
warnMissingAdapter(logger, settings);
|
|
108
108
|
pipeline.manifest.checkOrigin = settings.config.security.checkOrigin && settings.buildOutput === "server";
|
|
109
|
+
pipeline.manifest.actionBodySizeLimit = settings.config.security.actionBodySizeLimit;
|
|
109
110
|
pipeline.setManifestData(routesList);
|
|
110
111
|
}
|
|
111
112
|
viteServer.watcher.on("add", rebuildManifest.bind(null, null));
|
|
@@ -251,6 +252,7 @@ function createDevelopmentManifest(settings) {
|
|
|
251
252
|
inlinedScripts: /* @__PURE__ */ new Map(),
|
|
252
253
|
i18n: i18nManifest,
|
|
253
254
|
checkOrigin: (settings.config.security?.checkOrigin && settings.buildOutput === "server") ?? false,
|
|
255
|
+
actionBodySizeLimit: settings.config.security.actionBodySizeLimit,
|
|
254
256
|
key: hasEnvironmentKey() ? getEnvironmentKey() : createKey(),
|
|
255
257
|
middleware() {
|
|
256
258
|
return {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.18.0",
|
|
4
4
|
"description": "Astro is a modern site builder with web best practices, performance, and DX front-of-mind.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "withastro",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"dlv": "^1.1.3",
|
|
114
114
|
"dset": "^3.1.4",
|
|
115
115
|
"es-module-lexer": "^1.7.0",
|
|
116
|
-
"esbuild": "^0.27.
|
|
116
|
+
"esbuild": "^0.27.3",
|
|
117
117
|
"estree-walker": "^3.0.3",
|
|
118
118
|
"flattie": "^1.1.1",
|
|
119
119
|
"fontace": "~0.4.0",
|
|
@@ -193,8 +193,8 @@
|
|
|
193
193
|
"undici": "^6.23.0",
|
|
194
194
|
"unified": "^11.0.5",
|
|
195
195
|
"vitest": "^3.2.4",
|
|
196
|
-
"
|
|
197
|
-
"
|
|
196
|
+
"astro-scripts": "0.0.14",
|
|
197
|
+
"@astrojs/check": "0.9.6"
|
|
198
198
|
},
|
|
199
199
|
"engines": {
|
|
200
200
|
"node": "18.20.8 || ^20.3.0 || >=22.0.0",
|