astro 6.4.7 → 6.4.8
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/cli/infra/build-time-astro-version-provider.js +1 -1
- package/dist/content/content-layer.js +3 -3
- package/dist/core/constants.js +1 -1
- package/dist/core/dev/dev.js +1 -1
- package/dist/core/fetch/fetch-state.d.ts +6 -0
- package/dist/core/fetch/fetch-state.js +11 -1
- package/dist/core/i18n/handler.js +1 -1
- package/dist/core/messages/runtime.js +1 -1
- package/dist/core/routing/handler.js +3 -0
- package/dist/core/routing/rewrite.js +2 -1
- package/dist/core/util/pathname.d.ts +17 -16
- package/dist/core/util/pathname.js +6 -2
- package/package.json +3 -3
|
@@ -197,7 +197,7 @@ ${contentConfig.error.message}`
|
|
|
197
197
|
logger.info("Content config changed");
|
|
198
198
|
shouldClear = true;
|
|
199
199
|
}
|
|
200
|
-
if (previousAstroVersion && previousAstroVersion !== "6.4.
|
|
200
|
+
if (previousAstroVersion && previousAstroVersion !== "6.4.8") {
|
|
201
201
|
logger.info("Astro version changed");
|
|
202
202
|
shouldClear = true;
|
|
203
203
|
}
|
|
@@ -205,8 +205,8 @@ ${contentConfig.error.message}`
|
|
|
205
205
|
logger.info("Clearing content store");
|
|
206
206
|
this.#store.clearAll();
|
|
207
207
|
}
|
|
208
|
-
if ("6.4.
|
|
209
|
-
this.#store.metaStore().set("astro-version", "6.4.
|
|
208
|
+
if ("6.4.8") {
|
|
209
|
+
this.#store.metaStore().set("astro-version", "6.4.8");
|
|
210
210
|
}
|
|
211
211
|
if (currentConfigDigest) {
|
|
212
212
|
this.#store.metaStore().set("content-config-digest", currentConfigDigest);
|
package/dist/core/constants.js
CHANGED
package/dist/core/dev/dev.js
CHANGED
|
@@ -37,7 +37,7 @@ async function dev(inlineConfig) {
|
|
|
37
37
|
await telemetry.record([]);
|
|
38
38
|
const restart = await createContainerWithAutomaticRestart({ inlineConfig, fs });
|
|
39
39
|
const logger = restart.container.logger;
|
|
40
|
-
const currentVersion = "6.4.
|
|
40
|
+
const currentVersion = "6.4.8";
|
|
41
41
|
const isPrerelease = currentVersion.includes("-");
|
|
42
42
|
if (!isPrerelease) {
|
|
43
43
|
try {
|
|
@@ -134,6 +134,12 @@ export declare class FetchState implements AstroFetchState {
|
|
|
134
134
|
status: number;
|
|
135
135
|
/** Whether user middleware should be skipped for this request. */
|
|
136
136
|
skipMiddleware: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Set to `true` when the request path was encoded too many times to fully
|
|
139
|
+
* decode (see {@link validateAndDecodePathname}). These requests are
|
|
140
|
+
* rejected with a `400` before middleware or routing run.
|
|
141
|
+
*/
|
|
142
|
+
invalidEncoding: boolean;
|
|
137
143
|
/** A flag that tells the render content if the rewriting was triggered. */
|
|
138
144
|
isRewriting: boolean;
|
|
139
145
|
/** A safety net in case of loops (rewrite counter). */
|
|
@@ -29,7 +29,7 @@ import { getParams, getProps } from "../render/index.js";
|
|
|
29
29
|
import { Rewrites } from "../rewrites/handler.js";
|
|
30
30
|
import { isRoute404or500, isRouteServerIsland } from "../routing/match.js";
|
|
31
31
|
import { normalizeUrl } from "../util/normalized-url.js";
|
|
32
|
-
import { validateAndDecodePathname } from "../util/pathname.js";
|
|
32
|
+
import { MultiLevelEncodingError, validateAndDecodePathname } from "../util/pathname.js";
|
|
33
33
|
import { getOriginPathname, setOriginPathname } from "../routing/rewrite.js";
|
|
34
34
|
import { computePathnameFromDomain } from "../i18n/domain.js";
|
|
35
35
|
import { getCustom404Route, routeHasHtmlExtension } from "../routing/helpers.js";
|
|
@@ -88,6 +88,12 @@ class FetchState {
|
|
|
88
88
|
status = 200;
|
|
89
89
|
/** Whether user middleware should be skipped for this request. */
|
|
90
90
|
skipMiddleware = false;
|
|
91
|
+
/**
|
|
92
|
+
* Set to `true` when the request path was encoded too many times to fully
|
|
93
|
+
* decode (see {@link validateAndDecodePathname}). These requests are
|
|
94
|
+
* rejected with a `400` before middleware or routing run.
|
|
95
|
+
*/
|
|
96
|
+
invalidEncoding = false;
|
|
91
97
|
/** A flag that tells the render content if the rewriting was triggered. */
|
|
92
98
|
isRewriting = false;
|
|
93
99
|
/** A safety net in case of loops (rewrite counter). */
|
|
@@ -681,6 +687,10 @@ class FetchState {
|
|
|
681
687
|
try {
|
|
682
688
|
return validateAndDecodePathname(pathname);
|
|
683
689
|
} catch (e) {
|
|
690
|
+
if (e instanceof MultiLevelEncodingError) {
|
|
691
|
+
this.invalidEncoding = true;
|
|
692
|
+
return pathname;
|
|
693
|
+
}
|
|
684
694
|
this.pipeline.logger.error(null, e.toString());
|
|
685
695
|
return pathname;
|
|
686
696
|
}
|
|
@@ -47,7 +47,7 @@ class I18n {
|
|
|
47
47
|
if (typeHeader !== "page" && typeHeader !== "fallback") {
|
|
48
48
|
return response;
|
|
49
49
|
}
|
|
50
|
-
const url =
|
|
50
|
+
const url = state.url;
|
|
51
51
|
const currentLocale = state.computeCurrentLocale();
|
|
52
52
|
const isPrerendered = state.routeData.prerender;
|
|
53
53
|
const routerContext = {
|
|
@@ -65,6 +65,9 @@ class AstroHandler {
|
|
|
65
65
|
}
|
|
66
66
|
async handle(state) {
|
|
67
67
|
state.pipeline.usedFeatures |= ALL_PIPELINE_FEATURES;
|
|
68
|
+
if (state.invalidEncoding) {
|
|
69
|
+
return new Response(null, { status: 400, statusText: "Bad Request" });
|
|
70
|
+
}
|
|
68
71
|
const trailingSlashRedirect = this.#trailingSlashHandler.handle(state);
|
|
69
72
|
if (trailingSlashRedirect) {
|
|
70
73
|
return trailingSlashRedirect;
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
trimSlashes
|
|
11
11
|
} from "../path.js";
|
|
12
12
|
import { createRequest } from "../request.js";
|
|
13
|
+
import { validateAndDecodePathname } from "../util/pathname.js";
|
|
13
14
|
import { DEFAULT_404_ROUTE } from "./internal/astro-designed-error-pages.js";
|
|
14
15
|
import { isRoute404, isRoute500 } from "./internal/route-errors.js";
|
|
15
16
|
function findRouteToRewrite({
|
|
@@ -36,7 +37,7 @@ function findRouteToRewrite({
|
|
|
36
37
|
buildFormat
|
|
37
38
|
);
|
|
38
39
|
newUrl.pathname = resolvedUrlPathname;
|
|
39
|
-
const decodedPathname =
|
|
40
|
+
const decodedPathname = validateAndDecodePathname(pathname);
|
|
40
41
|
if (isRoute404(decodedPathname)) {
|
|
41
42
|
const errorRoute = routes.find((route) => route.route === "/404");
|
|
42
43
|
if (errorRoute) {
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* decoded iteratively instead of rejected. Kept for backwards compatibility
|
|
8
|
-
* in case third-party code references the class.
|
|
2
|
+
* Thrown when a URL path is encoded so many times that we give up decoding it
|
|
3
|
+
* (see {@link validateAndDecodePathname}). When this happens we reject the
|
|
4
|
+
* request with a `400` instead of guessing the path. If we let a half-decoded
|
|
5
|
+
* path through, your middleware might check one path while Astro routes to a
|
|
6
|
+
* different one.
|
|
9
7
|
*/
|
|
10
8
|
export declare class MultiLevelEncodingError extends Error {
|
|
11
9
|
constructor();
|
|
12
10
|
}
|
|
13
11
|
/**
|
|
14
|
-
* Decodes a
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* fully resolve it so middleware always sees the true decoded path.
|
|
12
|
+
* Decodes a URL path over and over until it stops changing, so a path that was
|
|
13
|
+
* encoded several times ends up as a single, final path. This stops someone
|
|
14
|
+
* from sneaking a path like `/admin` past middleware by encoding it multiple
|
|
15
|
+
* times — middleware always sees the real, decoded path.
|
|
19
16
|
*
|
|
20
|
-
* @param pathname - The
|
|
21
|
-
* @returns The fully decoded
|
|
22
|
-
* @throws Error if the
|
|
23
|
-
*
|
|
17
|
+
* @param pathname - The path to decode
|
|
18
|
+
* @returns The final, fully decoded path
|
|
19
|
+
* @throws Error if the path has broken encoding that can't be decoded at all
|
|
20
|
+
* (for example a lone `%` that isn't followed by two hex digits)
|
|
21
|
+
* @throws MultiLevelEncodingError if the path is still changing after
|
|
22
|
+
* {@link MAX_DECODE_ITERATIONS} tries (it was encoded too many times).
|
|
23
|
+
* Handing back a half-decoded path here would bring back the security hole
|
|
24
|
+
* this function exists to close.
|
|
24
25
|
*/
|
|
25
26
|
export declare function validateAndDecodePathname(pathname: string): string;
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
class MultiLevelEncodingError extends Error {
|
|
2
2
|
constructor() {
|
|
3
|
-
super("
|
|
3
|
+
super("URL encoding depth exceeded the maximum number of decode iterations");
|
|
4
4
|
this.name = "MultiLevelEncodingError";
|
|
5
5
|
}
|
|
6
6
|
}
|
|
7
|
+
const MAX_DECODE_ITERATIONS = 10;
|
|
7
8
|
function validateAndDecodePathname(pathname) {
|
|
8
9
|
let decoded;
|
|
9
10
|
try {
|
|
@@ -12,7 +13,10 @@ function validateAndDecodePathname(pathname) {
|
|
|
12
13
|
throw new Error("Invalid URL encoding");
|
|
13
14
|
}
|
|
14
15
|
let iterations = 0;
|
|
15
|
-
while (decoded !== pathname
|
|
16
|
+
while (decoded !== pathname) {
|
|
17
|
+
if (iterations >= MAX_DECODE_ITERATIONS) {
|
|
18
|
+
throw new MultiLevelEncodingError();
|
|
19
|
+
}
|
|
16
20
|
pathname = decoded;
|
|
17
21
|
try {
|
|
18
22
|
decoded = decodeURI(pathname);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "astro",
|
|
3
|
-
"version": "6.4.
|
|
3
|
+
"version": "6.4.8",
|
|
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",
|
|
@@ -166,8 +166,8 @@
|
|
|
166
166
|
"yargs-parser": "^22.0.0",
|
|
167
167
|
"zod": "^4.3.6",
|
|
168
168
|
"@astrojs/internal-helpers": "0.10.0",
|
|
169
|
-
"@astrojs/
|
|
170
|
-
"@astrojs/
|
|
169
|
+
"@astrojs/telemetry": "3.3.2",
|
|
170
|
+
"@astrojs/markdown-remark": "7.2.0"
|
|
171
171
|
},
|
|
172
172
|
"optionalDependencies": {
|
|
173
173
|
"sharp": "^0.34.0"
|