astro 5.16.2 → 5.16.3
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.d.ts +5 -4
- package/dist/actions/runtime/server.js +0 -1
- package/dist/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/content/content-layer.js +3 -3
- package/dist/core/app/index.js +8 -2
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/messages.js +2 -2
- package/dist/core/render-context.js +8 -3
- package/dist/core/util/pathname.d.ts +10 -0
- package/dist/core/util/pathname.js +17 -0
- package/dist/vite-plugin-astro-server/request.js +9 -2
- package/package.json +5 -5
|
@@ -6,19 +6,20 @@ export * from './shared.js';
|
|
|
6
6
|
export type ActionAccept = 'form' | 'json';
|
|
7
7
|
export type ActionHandler<TInputSchema, TOutput> = TInputSchema extends z.ZodType ? (input: z.infer<TInputSchema>, context: ActionAPIContext) => MaybePromise<TOutput> : (input: any, context: ActionAPIContext) => MaybePromise<TOutput>;
|
|
8
8
|
export type ActionReturnType<T extends ActionHandler<any, any>> = Awaited<ReturnType<T>>;
|
|
9
|
-
|
|
9
|
+
export type InferKey = '__internalInfer';
|
|
10
10
|
/**
|
|
11
11
|
* Infers the type of an action's input based on its Zod schema
|
|
12
12
|
*
|
|
13
13
|
* @see https://docs.astro.build/en/reference/modules/astro-actions/#actioninputschema
|
|
14
14
|
*/
|
|
15
15
|
export type ActionInputSchema<T extends ActionClient<any, any, any>> = T extends {
|
|
16
|
-
[
|
|
17
|
-
} ? T[
|
|
16
|
+
[key in InferKey]: any;
|
|
17
|
+
} ? T[InferKey] : never;
|
|
18
18
|
export type ActionClient<TOutput, TAccept extends ActionAccept | undefined, TInputSchema extends z.ZodType | undefined> = TInputSchema extends z.ZodType ? ((input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<SafeResult<z.input<TInputSchema> extends ErrorInferenceObject ? z.input<TInputSchema> : ErrorInferenceObject, Awaited<TOutput>>>) & {
|
|
19
19
|
queryString: string;
|
|
20
20
|
orThrow: (input: TAccept extends 'form' ? FormData : z.input<TInputSchema>) => Promise<Awaited<TOutput>>;
|
|
21
|
-
|
|
21
|
+
} & {
|
|
22
|
+
[key in InferKey]: TInputSchema;
|
|
22
23
|
} : ((input?: any) => Promise<SafeResult<never, Awaited<TOutput>>>) & {
|
|
23
24
|
orThrow: (input?: any) => Promise<Awaited<TOutput>>;
|
|
24
25
|
};
|
|
@@ -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.16.
|
|
167
|
+
if (previousAstroVersion && previousAstroVersion !== "5.16.3") {
|
|
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.16.
|
|
176
|
-
await this.#store.metaStore().set("astro-version", "5.16.
|
|
175
|
+
if ("5.16.3") {
|
|
176
|
+
await this.#store.metaStore().set("astro-version", "5.16.3");
|
|
177
177
|
}
|
|
178
178
|
if (currentConfigDigest) {
|
|
179
179
|
await this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
package/dist/core/app/index.js
CHANGED
|
@@ -30,6 +30,7 @@ import { ensure404Route } from "../routing/astro-designed-error-pages.js";
|
|
|
30
30
|
import { createDefaultRoutes } from "../routing/default.js";
|
|
31
31
|
import { matchRoute } from "../routing/match.js";
|
|
32
32
|
import { PERSIST_SYMBOL } from "../session.js";
|
|
33
|
+
import { validateAndDecodePathname } from "../util/pathname.js";
|
|
33
34
|
import { AppPipeline } from "./pipeline.js";
|
|
34
35
|
import { deserializeManifest } from "./common.js";
|
|
35
36
|
class App {
|
|
@@ -197,7 +198,7 @@ class App {
|
|
|
197
198
|
const url = new URL(request.url);
|
|
198
199
|
const pathname = prependForwardSlash(this.removeBase(url.pathname));
|
|
199
200
|
try {
|
|
200
|
-
return
|
|
201
|
+
return validateAndDecodePathname(pathname);
|
|
201
202
|
} catch (e) {
|
|
202
203
|
this.getAdapterLogger().error(e.toString());
|
|
203
204
|
return pathname;
|
|
@@ -218,7 +219,12 @@ class App {
|
|
|
218
219
|
if (!pathname) {
|
|
219
220
|
pathname = prependForwardSlash(this.removeBase(url.pathname));
|
|
220
221
|
}
|
|
221
|
-
|
|
222
|
+
try {
|
|
223
|
+
pathname = validateAndDecodePathname(pathname);
|
|
224
|
+
} catch {
|
|
225
|
+
return void 0;
|
|
226
|
+
}
|
|
227
|
+
let routeData = matchRoute(pathname, this.#manifestData);
|
|
222
228
|
if (!routeData) return void 0;
|
|
223
229
|
if (allowPrerenderedRoutes) {
|
|
224
230
|
return routeData;
|
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.16.
|
|
25
|
+
const currentVersion = "5.16.3";
|
|
26
26
|
const isPrerelease = currentVersion.includes("-");
|
|
27
27
|
if (!isPrerelease) {
|
|
28
28
|
try {
|
package/dist/core/messages.js
CHANGED
|
@@ -38,7 +38,7 @@ function serverStart({
|
|
|
38
38
|
host,
|
|
39
39
|
base
|
|
40
40
|
}) {
|
|
41
|
-
const version = "5.16.
|
|
41
|
+
const version = "5.16.3";
|
|
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.16.
|
|
278
|
+
`v${"5.16.3"}`
|
|
279
279
|
)} ${headline}`
|
|
280
280
|
);
|
|
281
281
|
}
|
|
@@ -30,6 +30,7 @@ import { getParams, getProps, Slots } from "./render/index.js";
|
|
|
30
30
|
import { isRoute404or500, isRouteExternalRedirect, isRouteServerIsland } from "./routing/match.js";
|
|
31
31
|
import { copyRequest, getOriginPathname, setOriginPathname } from "./routing/rewrite.js";
|
|
32
32
|
import { AstroSession } from "./session.js";
|
|
33
|
+
import { validateAndDecodePathname } from "./util/pathname.js";
|
|
33
34
|
const apiContextRoutesSymbol = Symbol.for("context.routes");
|
|
34
35
|
class RenderContext {
|
|
35
36
|
constructor(pipeline, locals, middleware, actions, pathname, request, routeData, status, clientAddress, cookies = new AstroCookies(request), params = getParams(routeData, pathname), url = RenderContext.#createNormalizedUrl(request.url), props = {}, partial = void 0, shouldInjectCspMetaTags = !!pipeline.manifest.csp, session = pipeline.manifest.sessionConfig ? new AstroSession(cookies, pipeline.manifest.sessionConfig, pipeline.runtimeMode) : void 0) {
|
|
@@ -53,10 +54,14 @@ class RenderContext {
|
|
|
53
54
|
static #createNormalizedUrl(requestUrl) {
|
|
54
55
|
const url = new URL(requestUrl);
|
|
55
56
|
try {
|
|
56
|
-
url.pathname =
|
|
57
|
-
}
|
|
58
|
-
|
|
57
|
+
url.pathname = validateAndDecodePathname(url.pathname);
|
|
58
|
+
} catch {
|
|
59
|
+
try {
|
|
60
|
+
url.pathname = decodeURI(url.pathname);
|
|
61
|
+
} catch {
|
|
62
|
+
}
|
|
59
63
|
}
|
|
64
|
+
return url;
|
|
60
65
|
}
|
|
61
66
|
/**
|
|
62
67
|
* A flag that tells the render content if the rewriting was triggered
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates that a pathname is not multi-level encoded.
|
|
3
|
+
* Detects if a pathname contains encoding that was encoded again (e.g., %2561dmin where %25 decodes to %).
|
|
4
|
+
* This prevents double/triple encoding bypasses of security checks.
|
|
5
|
+
*
|
|
6
|
+
* @param pathname - The pathname to validate
|
|
7
|
+
* @returns The decoded pathname if valid
|
|
8
|
+
* @throws Error if multi-level encoding is detected
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateAndDecodePathname(pathname: string): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
function validateAndDecodePathname(pathname) {
|
|
2
|
+
let decoded;
|
|
3
|
+
try {
|
|
4
|
+
decoded = decodeURI(pathname);
|
|
5
|
+
} catch (_e) {
|
|
6
|
+
throw new Error("Invalid URL encoding");
|
|
7
|
+
}
|
|
8
|
+
const hasDecoding = decoded !== pathname;
|
|
9
|
+
const decodedStillHasEncoding = /%[0-9a-fA-F]{2}/.test(decoded);
|
|
10
|
+
if (hasDecoding && decodedStillHasEncoding) {
|
|
11
|
+
throw new Error("Multi-level URL encoding is not allowed");
|
|
12
|
+
}
|
|
13
|
+
return decoded;
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
validateAndDecodePathname
|
|
17
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hasFileExtension } from "@astrojs/internal-helpers/path";
|
|
2
2
|
import { appendForwardSlash, removeTrailingForwardSlash } from "../core/path.js";
|
|
3
|
+
import { validateAndDecodePathname } from "../core/util/pathname.js";
|
|
3
4
|
import { runWithErrorHandling } from "./controller.js";
|
|
4
5
|
import { recordServerError } from "./error.js";
|
|
5
6
|
import { handle500Response } from "./response.js";
|
|
@@ -18,9 +19,15 @@ async function handleRequest({
|
|
|
18
19
|
if (config.trailingSlash === "never" && !incomingRequest.url) {
|
|
19
20
|
pathname = "";
|
|
20
21
|
} else {
|
|
21
|
-
|
|
22
|
+
try {
|
|
23
|
+
pathname = validateAndDecodePathname(url.pathname);
|
|
24
|
+
} catch {
|
|
25
|
+
incomingResponse.writeHead(404, { "Content-Type": "text/plain" });
|
|
26
|
+
incomingResponse.end("Not Found");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
22
29
|
}
|
|
23
|
-
url.pathname = removeTrailingForwardSlash(config.base) +
|
|
30
|
+
url.pathname = removeTrailingForwardSlash(config.base) + pathname;
|
|
24
31
|
if (config.trailingSlash === "never") {
|
|
25
32
|
url.pathname = removeTrailingForwardSlash(url.pathname);
|
|
26
33
|
} else if (config.trailingSlash === "always" && !hasFileExtension(url.pathname)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "5.16.
|
|
3
|
+
"version": "5.16.3",
|
|
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",
|
|
@@ -155,8 +155,8 @@
|
|
|
155
155
|
"zod-to-json-schema": "^3.25.0",
|
|
156
156
|
"zod-to-ts": "^1.2.0",
|
|
157
157
|
"@astrojs/markdown-remark": "6.3.9",
|
|
158
|
-
"@astrojs/
|
|
159
|
-
"@astrojs/
|
|
158
|
+
"@astrojs/telemetry": "3.3.0",
|
|
159
|
+
"@astrojs/internal-helpers": "0.7.5"
|
|
160
160
|
},
|
|
161
161
|
"optionalDependencies": {
|
|
162
162
|
"sharp": "^0.34.0"
|
|
@@ -195,8 +195,8 @@
|
|
|
195
195
|
"undici": "^6.22.0",
|
|
196
196
|
"unified": "^11.0.5",
|
|
197
197
|
"vitest": "^3.2.4",
|
|
198
|
-
"
|
|
199
|
-
"
|
|
198
|
+
"astro-scripts": "0.0.14",
|
|
199
|
+
"@astrojs/check": "0.9.6"
|
|
200
200
|
},
|
|
201
201
|
"engines": {
|
|
202
202
|
"node": "18.20.8 || ^20.3.0 || >=22.0.0",
|