Complete residential site build with Docker Compose & Gitea Actions deployment
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Creates a normalized URL from a request URL string.
|
||||
* Decodes and validates the pathname, collapses duplicate slashes.
|
||||
*/
|
||||
export declare function createNormalizedUrl(requestUrl: string): URL;
|
||||
/**
|
||||
* Normalizes an already-parsed URL in place: decodes and validates the
|
||||
* pathname, collapses duplicate slashes. Returns the same URL object.
|
||||
*/
|
||||
export declare function normalizeUrl(url: URL): URL;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { collapseDuplicateSlashes } from "@astrojs/internal-helpers/path";
|
||||
import { MultiLevelEncodingError, validateAndDecodePathname } from "./pathname.js";
|
||||
function createNormalizedUrl(requestUrl) {
|
||||
return normalizeUrl(new URL(requestUrl));
|
||||
}
|
||||
function normalizeUrl(url) {
|
||||
try {
|
||||
url.pathname = validateAndDecodePathname(url.pathname);
|
||||
} catch (e) {
|
||||
if (e instanceof MultiLevelEncodingError) {
|
||||
throw e;
|
||||
}
|
||||
try {
|
||||
url.pathname = decodeURI(url.pathname);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
url.pathname = collapseDuplicateSlashes(url.pathname);
|
||||
return url;
|
||||
}
|
||||
export {
|
||||
createNormalizedUrl,
|
||||
normalizeUrl
|
||||
};
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Error thrown when multi-level URL encoding is detected in a pathname.
|
||||
* This is a distinct error type so callers can handle it specifically
|
||||
* (e.g., returning a 400 response) rather than falling back to partial decoding.
|
||||
*/
|
||||
export declare class MultiLevelEncodingError extends Error {
|
||||
constructor();
|
||||
}
|
||||
/**
|
||||
* Validates that a pathname is not multi-level encoded.
|
||||
* Detects if a pathname contains encoding that was encoded again (e.g., %2561dmin where %25 decodes to %).
|
||||
* This prevents double/triple encoding bypasses of security checks.
|
||||
*
|
||||
* @param pathname - The pathname to validate
|
||||
* @returns The decoded pathname if valid
|
||||
* @throws MultiLevelEncodingError if multi-level encoding is detected
|
||||
* @throws Error if the pathname contains invalid URL encoding
|
||||
*/
|
||||
export declare function validateAndDecodePathname(pathname: string): string;
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
class MultiLevelEncodingError extends Error {
|
||||
constructor() {
|
||||
super("Multi-level URL encoding is not allowed");
|
||||
this.name = "MultiLevelEncodingError";
|
||||
}
|
||||
}
|
||||
function validateAndDecodePathname(pathname) {
|
||||
let decoded;
|
||||
try {
|
||||
decoded = decodeURI(pathname);
|
||||
} catch (_e) {
|
||||
throw new Error("Invalid URL encoding");
|
||||
}
|
||||
const hasDecoding = decoded !== pathname;
|
||||
const decodedStillHasEncoding = /%[0-9a-fA-F]{2}/.test(decoded);
|
||||
if (hasDecoding && decodedStillHasEncoding) {
|
||||
throw new MultiLevelEncodingError();
|
||||
}
|
||||
return decoded;
|
||||
}
|
||||
export {
|
||||
MultiLevelEncodingError,
|
||||
validateAndDecodePathname
|
||||
};
|
||||
Reference in New Issue
Block a user